use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE};
use rustc_hir::PrimTy;
use rustc_middle::bug;
-use rustc_middle::ty::{self, DefIdTree};
+use rustc_middle::ty::DefIdTree;
use rustc_session::Session;
use rustc_span::hygiene::MacroKind;
use rustc_span::lev_distance::find_best_match_for_name;
pub descr: &'static str,
pub path: Path,
pub accessible: bool,
+ /// An extra note that should be issued if this item is suggested
+ pub note: Option<String>,
}
/// Adjust the impl span so that just the `impl` keyword is taken by removing
err.span_label(span, label);
if let Some((suggestions, msg, applicability)) = suggestion {
+ if suggestions.is_empty() {
+ err.help(&msg);
+ return err;
+ }
err.multipart_suggestion(&msg, suggestions, applicability);
}
// let foo =...
// ^^^ given this Span
// ------- get this Span to have an applicable suggestion
+
+ // edit:
+ // only do this if the const and usage of the non-constant value are on the same line
+ // the further the two are apart, the higher the chance of the suggestion being wrong
+ // also make sure that the pos for the suggestion is not 0 (ICE #90878)
+
let sp =
self.session.source_map().span_extend_to_prev_str(ident.span, current, true);
- if sp.lo().0 == 0 {
+
+ let pos_for_suggestion = sp.lo().0.saturating_sub(current.len() as u32);
+
+ if sp.lo().0 == 0
+ || pos_for_suggestion == 0
+ || self.session.source_map().is_multiline(sp)
+ {
err.span_label(ident.span, &format!("this would need to be a `{}`", sugg));
} else {
- let sp = sp.with_lo(BytePos(sp.lo().0 - current.len() as u32));
+ let sp = sp.with_lo(BytePos(pos_for_suggestion));
err.span_suggestion(
sp,
&format!("consider using `{}` instead of `{}`", sugg, current),
suggestions.extend(
BUILTIN_ATTRIBUTES
.iter()
- .map(|(name, ..)| TypoSuggestion::typo_from_res(*name, res)),
+ .map(|attr| TypoSuggestion::typo_from_res(attr.name, res)),
);
}
}
return;
}
+ // #90113: Do not count an inaccessible reexported item as a candidate.
+ if let NameBindingKind::Import { binding, .. } = name_binding.kind {
+ if this.is_accessible_from(binding.vis, parent_scope.module)
+ && !this.is_accessible_from(name_binding.vis, parent_scope.module)
+ {
+ return;
+ }
+ }
+
// collect results based on the filter function
// avoid suggesting anything from the same module in which we are resolving
+ // avoid suggesting anything with a hygienic name
if ident.name == lookup_ident.name
&& ns == namespace
&& !ptr::eq(in_module, parent_scope.module)
+ && !ident.span.normalize_to_macros_2_0().from_expansion()
{
let res = name_binding.res();
if filter_fn(res) {
}
if candidates.iter().all(|v: &ImportSuggestion| v.did != did) {
+ // See if we're recommending TryFrom, TryInto, or FromIterator and add
+ // a note about editions
+ let note = if let Some(did) = did {
+ let requires_note = !did.is_local()
+ && this.cstore().item_attrs(did, this.session).iter().any(
+ |attr| {
+ if attr.has_name(sym::rustc_diagnostic_item) {
+ [sym::TryInto, sym::TryFrom, sym::FromIterator]
+ .map(|x| Some(x))
+ .contains(&attr.value_str())
+ } else {
+ false
+ }
+ },
+ );
+
+ requires_note.then(|| {
+ format!(
+ "'{}' is included in the prelude starting in Edition 2021",
+ path_names_to_string(&path)
+ )
+ })
+ } else {
+ None
+ };
+
candidates.push(ImportSuggestion {
did,
descr: res.descr(),
path,
accessible: child_accessible,
+ note,
});
}
}
(b1, b2, misc1, misc2, false)
};
- let mut err = struct_span_err!(
- self.session,
- ident.span,
- E0659,
- "`{ident}` is ambiguous ({why})",
- why = kind.descr()
- );
+ let mut err = struct_span_err!(self.session, ident.span, E0659, "`{ident}` is ambiguous");
err.span_label(ident.span, "ambiguous name");
+ err.note(&format!("ambiguous because of {}", kind.descr()));
let mut could_refer_to = |b: &NameBinding<'_>, misc: AmbiguityErrorMisc, also: &str| {
let what = self.binding_description(b, ident, misc == AmbiguityErrorMisc::FromPrelude);
);
let def_span = self.session.source_map().guess_head_span(binding.span);
let mut note_span = MultiSpan::from_span(def_span);
- if !first && binding.vis == ty::Visibility::Public {
+ if !first && binding.vis.is_public() {
note_span.push_span_label(def_span, "consider importing it directly".into());
}
err.span_note(note_span, &msg);
if fst.ident.span.rust_2018() && !fst.ident.is_path_segment_keyword() =>
{
// Insert a placeholder that's later replaced by `self`/`super`/etc.
- path.insert(0, Segment::from_ident(Ident::invalid()));
+ path.insert(0, Segment::from_ident(Ident::empty()));
}
_ => return None,
}
module: ModuleOrUniformRoot<'b>,
ident: Ident,
) -> Option<(Option<Suggestion>, Vec<String>)> {
- let mut crate_module = if let ModuleOrUniformRoot::Module(module) = module {
- module
- } else {
+ let ModuleOrUniformRoot::Module(mut crate_module) = module else {
return None;
};
return;
}
- let mut accessible_path_strings: Vec<(String, &str, Option<DefId>)> = Vec::new();
- let mut inaccessible_path_strings: Vec<(String, &str, Option<DefId>)> = Vec::new();
+ let mut accessible_path_strings: Vec<(String, &str, Option<DefId>, &Option<String>)> =
+ Vec::new();
+ let mut inaccessible_path_strings: Vec<(String, &str, Option<DefId>, &Option<String>)> =
+ Vec::new();
candidates.iter().for_each(|c| {
(if c.accessible { &mut accessible_path_strings } else { &mut inaccessible_path_strings })
- .push((path_names_to_string(&c.path), c.descr, c.did))
+ .push((path_names_to_string(&c.path), c.descr, c.did, &c.note))
});
// we want consistent results across executions, but candidates are produced
let instead = if instead { " instead" } else { "" };
let mut msg = format!("consider importing {} {}{}", determiner, kind, instead);
+ for note in accessible_path_strings.iter().map(|cand| cand.3.as_ref()).flatten() {
+ err.note(note);
+ }
+
if let Some(span) = use_placement_span {
for candidate in &mut accessible_path_strings {
// produce an additional newline to separate the new use statement
assert!(!inaccessible_path_strings.is_empty());
if inaccessible_path_strings.len() == 1 {
- let (name, descr, def_id) = &inaccessible_path_strings[0];
+ let (name, descr, def_id, note) = &inaccessible_path_strings[0];
let msg = format!("{} `{}` exists but is inaccessible", descr, name);
if let Some(local_def_id) = def_id.and_then(|did| did.as_local()) {
} else {
err.note(&msg);
}
+ if let Some(note) = (*note).as_deref() {
+ err.note(note);
+ }
} else {
- let (_, descr_first, _) = &inaccessible_path_strings[0];
+ let (_, descr_first, _, _) = &inaccessible_path_strings[0];
let descr = if inaccessible_path_strings
.iter()
.skip(1)
- .all(|(_, descr, _)| descr == descr_first)
+ .all(|(_, descr, _, _)| descr == descr_first)
{
descr_first.to_string()
} else {
let mut has_colon = false;
let mut spans = Vec::new();
- for (name, _, def_id) in &inaccessible_path_strings {
+ for (name, _, def_id, _) in &inaccessible_path_strings {
if let Some(local_def_id) = def_id.and_then(|did| did.as_local()) {
let span = definitions.def_span(local_def_id);
let span = session.source_map().guess_head_span(span);
multi_span.push_span_label(span, format!("`{}`: not accessible", name));
}
+ for note in inaccessible_path_strings.iter().map(|cand| cand.3.as_ref()).flatten() {
+ err.note(note);
+ }
+
err.span_note(multi_span, &msg);
}
}