From d96f9d47ac35b637906edb1d5d112549164e096c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 25 Jul 2017 22:01:31 -0700 Subject: [PATCH] Point at return type always when type mismatch against it Before this, the diagnostic errors would only point at the return type when changing it would be a possible solution to a type error. Add a label to the return type without a suggestion to change in order to make the source of the expected type obvious. Follow up to #42850, fixes #25133, fixes #41897. --- src/librustc/hir/map/mod.rs | 2 +- src/librustc/ty/mod.rs | 1 + src/librustc_typeck/check/mod.rs | 53 ++++++++++++------- .../struct-path-self-type-mismatch.rs | 12 ++--- .../block-must-not-have-result-res.stderr | 2 + src/test/ui/block-result/issue-13624.stderr | 2 + src/test/ui/block-result/issue-22645.stderr | 3 ++ src/test/ui/block-result/issue-5500.stderr | 2 + src/test/ui/impl-trait/equality.stderr | 3 ++ src/test/ui/mismatched_types/abridged.stderr | 14 +++++ 10 files changed, 68 insertions(+), 26 deletions(-) diff --git a/src/librustc/hir/map/mod.rs b/src/librustc/hir/map/mod.rs index 3fdd9c34f46d9..5cec697c6bbd9 100644 --- a/src/librustc/hir/map/mod.rs +++ b/src/librustc/hir/map/mod.rs @@ -664,7 +664,7 @@ impl<'hir> Map<'hir> { match *node { NodeExpr(ref expr) => { match expr.node { - ExprWhile(..) | ExprLoop(..) => true, + ExprWhile(..) | ExprLoop(..) | ExprIf(..) => true, _ => false, } } diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 2a11b19f85561..0f152d999df59 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -488,6 +488,7 @@ impl<'tcx> TyS<'tcx> { TypeVariants::TyFnPtr(..) | TypeVariants::TyDynamic(..) | TypeVariants::TyClosure(..) | + TypeVariants::TyInfer(..) | TypeVariants::TyProjection(..) => false, _ => true, } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 3c570d85147fa..73aa5ac5a0f0a 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -4187,8 +4187,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { ty } - /// Given a `NodeId`, return the `FnDecl` of the method it is enclosed by and whether it is - /// `fn main` if it is a method, `None` otherwise. + /// Given a `NodeId`, return the `FnDecl` of the method it is enclosed by and whether a + /// suggetion can be made, `None` otherwise. pub fn get_fn_decl(&self, blk_id: ast::NodeId) -> Option<(hir::FnDecl, bool)> { // Get enclosing Fn, if it is a function or a trait method, unless there's a `loop` or // `while` before reaching it, as block tail returns are not available in them. @@ -4199,14 +4199,23 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { name, node: hir::ItemFn(ref decl, ..), .. }) = parent { decl.clone().and_then(|decl| { - // This is less than ideal, it will not present the return type span on any - // method called `main`, regardless of whether it is actually the entry point. - Some((decl, name == Symbol::intern("main"))) + // This is less than ideal, it will not suggest a return type span on any + // method called `main`, regardless of whether it is actually the entry point, + // but it will still present it as the reason for the expected type. + Some((decl, name != Symbol::intern("main"))) }) } else if let Node::NodeTraitItem(&hir::TraitItem { node: hir::TraitItemKind::Method(hir::MethodSig { ref decl, .. }, ..), .. + }) = parent { + decl.clone().and_then(|decl| { + Some((decl, true)) + }) + } else if let Node::NodeImplItem(&hir::ImplItem { + node: hir::ImplItemKind::Method(hir::MethodSig { + ref decl, .. + }, ..), .. }) = parent { decl.clone().and_then(|decl| { Some((decl, false)) @@ -4233,11 +4242,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { blk_id: ast::NodeId) { self.suggest_missing_semicolon(err, expression, expected, cause_span); - if let Some((fn_decl, is_main)) = self.get_fn_decl(blk_id) { - // `fn main()` must return `()`, do not suggest changing return type - if !is_main { - self.suggest_missing_return_type(err, &fn_decl, found); - } + if let Some((fn_decl, can_suggest)) = self.get_fn_decl(blk_id) { + self.suggest_missing_return_type(err, &fn_decl, expected, found, can_suggest); } } @@ -4293,20 +4299,31 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { fn suggest_missing_return_type(&self, err: &mut DiagnosticBuilder<'tcx>, fn_decl: &hir::FnDecl, - ty: Ty<'tcx>) { + expected: Ty<'tcx>, + found: Ty<'tcx>, + can_suggest: bool) { - // Only recommend changing the return type for methods that + // Only suggest changing the return type for methods that // haven't set a return type at all (and aren't `fn main()` or an impl). - if let &hir::FnDecl { - output: hir::FunctionRetTy::DefaultReturn(span), .. - } = fn_decl { - if ty.is_suggestable() { + match (&fn_decl.output, found.is_suggestable(), can_suggest) { + (&hir::FunctionRetTy::DefaultReturn(span), true, true) => { err.span_suggestion(span, "try adding a return type", - format!("-> {} ", ty)); - } else { + format!("-> {} ", found)); + } + (&hir::FunctionRetTy::DefaultReturn(span), false, true) => { err.span_label(span, "possibly return type missing here?"); } + (&hir::FunctionRetTy::DefaultReturn(span), _, _) => { + // `fn main()` must return `()`, do not suggest changing return type + err.span_label(span, "expected `()` because of default return type"); + } + (&hir::FunctionRetTy::Return(ref ty), _, _) => { + // Only point to return type if the expected type is the return type, as if they + // are not, the expectation must have been caused by something else. + err.span_label(ty.span, + format!("expected `{}` because of return type", expected)); + } } } diff --git a/src/test/compile-fail/struct-path-self-type-mismatch.rs b/src/test/compile-fail/struct-path-self-type-mismatch.rs index f694e7d277c7f..ad568b41fcbf3 100644 --- a/src/test/compile-fail/struct-path-self-type-mismatch.rs +++ b/src/test/compile-fail/struct-path-self-type-mismatch.rs @@ -20,17 +20,15 @@ impl Bar for Foo { } impl Foo { - fn new(u: U) -> Foo { + fn new(u: U) -> Foo { //~ NOTE expected `Foo` because of return type Self { //~^ ERROR mismatched types - //~| expected type parameter, found a different type parameter - //~| expected type `Foo` - //~| found type `Foo` + //~| NOTE expected type parameter, found a different type parameter + //~| NOTE expected type `Foo` inner: u //~^ ERROR mismatched types - //~| expected type parameter, found a different type parameter - //~| expected type `T` - //~| found type `U` + //~| NOTE expected type parameter, found a different type parameter + //~| NOTE expected type `T` } } } diff --git a/src/test/ui/block-result/block-must-not-have-result-res.stderr b/src/test/ui/block-result/block-must-not-have-result-res.stderr index f60a0c2e5f6e0..20c7dc416f3bb 100644 --- a/src/test/ui/block-result/block-must-not-have-result-res.stderr +++ b/src/test/ui/block-result/block-must-not-have-result-res.stderr @@ -1,6 +1,8 @@ error[E0308]: mismatched types --> $DIR/block-must-not-have-result-res.rs:15:9 | +14 | fn drop(&mut self) { + | - expected `()` because of default return type 15 | true //~ ERROR mismatched types | ^^^^ expected (), found bool | diff --git a/src/test/ui/block-result/issue-13624.stderr b/src/test/ui/block-result/issue-13624.stderr index 41113eb7a573e..cd8c28cd2cfa8 100644 --- a/src/test/ui/block-result/issue-13624.stderr +++ b/src/test/ui/block-result/issue-13624.stderr @@ -1,6 +1,8 @@ error[E0308]: mismatched types --> $DIR/issue-13624.rs:17:5 | +16 | pub fn get_enum_struct_variant() -> () { + | -- expected `()` because of return type 17 | Enum::EnumStructVariant { x: 1, y: 2, z: 3 } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected (), found enum `a::Enum` | diff --git a/src/test/ui/block-result/issue-22645.stderr b/src/test/ui/block-result/issue-22645.stderr index a9bcc8bea94cc..c6113ae0c9f60 100644 --- a/src/test/ui/block-result/issue-22645.stderr +++ b/src/test/ui/block-result/issue-22645.stderr @@ -11,6 +11,9 @@ error[E0277]: the trait bound `{integer}: Scalar` is not satisfied error[E0308]: mismatched types --> $DIR/issue-22645.rs:25:3 | +23 | fn main() { + | - expected `()` because of default return type +24 | let b = Bob + 3.5; 25 | b + 3 //~ ERROR E0277 | ^^^^^ expected (), found struct `Bob` | diff --git a/src/test/ui/block-result/issue-5500.stderr b/src/test/ui/block-result/issue-5500.stderr index bd670a14f247e..29dbd5a8cf599 100644 --- a/src/test/ui/block-result/issue-5500.stderr +++ b/src/test/ui/block-result/issue-5500.stderr @@ -1,6 +1,8 @@ error[E0308]: mismatched types --> $DIR/issue-5500.rs:12:5 | +11 | fn main() { + | - expected `()` because of default return type 12 | &panic!() | ^^^^^^^^^ expected (), found reference | diff --git a/src/test/ui/impl-trait/equality.stderr b/src/test/ui/impl-trait/equality.stderr index 3fc08a0900fb9..791ce4860d0cd 100644 --- a/src/test/ui/impl-trait/equality.stderr +++ b/src/test/ui/impl-trait/equality.stderr @@ -1,6 +1,9 @@ error[E0308]: mismatched types --> $DIR/equality.rs:25:5 | +21 | fn two(x: bool) -> impl Foo { + | -------- expected `_` because of return type +... 25 | 0_u32 | ^^^^^ expected i32, found u32 | diff --git a/src/test/ui/mismatched_types/abridged.stderr b/src/test/ui/mismatched_types/abridged.stderr index d40bc3b333903..8c63d7d6f91c5 100644 --- a/src/test/ui/mismatched_types/abridged.stderr +++ b/src/test/ui/mismatched_types/abridged.stderr @@ -1,6 +1,8 @@ error[E0308]: mismatched types --> $DIR/abridged.rs:26:5 | +25 | fn a() -> Foo { + | --- expected `Foo` because of return type 26 | Some(Foo { bar: 1 }) | ^^^^^^^^^^^^^^^^^^^^ expected struct `Foo`, found enum `std::option::Option` | @@ -10,6 +12,8 @@ error[E0308]: mismatched types error[E0308]: mismatched types --> $DIR/abridged.rs:30:5 | +29 | fn a2() -> Foo { + | --- expected `Foo` because of return type 30 | Ok(Foo { bar: 1}) | ^^^^^^^^^^^^^^^^^ expected struct `Foo`, found enum `std::result::Result` | @@ -19,6 +23,8 @@ error[E0308]: mismatched types error[E0308]: mismatched types --> $DIR/abridged.rs:34:5 | +33 | fn b() -> Option { + | ----------- expected `std::option::Option` because of return type 34 | Foo { bar: 1 } | ^^^^^^^^^^^^^^ expected enum `std::option::Option`, found struct `Foo` | @@ -28,6 +34,8 @@ error[E0308]: mismatched types error[E0308]: mismatched types --> $DIR/abridged.rs:38:5 | +37 | fn c() -> Result { + | ---------------- expected `std::result::Result` because of return type 38 | Foo { bar: 1 } | ^^^^^^^^^^^^^^ expected enum `std::result::Result`, found struct `Foo` | @@ -37,6 +45,9 @@ error[E0308]: mismatched types error[E0308]: mismatched types --> $DIR/abridged.rs:49:5 | +41 | fn d() -> X, String> { + | ---------------------------- expected `X, std::string::String>` because of return type +... 49 | x | ^ expected struct `std::string::String`, found integral variable | @@ -46,6 +57,9 @@ error[E0308]: mismatched types error[E0308]: mismatched types --> $DIR/abridged.rs:60:5 | +52 | fn e() -> X, String> { + | ---------------------------- expected `X, std::string::String>` because of return type +... 60 | x | ^ expected struct `std::string::String`, found integral variable |