diff options
Diffstat (limited to 'compiler/rustc_resolve')
| -rw-r--r-- | compiler/rustc_resolve/src/diagnostics.rs | 13 | ||||
| -rw-r--r-- | compiler/rustc_resolve/src/ident.rs | 9 | ||||
| -rw-r--r-- | compiler/rustc_resolve/src/imports.rs | 81 | ||||
| -rw-r--r-- | compiler/rustc_resolve/src/late.rs | 71 | ||||
| -rw-r--r-- | compiler/rustc_resolve/src/late/diagnostics.rs | 4 | ||||
| -rw-r--r-- | compiler/rustc_resolve/src/lib.rs | 26 |
6 files changed, 151 insertions, 53 deletions
diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 37771693417..7d62d67d64f 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -161,6 +161,7 @@ impl<'a> Resolver<'a> { found_use, DiagnosticMode::Normal, path, + "", ); err.emit(); } else if let Some((span, msg, sugg, appl)) = suggestion { @@ -690,6 +691,7 @@ impl<'a> Resolver<'a> { FoundUse::Yes, DiagnosticMode::Pattern, vec![], + "", ); } err @@ -1344,6 +1346,7 @@ impl<'a> Resolver<'a> { FoundUse::Yes, DiagnosticMode::Normal, vec![], + "", ); if macro_kind == MacroKind::Derive && (ident.name == sym::Send || ident.name == sym::Sync) { @@ -2309,7 +2312,7 @@ enum FoundUse { } /// Whether a binding is part of a pattern or a use statement. Used for diagnostics. -enum DiagnosticMode { +pub(crate) enum DiagnosticMode { Normal, /// The binding is part of a pattern Pattern, @@ -2324,6 +2327,8 @@ pub(crate) fn import_candidates( // This is `None` if all placement locations are inside expansions use_placement_span: Option<Span>, candidates: &[ImportSuggestion], + mode: DiagnosticMode, + append: &str, ) { show_candidates( session, @@ -2333,8 +2338,9 @@ pub(crate) fn import_candidates( candidates, Instead::Yes, FoundUse::Yes, - DiagnosticMode::Import, + mode, vec![], + append, ); } @@ -2352,6 +2358,7 @@ fn show_candidates( found_use: FoundUse, mode: DiagnosticMode, path: Vec<Segment>, + append: &str, ) { if candidates.is_empty() { return; @@ -2416,7 +2423,7 @@ fn show_candidates( // produce an additional newline to separate the new use statement // from the directly following item. let additional_newline = if let FoundUse::Yes = found_use { "" } else { "\n" }; - candidate.0 = format!("{}{};\n{}", add_use, &candidate.0, additional_newline); + candidate.0 = format!("{add_use}{}{append};\n{additional_newline}", &candidate.0); } err.span_suggestions( diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 0c4b35b8833..e41fe325b81 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -820,13 +820,12 @@ impl<'a> Resolver<'a> { // binding if it exists. What we really want here is having two separate scopes in // a module - one for non-globs and one for globs, but until that's done use this // hack to avoid inconsistent resolution ICEs during import validation. - let binding = [resolution.binding, resolution.shadowed_glob] - .into_iter() - .filter_map(|binding| match (binding, ignore_binding) { + let binding = [resolution.binding, resolution.shadowed_glob].into_iter().find_map( + |binding| match (binding, ignore_binding) { (Some(binding), Some(ignored)) if ptr::eq(binding, ignored) => None, _ => binding, - }) - .next(); + }, + ); let Some(binding) = binding else { return Err((Determined, Weak::No)); }; diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 4d896b05526..00f65ac37b6 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -1,6 +1,6 @@ //! A bunch of methods and structures more or less related to resolving imports. -use crate::diagnostics::{import_candidates, Suggestion}; +use crate::diagnostics::{import_candidates, DiagnosticMode, Suggestion}; use crate::Determinacy::{self, *}; use crate::Namespace::*; use crate::{module_to_string, names_to_string, ImportSuggestion}; @@ -402,7 +402,7 @@ struct UnresolvedImportError { label: Option<String>, note: Option<String>, suggestion: Option<Suggestion>, - candidate: Option<Vec<ImportSuggestion>>, + candidates: Option<Vec<ImportSuggestion>>, } pub struct ImportResolver<'a, 'b> { @@ -475,12 +475,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { errors = vec![]; } if seen_spans.insert(err.span) { - let path = import_path_to_string( - &import.module_path.iter().map(|seg| seg.ident).collect::<Vec<_>>(), - &import.kind, - err.span, - ); - errors.push((path, err)); + errors.push((import, err)); prev_root_id = import.root_id; } } else if is_indeterminate { @@ -494,10 +489,12 @@ impl<'a, 'b> ImportResolver<'a, 'b> { label: None, note: None, suggestion: None, - candidate: None, + candidates: None, }; + // FIXME: there should be a better way of doing this than + // formatting this as a string then checking for `::` if path.contains("::") { - errors.push((path, err)) + errors.push((import, err)) } } } @@ -507,7 +504,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { } } - fn throw_unresolved_import_error(&self, errors: Vec<(String, UnresolvedImportError)>) { + fn throw_unresolved_import_error(&self, errors: Vec<(&Import<'_>, UnresolvedImportError)>) { if errors.is_empty() { return; } @@ -516,7 +513,17 @@ impl<'a, 'b> ImportResolver<'a, 'b> { const MAX_LABEL_COUNT: usize = 10; let span = MultiSpan::from_spans(errors.iter().map(|(_, err)| err.span).collect()); - let paths = errors.iter().map(|(path, _)| format!("`{}`", path)).collect::<Vec<_>>(); + let paths = errors + .iter() + .map(|(import, err)| { + let path = import_path_to_string( + &import.module_path.iter().map(|seg| seg.ident).collect::<Vec<_>>(), + &import.kind, + err.span, + ); + format!("`{path}`") + }) + .collect::<Vec<_>>(); let msg = format!("unresolved import{} {}", pluralize!(paths.len()), paths.join(", "),); let mut diag = struct_span_err!(self.r.session, span, E0432, "{}", &msg); @@ -525,7 +532,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { diag.note(note); } - for (_, err) in errors.into_iter().take(MAX_LABEL_COUNT) { + for (import, err) in errors.into_iter().take(MAX_LABEL_COUNT) { if let Some(label) = err.label { diag.span_label(err.span, label); } @@ -538,14 +545,36 @@ impl<'a, 'b> ImportResolver<'a, 'b> { diag.multipart_suggestion(&msg, suggestions, applicability); } - if let Some(candidate) = &err.candidate { - import_candidates( - self.r.session, - &self.r.untracked.source_span, - &mut diag, - Some(err.span), - &candidate, - ) + if let Some(candidates) = &err.candidates { + match &import.kind { + ImportKind::Single { nested: false, source, target, .. } => import_candidates( + self.r.session, + &self.r.untracked.source_span, + &mut diag, + Some(err.span), + &candidates, + DiagnosticMode::Import, + (source != target) + .then(|| format!(" as {target}")) + .as_deref() + .unwrap_or(""), + ), + ImportKind::Single { nested: true, source, target, .. } => { + import_candidates( + self.r.session, + &self.r.untracked.source_span, + &mut diag, + None, + &candidates, + DiagnosticMode::Normal, + (source != target) + .then(|| format!(" as {target}")) + .as_deref() + .unwrap_or(""), + ); + } + _ => {} + } } } @@ -707,14 +736,14 @@ impl<'a, 'b> ImportResolver<'a, 'b> { String::from("a similar path exists"), Applicability::MaybeIncorrect, )), - candidate: None, + candidates: None, }, None => UnresolvedImportError { span, label: Some(label), note: None, suggestion, - candidate: None, + candidates: None, }, }; return Some(err); @@ -761,7 +790,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { )), note: None, suggestion: None, - candidate: None, + candidates: None, }); } } @@ -873,7 +902,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { let resolutions = resolutions.as_ref().into_iter().flat_map(|r| r.iter()); let names = resolutions .filter_map(|(BindingKey { ident: i, .. }, resolution)| { - if *i == ident { + if i.name == ident.name { return None; } // Never suggest the same name match *resolution.borrow() { @@ -943,7 +972,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { label: Some(label), note, suggestion, - candidate: if !parent_suggestion.is_empty() { + candidates: if !parent_suggestion.is_empty() { Some(parent_suggestion) } else { None diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 5b7a00101e9..ca43762aa21 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -16,7 +16,7 @@ use rustc_ast::ptr::P; use rustc_ast::visit::{self, AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor}; use rustc_ast::*; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; -use rustc_errors::{DiagnosticArgValue, DiagnosticId, IntoDiagnosticArg}; +use rustc_errors::{Applicability, DiagnosticArgValue, DiagnosticId, IntoDiagnosticArg}; use rustc_hir::def::Namespace::{self, *}; use rustc_hir::def::{self, CtorKind, DefKind, LifetimeRes, PartialRes, PerNS}; use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID, LOCAL_CRATE}; @@ -536,6 +536,9 @@ struct DiagnosticMetadata<'ast> { in_assignment: Option<&'ast Expr>, is_assign_rhs: bool, + /// Used to detect possible `.` -> `..` typo when calling methods. + in_range: Option<(&'ast Expr, &'ast Expr)>, + /// If we are currently in a trait object definition. Used to point at the bounds when /// encountering a struct or enum. current_trait_object: Option<&'ast [ast::GenericBound]>, @@ -648,7 +651,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { let prev = self.diagnostic_metadata.current_trait_object; let prev_ty = self.diagnostic_metadata.current_type_path; match ty.kind { - TyKind::Rptr(None, _) => { + TyKind::Ref(None, _) => { // Elided lifetime in reference: we resolve as if there was some lifetime `'_` with // NodeId `ty.id`. // This span will be used in case of elision failure. @@ -1512,7 +1515,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { count: 1, }; let elision_candidate = LifetimeElisionCandidate::Missing(missing_lifetime); - for rib in self.lifetime_ribs.iter().rev() { + for (i, rib) in self.lifetime_ribs.iter().enumerate().rev() { debug!(?rib.kind); match rib.kind { LifetimeRibKind::AnonymousCreateParameter { binder, .. } => { @@ -1529,16 +1532,31 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { } else { ("`'_` cannot be used here", "`'_` is a reserved lifetime name") }; - rustc_errors::struct_span_err!( + let mut diag = rustc_errors::struct_span_err!( self.r.session, lifetime.ident.span, E0637, "{}", msg, - ) - .span_label(lifetime.ident.span, note) - .emit(); - + ); + diag.span_label(lifetime.ident.span, note); + if elided { + for rib in self.lifetime_ribs[i..].iter().rev() { + if let LifetimeRibKind::Generics { + span, + kind: LifetimeBinderKind::PolyTrait | LifetimeBinderKind::WhereBound, + .. + } = &rib.kind + { + diag.span_help( + *span, + "consider introducing a higher-ranked lifetime here with `for<'a>`", + ); + break; + } + } + } + diag.emit(); self.record_lifetime_res(lifetime.id, LifetimeRes::Error, elision_candidate); return; } @@ -2001,7 +2019,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { impl<'a> Visitor<'a> for SelfVisitor<'_, '_> { fn visit_ty(&mut self, ty: &'a Ty) { trace!("SelfVisitor considering ty={:?}", ty); - if let TyKind::Rptr(lt, ref mt) = ty.kind && self.is_self_ty(&mt.ty) { + if let TyKind::Ref(lt, ref mt) = ty.kind && self.is_self_ty(&mt.ty) { let lt_id = if let Some(lt) = lt { lt.id } else { @@ -3320,6 +3338,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { ); } + #[instrument(level = "debug", skip(self))] fn smart_resolve_path_fragment( &mut self, qself: &Option<P<QSelf>>, @@ -3327,10 +3346,6 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { source: PathSource<'ast>, finalize: Finalize, ) -> PartialRes { - debug!( - "smart_resolve_path_fragment(qself={:?}, path={:?}, finalize={:?})", - qself, path, finalize, - ); let ns = source.namespace(); let Finalize { node_id, path_span, .. } = finalize; @@ -3341,8 +3356,28 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { let def_id = this.parent_scope.module.nearest_parent_mod(); let instead = res.is_some(); - let suggestion = - if res.is_none() { this.report_missing_type_error(path) } else { None }; + let suggestion = if let Some((start, end)) = this.diagnostic_metadata.in_range + && path[0].ident.span.lo() == end.span.lo() + { + let mut sugg = "."; + let mut span = start.span.between(end.span); + if span.lo() + BytePos(2) == span.hi() { + // There's no space between the start, the range op and the end, suggest + // removal which will look better. + span = span.with_lo(span.lo() + BytePos(1)); + sugg = ""; + } + Some(( + span, + "you might have meant to write `.` instead of `..`", + sugg.to_string(), + Applicability::MaybeIncorrect, + )) + } else if res.is_none() { + this.report_missing_type_error(path) + } else { + None + }; this.r.use_injections.push(UseError { err, @@ -4005,6 +4040,12 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { self.visit_expr(rhs); self.diagnostic_metadata.is_assign_rhs = false; } + ExprKind::Range(Some(ref start), Some(ref end), RangeLimits::HalfOpen) => { + self.diagnostic_metadata.in_range = Some((start, end)); + self.resolve_expr(start, Some(expr)); + self.resolve_expr(end, Some(expr)); + self.diagnostic_metadata.in_range = None; + } _ => { visit::walk_expr(self, expr); } diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 600308b6508..74522f18542 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -1554,7 +1554,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { fn extract_node_id(t: &Ty) -> Option<NodeId> { match t.kind { TyKind::Path(None, _) => Some(t.id), - TyKind::Rptr(_, ref mut_ty) => extract_node_id(&mut_ty.ty), + TyKind::Ref(_, ref mut_ty) => extract_node_id(&mut_ty.ty), // This doesn't handle the remaining `Ty` variants as they are not // that commonly the self_type, it might be interesting to provide // support for those in future. @@ -2189,7 +2189,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { Some(LifetimeUseSet::One { use_span, use_ctxt }) => { debug!(?param.ident, ?param.ident.span, ?use_span); - let elidable = matches!(use_ctxt, LifetimeCtxt::Rptr); + let elidable = matches!(use_ctxt, LifetimeCtxt::Ref); let deletion_span = deletion_span(); self.r.lint_buffer.buffer_lint_with_diagnostic( diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 4861ee746aa..2182b736937 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -1686,6 +1686,24 @@ impl<'a> Resolver<'a> { .or_insert_with(|| self.arenas.alloc_name_resolution()) } + /// Test if AmbiguityError ambi is any identical to any one inside ambiguity_errors + fn matches_previous_ambiguity_error(&mut self, ambi: &AmbiguityError<'_>) -> bool { + for ambiguity_error in &self.ambiguity_errors { + // if the span location and ident as well as its span are the same + if ambiguity_error.kind == ambi.kind + && ambiguity_error.ident == ambi.ident + && ambiguity_error.ident.span == ambi.ident.span + && ambiguity_error.b1.span == ambi.b1.span + && ambiguity_error.b2.span == ambi.b2.span + && ambiguity_error.misc1 == ambi.misc1 + && ambiguity_error.misc2 == ambi.misc2 + { + return true; + } + } + false + } + fn record_use( &mut self, ident: Ident, @@ -1693,14 +1711,18 @@ impl<'a> Resolver<'a> { is_lexical_scope: bool, ) { if let Some((b2, kind)) = used_binding.ambiguity { - self.ambiguity_errors.push(AmbiguityError { + let ambiguity_error = AmbiguityError { kind, ident, b1: used_binding, b2, misc1: AmbiguityErrorMisc::None, misc2: AmbiguityErrorMisc::None, - }); + }; + if !self.matches_previous_ambiguity_error(&ambiguity_error) { + // avoid dumplicated span information to be emitt out + self.ambiguity_errors.push(ambiguity_error); + } } if let NameBindingKind::Import { import, binding, ref used } = used_binding.kind { // Avoid marking `extern crate` items that refer to a name from extern prelude, |
