]> git.lizzy.rs Git - rust.git/blobdiff - src/librustc_typeck/check/mod.rs
Auto merge of #43737 - GuillaumeGomez:duplicate-method, r=eddyb
[rust.git] / src / librustc_typeck / check / mod.rs
index 17aae7dc04f2ae0dbc295ddebb88d1c2ddac944c..b9a9a954006572b7ea2433582460ad3ffa0e8f0d 100644 (file)
@@ -2960,6 +2960,12 @@ fn check_field(&self,
                                                format!("did you mean `{}`?", suggested_field_name));
                             } else {
                                 err.span_label(field.span, "unknown field");
+                                let struct_variant_def = def.struct_variant();
+                                let field_names = self.available_field_names(struct_variant_def);
+                                if !field_names.is_empty() {
+                                    err.note(&format!("available fields are: {}",
+                                                      self.name_series_display(field_names)));
+                                }
                             };
                     }
                     ty::TyRawPtr(..) => {
@@ -2983,7 +2989,7 @@ fn check_field(&self,
     // Return an hint about the closest match in field names
     fn suggest_field_name(variant: &'tcx ty::VariantDef,
                           field: &Spanned<ast::Name>,
-                          skip : Vec<InternedString>)
+                          skip: Vec<InternedString>)
                           -> Option<Symbol> {
         let name = field.node.as_str();
         let names = variant.fields.iter().filter_map(|field| {
@@ -2996,8 +3002,29 @@ fn suggest_field_name(variant: &'tcx ty::VariantDef,
             }
         });
 
-        // only find fits with at least one matching letter
-        find_best_match_for_name(names, &name, Some(name.len()))
+        find_best_match_for_name(names, &name, None)
+    }
+
+    fn available_field_names(&self, variant: &'tcx ty::VariantDef) -> Vec<ast::Name> {
+        let mut available = Vec::new();
+        for field in variant.fields.iter() {
+            let (_, def_scope) = self.tcx.adjust(field.name, variant.did, self.body_id);
+            if field.vis.is_accessible_from(def_scope, self.tcx) {
+                available.push(field.name);
+            }
+        }
+        available
+    }
+
+    fn name_series_display(&self, names: Vec<ast::Name>) -> String {
+        // dynamic limit, to never omit just one field
+        let limit = if names.len() == 6 { 6 } else { 5 };
+        let mut display = names.iter().take(limit)
+            .map(|n| format!("`{}`", n)).collect::<Vec<_>>().join(", ");
+        if names.len() > limit {
+            display = format!("{} ... and {} others", display, names.len() - limit);
+        }
+        display
     }
 
     // Check tuple index expressions
@@ -3111,13 +3138,22 @@ fn report_unknown_field(&self,
                            format!("field does not exist - did you mean `{}`?", field_name));
         } else {
             match ty.sty {
-                ty::TyAdt(adt, ..) if adt.is_enum() => {
-                    err.span_label(field.name.span, format!("`{}::{}` does not have this field",
-                                                             ty, variant.name));
-                }
-                _ => {
-                    err.span_label(field.name.span, format!("`{}` does not have this field", ty));
+                ty::TyAdt(adt, ..) => {
+                    if adt.is_enum() {
+                        err.span_label(field.name.span,
+                                       format!("`{}::{}` does not have this field",
+                                               ty, variant.name));
+                    } else {
+                        err.span_label(field.name.span,
+                                       format!("`{}` does not have this field", ty));
+                    }
+                    let available_field_names = self.available_field_names(variant);
+                    if !available_field_names.is_empty() {
+                        err.note(&format!("available fields are: {}",
+                                          self.name_series_display(available_field_names)));
+                    }
                 }
+                _ => bug!("non-ADT passed to report_unknown_field")
             }
         };
         err.emit();
@@ -3980,7 +4016,7 @@ pub fn resolve_ty_and_def_ufcs<'b>(&self,
             Ok(def) => def,
             Err(error) => {
                 let def = match error {
-                    method::MethodError::PrivateMatch(def) => def,
+                    method::MethodError::PrivateMatch(def, _) => def,
                     _ => Def::Err,
                 };
                 if item_name != keywords::Invalid.name() {
@@ -4193,8 +4229,8 @@ fn check_block_with_expected(&self,
         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.
@@ -4205,14 +4241,23 @@ pub fn get_fn_decl(&self, blk_id: ast::NodeId) -> Option<(hir::FnDecl, bool)> {
                 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))
@@ -4239,11 +4284,8 @@ pub fn suggest_mismatched_types_on_tail(&self,
                                             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);
         }
     }
 
@@ -4299,20 +4341,37 @@ fn suggest_missing_semicolon(&self,
     fn suggest_missing_return_type(&self,
                                    err: &mut DiagnosticBuilder<'tcx>,
                                    fn_decl: &hir::FnDecl,
-                                   ty: Ty<'tcx>) {
-
-        // Only recommend changing the return type for methods that
+                                   expected: Ty<'tcx>,
+                                   found: Ty<'tcx>,
+                                   can_suggest: bool) {
+        // 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.
+                debug!("suggest_missing_return_type: return type {:?} node {:?}", ty, ty.node);
+                let sp = ty.span;
+                let ty = AstConv::ast_ty_to_ty(self, ty);
+                debug!("suggest_missing_return_type: return type sty {:?}", ty.sty);
+                debug!("suggest_missing_return_type: expected type sty {:?}", ty.sty);
+                if ty.sty == expected.sty {
+                    err.span_label(sp, format!("expected `{}` because of return type",
+                                               expected));
+                }
+            }
         }
     }