From 5598f351627bf1c235e7b7d06bd60ad0bad537cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sat, 24 Dec 2016 17:44:41 -0800 Subject: [PATCH 1/5] Suggest solutions for `fn foo(&foo: Foo)` --- src/librustc_typeck/check/_match.rs | 21 ++++++++++++++- src/librustc_typeck/check/demand.rs | 18 ++++++++++--- src/librustc_typeck/check/mod.rs | 2 +- src/librustc_typeck/check/wfcheck.rs | 4 ++- src/test/ui/mismatched_types/issue-38371.rs | 26 +++++++++++++++++++ .../ui/mismatched_types/issue-38371.stderr | 13 ++++++++++ 6 files changed, 77 insertions(+), 7 deletions(-) create mode 100644 src/test/ui/mismatched_types/issue-38371.rs create mode 100644 src/test/ui/mismatched_types/issue-38371.stderr diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index 9b86196b3ece2..fcd61b3c7c8c9 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -27,6 +27,10 @@ use syntax_pos::Span; impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { pub fn check_pat(&self, pat: &'gcx hir::Pat, expected: Ty<'tcx>) { + self.check_pat_arg(pat, expected, false); + } + + pub fn check_pat_arg(&self, pat: &'gcx hir::Pat, expected: Ty<'tcx>, is_arg: bool) { let tcx = self.tcx; debug!("check_pat(pat={:?},expected={:?})", pat, expected); @@ -212,7 +216,22 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { let mt = ty::TypeAndMut { ty: inner_ty, mutbl: mutbl }; let region = self.next_region_var(infer::PatternRegion(pat.span)); let rptr_ty = tcx.mk_ref(region, mt); - self.demand_eqtype(pat.span, expected, rptr_ty); + let err = self.demand_eqtype_diag(pat.span, expected, rptr_ty); + if let Some(mut err) = err { + if is_arg { + if let Ok(snippet) = self.sess().codemap() + .span_to_snippet(pat.span) + { + err.help(&format!("did you mean `{}: &{}`?", + snippet, + expected)); + err.help(&format!("did you mean `{}: {}`?", + &snippet[1..], + expected)); + } + } + err.emit(); + } (rptr_ty, inner_ty) } }; diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs index 393d9341a0843..8838eb9b1b3ff 100644 --- a/src/librustc_typeck/check/demand.rs +++ b/src/librustc_typeck/check/demand.rs @@ -19,6 +19,7 @@ use syntax_pos::{self, Span}; use rustc::hir; use rustc::hir::def::Def; use rustc::ty::{self, AssociatedItem}; +use errors::DiagnosticBuilder; use super::method::probe; @@ -38,20 +39,29 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } pub fn demand_eqtype(&self, sp: Span, expected: Ty<'tcx>, actual: Ty<'tcx>) { - self.demand_eqtype_with_origin(&self.misc(sp), expected, actual); + if let Some(mut err) = self.demand_eqtype_diag(sp, expected, actual) { + err.emit(); + } + } + + pub fn demand_eqtype_diag(&self, + sp: Span, + expected: Ty<'tcx>, + actual: Ty<'tcx>) -> Option> { + self.demand_eqtype_with_origin(&self.misc(sp), expected, actual) } pub fn demand_eqtype_with_origin(&self, cause: &ObligationCause<'tcx>, expected: Ty<'tcx>, - actual: Ty<'tcx>) - { + actual: Ty<'tcx>) -> Option> { match self.eq_types(false, cause, actual, expected) { Ok(InferOk { obligations, value: () }) => { self.register_predicates(obligations); + None }, Err(e) => { - self.report_mismatched_types(cause, expected, actual, e).emit(); + Some(self.report_mismatched_types(cause, expected, actual, e)) } } } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 7275fbd12036b..b89654138dd13 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -798,7 +798,7 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>, fcx.register_old_wf_obligation(arg_ty, arg.pat.span, traits::MiscObligation); // Check the pattern. - fcx.check_pat(&arg.pat, arg_ty); + fcx.check_pat_arg(&arg.pat, arg_ty, true); fcx.write_ty(arg.id, arg_ty); } diff --git a/src/librustc_typeck/check/wfcheck.rs b/src/librustc_typeck/check/wfcheck.rs index c80db7fa4d0e0..4c124cdd60c0f 100644 --- a/src/librustc_typeck/check/wfcheck.rs +++ b/src/librustc_typeck/check/wfcheck.rs @@ -505,7 +505,9 @@ impl<'ccx, 'gcx> CheckTypeWellFormedVisitor<'ccx, 'gcx> { debug!("check_method_receiver: receiver ty = {:?}", rcvr_ty); let cause = fcx.cause(span, ObligationCauseCode::MethodReceiver); - fcx.demand_eqtype_with_origin(&cause, rcvr_ty, self_arg_ty); + if let Some(mut err) = fcx.demand_eqtype_with_origin(&cause, rcvr_ty, self_arg_ty) { + err.emit(); + } } fn check_variances_for_type_defn(&self, diff --git a/src/test/ui/mismatched_types/issue-38371.rs b/src/test/ui/mismatched_types/issue-38371.rs new file mode 100644 index 0000000000000..20998447737d2 --- /dev/null +++ b/src/test/ui/mismatched_types/issue-38371.rs @@ -0,0 +1,26 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +struct Foo { +} + +fn foo(&foo: Foo) { // illegal syntax +} + +fn bar(foo: Foo) { // legal +} + +fn qux(foo: &Foo) { // legal +} + +fn zar(&foo: &Foo) { // legal +} + +fn main() {} diff --git a/src/test/ui/mismatched_types/issue-38371.stderr b/src/test/ui/mismatched_types/issue-38371.stderr new file mode 100644 index 0000000000000..8b27218f497d1 --- /dev/null +++ b/src/test/ui/mismatched_types/issue-38371.stderr @@ -0,0 +1,13 @@ +error[E0308]: mismatched types + --> $DIR/issue-38371.rs:14:8 + | +14 | fn foo(&foo: Foo) { // illegal syntax + | ^^^^ expected struct `Foo`, found reference + | + = note: expected type `Foo` + = note: found type `&_` + = help: did you mean `&foo: &Foo`? + = help: did you mean `foo: Foo`? + +error: aborting due to previous error + From f2dd75cb865524ca558630a2e785ca55d6d287ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 4 Jan 2017 13:47:04 -0800 Subject: [PATCH 2/5] review comments --- src/librustc_typeck/check/_match.rs | 3 --- src/test/ui/mismatched_types/issue-38371.stderr | 3 +-- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index fcd61b3c7c8c9..f05c9a00be2e7 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -223,9 +223,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { .span_to_snippet(pat.span) { err.help(&format!("did you mean `{}: &{}`?", - snippet, - expected)); - err.help(&format!("did you mean `{}: {}`?", &snippet[1..], expected)); } diff --git a/src/test/ui/mismatched_types/issue-38371.stderr b/src/test/ui/mismatched_types/issue-38371.stderr index 8b27218f497d1..e0d80f890d912 100644 --- a/src/test/ui/mismatched_types/issue-38371.stderr +++ b/src/test/ui/mismatched_types/issue-38371.stderr @@ -6,8 +6,7 @@ error[E0308]: mismatched types | = note: expected type `Foo` = note: found type `&_` - = help: did you mean `&foo: &Foo`? - = help: did you mean `foo: Foo`? + = help: did you mean `foo: &Foo`? error: aborting due to previous error From 563ecc1b1fe8f8daea20910a23b7090411cf2de8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 5 Jan 2017 17:40:26 -0800 Subject: [PATCH 3/5] add test case --- src/test/ui/mismatched_types/issue-38371.rs | 18 +++++++++++----- .../ui/mismatched_types/issue-38371.stderr | 21 ++++++++++++++++--- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/src/test/ui/mismatched_types/issue-38371.rs b/src/test/ui/mismatched_types/issue-38371.rs index 20998447737d2..36191ebc54037 100644 --- a/src/test/ui/mismatched_types/issue-38371.rs +++ b/src/test/ui/mismatched_types/issue-38371.rs @@ -1,4 +1,4 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -7,20 +7,28 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(slice_patterns)] + struct Foo { } -fn foo(&foo: Foo) { // illegal syntax +fn foo(&foo: Foo) { +} + +fn bar(foo: Foo) { +} + +fn qux(foo: &Foo) { } -fn bar(foo: Foo) { // legal +fn zar(&foo: &Foo) { } -fn qux(foo: &Foo) { // legal +fn agh(&&bar: &u32) { } -fn zar(&foo: &Foo) { // legal +fn ugh(&[bar]: &u32) { } fn main() {} diff --git a/src/test/ui/mismatched_types/issue-38371.stderr b/src/test/ui/mismatched_types/issue-38371.stderr index e0d80f890d912..5892b892fa7b1 100644 --- a/src/test/ui/mismatched_types/issue-38371.stderr +++ b/src/test/ui/mismatched_types/issue-38371.stderr @@ -1,12 +1,27 @@ error[E0308]: mismatched types - --> $DIR/issue-38371.rs:14:8 + --> $DIR/issue-38371.rs:16:8 | -14 | fn foo(&foo: Foo) { // illegal syntax +16 | fn foo(&foo: Foo) { | ^^^^ expected struct `Foo`, found reference | = note: expected type `Foo` = note: found type `&_` = help: did you mean `foo: &Foo`? -error: aborting due to previous error +error[E0308]: mismatched types + --> $DIR/issue-38371.rs:28:9 + | +28 | fn agh(&&bar: &u32) { + | ^^^^ expected u32, found reference + | + = note: expected type `u32` + = note: found type `&_` + +error[E0529]: expected an array or slice, found `u32` + --> $DIR/issue-38371.rs:31:9 + | +31 | fn ugh(&[bar]: &u32) { + | ^^^^^ pattern cannot match with input type `u32` + +error: aborting due to 3 previous errors From 59d7d4c80ef94ec5b001ffe1a8bac40694c50c3f Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 10 Jan 2017 14:45:30 -0500 Subject: [PATCH 4/5] only emit suggestion to move `&` if the inner pattern is a binding --- src/librustc_typeck/check/_match.rs | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index f05c9a00be2e7..f7fd78097b80b 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -30,10 +30,14 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { self.check_pat_arg(pat, expected, false); } + /// The `is_arg` argument indicates whether this pattern is the + /// *outermost* pattern in an argument (e.g., in `fn foo(&x: + /// &u32)`, it is true for the `&x` pattern but not `x`). This is + /// used to tailor error reporting. pub fn check_pat_arg(&self, pat: &'gcx hir::Pat, expected: Ty<'tcx>, is_arg: bool) { let tcx = self.tcx; - debug!("check_pat(pat={:?},expected={:?})", pat, expected); + debug!("check_pat(pat={:?},expected={:?},is_arg={})", pat, expected, is_arg); let ty = match pat.node { PatKind::Wild => { @@ -206,6 +210,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // can, to avoid creating needless variables. This // also helps with the bad interactions of the given // hack detailed in (*) below. + debug!("check_pat_arg: expected={:?}", expected); let (rptr_ty, inner_ty) = match expected.sty { ty::TyRef(_, mt) if mt.mutbl == mutbl => { (expected, mt.ty) @@ -216,15 +221,21 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { let mt = ty::TypeAndMut { ty: inner_ty, mutbl: mutbl }; let region = self.next_region_var(infer::PatternRegion(pat.span)); let rptr_ty = tcx.mk_ref(region, mt); + debug!("check_pat_arg: demanding {:?} = {:?}", expected, rptr_ty); let err = self.demand_eqtype_diag(pat.span, expected, rptr_ty); + + // Look for a case like `fn foo(&foo: u32)` and suggest + // `fn foo(foo: &u32)` if let Some(mut err) = err { if is_arg { - if let Ok(snippet) = self.sess().codemap() - .span_to_snippet(pat.span) - { - err.help(&format!("did you mean `{}: &{}`?", - &snippet[1..], - expected)); + if let PatKind::Binding(..) = inner.node { + if let Ok(snippet) = self.sess().codemap() + .span_to_snippet(pat.span) + { + err.help(&format!("did you mean `{}: &{}`?", + &snippet[1..], + expected)); + } } } err.emit(); From d723e02dfc94e8ab2e8d3cb7930febeed0a7651c Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 10 Jan 2017 15:13:21 -0500 Subject: [PATCH 5/5] update test case to include `fn(&&u32: Foo)` --- src/test/ui/mismatched_types/issue-38371.rs | 3 +++ src/test/ui/mismatched_types/issue-38371.stderr | 15 ++++++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/test/ui/mismatched_types/issue-38371.rs b/src/test/ui/mismatched_types/issue-38371.rs index 36191ebc54037..cf66330017f58 100644 --- a/src/test/ui/mismatched_types/issue-38371.rs +++ b/src/test/ui/mismatched_types/issue-38371.rs @@ -28,6 +28,9 @@ fn zar(&foo: &Foo) { fn agh(&&bar: &u32) { } +fn bgh(&&bar: u32) { +} + fn ugh(&[bar]: &u32) { } diff --git a/src/test/ui/mismatched_types/issue-38371.stderr b/src/test/ui/mismatched_types/issue-38371.stderr index 5892b892fa7b1..b0e56094fcff3 100644 --- a/src/test/ui/mismatched_types/issue-38371.stderr +++ b/src/test/ui/mismatched_types/issue-38371.stderr @@ -17,11 +17,20 @@ error[E0308]: mismatched types = note: expected type `u32` = note: found type `&_` +error[E0308]: mismatched types + --> $DIR/issue-38371.rs:31:8 + | +31 | fn bgh(&&bar: u32) { + | ^^^^^ expected u32, found reference + | + = note: expected type `u32` + = note: found type `&_` + error[E0529]: expected an array or slice, found `u32` - --> $DIR/issue-38371.rs:31:9 + --> $DIR/issue-38371.rs:34:9 | -31 | fn ugh(&[bar]: &u32) { +34 | fn ugh(&[bar]: &u32) { | ^^^^^ pattern cannot match with input type `u32` -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors