summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_resolve/src/late/diagnostics.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_resolve/src/late/diagnostics.rs')
-rw-r--r--compiler/rustc_resolve/src/late/diagnostics.rs186
1 files changed, 99 insertions, 87 deletions
diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs
index 850f023b1..df59a350e 100644
--- a/compiler/rustc_resolve/src/late/diagnostics.rs
+++ b/compiler/rustc_resolve/src/late/diagnostics.rs
@@ -8,7 +8,7 @@ use crate::{PathResult, PathSource, Segment};
use rustc_ast::visit::{FnCtxt, FnKind, LifetimeCtxt};
use rustc_ast::{
self as ast, AssocItemKind, Expr, ExprKind, GenericParam, GenericParamKind, Item, ItemKind,
- NodeId, Path, Ty, TyKind, DUMMY_NODE_ID,
+ MethodCall, NodeId, Path, Ty, TyKind, DUMMY_NODE_ID,
};
use rustc_ast_pretty::pprust::path_segment_to_string;
use rustc_data_structures::fx::FxHashSet;
@@ -21,6 +21,7 @@ use rustc_hir::def::Namespace::{self, *};
use rustc_hir::def::{self, CtorKind, CtorOf, DefKind};
use rustc_hir::def_id::{DefId, CRATE_DEF_ID, LOCAL_CRATE};
use rustc_hir::PrimTy;
+use rustc_middle::ty::DefIdTree;
use rustc_session::lint;
use rustc_session::parse::feature_err;
use rustc_session::Session;
@@ -33,6 +34,8 @@ use rustc_span::{BytePos, Span};
use std::iter;
use std::ops::Deref;
+use thin_vec::ThinVec;
+
type Res = def::Res<ast::NodeId>;
/// A field or associated item from self type suggested in case of resolution failure.
@@ -78,7 +81,7 @@ fn import_candidate_to_enum_paths(suggestion: &ImportSuggestion) -> (String, Str
let path_len = suggestion.path.segments.len();
let enum_path = ast::Path {
span: suggestion.path.span,
- segments: suggestion.path.segments[0..path_len - 1].to_vec(),
+ segments: suggestion.path.segments[0..path_len - 1].iter().cloned().collect(),
tokens: None,
};
let enum_path_string = path_names_to_string(&enum_path);
@@ -150,7 +153,7 @@ struct BaseError {
#[derive(Debug)]
enum TypoCandidate {
Typo(TypoSuggestion),
- Shadowed(Res),
+ Shadowed(Res, Option<Span>),
None,
}
@@ -158,7 +161,7 @@ impl TypoCandidate {
fn to_opt_suggestion(self) -> Option<TypoSuggestion> {
match self {
TypoCandidate::Typo(sugg) => Some(sugg),
- TypoCandidate::Shadowed(_) | TypoCandidate::None => None,
+ TypoCandidate::Shadowed(_, _) | TypoCandidate::None => None,
}
}
}
@@ -219,26 +222,26 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
let (mod_prefix, mod_str, suggestion) = if path.len() == 1 {
debug!(?self.diagnostic_metadata.current_impl_items);
debug!(?self.diagnostic_metadata.current_function);
- let suggestion = if let Some(items) = self.diagnostic_metadata.current_impl_items
+ let suggestion = if self.current_trait_ref.is_none()
&& let Some((fn_kind, _)) = self.diagnostic_metadata.current_function
- && self.current_trait_ref.is_none()
&& let Some(FnCtxt::Assoc(_)) = fn_kind.ctxt()
+ && let Some(items) = self.diagnostic_metadata.current_impl_items
&& let Some(item) = items.iter().find(|i| {
- if let AssocItemKind::Fn(fn_) = &i.kind
- && !fn_.sig.decl.has_self()
- && i.ident.name == item_str.name
+ if let AssocItemKind::Fn(_) = &i.kind && i.ident.name == item_str.name
{
debug!(?item_str.name);
- debug!(?fn_.sig.decl.inputs);
return true
}
false
})
+ && let AssocItemKind::Fn(fn_) = &item.kind
{
+ debug!(?fn_);
+ let self_sugg = if fn_.sig.decl.has_self() { "self." } else { "Self::" };
Some((
- item_span,
+ item_span.shrink_to_lo(),
"consider using the associated function",
- format!("Self::{}", item.ident)
+ self_sugg.to_string()
))
} else {
None
@@ -279,6 +282,14 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
"you may want to use a bool value instead",
format!("{}", item_typo),
))
+ // FIXME(vincenzopalazzo): make the check smarter,
+ // and maybe expand with levenshtein distance checks
+ } else if item_str.as_str() == "printf" {
+ Some((
+ item_span,
+ "you may have meant to use the `print` macro",
+ "print!".to_owned(),
+ ))
} else {
suggestion
};
@@ -322,7 +333,12 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
}
self.suggest_bare_struct_literal(&mut err);
- self.suggest_pattern_match_with_let(&mut err, source, span);
+
+ if self.suggest_pattern_match_with_let(&mut err, source, span) {
+ // Fallback label.
+ err.span_label(base_error.span, &base_error.fallback_label);
+ return (err, Vec::new());
+ }
self.suggest_self_or_self_ref(&mut err, path, span);
self.detect_assoct_type_constraint_meant_as_path(&mut err, &base_error);
@@ -341,7 +357,11 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
if !self.type_ascription_suggestion(&mut err, base_error.span) {
let mut fallback =
self.suggest_trait_and_bounds(&mut err, source, res, span, &base_error);
+
+ // if we have suggested using pattern matching, then don't add needless suggestions
+ // for typos.
fallback |= self.suggest_typo(&mut err, source, path, span, &base_error);
+
if fallback {
// Fallback label.
err.span_label(base_error.span, &base_error.fallback_label);
@@ -387,11 +407,13 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
}
fn suggest_self_or_self_ref(&mut self, err: &mut Diagnostic, path: &[Segment], span: Span) {
- let is_assoc_fn = self.self_type_is_available();
+ if !self.self_type_is_available() {
+ return;
+ }
let Some(path_last_segment) = path.last() else { return };
let item_str = path_last_segment.ident;
// Emit help message for fake-self from other languages (e.g., `this` in Javascript).
- if ["this", "my"].contains(&item_str.as_str()) && is_assoc_fn {
+ if ["this", "my"].contains(&item_str.as_str()) {
err.span_suggestion_short(
span,
"you might have meant to use `self` here instead",
@@ -428,7 +450,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
fn try_lookup_name_relaxed(
&mut self,
- err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>,
+ err: &mut Diagnostic,
source: PathSource<'_>,
path: &[Segment],
span: Span,
@@ -442,7 +464,6 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
let is_enum_variant = &|res| matches!(res, Res::Def(DefKind::Variant, _));
let path_str = Segment::names_to_string(path);
let ident_span = path.last().map_or(span, |ident| ident.ident.span);
-
let mut candidates = self
.r
.lookup_import_candidates(ident, ns, &self.parent_scope, is_expected)
@@ -488,7 +509,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
.contains(span)
{
// Already reported this issue on the lhs of the type ascription.
- err.delay_as_bug();
+ err.downgrade_to_delayed_bug();
return (true, candidates);
}
}
@@ -607,7 +628,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
fn suggest_trait_and_bounds(
&mut self,
- err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>,
+ err: &mut Diagnostic,
source: PathSource<'_>,
res: Option<Res>,
span: Span,
@@ -682,7 +703,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
fn suggest_typo(
&mut self,
- err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>,
+ err: &mut Diagnostic,
source: PathSource<'_>,
path: &[Segment],
span: Span,
@@ -691,9 +712,20 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
let is_expected = &|res| source.is_expected(res);
let ident_span = path.last().map_or(span, |ident| ident.ident.span);
let typo_sugg = self.lookup_typo_candidate(path, source.namespace(), is_expected);
- if let TypoCandidate::Shadowed(res) = typo_sugg
- && let Some(id) = res.opt_def_id()
- && let Some(sugg_span) = self.r.opt_span(id)
+ let is_in_same_file = &|sp1, sp2| {
+ let source_map = self.r.session.source_map();
+ let file1 = source_map.span_to_filename(sp1);
+ let file2 = source_map.span_to_filename(sp2);
+ file1 == file2
+ };
+ // print 'you might have meant' if the candidate is (1) is a shadowed name with
+ // accessible definition and (2) either defined in the same crate as the typo
+ // (could be in a different file) or introduced in the same file as the typo
+ // (could belong to a different crate)
+ if let TypoCandidate::Shadowed(res, Some(sugg_span)) = typo_sugg
+ && res
+ .opt_def_id()
+ .map_or(false, |id| id.is_local() || is_in_same_file(span, sugg_span))
{
err.span_label(
sugg_span,
@@ -730,7 +762,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
fn err_code_special_cases(
&mut self,
- err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>,
+ err: &mut Diagnostic,
source: PathSource<'_>,
path: &[Segment],
span: Span,
@@ -784,19 +816,18 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
return false;
}
err.code(rustc_errors::error_code!(E0411));
- err.span_label(
- span,
- "`Self` is only available in impls, traits, and type definitions".to_string(),
- );
+ err.span_label(span, "`Self` is only available in impls, traits, and type definitions");
if let Some(item_kind) = self.diagnostic_metadata.current_item {
- err.span_label(
- item_kind.ident.span,
- format!(
- "`Self` not allowed in {} {}",
- item_kind.kind.article(),
- item_kind.kind.descr()
- ),
- );
+ if !item_kind.ident.span.is_dummy() {
+ err.span_label(
+ item_kind.ident.span,
+ format!(
+ "`Self` not allowed in {} {}",
+ item_kind.kind.article(),
+ item_kind.kind.descr()
+ ),
+ );
+ }
}
true
}
@@ -929,7 +960,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
err: &mut Diagnostic,
source: PathSource<'_>,
span: Span,
- ) {
+ ) -> bool {
if let PathSource::Expr(_) = source &&
let Some(Expr {
span: expr_span,
@@ -946,8 +977,10 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
"let ",
Applicability::MaybeIncorrect,
);
+ return true;
}
}
+ false
}
fn get_single_associated_item(
@@ -973,10 +1006,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
.collect();
if targets.len() == 1 {
let target = targets[0];
- return Some(TypoSuggestion::single_item_from_res(
- target.0.ident.name,
- target.1,
- ));
+ return Some(TypoSuggestion::single_item_from_ident(target.0.ident, target.1));
}
}
}
@@ -1003,11 +1033,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
};
// Confirm that the target is an associated type.
- let (ty, position, path) = if let ast::TyKind::Path(
- Some(ast::QSelf { ty, position, .. }),
- path,
- ) = &bounded_ty.kind
- {
+ let (ty, position, path) = if let ast::TyKind::Path(Some(qself), path) = &bounded_ty.kind {
// use this to verify that ident is a type param.
let Some(partial_res) = self.r.partial_res_map.get(&bounded_ty.id) else {
return false;
@@ -1018,7 +1044,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
) {
return false;
}
- (ty, position, path)
+ (&qself.ty, qself.position, path)
} else {
return false;
};
@@ -1054,12 +1080,12 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
.source_map()
.span_to_snippet(ty.span) // Account for `<&'a T as Foo>::Bar`.
.unwrap_or_else(|_| constrain_ident.to_string()),
- path.segments[..*position]
+ path.segments[..position]
.iter()
.map(|segment| path_segment_to_string(segment))
.collect::<Vec<_>>()
.join("::"),
- path.segments[*position..]
+ path.segments[position..]
.iter()
.map(|segment| path_segment_to_string(segment))
.collect::<Vec<_>>()
@@ -1151,7 +1177,9 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
let (lhs_span, rhs_span) = match &expr.kind {
ExprKind::Field(base, ident) => (base.span, ident.span),
- ExprKind::MethodCall(_, receiver, _, span) => (receiver.span, *span),
+ ExprKind::MethodCall(box MethodCall { receiver, span, .. }) => {
+ (receiver.span, *span)
+ }
_ => return false,
};
@@ -1423,13 +1451,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
err.span_label(span, "constructor is not visible here due to private fields");
}
- (
- Res::Def(
- DefKind::Union | DefKind::Variant | DefKind::Ctor(_, CtorKind::Fictive),
- def_id,
- ),
- _,
- ) if ns == ValueNS => {
+ (Res::Def(DefKind::Union | DefKind::Variant, def_id), _) if ns == ValueNS => {
bad_struct_syntax_suggestion(def_id);
}
(Res::Def(DefKind::Ctor(_, CtorKind::Const), def_id), _) if ns == ValueNS => {
@@ -1449,7 +1471,8 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
_ => return false,
}
}
- (Res::Def(DefKind::Ctor(_, CtorKind::Fn), def_id), _) if ns == ValueNS => {
+ (Res::Def(DefKind::Ctor(_, CtorKind::Fn), ctor_def_id), _) if ns == ValueNS => {
+ let def_id = self.r.parent(ctor_def_id);
if let Some(span) = self.def_span(def_id) {
err.span_label(span, &format!("`{}` defined here", path_str));
}
@@ -1526,7 +1549,6 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
_ => None,
}
}
-
// Fields are generally expected in the same contexts as locals.
if filter_fn(Res::Local(ast::DUMMY_NODE_ID)) {
if let Some(node_id) =
@@ -1618,7 +1640,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
// Locals and type parameters
for (ident, &res) in &rib.bindings {
if filter_fn(res) && ident.span.ctxt() == rib_ctxt {
- names.push(TypoSuggestion::typo_from_res(ident.name, res));
+ names.push(TypoSuggestion::typo_from_ident(*ident, res));
}
}
@@ -1647,9 +1669,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
Res::Def(DefKind::Mod, crate_id.as_def_id());
if filter_fn(crate_mod) {
- Some(TypoSuggestion::typo_from_res(
- ident.name, crate_mod,
- ))
+ Some(TypoSuggestion::typo_from_ident(*ident, crate_mod))
} else {
None
}
@@ -1668,7 +1688,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
// Add primitive types to the mix
if filter_fn(Res::PrimTy(PrimTy::Bool)) {
names.extend(PrimTy::ALL.iter().map(|prim_ty| {
- TypoSuggestion::typo_from_res(prim_ty.name(), Res::PrimTy(*prim_ty))
+ TypoSuggestion::typo_from_name(prim_ty.name(), Res::PrimTy(*prim_ty))
}))
}
} else {
@@ -1695,7 +1715,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
return TypoCandidate::None;
};
if found == name {
- TypoCandidate::Shadowed(sugg.res)
+ TypoCandidate::Shadowed(sugg.res, sugg.span)
} else {
TypoCandidate::Typo(sugg)
}
@@ -1796,35 +1816,28 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
false
}
- fn let_binding_suggestion(&self, err: &mut Diagnostic, ident_span: Span) -> bool {
- // try to give a suggestion for this pattern: `name = 1`, which is common in other languages
- let mut added_suggestion = false;
- if let Some(Expr { kind: ExprKind::Assign(lhs, _rhs, _), .. }) = self.diagnostic_metadata.in_assignment &&
+ // try to give a suggestion for this pattern: `name = blah`, which is common in other languages
+ // suggest `let name = blah` to introduce a new binding
+ fn let_binding_suggestion(&mut self, err: &mut Diagnostic, ident_span: Span) -> bool {
+ if let Some(Expr { kind: ExprKind::Assign(lhs, .. ), .. }) = self.diagnostic_metadata.in_assignment &&
let ast::ExprKind::Path(None, _) = lhs.kind {
- let sm = self.r.session.source_map();
- let line_span = sm.span_extend_to_line(ident_span);
- let ident_name = sm.span_to_snippet(ident_span).unwrap();
- // HACK(chenyukang): make sure ident_name is at the starting of the line to protect against macros
- if sm
- .span_to_snippet(line_span)
- .map_or(false, |s| s.trim().starts_with(&ident_name))
- {
+ if !ident_span.from_expansion() {
err.span_suggestion_verbose(
ident_span.shrink_to_lo(),
"you might have meant to introduce a new binding",
"let ".to_string(),
Applicability::MaybeIncorrect,
);
- added_suggestion = true;
+ return true;
}
}
- added_suggestion
+ false
}
fn find_module(&mut self, def_id: DefId) -> Option<(Module<'a>, ImportSuggestion)> {
let mut result = None;
let mut seen_modules = FxHashSet::default();
- let mut worklist = vec![(self.r.graph_root, Vec::new())];
+ let mut worklist = vec![(self.r.graph_root, ThinVec::new())];
while let Some((in_module, path_segments)) = worklist.pop() {
// abort if the module is already found
@@ -1927,7 +1940,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
err.span_suggestions(
span,
&msg,
- suggestable_variants.into_iter(),
+ suggestable_variants,
Applicability::MaybeIncorrect,
);
}
@@ -1950,11 +1963,12 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
));
}
} else {
- let needs_placeholder = |def_id: DefId, kind: CtorKind| {
+ let needs_placeholder = |ctor_def_id: DefId, kind: CtorKind| {
+ let def_id = self.r.parent(ctor_def_id);
let has_no_fields = self.r.field_names.get(&def_id).map_or(false, |f| f.is_empty());
match kind {
CtorKind::Const => false,
- CtorKind::Fn | CtorKind::Fictive if has_no_fields => false,
+ CtorKind::Fn if has_no_fields => false,
_ => true,
}
};
@@ -1966,7 +1980,6 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
.map(|(variant, kind)| match kind {
CtorKind::Const => variant,
CtorKind::Fn => format!("({}())", variant),
- CtorKind::Fictive => format!("({} {{}})", variant),
})
.collect::<Vec<_>>();
let no_suggestable_variant = suggestable_variants.is_empty();
@@ -1981,7 +1994,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
err.span_suggestions(
span,
msg,
- suggestable_variants.into_iter(),
+ suggestable_variants,
Applicability::MaybeIncorrect,
);
}
@@ -1992,7 +2005,6 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
.map(|(variant, _, kind)| (path_names_to_string(variant), kind))
.filter_map(|(variant, kind)| match kind {
CtorKind::Fn => Some(format!("({}(/* fields */))", variant)),
- CtorKind::Fictive => Some(format!("({} {{ /* fields */ }})", variant)),
_ => None,
})
.collect::<Vec<_>>();
@@ -2011,7 +2023,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
err.span_suggestions(
span,
msg,
- suggestable_variants_with_placeholders.into_iter(),
+ suggestable_variants_with_placeholders,
Applicability::HasPlaceholders,
);
}