}
fn report_with_use_injections(&mut self, krate: &Crate) {
- for UseError { mut err, candidates, def_id, instead, suggestion } in
+ for UseError { mut err, candidates, def_id, instead, suggestion, path } in
self.use_injections.drain(..)
{
let (span, found_use) = if let Some(def_id) = def_id.as_local() {
UsePlacementFinder::check(krate, self.def_id_to_node_id[def_id])
} else {
- (None, false)
+ (None, FoundUse::No)
};
if !candidates.is_empty() {
show_candidates(
&mut err,
span,
&candidates,
- instead,
+ if instead { Instead::Yes } else { Instead::No },
found_use,
+ IsPattern::No,
+ path,
);
} else if let Some((span, msg, sugg, appl)) = suggestion {
err.span_suggestion(span, msg, sugg, appl);
crate fn lint_if_path_starts_with_module(
&mut self,
- finalize: Finalize,
+ finalize: Option<Finalize>,
path: &[Segment],
second_binding: Option<&NameBinding<'_>>,
) {
- let (diag_id, diag_span) = match finalize {
- Finalize::No => return,
- Finalize::SimplePath(id, path_span) => (id, path_span),
- Finalize::UsePath { root_id, root_span, .. } => (root_id, root_span),
- Finalize::QPathTrait { qpath_id, qpath_span, .. } => (qpath_id, qpath_span),
+ let Some(Finalize { node_id, root_span, .. }) = finalize else {
+ return;
};
let first_name = match path.get(0) {
}
}
- let diag = BuiltinLintDiagnostics::AbsPathWithModule(diag_span);
+ let diag = BuiltinLintDiagnostics::AbsPathWithModule(root_span);
self.lint_buffer.buffer_lint_with_diagnostic(
ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE,
- diag_id,
- diag_span,
+ node_id,
+ root_span,
"absolute paths must start with `self`, `super`, \
`crate`, or an external crate name in the 2018 edition",
diag,
///
/// This takes the error provided, combines it with the span and any additional spans inside the
/// error and emits it.
- crate fn report_error(&self, span: Span, resolution_error: ResolutionError<'_>) {
+ crate fn report_error(&mut self, span: Span, resolution_error: ResolutionError<'a>) {
self.into_struct_error(span, resolution_error).emit();
}
crate fn into_struct_error(
- &self,
+ &mut self,
span: Span,
- resolution_error: ResolutionError<'_>,
+ resolution_error: ResolutionError<'a>,
) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
match resolution_error {
ResolutionError::GenericParamsFromOuterFunction(outer_res, has_generic_params) => {
}
err
}
- ResolutionError::VariableNotBoundInPattern(binding_error) => {
+ ResolutionError::VariableNotBoundInPattern(binding_error, parent_scope) => {
let BindingError { name, target, origin, could_be_path } = binding_error;
let target_sp = target.iter().copied().collect::<Vec<_>>();
for sp in origin_sp {
err.span_label(sp, "variable not in all patterns");
}
- if *could_be_path {
- let help_msg = format!(
- "if you meant to match on a variant or a `const` item, consider \
- making the path in the pattern qualified: `?::{}`",
- name,
+ if could_be_path {
+ let import_suggestions = self.lookup_import_candidates(
+ Ident::with_dummy_span(name),
+ Namespace::ValueNS,
+ &parent_scope,
+ &|res: Res| match res {
+ Res::Def(
+ DefKind::Ctor(CtorOf::Variant, CtorKind::Const)
+ | DefKind::Ctor(CtorOf::Struct, CtorKind::Const)
+ | DefKind::Const
+ | DefKind::AssocConst,
+ _,
+ ) => true,
+ _ => false,
+ },
+ );
+
+ if import_suggestions.is_empty() {
+ let help_msg = format!(
+ "if you meant to match on a variant or a `const` item, consider \
+ making the path in the pattern qualified: `path::to::ModOrType::{}`",
+ name,
+ );
+ err.span_help(span, &help_msg);
+ }
+ show_candidates(
+ &self.definitions,
+ self.session,
+ &mut err,
+ Some(span),
+ &import_suggestions,
+ Instead::No,
+ FoundUse::Yes,
+ IsPattern::Yes,
+ vec![],
);
- err.span_help(span, &help_msg);
}
err
}
}
crate fn report_vis_error(
- &self,
+ &mut self,
vis_resolution_error: VisResolutionError<'_>,
) -> ErrorGuaranteed {
match vis_resolution_error {
segms.push(ast::PathSegment::from_ident(ident));
let path = Path { span: name_binding.span, segments: segms, tokens: None };
let did = match res {
- Res::Def(DefKind::Ctor(..), did) => this.parent(did),
+ Res::Def(DefKind::Ctor(..), did) => this.opt_parent(did),
_ => res.opt_def_id(),
};
err,
None,
&import_suggestions,
- false,
- true,
+ Instead::No,
+ FoundUse::Yes,
+ IsPattern::No,
+ vec![],
);
if macro_kind == MacroKind::Derive && (ident.name == sym::Send || ident.name == sym::Sync) {
&parent_scope,
None,
false,
- false,
None,
) {
let desc = match binding.res() {
_,
) = binding.kind
{
- let def_id = self.parent(ctor_def_id).expect("no parent for a constructor");
+ let def_id = self.parent(ctor_def_id);
let fields = self.field_names.get(&def_id)?;
return fields.iter().map(|name| name.span).reduce(Span::to); // None for `struct Foo()`
}
opt_ns: Option<Namespace>, // `None` indicates a module path in import
parent_scope: &ParentScope<'a>,
ribs: Option<&PerNS<Vec<Rib<'a>>>>,
- unusable_binding: Option<&'a NameBinding<'a>>,
+ ignore_binding: Option<&'a NameBinding<'a>>,
module: Option<ModuleOrUniformRoot<'a>>,
i: usize,
ident: Ident,
ns_to_try,
parent_scope,
None,
- false,
- unusable_binding,
+ ignore_binding,
).ok()
} else if let Some(ribs) = ribs
&& let Some(TypeNS | ValueNS) = opt_ns
ident,
ns_to_try,
parent_scope,
- Finalize::No,
+ None,
&ribs[ns_to_try],
- unusable_binding,
+ ignore_binding,
) {
// we found a locally-imported or available item/module
Some(LexicalScopeBinding::Item(binding)) => Some(binding),
parent_scope,
None,
false,
- false,
- unusable_binding,
+ ignore_binding,
).ok()
};
if let Some(binding) = binding {
ident,
ValueNS,
parent_scope,
- Finalize::No,
+ None,
&ribs[ValueNS],
- unusable_binding,
+ ignore_binding,
)
} else {
None
(next_left_bracket == after_second_colon, from_second_colon)
}
+/// A suggestion has already been emitted, change the wording slightly to clarify that both are
+/// independent options.
+enum Instead {
+ Yes,
+ No,
+}
+
+/// Whether an existing place with an `use` item was found.
+enum FoundUse {
+ Yes,
+ No,
+}
+
+/// Whether a binding is part of a pattern or an expression. Used for diagnostics.
+enum IsPattern {
+ /// The binding is part of a pattern
+ Yes,
+ /// The binding is part of an expression
+ No,
+}
+
/// When an entity with a given name is not available in scope, we search for
/// entities with that name in all crates. This method allows outputting the
/// results of this search in a programmer-friendly way
// This is `None` if all placement locations are inside expansions
use_placement_span: Option<Span>,
candidates: &[ImportSuggestion],
- instead: bool,
- found_use: bool,
+ instead: Instead,
+ found_use: FoundUse,
+ is_pattern: IsPattern,
+ path: Vec<Segment>,
) {
if candidates.is_empty() {
return;
}
if !accessible_path_strings.is_empty() {
- let (determiner, kind) = if accessible_path_strings.len() == 1 {
- ("this", accessible_path_strings[0].1)
+ let (determiner, kind, name) = if accessible_path_strings.len() == 1 {
+ ("this", accessible_path_strings[0].1, format!(" `{}`", accessible_path_strings[0].0))
} else {
- ("one of these", "items")
+ ("one of these", "items", String::new())
};
- let instead = if instead { " instead" } else { "" };
- let mut msg = format!("consider importing {} {}{}", determiner, kind, instead);
+ let instead = if let Instead::Yes = instead { " instead" } else { "" };
+ let mut msg = if let IsPattern::Yes = is_pattern {
+ format!(
+ "if you meant to match on {}{}{}, use the full path in the pattern",
+ kind, instead, name
+ )
+ } else {
+ format!("consider importing {} {}{}", determiner, kind, instead)
+ };
for note in accessible_path_strings.iter().flat_map(|cand| cand.3.as_ref()) {
err.note(note);
}
- if let Some(span) = use_placement_span {
+ if let (IsPattern::Yes, Some(span)) = (is_pattern, use_placement_span) {
+ err.span_suggestions(
+ span,
+ &msg,
+ accessible_path_strings.into_iter().map(|a| a.0),
+ Applicability::MaybeIncorrect,
+ );
+ } else if let Some(span) = use_placement_span {
for candidate in &mut accessible_path_strings {
// produce an additional newline to separate the new use statement
// from the directly following item.
- let additional_newline = if found_use { "" } else { "\n" };
+ let additional_newline = if let FoundUse::Yes = found_use { "" } else { "\n" };
candidate.0 = format!("use {};\n{}", &candidate.0, additional_newline);
}
span,
&msg,
accessible_path_strings.into_iter().map(|a| a.0),
- Applicability::Unspecified,
+ Applicability::MaybeIncorrect,
);
+ if let [first, .., last] = &path[..] {
+ err.span_suggestion_verbose(
+ first.ident.span.until(last.ident.span),
+ &format!("if you import `{}`, refer to it directly", last.ident),
+ String::new(),
+ Applicability::Unspecified,
+ );
+ }
} else {
msg.push(':');
} else {
assert!(!inaccessible_path_strings.is_empty());
+ let prefix =
+ if let IsPattern::Yes = is_pattern { "you might have meant to match on " } else { "" };
if inaccessible_path_strings.len() == 1 {
let (name, descr, def_id, note) = &inaccessible_path_strings[0];
- let msg = format!("{} `{}` exists but is inaccessible", descr, name);
+ let msg = format!(
+ "{}{} `{}`{} exists but is inaccessible",
+ prefix,
+ descr,
+ name,
+ if let IsPattern::Yes = is_pattern { ", which" } else { "" }
+ );
if let Some(local_def_id) = def_id.and_then(|did| did.as_local()) {
let span = definitions.def_span(local_def_id);
"item".to_string()
};
- let mut msg = format!("these {}s exist but are inaccessible", descr);
+ let mut msg = format!("{}these {}s exist but are inaccessible", prefix, descr);
let mut has_colon = false;
let mut spans = Vec::new();
}
impl UsePlacementFinder {
- fn check(krate: &Crate, target_module: NodeId) -> (Option<Span>, bool) {
+ fn check(krate: &Crate, target_module: NodeId) -> (Option<Span>, FoundUse) {
let mut finder =
UsePlacementFinder { target_module, first_legal_span: None, first_use_span: None };
finder.visit_crate(krate);
if let Some(use_span) = finder.first_use_span {
- (Some(use_span), true)
+ (Some(use_span), FoundUse::Yes)
} else {
- (finder.first_legal_span, false)
+ (finder.first_legal_span, FoundUse::No)
}
}
}