use rustc_feature::BUILTIN_ATTRIBUTES;
use rustc_hir::def::Namespace::{self, *};
use rustc_hir::def::{self, CtorKind, CtorOf, DefKind, NonMacroAttrKind, PerNS};
-use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE};
+use rustc_hir::def_id::{DefId, CRATE_DEF_ID, LOCAL_CRATE};
use rustc_hir::PrimTy;
use rustc_middle::bug;
use rustc_middle::ty::DefIdTree;
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,
);
} else if let Some((span, msg, sugg, appl)) = suggestion {
err.span_suggestion(span, msg, sugg, appl);
///
/// 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,
);
- err.span_help(span, &help_msg);
}
err
}
}
ResolutionError::InvalidAsmSym => {
let mut err = self.session.struct_span_err(span, "invalid `sym` operand");
- err.span_label(span, &format!("is a local variable"));
+ err.span_label(span, "is a local variable");
err.help("`sym` operands must refer to either a function or a static");
err
}
}
crate fn report_vis_error(
- &self,
+ &mut self,
vis_resolution_error: VisResolutionError<'_>,
) -> ErrorGuaranteed {
match vis_resolution_error {
}
Scope::ExternPrelude => {
suggestions.extend(this.extern_prelude.iter().filter_map(|(ident, _)| {
- let res = Res::Def(DefKind::Mod, DefId::local(CRATE_DEF_INDEX));
+ let res = Res::Def(DefKind::Mod, CRATE_DEF_ID.to_def_id());
filter_fn(res).then_some(TypoSuggestion::typo_from_res(ident.name, res))
}));
}
err,
None,
&import_suggestions,
- false,
- true,
+ Instead::No,
+ FoundUse::Yes,
+ IsPattern::No,
);
if macro_kind == MacroKind::Derive && (ident.name == sym::Send || ident.name == sym::Sync) {
/// Given a `binding_span` of a binding within a use statement:
///
-/// ```
+/// ```ignore (illustrative)
/// use foo::{a, b, c};
-/// ^
+/// // ^
/// ```
///
/// then return the span until the next binding or the end of the statement:
///
-/// ```
+/// ```ignore (illustrative)
/// use foo::{a, b, c};
-/// ^^^
+/// // ^^^
/// ```
fn find_span_of_binding_until_next_binding(
sess: &Session,
/// Given a `binding_span`, return the span through to the comma or opening brace of the previous
/// binding.
///
-/// ```
+/// ```ignore (illustrative)
/// use foo::a::{a, b, c};
-/// ^^--- binding span
-/// |
-/// returned span
+/// // ^^--- binding span
+/// // |
+/// // returned span
///
/// use foo::{a, b, c};
-/// --- binding span
+/// // --- binding span
/// ```
fn extend_span_to_previous_binding(sess: &Session, binding_span: Span) -> Option<Span> {
let source_map = sess.source_map();
/// Given a `use_span` of a binding within a use statement, returns the highlighted span and if
/// it is a nested use tree.
///
-/// ```
+/// ```ignore (illustrative)
/// use foo::a::{b, c};
-/// ^^^^^^^^^^ // false
+/// // ^^^^^^^^^^ -- false
///
/// use foo::{a, b, c};
-/// ^^^^^^^^^^ // true
+/// // ^^^^^^^^^^ -- true
///
/// use foo::{a, b::{c, d}};
-/// ^^^^^^^^^^^^^^^ // true
+/// // ^^^^^^^^^^^^^^^ -- true
/// ```
fn find_span_immediately_after_crate_name(
sess: &Session,
(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,
) {
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,
);
} 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)
}
}
}