summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_resolve/src/late
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_resolve/src/late')
-rw-r--r--compiler/rustc_resolve/src/late/diagnostics.rs227
1 files changed, 174 insertions, 53 deletions
diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs
index df6582580..c0e3f1aaf 100644
--- a/compiler/rustc_resolve/src/late/diagnostics.rs
+++ b/compiler/rustc_resolve/src/late/diagnostics.rs
@@ -4,20 +4,20 @@ use crate::late::{LifetimeBinderKind, LifetimeRes, LifetimeRibKind, LifetimeUseS
use crate::{errors, path_names_to_string};
use crate::{Module, ModuleKind, ModuleOrUniformRoot};
use crate::{PathResult, PathSource, Segment};
+use rustc_hir::def::Namespace::{self, *};
use rustc_ast::visit::{FnCtxt, FnKind, LifetimeCtxt};
use rustc_ast::{
self as ast, AssocItemKind, Expr, ExprKind, GenericParam, GenericParamKind, Item, ItemKind,
MethodCall, NodeId, Path, Ty, TyKind, DUMMY_NODE_ID,
};
-use rustc_ast_pretty::pprust::path_segment_to_string;
+use rustc_ast_pretty::pprust::where_bound_predicate_to_string;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::{
pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
MultiSpan,
};
use rustc_hir as hir;
-use rustc_hir::def::Namespace::{self, *};
use rustc_hir::def::{self, CtorKind, CtorOf, DefKind};
use rustc_hir::def_id::{DefId, CRATE_DEF_ID};
use rustc_hir::PrimTy;
@@ -29,6 +29,7 @@ use rustc_span::hygiene::MacroKind;
use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::Span;
+use std::borrow::Cow;
use std::iter;
use std::ops::Deref;
@@ -148,6 +149,7 @@ struct BaseError {
span_label: Option<(Span, &'static str)>,
could_be_expr: bool,
suggestion: Option<(Span, &'static str, String)>,
+ module: Option<DefId>,
}
#[derive(Debug)]
@@ -209,19 +211,24 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
_ => false,
},
suggestion: None,
+ module: None,
}
} else {
let item_span = path.last().unwrap().ident.span;
- let (mod_prefix, mod_str, suggestion) = if path.len() == 1 {
+ let (mod_prefix, mod_str, module, suggestion) = if path.len() == 1 {
debug!(?self.diagnostic_metadata.current_impl_items);
debug!(?self.diagnostic_metadata.current_function);
let suggestion = if self.current_trait_ref.is_none()
&& let Some((fn_kind, _)) = self.diagnostic_metadata.current_function
&& let Some(FnCtxt::Assoc(_)) = fn_kind.ctxt()
+ && let FnKind::Fn(_, _, sig, ..) = fn_kind
&& let Some(items) = self.diagnostic_metadata.current_impl_items
&& let Some(item) = items.iter().find(|i| {
if let AssocItemKind::Fn(..) | AssocItemKind::Const(..) = &i.kind
&& i.ident.name == item_str.name
+ // don't suggest if the item is in Fn signature arguments
+ // issue #112590
+ && !sig.span.contains(item_span)
{
debug!(?item_str.name);
return true
@@ -246,26 +253,37 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
} else {
None
};
- (String::new(), "this scope".to_string(), suggestion)
+ (String::new(), "this scope".to_string(), None, suggestion)
} else if path.len() == 2 && path[0].ident.name == kw::PathRoot {
if self.r.tcx.sess.edition() > Edition::Edition2015 {
// In edition 2018 onwards, the `::foo` syntax may only pull from the extern prelude
// which overrides all other expectations of item type
expected = "crate";
- (String::new(), "the list of imported crates".to_string(), None)
+ (String::new(), "the list of imported crates".to_string(), None, None)
} else {
- (String::new(), "the crate root".to_string(), None)
+ (
+ String::new(),
+ "the crate root".to_string(),
+ Some(CRATE_DEF_ID.to_def_id()),
+ None,
+ )
}
} else if path.len() == 2 && path[0].ident.name == kw::Crate {
- (String::new(), "the crate root".to_string(), None)
+ (String::new(), "the crate root".to_string(), Some(CRATE_DEF_ID.to_def_id()), None)
} else {
let mod_path = &path[..path.len() - 1];
- let mod_prefix = match self.resolve_path(mod_path, Some(TypeNS), None) {
+ let mod_res = self.resolve_path(mod_path, Some(TypeNS), None);
+ let mod_prefix = match mod_res {
PathResult::Module(ModuleOrUniformRoot::Module(module)) => module.res(),
_ => None,
- }
- .map_or_else(String::new, |res| format!("{} ", res.descr()));
- (mod_prefix, format!("`{}`", Segment::names_to_string(mod_path)), None)
+ };
+
+ let module_did = mod_prefix.as_ref().and_then(Res::mod_def_id);
+
+ let mod_prefix =
+ mod_prefix.map_or_else(String::new, |res| (format!("{} ", res.descr())));
+
+ (mod_prefix, format!("`{}`", Segment::names_to_string(mod_path)), module_did, None)
};
let (fallback_label, suggestion) = if path_str == "async"
@@ -299,21 +317,68 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
span_label: None,
could_be_expr: false,
suggestion,
+ module,
}
}
}
+ /// Try to suggest for a module path that cannot be resolved.
+ /// Such as `fmt::Debug` where `fmt` is not resolved without importing,
+ /// here we search with `lookup_import_candidates` for a module named `fmt`
+ /// with `TypeNS` as namespace.
+ ///
+ /// We need a separate function here because we won't suggest for a path with single segment
+ /// and we won't change `SourcePath` api `is_expected` to match `Type` with `DefKind::Mod`
+ pub(crate) fn smart_resolve_partial_mod_path_errors(
+ &mut self,
+ prefix_path: &[Segment],
+ path: &[Segment],
+ ) -> Vec<ImportSuggestion> {
+ let next_seg = if path.len() >= prefix_path.len() + 1 && prefix_path.len() == 1 {
+ path.get(prefix_path.len())
+ } else {
+ None
+ };
+ if let Some(segment) = prefix_path.last() &&
+ let Some(next_seg) = next_seg {
+ let candidates = self.r.lookup_import_candidates(
+ segment.ident,
+ Namespace::TypeNS,
+ &self.parent_scope,
+ &|res: Res| matches!(res, Res::Def(DefKind::Mod, _)),
+ );
+ // double check next seg is valid
+ candidates
+ .into_iter()
+ .filter(|candidate| {
+ if let Some(def_id) = candidate.did &&
+ let Some(module) = self.r.get_module(def_id) {
+ self.r.resolutions(module).borrow().iter().any(|(key, _r)| {
+ key.ident.name == next_seg.ident.name
+ })
+ } else {
+ false
+ }
+ })
+ .collect::<Vec<_>>()
+ } else {
+ Vec::new()
+ }
+ }
+
/// Handles error reporting for `smart_resolve_path_fragment` function.
/// Creates base error and amends it with one short label and possibly some longer helps/notes.
pub(crate) fn smart_resolve_report_errors(
&mut self,
path: &[Segment],
+ full_path: &[Segment],
span: Span,
source: PathSource<'_>,
res: Option<Res>,
) -> (DiagnosticBuilder<'tcx, ErrorGuaranteed>, Vec<ImportSuggestion>) {
debug!(?res, ?source);
let base_error = self.make_base_error(path, span, source, res);
+
let code = source.error_code(res.is_some());
let mut err = self.r.tcx.sess.struct_span_err_with_code(
base_error.span,
@@ -348,7 +413,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
}
let (found, candidates) =
- self.try_lookup_name_relaxed(&mut err, source, path, span, res, &base_error);
+ self.try_lookup_name_relaxed(&mut err, source, path, full_path, span, res, &base_error);
if found {
return (err, candidates);
}
@@ -365,6 +430,10 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
}
self.err_code_special_cases(&mut err, source, path, span);
+ if let Some(module) = base_error.module {
+ self.r.find_cfg_stripped(&mut err, &path.last().unwrap().ident.name, module);
+ }
+
(err, candidates)
}
@@ -450,6 +519,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
err: &mut Diagnostic,
source: PathSource<'_>,
path: &[Segment],
+ full_path: &[Segment],
span: Span,
res: Option<Res>,
base_error: &BaseError,
@@ -476,7 +546,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
// Try to filter out intrinsics candidates, as long as we have
// some other candidates to suggest.
let intrinsic_candidates: Vec<_> = candidates
- .drain_filter(|sugg| {
+ .extract_if(|sugg| {
let path = path_names_to_string(&sugg.path);
path.starts_with("core::intrinsics::") || path.starts_with("std::intrinsics::")
})
@@ -619,6 +689,10 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
}
}
+ if candidates.is_empty() {
+ candidates = self.smart_resolve_partial_mod_path_errors(path, full_path);
+ }
+
return (false, candidates);
}
@@ -1030,7 +1104,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
};
// Confirm that the target is an associated type.
- let (ty, position, path) = if let ast::TyKind::Path(Some(qself), path) = &bounded_ty.kind {
+ let (ty, _, 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;
@@ -1059,7 +1133,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
return false;
}
if let (
- [ast::PathSegment { ident: constrain_ident, args: None, .. }],
+ [ast::PathSegment { args: None, .. }],
[ast::GenericBound::Trait(poly_trait_ref, ast::TraitBoundModifier::None)],
) = (&type_param_path.segments[..], &bounds[..])
{
@@ -1067,29 +1141,11 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
&poly_trait_ref.trait_ref.path.segments[..]
{
if ident.span == span {
+ let Some(new_where_bound_predicate) = mk_where_bound_predicate(path, poly_trait_ref, ty) else { return false; };
err.span_suggestion_verbose(
*where_span,
format!("constrain the associated type to `{}`", ident),
- format!(
- "{}: {}<{} = {}>",
- self.r
- .tcx
- .sess
- .source_map()
- .span_to_snippet(ty.span) // Account for `<&'a T as Foo>::Bar`.
- .unwrap_or_else(|_| constrain_ident.to_string()),
- path.segments[..position]
- .iter()
- .map(|segment| path_segment_to_string(segment))
- .collect::<Vec<_>>()
- .join("::"),
- path.segments[position..]
- .iter()
- .map(|segment| path_segment_to_string(segment))
- .collect::<Vec<_>>()
- .join("::"),
- ident,
- ),
+ where_bound_predicate_to_string(&new_where_bound_predicate),
Applicability::MaybeIncorrect,
);
}
@@ -1248,7 +1304,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
}),
) if followed_by_brace => {
if let Some(sp) = closing_brace {
- err.span_label(span, fallback_label);
+ err.span_label(span, fallback_label.to_string());
err.multipart_suggestion(
"surround the struct literal with parentheses",
vec![
@@ -1320,7 +1376,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
);
}
_ => {
- err.span_label(span, fallback_label);
+ err.span_label(span, fallback_label.to_string());
}
}
};
@@ -1333,7 +1389,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
}))
| PathSource::Struct,
) => {
- err.span_label(span, fallback_label);
+ err.span_label(span, fallback_label.to_string());
err.span_suggestion_verbose(
span.shrink_to_hi(),
"use `!` to invoke the macro",
@@ -1345,7 +1401,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
}
}
(Res::Def(DefKind::Macro(MacroKind::Bang), _), _) => {
- err.span_label(span, fallback_label);
+ err.span_label(span, fallback_label.to_string());
}
(Res::Def(DefKind::TyAlias, def_id), PathSource::Trait(_)) => {
err.span_label(span, "type aliases cannot be used as traits");
@@ -1513,7 +1569,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
);
}
(Res::SelfTyParam { .. } | Res::SelfTyAlias { .. }, _) if ns == ValueNS => {
- err.span_label(span, fallback_label);
+ err.span_label(span, fallback_label.to_string());
err.note("can't use `Self` as a constructor, you must use the implemented struct");
}
(Res::Def(DefKind::TyAlias | DefKind::AssocTy, _), _) if ns == ValueNS => {
@@ -1537,7 +1593,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
return None;
}
- let resolutions = self.r.resolutions(module);
+ let resolutions = self.r.resolutions(*module);
let targets = resolutions
.borrow()
.iter()
@@ -1829,6 +1885,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
path,
accessible: true,
note: None,
+ via_import: false,
},
));
} else {
@@ -2180,10 +2237,9 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
None => {
debug!(?param.ident, ?param.ident.span);
let deletion_span = deletion_span();
- // the give lifetime originates from expanded code so we won't be able to remove it #104432
- let lifetime_only_in_expanded_code =
- deletion_span.map(|sp| sp.in_derive_expansion()).unwrap_or(true);
- if !lifetime_only_in_expanded_code {
+
+ // if the lifetime originates from expanded code, we won't be able to remove it #104432
+ if deletion_span.is_some_and(|sp| !sp.in_derive_expansion()) {
self.r.lint_buffer.buffer_lint_with_diagnostic(
lint::builtin::UNUSED_LIFETIMES,
param.id,
@@ -2243,7 +2299,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
&self,
err: &mut Diagnostic,
name: Option<&str>,
- suggest: impl Fn(&mut Diagnostic, bool, Span, &str, String) -> bool,
+ suggest: impl Fn(&mut Diagnostic, bool, Span, Cow<'static, str>, String) -> bool,
) {
let mut suggest_note = true;
for rib in self.lifetime_ribs.iter().rev() {
@@ -2288,22 +2344,23 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
(span, sugg)
};
if higher_ranked {
- let message = format!(
+ let message = Cow::from(format!(
"consider making the {} lifetime-generic with a new `{}` lifetime",
kind.descr(),
name.unwrap_or("'a"),
- );
- should_continue = suggest(err, true, span, &message, sugg);
+ ));
+ should_continue = suggest(err, true, span, message, sugg);
err.note_once(
"for more information on higher-ranked polymorphism, visit \
https://doc.rust-lang.org/nomicon/hrtb.html",
);
} else if let Some(name) = name {
- let message = format!("consider introducing lifetime `{}` here", name);
- should_continue = suggest(err, false, span, &message, sugg);
+ let message =
+ Cow::from(format!("consider introducing lifetime `{}` here", name));
+ should_continue = suggest(err, false, span, message, sugg);
} else {
- let message = "consider introducing a named lifetime parameter";
- should_continue = suggest(err, false, span, &message, sugg);
+ let message = Cow::from("consider introducing a named lifetime parameter");
+ should_continue = suggest(err, false, span, message, sugg);
}
}
LifetimeRibKind::Item => break,
@@ -2584,6 +2641,70 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
}
}
+fn mk_where_bound_predicate(
+ path: &Path,
+ poly_trait_ref: &ast::PolyTraitRef,
+ ty: &ast::Ty,
+) -> Option<ast::WhereBoundPredicate> {
+ use rustc_span::DUMMY_SP;
+ let modified_segments = {
+ let mut segments = path.segments.clone();
+ let [preceding @ .., second_last, last] = segments.as_mut_slice() else { return None; };
+ let mut segments = ThinVec::from(preceding);
+
+ let added_constraint = ast::AngleBracketedArg::Constraint(ast::AssocConstraint {
+ id: DUMMY_NODE_ID,
+ ident: last.ident,
+ gen_args: None,
+ kind: ast::AssocConstraintKind::Equality {
+ term: ast::Term::Ty(ast::ptr::P(ast::Ty {
+ kind: ast::TyKind::Path(None, poly_trait_ref.trait_ref.path.clone()),
+ id: DUMMY_NODE_ID,
+ span: DUMMY_SP,
+ tokens: None,
+ })),
+ },
+ span: DUMMY_SP,
+ });
+
+ match second_last.args.as_deref_mut() {
+ Some(ast::GenericArgs::AngleBracketed(ast::AngleBracketedArgs { args, .. })) => {
+ args.push(added_constraint);
+ }
+ Some(_) => return None,
+ None => {
+ second_last.args =
+ Some(ast::ptr::P(ast::GenericArgs::AngleBracketed(ast::AngleBracketedArgs {
+ args: ThinVec::from([added_constraint]),
+ span: DUMMY_SP,
+ })));
+ }
+ }
+
+ segments.push(second_last.clone());
+ segments
+ };
+
+ let new_where_bound_predicate = ast::WhereBoundPredicate {
+ span: DUMMY_SP,
+ bound_generic_params: ThinVec::new(),
+ bounded_ty: ast::ptr::P(ty.clone()),
+ bounds: vec![ast::GenericBound::Trait(
+ ast::PolyTraitRef {
+ bound_generic_params: ThinVec::new(),
+ trait_ref: ast::TraitRef {
+ path: ast::Path { segments: modified_segments, span: DUMMY_SP, tokens: None },
+ ref_id: DUMMY_NODE_ID,
+ },
+ span: DUMMY_SP,
+ },
+ ast::TraitBoundModifier::None,
+ )],
+ };
+
+ Some(new_where_bound_predicate)
+}
+
/// Report lifetime/lifetime shadowing as an error.
pub(super) fn signal_lifetime_shadowing(sess: &Session, orig: Ident, shadower: Ident) {
let mut err = struct_span_err!(