]> git.lizzy.rs Git - rust.git/commitdiff
Use the appropriate number of type arguments in suggestion
authorEsteban Küber <esteban@kuber.com.ar>
Tue, 10 Dec 2019 17:01:06 +0000 (09:01 -0800)
committerEsteban Küber <esteban@kuber.com.ar>
Tue, 10 Dec 2019 20:02:18 +0000 (12:02 -0800)
src/librustc/infer/error_reporting/need_type_info.rs
src/librustc/ty/context.rs
src/librustc_typeck/check/expr.rs
src/test/ui/issues/issue-65611.stderr
src/test/ui/question-mark-type-infer.stderr
src/test/ui/span/type-annotations-needed-expr.stderr

index 98134915b45d9a34d1263ec8cb638c0d909fa823..c5133e8843225396983438e5c69b8f5a700380fa 100644 (file)
@@ -20,7 +20,7 @@ struct FindLocalByTypeVisitor<'a, 'tcx> {
     found_arg_pattern: Option<&'tcx Pat>,
     found_ty: Option<Ty<'tcx>>,
     found_closure: Option<&'tcx ExprKind>,
-    found_method_call: Option<&'tcx ExprKind>,
+    found_method_call: Option<&'tcx Expr>,
 }
 
 impl<'a, 'tcx> FindLocalByTypeVisitor<'a, 'tcx> {
@@ -99,7 +99,7 @@ fn visit_expr(&mut self, expr: &'tcx Expr) {
         if self.node_matches_type(expr.hir_id).is_some() {
             match expr.kind {
                 ExprKind::Closure(..) => self.found_closure = Some(&expr.kind),
-                ExprKind::MethodCall(..) => self.found_method_call = Some(&expr.kind),
+                ExprKind::MethodCall(..) => self.found_method_call = Some(&expr),
                 _ => {}
             }
         }
@@ -211,8 +211,8 @@ pub fn need_type_info_err(
             let mut printer = ty::print::FmtPrinter::new(self.tcx, &mut s, Namespace::TypeNS);
             let ty_vars = self.type_variables.borrow();
             let getter = move |ty_vid| {
-                if let TypeVariableOriginKind::TypeParameterDefinition(name) =
-                    ty_vars.var_origin(ty_vid).kind {
+                let var_origin = ty_vars.var_origin(ty_vid);
+                if let TypeVariableOriginKind::TypeParameterDefinition(name) = var_origin.kind {
                     return Some(name.to_string());
                 }
                 None
@@ -238,7 +238,7 @@ pub fn need_type_info_err(
             span
         } else if let Some(
             ExprKind::MethodCall(_, call_span, _),
-        ) = local_visitor.found_method_call {
+        ) = local_visitor.found_method_call.map(|e| &e.kind) {
             // Point at the call instead of the whole expression:
             // error[E0284]: type annotations needed
             //  --> file.rs:2:5
@@ -375,16 +375,48 @@ pub fn need_type_info_err(
                 format!("consider giving this pattern {}", suffix)
             };
             err.span_label(pattern.span, msg);
-        } else if let Some(ExprKind::MethodCall(segment, ..)) = local_visitor.found_method_call {
-            if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(segment.ident.span) {
-                if segment.args.is_none() {
-                    err.span_suggestion(
-                        segment.ident.span,
-                        "consider specifying the type argument in the method call",
-                        // FIXME: we don't know how many type arguments should be set here.
-                        format!("{}::<_>", snippet),
-                        Applicability::HasPlaceholders,
-                    );
+        } else if let Some(e) = local_visitor.found_method_call {
+            if let ExprKind::MethodCall(segment, _call_sp, _args) = &e.kind {
+                if let (Ok(snippet), Some(tables), None) = (
+                    self.tcx.sess.source_map().span_to_snippet(segment.ident.span),
+                    self.in_progress_tables,
+                    &segment.args,
+                 ) {
+                    let borrow = tables.borrow();
+                    let sigs = borrow.node_method_sig();
+                    if let Some(sig) = sigs.get(e.hir_id) {
+                        let mut params = vec![];
+                        for arg in sig.inputs_and_output().skip_binder().iter() {
+                            if let ty::Param(param) = arg.kind {
+                                if param.name != kw::SelfUpper {
+                                    let name = param.name.to_string();
+                                    if !params.contains(&name) {
+                                        params.push(name);
+                                    }
+                                }
+                            }
+                        }
+                        if !params.is_empty() {
+                            err.span_suggestion(
+                                segment.ident.span,
+                                &format!(
+                                    "consider specifying the type argument{} in the method call",
+                                    if params.len() > 1 {
+                                        "s"
+                                    } else {
+                                        ""
+                                    },
+                                ),
+                                format!("{}::<{}>", snippet, params.join(", ")),
+                                Applicability::HasPlaceholders,
+                            );
+                        } else {
+                            err.span_label(e.span, &format!(
+                                "this method call resolves to `{:?}`",
+                                sig.output().skip_binder(),
+                            ));
+                        }
+                    }
                 }
             }
         }
index f7e422b0403dcf88dd59a63a89e98fb69498001a..2da43bdcc1e261ba964c6da85602eb2c7dcecec7 100644 (file)
@@ -338,6 +338,8 @@ pub struct TypeckTables<'tcx> {
     /// typeck::check::fn_ctxt for details.
     node_types: ItemLocalMap<Ty<'tcx>>,
 
+    node_method_sig: ItemLocalMap<ty::PolyFnSig<'tcx>>,
+
     /// Stores the type parameters which were substituted to obtain the type
     /// of this node. This only applies to nodes that refer to entities
     /// parameterized by type parameters, such as generic fns, types, or
@@ -442,6 +444,7 @@ pub fn empty(local_id_root: Option<DefId>) -> TypeckTables<'tcx> {
             user_provided_types: Default::default(),
             user_provided_sigs: Default::default(),
             node_types: Default::default(),
+            node_method_sig: Default::default(),
             node_substs: Default::default(),
             adjustments: Default::default(),
             pat_binding_modes: Default::default(),
@@ -542,6 +545,20 @@ pub fn node_types_mut(&mut self) -> LocalTableInContextMut<'_, Ty<'tcx>> {
         }
     }
 
+    pub fn node_method_sig(&self) -> LocalTableInContext<'_, ty::PolyFnSig<'tcx>> {
+        LocalTableInContext {
+            local_id_root: self.local_id_root,
+            data: &self.node_method_sig
+        }
+    }
+
+    pub fn node_method_sig_mut(&mut self) -> LocalTableInContextMut<'_, ty::PolyFnSig<'tcx>> {
+        LocalTableInContextMut {
+            local_id_root: self.local_id_root,
+            data: &mut self.node_method_sig
+        }
+    }
+
     pub fn node_type(&self, id: hir::HirId) -> Ty<'tcx> {
         self.node_type_opt(id).unwrap_or_else(||
             bug!("node_type: no type for node `{}`",
@@ -748,6 +765,7 @@ fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHas
             ref user_provided_types,
             ref user_provided_sigs,
             ref node_types,
+            ref node_method_sig,
             ref node_substs,
             ref adjustments,
             ref pat_binding_modes,
@@ -774,6 +792,7 @@ fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHas
             user_provided_types.hash_stable(hcx, hasher);
             user_provided_sigs.hash_stable(hcx, hasher);
             node_types.hash_stable(hcx, hasher);
+            node_method_sig.hash_stable(hcx, hasher);
             node_substs.hash_stable(hcx, hasher);
             adjustments.hash_stable(hcx, hasher);
             pat_binding_modes.hash_stable(hcx, hasher);
index 5bfc60c75406745e0c0a7ac2c2db42eca0599edf..5f971a1ad505b3b7048676432106e921c3cc78af 100644 (file)
@@ -871,6 +871,27 @@ fn check_method_call(
 
         let method = match self.lookup_method(rcvr_t, segment, span, expr, rcvr) {
             Ok(method) => {
+                let sig = self.tcx.fn_sig(method.def_id);
+                // We could add a "consider `foo::<params>`" suggestion here, but I wasn't able to
+                // trigger this codepath causing `structuraly_resolved_type` to emit an error.
+
+                // We could do this only when type params are present in the method to reducte
+                // memory usage, but doing it unconditionally lets us also point at the method
+                // expression and state the resolved return value:
+                // ```
+                // error[E0282]: type annotations needed
+                //    --> $DIR/issue-65611.rs:59:20
+                //    |
+                // LL |     let x = buffer.last().unwrap().0.clone();
+                //    |             -------^^^^--
+                //    |             |      |
+                //    |             |      cannot infer type for `T`
+                //    |             this method call resolves to `std::option::Option<&T>`
+                //    |
+                //    = note: type must be known at this point
+                // ```
+                self.tables.borrow_mut().node_method_sig_mut().insert(expr.hir_id, sig);
+
                 self.write_method_call(expr.hir_id, method);
                 Ok(method)
             }
index 905c5ae9461d04be6d386d981f60a5225e21cede..3be08b233e470acafffbd1dfbcab06ada41b4e6a 100644 (file)
@@ -2,10 +2,10 @@ error[E0282]: type annotations needed
   --> $DIR/issue-65611.rs:59:20
    |
 LL |     let x = buffer.last().unwrap().0.clone();
-   |                    ^^^^
-   |                    |
-   |                    cannot infer type for `T`
-   |                    help: consider specifying the type argument in the method call: `last::<_>`
+   |             -------^^^^--
+   |             |      |
+   |             |      cannot infer type for `T`
+   |             this method call resolves to `std::option::Option<&T>`
    |
    = note: type must be known at this point
 
index d32d94976451ab6791c74cda487928efd8d2c28f..7911701946cd3db3723e6372177ca6732a3181c4 100644 (file)
@@ -5,7 +5,7 @@ LL |     l.iter().map(f).collect()?
    |                     ^^^^^^^
    |                     |
    |                     cannot infer type
-   |                     help: consider specifying the type argument in the method call: `collect::<_>`
+   |                     help: consider specifying the type argument in the method call: `collect::<B>`
    |
    = note: cannot resolve `<_ as std::ops::Try>::Ok == _`
 
index 1efb2720e0c555531892c49961ca65f1a1c58532..bc1c2f6164b61001079edcb66b9ef476b5dcc218 100644 (file)
@@ -5,7 +5,7 @@ LL |     let _ = (vec![1,2,3]).into_iter().sum() as f64;
    |                                       ^^^
    |                                       |
    |                                       cannot infer type for `S`
-   |                                       help: consider specifying the type argument in the method call: `sum::<_>`
+   |                                       help: consider specifying the type argument in the method call: `sum::<S>`
    |
    = note: type must be known at this point