diff options
| author | yukang <moorekang@gmail.com> | 2022-09-18 15:35:21 +0800 |
|---|---|---|
| committer | yukang <moorekang@gmail.com> | 2022-12-03 22:41:12 +0800 |
| commit | fb004e9a95793b7672d55b2984d65d4ac00a3cfc (patch) | |
| tree | 033e66ea5bd1771498488df7520e37976531d7e3 /compiler | |
| parent | 28a53cdb4695b71cb9ee39959df88542056479cd (diff) | |
| download | rust-fb004e9a95793b7672d55b2984d65d4ac00a3cfc.tar.gz rust-fb004e9a95793b7672d55b2984d65d4ac00a3cfc.zip | |
fix #101749, use . instead of :: when accessing a method of an object
Diffstat (limited to 'compiler')
| -rw-r--r-- | compiler/rustc_errors/src/lib.rs | 1 | ||||
| -rw-r--r-- | compiler/rustc_hir_typeck/src/expr.rs | 1 | ||||
| -rw-r--r-- | compiler/rustc_hir_typeck/src/method/suggest.rs | 64 | ||||
| -rw-r--r-- | compiler/rustc_resolve/src/diagnostics.rs | 11 | ||||
| -rw-r--r-- | compiler/rustc_resolve/src/late.rs | 28 |
5 files changed, 90 insertions, 15 deletions
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index f8747386c04..649165f5557 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -470,6 +470,7 @@ pub enum StashKey { /// Maybe there was a typo where a comma was forgotten before /// FRU syntax MaybeFruTypo, + CallAssocMethod, } fn default_track_diagnostic(_: &Diagnostic) {} diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index de30bfe6923..c873b0cd08f 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -528,6 +528,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.resolve_ty_and_res_fully_qualified_call(qpath, expr.hir_id, expr.span); let ty = match res { Res::Err => { + self.suggest_assoc_method_call(segs); let e = self.tcx.sess.delay_span_bug(qpath.span(), "`Res::Err` but no error emitted"); self.set_tainted_by_errors(e); diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index d0ea2b0e664..3549a48250f 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -5,6 +5,7 @@ use crate::errors; use crate::FnCtxt; use rustc_ast::ast::Mutability; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_errors::StashKey; use rustc_errors::{ pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan, @@ -13,6 +14,8 @@ use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; +use rustc_hir::PatKind::Binding; +use rustc_hir::PathSegment; use rustc_hir::{ExprKind, Node, QPath}; use rustc_infer::infer::{ type_variable::{TypeVariableOrigin, TypeVariableOriginKind}, @@ -35,11 +38,11 @@ use rustc_trait_selection::traits::{ FulfillmentError, Obligation, ObligationCause, ObligationCauseCode, }; -use std::cmp::Ordering; -use std::iter; - use super::probe::{AutorefOrPtrAdjustment, IsSuggestion, Mode, ProbeScope}; use super::{CandidateSource, MethodError, NoMatchData}; +use rustc_hir::intravisit::Visitor; +use std::cmp::Ordering; +use std::iter; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn is_fn_ty(&self, ty: Ty<'tcx>, span: Span) -> bool { @@ -1470,6 +1473,61 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { false } + /// For code `rect::area(...)`, + /// if `rect` is a local variable and `area` is a valid assoc method for it, + /// we try to suggest `rect.area()` + pub(crate) fn suggest_assoc_method_call(&self, segs: &[PathSegment<'_>]) { + debug!("suggest_assoc_method_call segs: {:?}", segs); + let [seg1, seg2] = segs else { return; }; + let Some(mut diag) = + self.tcx.sess.diagnostic().steal_diagnostic(seg1.ident.span, StashKey::CallAssocMethod) + else { return }; + + let map = self.infcx.tcx.hir(); + let body = map.body(rustc_hir::BodyId { hir_id: self.body_id }); + struct LetVisitor<'a> { + result: Option<&'a hir::Expr<'a>>, + ident_name: Symbol, + } + + impl<'v> Visitor<'v> for LetVisitor<'v> { + fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) { + if let hir::StmtKind::Local(hir::Local { pat, init, .. }) = &ex.kind { + if let Binding(_, _, ident, ..) = pat.kind && + ident.name == self.ident_name { + self.result = *init; + } + } + hir::intravisit::walk_stmt(self, ex); + } + } + + let mut visitor = LetVisitor { result: None, ident_name: seg1.ident.name }; + visitor.visit_body(&body); + + let parent = self.tcx.hir().get_parent_node(seg1.hir_id); + if let Some(Node::Expr(call_expr)) = self.tcx.hir().find(parent) && + let Some(expr) = visitor.result { + let self_ty = self.node_ty(expr.hir_id); + let probe = self.lookup_probe( + seg2.ident, + self_ty, + call_expr, + ProbeScope::TraitsInScope, + ); + if probe.is_ok() { + let sm = self.infcx.tcx.sess.source_map(); + diag.span_suggestion_verbose( + sm.span_extend_while(seg1.ident.span.shrink_to_hi(), |c| c == ':').unwrap(), + "you may have meant to call an instance method", + ".".to_string(), + Applicability::MaybeIncorrect + ); + } + } + diag.emit(); + } + /// Suggest calling a method on a field i.e. `a.field.bar()` instead of `a.bar()` fn suggest_calling_method_on_field( &self, diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index bc3a710e84b..f6b6cf3a94c 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1840,13 +1840,16 @@ impl<'a> Resolver<'a> { (format!("use of undeclared type `{}`", ident), suggestion) } else { - let suggestion = if ident.name == sym::alloc { - Some(( + let mut suggestion = None; + if ident.name == sym::alloc { + suggestion = Some(( vec![], String::from("add `extern crate alloc` to use the `alloc` crate"), Applicability::MaybeIncorrect, )) - } else { + } + + suggestion = suggestion.or_else(|| { self.find_similarly_named_module_or_crate(ident.name, &parent_scope.module).map( |sugg| { ( @@ -1856,7 +1859,7 @@ impl<'a> Resolver<'a> { ) }, ) - }; + }); (format!("use of undeclared crate or module `{}`", ident), suggestion) } } diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 5072d2aad16..107987c2673 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -3364,13 +3364,13 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // Before we start looking for candidates, we have to get our hands // on the type user is trying to perform invocation on; basically: // we're transforming `HashMap::new` into just `HashMap`. - let path = match path.split_last() { + let prefix_path = match path.split_last() { Some((_, path)) if !path.is_empty() => path, _ => return Some(parent_err), }; let (mut err, candidates) = - this.smart_resolve_report_errors(path, path_span, PathSource::Type, None); + this.smart_resolve_report_errors(prefix_path, path_span, PathSource::Type, None); // There are two different error messages user might receive at // this point: @@ -3414,11 +3414,23 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { if this.should_report_errs() { if candidates.is_empty() { - // When there is no suggested imports, we can just emit the error - // and suggestions immediately. Note that we bypass the usually error - // reporting routine (ie via `self.r.report_error`) because we need - // to post-process the `ResolutionError` above. - err.emit(); + if path.len() == 2 && prefix_path.len() == 1 { + // Delay to check whether methond name is an associated function or not + // ``` + // let foo = Foo {}; + // foo::bar(); // possibly suggest to foo.bar(); + //``` + err.stash( + prefix_path[0].ident.span, + rustc_errors::StashKey::CallAssocMethod, + ); + } else { + // When there is no suggested imports, we can just emit the error + // and suggestions immediately. Note that we bypass the usually error + // reporting routine (ie via `self.r.report_error`) because we need + // to post-process the `ResolutionError` above. + err.emit(); + } } else { // If there are suggested imports, the error reporting is delayed this.r.use_injections.push(UseError { @@ -3427,7 +3439,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { def_id, instead: false, suggestion: None, - path: path.into(), + path: prefix_path.into(), is_call: source.is_call(), }); } |
