2020-03-07 12:18:29 -06:00
|
|
|
use std::ptr;
|
2018-09-20 10:15:52 -05:00
|
|
|
|
2022-04-08 15:52:18 -05:00
|
|
|
use rustc_ast::ptr::P;
|
|
|
|
use rustc_ast::visit::{self, Visitor};
|
|
|
|
use rustc_ast::{self as ast, Crate, ItemKind, ModKind, NodeId, Path, CRATE_NODE_ID};
|
2020-01-11 10:02:46 -06:00
|
|
|
use rustc_ast_pretty::pprust;
|
2019-12-23 22:02:53 -06:00
|
|
|
use rustc_data_structures::fx::FxHashSet;
|
2022-04-08 15:52:18 -05:00
|
|
|
use rustc_errors::struct_span_err;
|
|
|
|
use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan};
|
2019-11-29 19:34:18 -06:00
|
|
|
use rustc_feature::BUILTIN_ATTRIBUTES;
|
2020-01-04 19:37:57 -06:00
|
|
|
use rustc_hir::def::Namespace::{self, *};
|
2022-04-08 15:51:55 -05:00
|
|
|
use rustc_hir::def::{self, CtorKind, CtorOf, DefKind, NonMacroAttrKind, PerNS};
|
2022-05-30 11:49:17 -05:00
|
|
|
use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID, LOCAL_CRATE};
|
2021-02-02 12:16:35 -06:00
|
|
|
use rustc_hir::PrimTy;
|
2022-05-30 11:49:17 -05:00
|
|
|
use rustc_index::vec::IndexVec;
|
2020-03-29 10:19:48 -05:00
|
|
|
use rustc_middle::bug;
|
2021-11-07 21:53:26 -06:00
|
|
|
use rustc_middle::ty::DefIdTree;
|
2022-04-08 15:52:18 -05:00
|
|
|
use rustc_session::lint::builtin::ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE;
|
|
|
|
use rustc_session::lint::builtin::MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS;
|
|
|
|
use rustc_session::lint::BuiltinLintDiagnostics;
|
2020-03-11 06:49:08 -05:00
|
|
|
use rustc_session::Session;
|
2022-04-08 15:51:55 -05:00
|
|
|
use rustc_span::edition::Edition;
|
2019-12-31 11:15:40 -06:00
|
|
|
use rustc_span::hygiene::MacroKind;
|
Move lev_distance to rustc_ast, make non-generic
rustc_ast currently has a few dependencies on rustc_lexer. Ideally, an AST
would not have any dependency its lexer, for minimizing unnecessarily
design-time dependencies. Breaking this dependency would also have practical
benefits, since modifying rustc_lexer would not trigger a rebuild of rustc_ast.
This commit does not remove the rustc_ast --> rustc_lexer dependency,
but it does remove one of the sources of this dependency, which is the
code that handles fuzzy matching between symbol names for making suggestions
in diagnostics. Since that code depends only on Symbol, it is easy to move
it to rustc_span. It might even be best to move it to a separate crate,
since other tools such as Cargo use the same algorithm, and have simply
contain a duplicate of the code.
This changes the signature of find_best_match_for_name so that it is no
longer generic over its input. I checked the optimized binaries, and this
function was duplicated at nearly every call site, because most call sites
used short-lived iterator chains, generic over Map and such. But there's
no good reason for a function like this to be generic, since all it does
is immediately convert the generic input (the Iterator impl) to a concrete
Vec<Symbol>. This has all of the costs of generics (duplicated method bodies)
with no benefit.
Changing find_best_match_for_name to be non-generic removed about 10KB of
code from the optimized binary. I know it's a drop in the bucket, but we have
to start reducing binary size, and beginning to tame over-use of generics
is part of that.
2020-11-12 13:24:10 -06:00
|
|
|
use rustc_span::lev_distance::find_best_match_for_name;
|
2020-01-01 12:25:28 -06:00
|
|
|
use rustc_span::source_map::SourceMap;
|
2020-07-07 20:04:10 -05:00
|
|
|
use rustc_span::symbol::{kw, sym, Ident, Symbol};
|
2022-03-23 21:03:04 -05:00
|
|
|
use rustc_span::{BytePos, Span};
|
2020-08-14 01:05:01 -05:00
|
|
|
use tracing::debug;
|
2018-09-20 10:15:52 -05:00
|
|
|
|
2020-03-07 09:49:13 -06:00
|
|
|
use crate::imports::{Import, ImportKind, ImportResolver};
|
2022-07-08 06:01:30 -05:00
|
|
|
use crate::late::{PatternSource, Rib};
|
2019-11-03 11:28:20 -06:00
|
|
|
use crate::path_names_to_string;
|
2022-04-08 15:52:18 -05:00
|
|
|
use crate::{AmbiguityError, AmbiguityErrorMisc, AmbiguityKind, BindingError, Finalize};
|
|
|
|
use crate::{HasGenericParams, MacroRulesScope, Module, ModuleKind, ModuleOrUniformRoot};
|
2022-04-08 15:51:55 -05:00
|
|
|
use crate::{LexicalScopeBinding, NameBinding, NameBindingKind, PrivacyError, VisResolutionError};
|
2022-04-08 15:52:18 -05:00
|
|
|
use crate::{ParentScope, PathResult, ResolutionError, Resolver, Scope, ScopeSet};
|
|
|
|
use crate::{Segment, UseError};
|
2019-07-11 13:13:11 -05:00
|
|
|
|
2022-04-16 21:01:17 -05:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests;
|
|
|
|
|
2019-07-11 13:13:11 -05:00
|
|
|
type Res = def::Res<ast::NodeId>;
|
|
|
|
|
|
|
|
/// A vector of spans and replacements, a message and applicability.
|
2022-05-20 18:51:09 -05:00
|
|
|
pub(crate) type Suggestion = (Vec<(Span, String)>, String, Applicability);
|
2019-07-11 13:13:11 -05:00
|
|
|
|
2020-06-25 09:16:38 -05:00
|
|
|
/// Potential candidate for an undeclared or out-of-scope label - contains the ident of a
|
|
|
|
/// similarly named label and whether or not it is reachable.
|
2022-05-20 18:51:09 -05:00
|
|
|
pub(crate) type LabelSuggestion = (Ident, bool);
|
2020-06-25 09:16:38 -05:00
|
|
|
|
2022-05-20 18:51:09 -05:00
|
|
|
pub(crate) enum SuggestionTarget {
|
2021-08-04 16:29:30 -05:00
|
|
|
/// The target has a similar name as the name used by the programmer (probably a typo)
|
|
|
|
SimilarlyNamed,
|
|
|
|
/// The target is the only valid item that can be used in the corresponding context
|
|
|
|
SingleItem,
|
|
|
|
}
|
|
|
|
|
2022-05-20 18:51:09 -05:00
|
|
|
pub(crate) struct TypoSuggestion {
|
2019-08-07 18:39:02 -05:00
|
|
|
pub candidate: Symbol,
|
|
|
|
pub res: Res,
|
2021-08-04 16:29:30 -05:00
|
|
|
pub target: SuggestionTarget,
|
2019-07-11 13:13:11 -05:00
|
|
|
}
|
|
|
|
|
2019-07-11 18:29:28 -05:00
|
|
|
impl TypoSuggestion {
|
2022-05-20 18:51:09 -05:00
|
|
|
pub(crate) fn typo_from_res(candidate: Symbol, res: Res) -> TypoSuggestion {
|
2021-08-04 16:29:30 -05:00
|
|
|
Self { candidate, res, target: SuggestionTarget::SimilarlyNamed }
|
|
|
|
}
|
2022-05-20 18:51:09 -05:00
|
|
|
pub(crate) fn single_item_from_res(candidate: Symbol, res: Res) -> TypoSuggestion {
|
2021-08-04 16:29:30 -05:00
|
|
|
Self { candidate, res, target: SuggestionTarget::SingleItem }
|
2019-07-11 18:29:28 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-11 13:13:11 -05:00
|
|
|
/// A free importable items suggested in case of resolution failure.
|
2022-05-20 18:51:09 -05:00
|
|
|
pub(crate) struct ImportSuggestion {
|
2019-08-07 18:39:02 -05:00
|
|
|
pub did: Option<DefId>,
|
2020-05-04 17:12:06 -05:00
|
|
|
pub descr: &'static str,
|
2019-07-11 13:13:11 -05:00
|
|
|
pub path: Path,
|
2020-06-13 12:58:46 -05:00
|
|
|
pub accessible: bool,
|
2021-10-25 19:04:35 -05:00
|
|
|
/// An extra note that should be issued if this item is suggested
|
|
|
|
pub note: Option<String>,
|
2019-07-11 13:13:11 -05:00
|
|
|
}
|
2019-02-06 11:15:23 -06:00
|
|
|
|
2019-08-08 15:32:58 -05:00
|
|
|
/// Adjust the impl span so that just the `impl` keyword is taken by removing
|
|
|
|
/// everything after `<` (`"impl<T> Iterator for A<T> {}" -> "impl"`) and
|
|
|
|
/// everything after the first whitespace (`"impl Iterator for A" -> "impl"`).
|
|
|
|
///
|
|
|
|
/// *Attention*: the method used is very fragile since it essentially duplicates the work of the
|
|
|
|
/// parser. If you need to use this function or something similar, please consider updating the
|
|
|
|
/// `source_map` functions and this function to something more robust.
|
2020-02-22 08:07:05 -06:00
|
|
|
fn reduce_impl_span_to_impl_keyword(sm: &SourceMap, impl_span: Span) -> Span {
|
|
|
|
let impl_span = sm.span_until_char(impl_span, '<');
|
2020-03-21 18:20:58 -05:00
|
|
|
sm.span_until_whitespace(impl_span)
|
2019-08-08 15:32:58 -05:00
|
|
|
}
|
|
|
|
|
2019-08-15 17:18:14 -05:00
|
|
|
impl<'a> Resolver<'a> {
|
2022-05-20 18:51:09 -05:00
|
|
|
pub(crate) fn report_errors(&mut self, krate: &Crate) {
|
2022-04-08 15:52:18 -05:00
|
|
|
self.report_with_use_injections(krate);
|
|
|
|
|
|
|
|
for &(span_use, span_def) in &self.macro_expanded_macro_export_errors {
|
|
|
|
let msg = "macro-expanded `macro_export` macros from the current crate \
|
|
|
|
cannot be referred to by absolute paths";
|
|
|
|
self.lint_buffer.buffer_lint_with_diagnostic(
|
|
|
|
MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS,
|
|
|
|
CRATE_NODE_ID,
|
|
|
|
span_use,
|
|
|
|
msg,
|
|
|
|
BuiltinLintDiagnostics::MacroExpandedMacroExportsAccessedByAbsolutePaths(span_def),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
for ambiguity_error in &self.ambiguity_errors {
|
|
|
|
self.report_ambiguity_error(ambiguity_error);
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut reported_spans = FxHashSet::default();
|
|
|
|
for error in &self.privacy_errors {
|
|
|
|
if reported_spans.insert(error.dedup_span) {
|
|
|
|
self.report_privacy_error(error);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn report_with_use_injections(&mut self, krate: &Crate) {
|
2022-04-23 18:41:36 -05:00
|
|
|
for UseError { mut err, candidates, def_id, instead, suggestion, path } in
|
2022-04-08 15:52:18 -05:00
|
|
|
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 {
|
2022-04-29 21:40:36 -05:00
|
|
|
(None, FoundUse::No)
|
2022-04-08 15:52:18 -05:00
|
|
|
};
|
|
|
|
if !candidates.is_empty() {
|
|
|
|
show_candidates(
|
2022-05-30 11:49:17 -05:00
|
|
|
&self.session,
|
|
|
|
&self.source_span,
|
2022-04-08 15:52:18 -05:00
|
|
|
&mut err,
|
|
|
|
span,
|
|
|
|
&candidates,
|
2022-04-29 21:40:36 -05:00
|
|
|
if instead { Instead::Yes } else { Instead::No },
|
2022-04-08 15:52:18 -05:00
|
|
|
found_use,
|
2022-04-29 21:40:36 -05:00
|
|
|
IsPattern::No,
|
2022-04-23 18:41:36 -05:00
|
|
|
path,
|
2022-04-08 15:52:18 -05:00
|
|
|
);
|
|
|
|
} else if let Some((span, msg, sugg, appl)) = suggestion {
|
|
|
|
err.span_suggestion(span, msg, sugg, appl);
|
|
|
|
}
|
|
|
|
err.emit();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-20 18:51:09 -05:00
|
|
|
pub(crate) fn report_conflict<'b>(
|
2022-04-08 15:52:18 -05:00
|
|
|
&mut self,
|
|
|
|
parent: Module<'_>,
|
|
|
|
ident: Ident,
|
|
|
|
ns: Namespace,
|
|
|
|
new_binding: &NameBinding<'b>,
|
|
|
|
old_binding: &NameBinding<'b>,
|
|
|
|
) {
|
|
|
|
// Error on the second of two conflicting names
|
|
|
|
if old_binding.span.lo() > new_binding.span.lo() {
|
|
|
|
return self.report_conflict(parent, ident, ns, old_binding, new_binding);
|
|
|
|
}
|
|
|
|
|
|
|
|
let container = match parent.kind {
|
|
|
|
ModuleKind::Def(kind, _, _) => kind.descr(parent.def_id()),
|
2022-07-25 12:45:26 -05:00
|
|
|
ModuleKind::Block => "block",
|
2022-04-08 15:52:18 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
let old_noun = match old_binding.is_import() {
|
|
|
|
true => "import",
|
|
|
|
false => "definition",
|
|
|
|
};
|
|
|
|
|
|
|
|
let new_participle = match new_binding.is_import() {
|
|
|
|
true => "imported",
|
|
|
|
false => "defined",
|
|
|
|
};
|
|
|
|
|
|
|
|
let (name, span) =
|
|
|
|
(ident.name, self.session.source_map().guess_head_span(new_binding.span));
|
|
|
|
|
|
|
|
if let Some(s) = self.name_already_seen.get(&name) {
|
|
|
|
if s == &span {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let old_kind = match (ns, old_binding.module()) {
|
|
|
|
(ValueNS, _) => "value",
|
|
|
|
(MacroNS, _) => "macro",
|
|
|
|
(TypeNS, _) if old_binding.is_extern_crate() => "extern crate",
|
|
|
|
(TypeNS, Some(module)) if module.is_normal() => "module",
|
|
|
|
(TypeNS, Some(module)) if module.is_trait() => "trait",
|
|
|
|
(TypeNS, _) => "type",
|
|
|
|
};
|
|
|
|
|
|
|
|
let msg = format!("the name `{}` is defined multiple times", name);
|
|
|
|
|
|
|
|
let mut err = match (old_binding.is_extern_crate(), new_binding.is_extern_crate()) {
|
|
|
|
(true, true) => struct_span_err!(self.session, span, E0259, "{}", msg),
|
|
|
|
(true, _) | (_, true) => match new_binding.is_import() && old_binding.is_import() {
|
|
|
|
true => struct_span_err!(self.session, span, E0254, "{}", msg),
|
|
|
|
false => struct_span_err!(self.session, span, E0260, "{}", msg),
|
|
|
|
},
|
|
|
|
_ => match (old_binding.is_import(), new_binding.is_import()) {
|
|
|
|
(false, false) => struct_span_err!(self.session, span, E0428, "{}", msg),
|
|
|
|
(true, true) => struct_span_err!(self.session, span, E0252, "{}", msg),
|
|
|
|
_ => struct_span_err!(self.session, span, E0255, "{}", msg),
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
err.note(&format!(
|
|
|
|
"`{}` must be defined only once in the {} namespace of this {}",
|
|
|
|
name,
|
|
|
|
ns.descr(),
|
|
|
|
container
|
|
|
|
));
|
|
|
|
|
|
|
|
err.span_label(span, format!("`{}` re{} here", name, new_participle));
|
|
|
|
err.span_label(
|
|
|
|
self.session.source_map().guess_head_span(old_binding.span),
|
|
|
|
format!("previous {} of the {} `{}` here", old_noun, old_kind, name),
|
|
|
|
);
|
|
|
|
|
|
|
|
// See https://github.com/rust-lang/rust/issues/32354
|
|
|
|
use NameBindingKind::Import;
|
|
|
|
let import = match (&new_binding.kind, &old_binding.kind) {
|
|
|
|
// If there are two imports where one or both have attributes then prefer removing the
|
|
|
|
// import without attributes.
|
|
|
|
(Import { import: new, .. }, Import { import: old, .. })
|
|
|
|
if {
|
|
|
|
!new_binding.span.is_dummy()
|
|
|
|
&& !old_binding.span.is_dummy()
|
|
|
|
&& (new.has_attributes || old.has_attributes)
|
|
|
|
} =>
|
|
|
|
{
|
|
|
|
if old.has_attributes {
|
|
|
|
Some((new, new_binding.span, true))
|
|
|
|
} else {
|
|
|
|
Some((old, old_binding.span, true))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Otherwise prioritize the new binding.
|
|
|
|
(Import { import, .. }, other) if !new_binding.span.is_dummy() => {
|
|
|
|
Some((import, new_binding.span, other.is_import()))
|
|
|
|
}
|
|
|
|
(other, Import { import, .. }) if !old_binding.span.is_dummy() => {
|
|
|
|
Some((import, old_binding.span, other.is_import()))
|
|
|
|
}
|
|
|
|
_ => None,
|
|
|
|
};
|
|
|
|
|
|
|
|
// Check if the target of the use for both bindings is the same.
|
|
|
|
let duplicate = new_binding.res().opt_def_id() == old_binding.res().opt_def_id();
|
|
|
|
let has_dummy_span = new_binding.span.is_dummy() || old_binding.span.is_dummy();
|
|
|
|
let from_item =
|
|
|
|
self.extern_prelude.get(&ident).map_or(true, |entry| entry.introduced_by_item);
|
|
|
|
// Only suggest removing an import if both bindings are to the same def, if both spans
|
|
|
|
// aren't dummy spans. Further, if both bindings are imports, then the ident must have
|
|
|
|
// been introduced by an item.
|
|
|
|
let should_remove_import = duplicate
|
|
|
|
&& !has_dummy_span
|
|
|
|
&& ((new_binding.is_extern_crate() || old_binding.is_extern_crate()) || from_item);
|
|
|
|
|
|
|
|
match import {
|
|
|
|
Some((import, span, true)) if should_remove_import && import.is_nested() => {
|
|
|
|
self.add_suggestion_for_duplicate_nested_use(&mut err, import, span)
|
|
|
|
}
|
|
|
|
Some((import, _, true)) if should_remove_import && !import.is_glob() => {
|
|
|
|
// Simple case - remove the entire import. Due to the above match arm, this can
|
|
|
|
// only be a single use so just remove it entirely.
|
|
|
|
err.tool_only_span_suggestion(
|
|
|
|
import.use_span_with_attributes,
|
|
|
|
"remove unnecessary import",
|
2022-06-13 02:01:16 -05:00
|
|
|
"",
|
2022-04-08 15:52:18 -05:00
|
|
|
Applicability::MaybeIncorrect,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
Some((import, span, _)) => {
|
|
|
|
self.add_suggestion_for_rename_of_use(&mut err, name, import, span)
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
|
|
|
|
err.emit();
|
|
|
|
self.name_already_seen.insert(name, span);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// This function adds a suggestion to change the binding name of a new import that conflicts
|
|
|
|
/// with an existing import.
|
|
|
|
///
|
|
|
|
/// ```text,ignore (diagnostic)
|
|
|
|
/// help: you can use `as` to change the binding name of the import
|
|
|
|
/// |
|
|
|
|
/// LL | use foo::bar as other_bar;
|
|
|
|
/// | ^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
/// ```
|
|
|
|
fn add_suggestion_for_rename_of_use(
|
|
|
|
&self,
|
|
|
|
err: &mut Diagnostic,
|
|
|
|
name: Symbol,
|
|
|
|
import: &Import<'_>,
|
|
|
|
binding_span: Span,
|
|
|
|
) {
|
|
|
|
let suggested_name = if name.as_str().chars().next().unwrap().is_uppercase() {
|
|
|
|
format!("Other{}", name)
|
|
|
|
} else {
|
|
|
|
format!("other_{}", name)
|
|
|
|
};
|
|
|
|
|
|
|
|
let mut suggestion = None;
|
|
|
|
match import.kind {
|
|
|
|
ImportKind::Single { type_ns_only: true, .. } => {
|
|
|
|
suggestion = Some(format!("self as {}", suggested_name))
|
|
|
|
}
|
|
|
|
ImportKind::Single { source, .. } => {
|
|
|
|
if let Some(pos) =
|
|
|
|
source.span.hi().0.checked_sub(binding_span.lo().0).map(|pos| pos as usize)
|
|
|
|
{
|
|
|
|
if let Ok(snippet) = self.session.source_map().span_to_snippet(binding_span) {
|
|
|
|
if pos <= snippet.len() {
|
|
|
|
suggestion = Some(format!(
|
|
|
|
"{} as {}{}",
|
|
|
|
&snippet[..pos],
|
|
|
|
suggested_name,
|
|
|
|
if snippet.ends_with(';') { ";" } else { "" }
|
|
|
|
))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ImportKind::ExternCrate { source, target } => {
|
|
|
|
suggestion = Some(format!(
|
|
|
|
"extern crate {} as {};",
|
|
|
|
source.unwrap_or(target.name),
|
|
|
|
suggested_name,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
_ => unreachable!(),
|
|
|
|
}
|
|
|
|
|
|
|
|
let rename_msg = "you can use `as` to change the binding name of the import";
|
|
|
|
if let Some(suggestion) = suggestion {
|
|
|
|
err.span_suggestion(
|
|
|
|
binding_span,
|
|
|
|
rename_msg,
|
|
|
|
suggestion,
|
|
|
|
Applicability::MaybeIncorrect,
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
err.span_label(binding_span, rename_msg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// This function adds a suggestion to remove an unnecessary binding from an import that is
|
|
|
|
/// nested. In the following example, this function will be invoked to remove the `a` binding
|
|
|
|
/// in the second use statement:
|
|
|
|
///
|
|
|
|
/// ```ignore (diagnostic)
|
|
|
|
/// use issue_52891::a;
|
|
|
|
/// use issue_52891::{d, a, e};
|
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// The following suggestion will be added:
|
|
|
|
///
|
|
|
|
/// ```ignore (diagnostic)
|
|
|
|
/// use issue_52891::{d, a, e};
|
|
|
|
/// ^-- help: remove unnecessary import
|
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// If the nested use contains only one import then the suggestion will remove the entire
|
|
|
|
/// line.
|
|
|
|
///
|
|
|
|
/// It is expected that the provided import is nested - this isn't checked by the
|
|
|
|
/// function. If this invariant is not upheld, this function's behaviour will be unexpected
|
|
|
|
/// as characters expected by span manipulations won't be present.
|
|
|
|
fn add_suggestion_for_duplicate_nested_use(
|
|
|
|
&self,
|
|
|
|
err: &mut Diagnostic,
|
|
|
|
import: &Import<'_>,
|
|
|
|
binding_span: Span,
|
|
|
|
) {
|
|
|
|
assert!(import.is_nested());
|
|
|
|
let message = "remove unnecessary import";
|
|
|
|
|
|
|
|
// Two examples will be used to illustrate the span manipulations we're doing:
|
|
|
|
//
|
|
|
|
// - Given `use issue_52891::{d, a, e};` where `a` is a duplicate then `binding_span` is
|
|
|
|
// `a` and `import.use_span` is `issue_52891::{d, a, e};`.
|
|
|
|
// - Given `use issue_52891::{d, e, a};` where `a` is a duplicate then `binding_span` is
|
|
|
|
// `a` and `import.use_span` is `issue_52891::{d, e, a};`.
|
|
|
|
|
|
|
|
let (found_closing_brace, span) =
|
|
|
|
find_span_of_binding_until_next_binding(self.session, binding_span, import.use_span);
|
|
|
|
|
|
|
|
// If there was a closing brace then identify the span to remove any trailing commas from
|
|
|
|
// previous imports.
|
|
|
|
if found_closing_brace {
|
|
|
|
if let Some(span) = extend_span_to_previous_binding(self.session, span) {
|
2022-06-13 02:01:16 -05:00
|
|
|
err.tool_only_span_suggestion(span, message, "", Applicability::MaybeIncorrect);
|
2022-04-08 15:52:18 -05:00
|
|
|
} else {
|
|
|
|
// Remove the entire line if we cannot extend the span back, this indicates an
|
|
|
|
// `issue_52891::{self}` case.
|
|
|
|
err.span_suggestion(
|
|
|
|
import.use_span_with_attributes,
|
|
|
|
message,
|
2022-06-13 01:48:40 -05:00
|
|
|
"",
|
2022-04-08 15:52:18 -05:00
|
|
|
Applicability::MaybeIncorrect,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-06-13 01:48:40 -05:00
|
|
|
err.span_suggestion(span, message, "", Applicability::MachineApplicable);
|
2022-04-08 15:52:18 -05:00
|
|
|
}
|
|
|
|
|
2022-05-20 18:51:09 -05:00
|
|
|
pub(crate) fn lint_if_path_starts_with_module(
|
2022-04-08 15:52:18 -05:00
|
|
|
&mut self,
|
2022-04-30 08:26:36 -05:00
|
|
|
finalize: Option<Finalize>,
|
2022-04-08 15:52:18 -05:00
|
|
|
path: &[Segment],
|
|
|
|
second_binding: Option<&NameBinding<'_>>,
|
|
|
|
) {
|
2022-04-30 08:26:36 -05:00
|
|
|
let Some(Finalize { node_id, root_span, .. }) = finalize else {
|
|
|
|
return;
|
2022-04-08 15:52:18 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
let first_name = match path.get(0) {
|
|
|
|
// In the 2018 edition this lint is a hard error, so nothing to do
|
|
|
|
Some(seg) if seg.ident.span.rust_2015() && self.session.rust_2015() => seg.ident.name,
|
|
|
|
_ => return,
|
|
|
|
};
|
|
|
|
|
|
|
|
// We're only interested in `use` paths which should start with
|
|
|
|
// `{{root}}` currently.
|
|
|
|
if first_name != kw::PathRoot {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
match path.get(1) {
|
|
|
|
// If this import looks like `crate::...` it's already good
|
|
|
|
Some(Segment { ident, .. }) if ident.name == kw::Crate => return,
|
|
|
|
// Otherwise go below to see if it's an extern crate
|
|
|
|
Some(_) => {}
|
|
|
|
// If the path has length one (and it's `PathRoot` most likely)
|
|
|
|
// then we don't know whether we're gonna be importing a crate or an
|
|
|
|
// item in our crate. Defer this lint to elsewhere
|
|
|
|
None => return,
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the first element of our path was actually resolved to an
|
|
|
|
// `ExternCrate` (also used for `crate::...`) then no need to issue a
|
|
|
|
// warning, this looks all good!
|
|
|
|
if let Some(binding) = second_binding {
|
|
|
|
if let NameBindingKind::Import { import, .. } = binding.kind {
|
|
|
|
// Careful: we still want to rewrite paths from renamed extern crates.
|
|
|
|
if let ImportKind::ExternCrate { source: None, .. } = import.kind {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-30 08:26:36 -05:00
|
|
|
let diag = BuiltinLintDiagnostics::AbsPathWithModule(root_span);
|
2022-04-08 15:52:18 -05:00
|
|
|
self.lint_buffer.buffer_lint_with_diagnostic(
|
|
|
|
ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE,
|
2022-04-30 08:26:36 -05:00
|
|
|
node_id,
|
|
|
|
root_span,
|
2022-04-08 15:52:18 -05:00
|
|
|
"absolute paths must start with `self`, `super`, \
|
|
|
|
`crate`, or an external crate name in the 2018 edition",
|
|
|
|
diag,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2022-05-20 18:51:09 -05:00
|
|
|
pub(crate) fn add_module_candidates(
|
2019-08-15 17:18:14 -05:00
|
|
|
&mut self,
|
|
|
|
module: Module<'a>,
|
|
|
|
names: &mut Vec<TypoSuggestion>,
|
|
|
|
filter_fn: &impl Fn(Res) -> bool,
|
|
|
|
) {
|
2019-09-09 15:04:26 -05:00
|
|
|
for (key, resolution) in self.resolutions(module).borrow().iter() {
|
2019-08-15 17:18:14 -05:00
|
|
|
if let Some(binding) = resolution.borrow().binding {
|
|
|
|
let res = binding.res();
|
|
|
|
if filter_fn(res) {
|
2021-08-04 16:29:30 -05:00
|
|
|
names.push(TypoSuggestion::typo_from_res(key.ident.name, res));
|
2019-08-15 17:18:14 -05:00
|
|
|
}
|
2019-07-11 18:29:28 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-08 15:32:58 -05:00
|
|
|
/// Combines an error with provided span and emits it.
|
|
|
|
///
|
|
|
|
/// This takes the error provided, combines it with the span and any additional spans inside the
|
|
|
|
/// error and emits it.
|
2022-05-20 18:51:09 -05:00
|
|
|
pub(crate) fn report_error(&mut self, span: Span, resolution_error: ResolutionError<'a>) {
|
2019-08-08 15:32:58 -05:00
|
|
|
self.into_struct_error(span, resolution_error).emit();
|
|
|
|
}
|
|
|
|
|
2022-05-20 18:51:09 -05:00
|
|
|
pub(crate) fn into_struct_error(
|
2021-11-17 13:37:46 -06:00
|
|
|
&mut self,
|
2019-08-08 15:32:58 -05:00
|
|
|
span: Span,
|
2021-11-17 13:37:46 -06:00
|
|
|
resolution_error: ResolutionError<'a>,
|
2022-01-23 12:34:26 -06:00
|
|
|
) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
|
2019-08-08 15:32:58 -05:00
|
|
|
match resolution_error {
|
2019-10-05 10:55:58 -05:00
|
|
|
ResolutionError::GenericParamsFromOuterFunction(outer_res, has_generic_params) => {
|
2019-08-08 15:32:58 -05:00
|
|
|
let mut err = struct_span_err!(
|
|
|
|
self.session,
|
|
|
|
span,
|
|
|
|
E0401,
|
|
|
|
"can't use generic parameters from outer function",
|
|
|
|
);
|
2022-07-12 23:24:38 -05:00
|
|
|
err.span_label(span, "use of generic parameter from outer function");
|
2019-08-08 15:32:58 -05:00
|
|
|
|
2020-02-22 08:07:05 -06:00
|
|
|
let sm = self.session.source_map();
|
2019-08-08 15:32:58 -05:00
|
|
|
match outer_res {
|
2022-02-09 05:03:27 -06:00
|
|
|
Res::SelfTy { trait_: maybe_trait_defid, alias_to: maybe_impl_defid } => {
|
2019-08-08 15:32:58 -05:00
|
|
|
if let Some(impl_span) =
|
2020-09-01 07:30:16 -05:00
|
|
|
maybe_impl_defid.and_then(|(def_id, _)| self.opt_span(def_id))
|
2019-08-08 15:32:58 -05:00
|
|
|
{
|
|
|
|
err.span_label(
|
2020-02-22 08:07:05 -06:00
|
|
|
reduce_impl_span_to_impl_keyword(sm, impl_span),
|
2019-08-08 15:32:58 -05:00
|
|
|
"`Self` type implicitly declared here, by this `impl`",
|
|
|
|
);
|
|
|
|
}
|
|
|
|
match (maybe_trait_defid, maybe_impl_defid) {
|
|
|
|
(Some(_), None) => {
|
|
|
|
err.span_label(span, "can't use `Self` here");
|
|
|
|
}
|
|
|
|
(_, Some(_)) => {
|
|
|
|
err.span_label(span, "use a type here instead");
|
|
|
|
}
|
|
|
|
(None, None) => bug!("`impl` without trait nor type?"),
|
|
|
|
}
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
Res::Def(DefKind::TyParam, def_id) => {
|
2020-06-20 13:59:29 -05:00
|
|
|
if let Some(span) = self.opt_span(def_id) {
|
2019-08-08 15:32:58 -05:00
|
|
|
err.span_label(span, "type parameter from outer function");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Res::Def(DefKind::ConstParam, def_id) => {
|
2020-06-20 13:59:29 -05:00
|
|
|
if let Some(span) = self.opt_span(def_id) {
|
2019-08-08 15:32:58 -05:00
|
|
|
err.span_label(span, "const parameter from outer function");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
bug!(
|
|
|
|
"GenericParamsFromOuterFunction should only be used with Res::SelfTy, \
|
2020-11-20 16:31:36 -06:00
|
|
|
DefKind::TyParam or DefKind::ConstParam"
|
2019-08-08 15:32:58 -05:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-05 10:55:58 -05:00
|
|
|
if has_generic_params == HasGenericParams::Yes {
|
|
|
|
// Try to retrieve the span of the function signature and generate a new
|
|
|
|
// message with a local type or const parameter.
|
2020-02-27 06:34:08 -06:00
|
|
|
let sugg_msg = "try using a local generic parameter instead";
|
2020-02-22 08:07:05 -06:00
|
|
|
if let Some((sugg_span, snippet)) = sm.generate_local_type_param_snippet(span) {
|
2019-10-05 10:55:58 -05:00
|
|
|
// Suggest the modification to the user
|
|
|
|
err.span_suggestion(
|
|
|
|
sugg_span,
|
|
|
|
sugg_msg,
|
|
|
|
snippet,
|
|
|
|
Applicability::MachineApplicable,
|
|
|
|
);
|
2020-02-22 08:07:05 -06:00
|
|
|
} else if let Some(sp) = sm.generate_fn_name_span(span) {
|
2019-10-05 10:55:58 -05:00
|
|
|
err.span_label(
|
|
|
|
sp,
|
2022-07-25 08:40:00 -05:00
|
|
|
"try adding a local generic parameter in this method instead",
|
2019-10-05 10:55:58 -05:00
|
|
|
);
|
|
|
|
} else {
|
2020-02-27 06:34:08 -06:00
|
|
|
err.help("try using a local generic parameter instead");
|
2019-10-05 10:55:58 -05:00
|
|
|
}
|
2019-08-08 15:32:58 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
err
|
|
|
|
}
|
|
|
|
ResolutionError::NameAlreadyUsedInParameterList(name, first_use_span) => {
|
2019-08-11 12:09:50 -05:00
|
|
|
let mut err = struct_span_err!(
|
|
|
|
self.session,
|
|
|
|
span,
|
|
|
|
E0403,
|
|
|
|
"the name `{}` is already used for a generic \
|
|
|
|
parameter in this item's generic parameters",
|
|
|
|
name,
|
|
|
|
);
|
2019-08-08 15:32:58 -05:00
|
|
|
err.span_label(span, "already used");
|
|
|
|
err.span_label(first_use_span, format!("first use of `{}`", name));
|
|
|
|
err
|
|
|
|
}
|
2021-09-25 10:53:37 -05:00
|
|
|
ResolutionError::MethodNotMemberOfTrait(method, trait_, candidate) => {
|
2019-08-08 15:32:58 -05:00
|
|
|
let mut err = struct_span_err!(
|
|
|
|
self.session,
|
|
|
|
span,
|
|
|
|
E0407,
|
|
|
|
"method `{}` is not a member of trait `{}`",
|
|
|
|
method,
|
|
|
|
trait_
|
|
|
|
);
|
|
|
|
err.span_label(span, format!("not a member of trait `{}`", trait_));
|
2021-09-25 10:53:37 -05:00
|
|
|
if let Some(candidate) = candidate {
|
|
|
|
err.span_suggestion(
|
|
|
|
method.span,
|
|
|
|
"there is an associated function with a similar name",
|
|
|
|
candidate.to_ident_string(),
|
|
|
|
Applicability::MaybeIncorrect,
|
|
|
|
);
|
|
|
|
}
|
2019-08-08 15:32:58 -05:00
|
|
|
err
|
|
|
|
}
|
2021-09-25 10:53:37 -05:00
|
|
|
ResolutionError::TypeNotMemberOfTrait(type_, trait_, candidate) => {
|
2019-08-08 15:32:58 -05:00
|
|
|
let mut err = struct_span_err!(
|
|
|
|
self.session,
|
|
|
|
span,
|
|
|
|
E0437,
|
|
|
|
"type `{}` is not a member of trait `{}`",
|
|
|
|
type_,
|
|
|
|
trait_
|
|
|
|
);
|
|
|
|
err.span_label(span, format!("not a member of trait `{}`", trait_));
|
2021-09-25 10:53:37 -05:00
|
|
|
if let Some(candidate) = candidate {
|
|
|
|
err.span_suggestion(
|
|
|
|
type_.span,
|
|
|
|
"there is an associated type with a similar name",
|
|
|
|
candidate.to_ident_string(),
|
|
|
|
Applicability::MaybeIncorrect,
|
|
|
|
);
|
|
|
|
}
|
2019-08-08 15:32:58 -05:00
|
|
|
err
|
|
|
|
}
|
2021-09-25 10:53:37 -05:00
|
|
|
ResolutionError::ConstNotMemberOfTrait(const_, trait_, candidate) => {
|
2019-08-08 15:32:58 -05:00
|
|
|
let mut err = struct_span_err!(
|
|
|
|
self.session,
|
|
|
|
span,
|
|
|
|
E0438,
|
|
|
|
"const `{}` is not a member of trait `{}`",
|
|
|
|
const_,
|
|
|
|
trait_
|
|
|
|
);
|
|
|
|
err.span_label(span, format!("not a member of trait `{}`", trait_));
|
2021-09-25 10:53:37 -05:00
|
|
|
if let Some(candidate) = candidate {
|
|
|
|
err.span_suggestion(
|
|
|
|
const_.span,
|
|
|
|
"there is an associated constant with a similar name",
|
|
|
|
candidate.to_ident_string(),
|
|
|
|
Applicability::MaybeIncorrect,
|
|
|
|
);
|
|
|
|
}
|
2019-08-08 15:32:58 -05:00
|
|
|
err
|
|
|
|
}
|
2021-11-17 13:37:46 -06:00
|
|
|
ResolutionError::VariableNotBoundInPattern(binding_error, parent_scope) => {
|
2019-08-09 15:21:45 -05:00
|
|
|
let BindingError { name, target, origin, could_be_path } = binding_error;
|
2019-08-09 04:43:26 -05:00
|
|
|
|
2019-08-09 15:21:45 -05:00
|
|
|
let target_sp = target.iter().copied().collect::<Vec<_>>();
|
|
|
|
let origin_sp = origin.iter().copied().collect::<Vec<_>>();
|
2019-08-09 04:43:26 -05:00
|
|
|
|
2019-08-08 15:32:58 -05:00
|
|
|
let msp = MultiSpan::from_spans(target_sp.clone());
|
2019-11-11 16:34:57 -06:00
|
|
|
let mut err = struct_span_err!(
|
|
|
|
self.session,
|
2019-08-08 15:32:58 -05:00
|
|
|
msp,
|
2019-11-11 16:34:57 -06:00
|
|
|
E0408,
|
|
|
|
"variable `{}` is not bound in all patterns",
|
|
|
|
name,
|
2019-08-08 15:32:58 -05:00
|
|
|
);
|
|
|
|
for sp in target_sp {
|
2019-08-09 04:43:26 -05:00
|
|
|
err.span_label(sp, format!("pattern doesn't bind `{}`", name));
|
2019-08-08 15:32:58 -05:00
|
|
|
}
|
|
|
|
for sp in origin_sp {
|
|
|
|
err.span_label(sp, "variable not in all patterns");
|
|
|
|
}
|
2021-11-17 13:37:46 -06:00
|
|
|
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(
|
2022-05-30 11:49:17 -05:00
|
|
|
&self.session,
|
|
|
|
&self.source_span,
|
2021-11-17 13:37:46 -06:00
|
|
|
&mut err,
|
|
|
|
Some(span),
|
|
|
|
&import_suggestions,
|
2022-04-29 21:40:36 -05:00
|
|
|
Instead::No,
|
|
|
|
FoundUse::Yes,
|
|
|
|
IsPattern::Yes,
|
2022-04-23 18:41:36 -05:00
|
|
|
vec![],
|
2019-08-09 15:21:45 -05:00
|
|
|
);
|
2019-08-09 04:43:26 -05:00
|
|
|
}
|
2019-08-08 15:32:58 -05:00
|
|
|
err
|
|
|
|
}
|
|
|
|
ResolutionError::VariableBoundWithDifferentMode(variable_name, first_binding_span) => {
|
|
|
|
let mut err = struct_span_err!(
|
|
|
|
self.session,
|
|
|
|
span,
|
|
|
|
E0409,
|
2020-03-03 20:58:52 -06:00
|
|
|
"variable `{}` is bound inconsistently across alternatives separated by `|`",
|
2019-08-08 15:32:58 -05:00
|
|
|
variable_name
|
|
|
|
);
|
|
|
|
err.span_label(span, "bound in different ways");
|
|
|
|
err.span_label(first_binding_span, "first binding");
|
|
|
|
err
|
|
|
|
}
|
|
|
|
ResolutionError::IdentifierBoundMoreThanOnceInParameterList(identifier) => {
|
|
|
|
let mut err = struct_span_err!(
|
|
|
|
self.session,
|
|
|
|
span,
|
|
|
|
E0415,
|
|
|
|
"identifier `{}` is bound more than once in this parameter list",
|
|
|
|
identifier
|
|
|
|
);
|
|
|
|
err.span_label(span, "used as parameter more than once");
|
|
|
|
err
|
|
|
|
}
|
|
|
|
ResolutionError::IdentifierBoundMoreThanOnceInSamePattern(identifier) => {
|
|
|
|
let mut err = struct_span_err!(
|
|
|
|
self.session,
|
|
|
|
span,
|
|
|
|
E0416,
|
|
|
|
"identifier `{}` is bound more than once in the same pattern",
|
|
|
|
identifier
|
|
|
|
);
|
|
|
|
err.span_label(span, "used in a pattern more than once");
|
|
|
|
err
|
|
|
|
}
|
2020-06-25 09:16:38 -05:00
|
|
|
ResolutionError::UndeclaredLabel { name, suggestion } => {
|
2019-08-08 15:32:58 -05:00
|
|
|
let mut err = struct_span_err!(
|
|
|
|
self.session,
|
|
|
|
span,
|
|
|
|
E0426,
|
|
|
|
"use of undeclared label `{}`",
|
|
|
|
name
|
|
|
|
);
|
2020-06-25 09:16:38 -05:00
|
|
|
|
|
|
|
err.span_label(span, format!("undeclared label `{}`", name));
|
|
|
|
|
|
|
|
match suggestion {
|
|
|
|
// A reachable label with a similar name exists.
|
|
|
|
Some((ident, true)) => {
|
|
|
|
err.span_label(ident.span, "a label with a similar name is reachable");
|
|
|
|
err.span_suggestion(
|
|
|
|
span,
|
|
|
|
"try using similarly named label",
|
2022-06-13 01:48:40 -05:00
|
|
|
ident.name,
|
2020-06-25 09:16:38 -05:00
|
|
|
Applicability::MaybeIncorrect,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
// An unreachable label with a similar name exists.
|
|
|
|
Some((ident, false)) => {
|
|
|
|
err.span_label(
|
|
|
|
ident.span,
|
|
|
|
"a label with a similar name exists but is unreachable",
|
|
|
|
);
|
|
|
|
}
|
|
|
|
// No similarly-named labels exist.
|
|
|
|
None => (),
|
2019-08-08 15:32:58 -05:00
|
|
|
}
|
2020-06-25 09:16:38 -05:00
|
|
|
|
2019-08-08 15:32:58 -05:00
|
|
|
err
|
|
|
|
}
|
2020-05-03 11:54:21 -05:00
|
|
|
ResolutionError::SelfImportsOnlyAllowedWithin { root, span_with_rename } => {
|
|
|
|
let mut err = struct_span_err!(
|
|
|
|
self.session,
|
|
|
|
span,
|
|
|
|
E0429,
|
|
|
|
"{}",
|
|
|
|
"`self` imports are only allowed within a { } list"
|
|
|
|
);
|
|
|
|
|
|
|
|
// None of the suggestions below would help with a case like `use self`.
|
|
|
|
if !root {
|
|
|
|
// use foo::bar::self -> foo::bar
|
|
|
|
// use foo::bar::self as abc -> foo::bar as abc
|
|
|
|
err.span_suggestion(
|
|
|
|
span,
|
2020-05-19 15:12:41 -05:00
|
|
|
"consider importing the module directly",
|
2022-06-13 01:48:40 -05:00
|
|
|
"",
|
2020-05-03 11:54:21 -05:00
|
|
|
Applicability::MachineApplicable,
|
|
|
|
);
|
|
|
|
|
|
|
|
// use foo::bar::self -> foo::bar::{self}
|
|
|
|
// use foo::bar::self as abc -> foo::bar::{self as abc}
|
|
|
|
let braces = vec![
|
|
|
|
(span_with_rename.shrink_to_lo(), "{".to_string()),
|
|
|
|
(span_with_rename.shrink_to_hi(), "}".to_string()),
|
|
|
|
];
|
|
|
|
err.multipart_suggestion(
|
2020-05-19 15:12:41 -05:00
|
|
|
"alternatively, use the multi-path `use` syntax to import `self`",
|
2020-05-03 11:54:21 -05:00
|
|
|
braces,
|
|
|
|
Applicability::MachineApplicable,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
err
|
|
|
|
}
|
2019-08-08 15:32:58 -05:00
|
|
|
ResolutionError::SelfImportCanOnlyAppearOnceInTheList => {
|
|
|
|
let mut err = struct_span_err!(
|
|
|
|
self.session,
|
|
|
|
span,
|
|
|
|
E0430,
|
|
|
|
"`self` import can only appear once in an import list"
|
|
|
|
);
|
|
|
|
err.span_label(span, "can only appear once in an import list");
|
|
|
|
err
|
|
|
|
}
|
|
|
|
ResolutionError::SelfImportOnlyInImportListWithNonEmptyPrefix => {
|
|
|
|
let mut err = struct_span_err!(
|
|
|
|
self.session,
|
|
|
|
span,
|
|
|
|
E0431,
|
|
|
|
"`self` import can only appear in an import list with \
|
|
|
|
a non-empty prefix"
|
|
|
|
);
|
|
|
|
err.span_label(span, "can only appear in an import list with a non-empty prefix");
|
|
|
|
err
|
|
|
|
}
|
|
|
|
ResolutionError::FailedToResolve { label, suggestion } => {
|
|
|
|
let mut err =
|
|
|
|
struct_span_err!(self.session, span, E0433, "failed to resolve: {}", &label);
|
|
|
|
err.span_label(span, label);
|
|
|
|
|
|
|
|
if let Some((suggestions, msg, applicability)) = suggestion {
|
2021-11-02 11:44:01 -05:00
|
|
|
if suggestions.is_empty() {
|
|
|
|
err.help(&msg);
|
|
|
|
return err;
|
|
|
|
}
|
2019-08-08 15:32:58 -05:00
|
|
|
err.multipart_suggestion(&msg, suggestions, applicability);
|
|
|
|
}
|
|
|
|
|
|
|
|
err
|
|
|
|
}
|
|
|
|
ResolutionError::CannotCaptureDynamicEnvironmentInFnItem => {
|
|
|
|
let mut err = struct_span_err!(
|
|
|
|
self.session,
|
|
|
|
span,
|
|
|
|
E0434,
|
|
|
|
"{}",
|
|
|
|
"can't capture dynamic environment in a fn item"
|
|
|
|
);
|
|
|
|
err.help("use the `|| { ... }` closure form instead");
|
|
|
|
err
|
|
|
|
}
|
2021-01-07 18:44:08 -06:00
|
|
|
ResolutionError::AttemptToUseNonConstantValueInConstant(ident, sugg, current) => {
|
2019-08-08 15:32:58 -05:00
|
|
|
let mut err = struct_span_err!(
|
|
|
|
self.session,
|
|
|
|
span,
|
|
|
|
E0435,
|
|
|
|
"attempt to use a non-constant value in a constant"
|
|
|
|
);
|
2021-01-07 18:44:08 -06:00
|
|
|
// let foo =...
|
|
|
|
// ^^^ given this Span
|
|
|
|
// ------- get this Span to have an applicable suggestion
|
2021-11-15 13:40:29 -06:00
|
|
|
|
|
|
|
// 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
|
|
|
|
|
2022-01-15 15:22:22 -06:00
|
|
|
let sp = self
|
|
|
|
.session
|
|
|
|
.source_map()
|
|
|
|
.span_extend_to_prev_str(ident.span, current, true, false);
|
2021-11-15 13:40:29 -06:00
|
|
|
|
2022-01-15 15:22:22 -06:00
|
|
|
match sp {
|
|
|
|
Some(sp) if !self.session.source_map().is_multiline(sp) => {
|
|
|
|
let sp = sp.with_lo(BytePos(sp.lo().0 - (current.len() as u32)));
|
|
|
|
err.span_suggestion(
|
|
|
|
sp,
|
|
|
|
&format!("consider using `{}` instead of `{}`", sugg, current),
|
|
|
|
format!("{} {}", sugg, ident),
|
|
|
|
Applicability::MaybeIncorrect,
|
|
|
|
);
|
|
|
|
err.span_label(span, "non-constant value");
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
err.span_label(ident.span, &format!("this would need to be a `{}`", sugg));
|
|
|
|
}
|
2021-01-07 18:44:08 -06:00
|
|
|
}
|
2022-01-15 15:22:22 -06:00
|
|
|
|
2019-08-08 15:32:58 -05:00
|
|
|
err
|
|
|
|
}
|
2021-05-19 11:51:42 -05:00
|
|
|
ResolutionError::BindingShadowsSomethingUnacceptable {
|
2022-07-08 06:01:30 -05:00
|
|
|
shadowing_binding,
|
2021-05-19 11:51:42 -05:00
|
|
|
name,
|
|
|
|
participle,
|
|
|
|
article,
|
2022-07-08 05:20:05 -05:00
|
|
|
shadowed_binding,
|
2021-05-19 11:51:42 -05:00
|
|
|
shadowed_binding_span,
|
|
|
|
} => {
|
2022-07-08 05:20:05 -05:00
|
|
|
let shadowed_binding_descr = shadowed_binding.descr();
|
2019-08-08 15:32:58 -05:00
|
|
|
let mut err = struct_span_err!(
|
|
|
|
self.session,
|
|
|
|
span,
|
|
|
|
E0530,
|
|
|
|
"{}s cannot shadow {}s",
|
2022-07-08 06:01:30 -05:00
|
|
|
shadowing_binding.descr(),
|
2021-05-19 11:51:42 -05:00
|
|
|
shadowed_binding_descr,
|
2019-08-08 15:32:58 -05:00
|
|
|
);
|
|
|
|
err.span_label(
|
|
|
|
span,
|
2021-05-19 11:51:42 -05:00
|
|
|
format!("cannot be named the same as {} {}", article, shadowed_binding_descr),
|
2019-08-03 18:07:35 -05:00
|
|
|
);
|
2022-07-08 06:01:30 -05:00
|
|
|
match (shadowing_binding, shadowed_binding) {
|
|
|
|
(
|
|
|
|
PatternSource::Match,
|
|
|
|
Res::Def(DefKind::Ctor(CtorOf::Variant | CtorOf::Struct, CtorKind::Fn), _),
|
|
|
|
) => {
|
2022-07-08 05:20:05 -05:00
|
|
|
err.span_suggestion(
|
|
|
|
span,
|
|
|
|
"try specify the pattern arguments",
|
|
|
|
format!("{}(..)", name),
|
|
|
|
Applicability::Unspecified,
|
2022-07-08 06:54:11 -05:00
|
|
|
);
|
2022-07-08 05:20:05 -05:00
|
|
|
}
|
|
|
|
_ => (),
|
|
|
|
}
|
2021-05-19 11:51:42 -05:00
|
|
|
let msg =
|
|
|
|
format!("the {} `{}` is {} here", shadowed_binding_descr, name, participle);
|
|
|
|
err.span_label(shadowed_binding_span, msg);
|
2019-08-08 15:32:58 -05:00
|
|
|
err
|
|
|
|
}
|
2021-06-01 11:44:49 -05:00
|
|
|
ResolutionError::ForwardDeclaredGenericParam => {
|
2019-08-08 15:32:58 -05:00
|
|
|
let mut err = struct_span_err!(
|
|
|
|
self.session,
|
|
|
|
span,
|
|
|
|
E0128,
|
2020-12-30 09:34:53 -06:00
|
|
|
"generic parameters with a default cannot use \
|
2019-08-08 15:32:58 -05:00
|
|
|
forward declared identifiers"
|
|
|
|
);
|
2022-07-09 13:18:56 -05:00
|
|
|
err.span_label(span, "defaulted generic parameters cannot be forward declared");
|
2019-08-08 15:32:58 -05:00
|
|
|
err
|
|
|
|
}
|
2020-07-18 15:35:50 -05:00
|
|
|
ResolutionError::ParamInTyOfConstParam(name) => {
|
2020-07-08 15:16:18 -05:00
|
|
|
let mut err = struct_span_err!(
|
|
|
|
self.session,
|
|
|
|
span,
|
|
|
|
E0770,
|
|
|
|
"the type of const parameters must not depend on other generic parameters"
|
|
|
|
);
|
2020-07-16 04:10:22 -05:00
|
|
|
err.span_label(
|
|
|
|
span,
|
|
|
|
format!("the type must not depend on the parameter `{}`", name),
|
|
|
|
);
|
2020-07-08 15:16:18 -05:00
|
|
|
err
|
|
|
|
}
|
2020-09-14 16:39:43 -05:00
|
|
|
ResolutionError::ParamInNonTrivialAnonConst { name, is_type } => {
|
2020-07-28 08:55:42 -05:00
|
|
|
let mut err = self.session.struct_span_err(
|
|
|
|
span,
|
2020-10-12 15:58:06 -05:00
|
|
|
"generic parameters may not be used in const operations",
|
2020-07-28 08:55:42 -05:00
|
|
|
);
|
2020-10-11 10:47:45 -05:00
|
|
|
err.span_label(span, &format!("cannot perform const operation using `{}`", name));
|
2020-09-14 16:39:43 -05:00
|
|
|
|
|
|
|
if is_type {
|
2020-10-12 15:57:04 -05:00
|
|
|
err.note("type parameters may not be used in const expressions");
|
2020-09-14 16:39:43 -05:00
|
|
|
} else {
|
2020-10-11 10:47:45 -05:00
|
|
|
err.help(&format!(
|
2020-10-12 07:18:29 -05:00
|
|
|
"const parameters may only be used as standalone arguments, i.e. `{}`",
|
2020-10-11 10:47:45 -05:00
|
|
|
name
|
|
|
|
));
|
2020-09-14 16:39:43 -05:00
|
|
|
}
|
2021-04-29 13:06:11 -05:00
|
|
|
|
|
|
|
if self.session.is_nightly_build() {
|
|
|
|
err.help(
|
2021-08-27 11:04:57 -05:00
|
|
|
"use `#![feature(generic_const_exprs)]` to allow generic const expressions",
|
2021-04-29 13:06:11 -05:00
|
|
|
);
|
|
|
|
}
|
2020-09-14 16:39:43 -05:00
|
|
|
|
2020-07-28 08:55:42 -05:00
|
|
|
err
|
|
|
|
}
|
2021-07-14 13:22:32 -05:00
|
|
|
ResolutionError::SelfInGenericParamDefault => {
|
2019-09-27 08:21:02 -05:00
|
|
|
let mut err = struct_span_err!(
|
|
|
|
self.session,
|
|
|
|
span,
|
|
|
|
E0735,
|
2021-07-14 13:22:32 -05:00
|
|
|
"generic parameters cannot use `Self` in their defaults"
|
2019-09-27 08:21:02 -05:00
|
|
|
);
|
2022-07-12 23:24:38 -05:00
|
|
|
err.span_label(span, "`Self` in generic parameter default");
|
2019-09-27 08:21:02 -05:00
|
|
|
err
|
|
|
|
}
|
2020-06-25 09:16:38 -05:00
|
|
|
ResolutionError::UnreachableLabel { name, definition_span, suggestion } => {
|
|
|
|
let mut err = struct_span_err!(
|
|
|
|
self.session,
|
|
|
|
span,
|
|
|
|
E0767,
|
|
|
|
"use of unreachable label `{}`",
|
|
|
|
name,
|
|
|
|
);
|
|
|
|
|
|
|
|
err.span_label(definition_span, "unreachable label defined here");
|
|
|
|
err.span_label(span, format!("unreachable label `{}`", name));
|
|
|
|
err.note(
|
|
|
|
"labels are unreachable through functions, closures, async blocks and modules",
|
|
|
|
);
|
|
|
|
|
|
|
|
match suggestion {
|
|
|
|
// A reachable label with a similar name exists.
|
|
|
|
Some((ident, true)) => {
|
|
|
|
err.span_label(ident.span, "a label with a similar name is reachable");
|
|
|
|
err.span_suggestion(
|
|
|
|
span,
|
|
|
|
"try using similarly named label",
|
2022-06-13 01:48:40 -05:00
|
|
|
ident.name,
|
2020-06-25 09:16:38 -05:00
|
|
|
Applicability::MaybeIncorrect,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
// An unreachable label with a similar name exists.
|
|
|
|
Some((ident, false)) => {
|
|
|
|
err.span_label(
|
|
|
|
ident.span,
|
|
|
|
"a label with a similar name exists but is also unreachable",
|
|
|
|
);
|
|
|
|
}
|
|
|
|
// No similarly-named labels exist.
|
|
|
|
None => (),
|
|
|
|
}
|
|
|
|
|
|
|
|
err
|
|
|
|
}
|
2022-01-08 07:49:38 -06:00
|
|
|
ResolutionError::TraitImplMismatch {
|
|
|
|
name,
|
|
|
|
kind,
|
|
|
|
code,
|
|
|
|
trait_item_span,
|
|
|
|
trait_path,
|
|
|
|
} => {
|
|
|
|
let mut err = self.session.struct_span_err_with_code(
|
|
|
|
span,
|
|
|
|
&format!(
|
|
|
|
"item `{}` is an associated {}, which doesn't match its trait `{}`",
|
|
|
|
name, kind, trait_path,
|
|
|
|
),
|
|
|
|
code,
|
|
|
|
);
|
|
|
|
err.span_label(span, "does not match trait");
|
|
|
|
err.span_label(trait_item_span, "item in trait");
|
|
|
|
err
|
|
|
|
}
|
2022-02-28 18:50:56 -06:00
|
|
|
ResolutionError::InvalidAsmSym => {
|
|
|
|
let mut err = self.session.struct_span_err(span, "invalid `sym` operand");
|
2022-04-21 10:46:03 -05:00
|
|
|
err.span_label(span, "is a local variable");
|
2022-02-28 18:50:56 -06:00
|
|
|
err.help("`sym` operands must refer to either a function or a static");
|
|
|
|
err
|
|
|
|
}
|
2019-08-08 15:32:58 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-20 18:51:09 -05:00
|
|
|
pub(crate) fn report_vis_error(
|
2021-11-17 13:37:46 -06:00
|
|
|
&mut self,
|
2022-01-27 03:44:25 -06:00
|
|
|
vis_resolution_error: VisResolutionError<'_>,
|
2022-01-23 12:34:26 -06:00
|
|
|
) -> ErrorGuaranteed {
|
2019-12-06 16:49:21 -06:00
|
|
|
match vis_resolution_error {
|
|
|
|
VisResolutionError::Relative2018(span, path) => {
|
|
|
|
let mut err = self.session.struct_span_err(
|
|
|
|
span,
|
2022-04-11 08:15:45 -05:00
|
|
|
"relative paths are not supported in visibilities in 2018 edition or later",
|
2019-12-06 16:49:21 -06:00
|
|
|
);
|
|
|
|
err.span_suggestion(
|
|
|
|
path.span,
|
|
|
|
"try",
|
|
|
|
format!("crate::{}", pprust::path_to_string(&path)),
|
|
|
|
Applicability::MaybeIncorrect,
|
|
|
|
);
|
|
|
|
err
|
|
|
|
}
|
|
|
|
VisResolutionError::AncestorOnly(span) => struct_span_err!(
|
|
|
|
self.session,
|
|
|
|
span,
|
|
|
|
E0742,
|
|
|
|
"visibilities can only be restricted to ancestor modules"
|
2019-12-22 16:42:04 -06:00
|
|
|
),
|
2019-12-06 16:49:21 -06:00
|
|
|
VisResolutionError::FailedToResolve(span, label, suggestion) => {
|
|
|
|
self.into_struct_error(span, ResolutionError::FailedToResolve { label, suggestion })
|
|
|
|
}
|
|
|
|
VisResolutionError::ExpectedFound(span, path_str, res) => {
|
|
|
|
let mut err = struct_span_err!(
|
|
|
|
self.session,
|
|
|
|
span,
|
|
|
|
E0577,
|
|
|
|
"expected module, found {} `{}`",
|
|
|
|
res.descr(),
|
|
|
|
path_str
|
|
|
|
);
|
|
|
|
err.span_label(span, "not a module");
|
|
|
|
err
|
|
|
|
}
|
|
|
|
VisResolutionError::Indeterminate(span) => struct_span_err!(
|
|
|
|
self.session,
|
|
|
|
span,
|
|
|
|
E0578,
|
|
|
|
"cannot determine resolution for the visibility"
|
2019-12-22 16:42:04 -06:00
|
|
|
),
|
2019-12-06 16:49:21 -06:00
|
|
|
VisResolutionError::ModuleOnly(span) => {
|
|
|
|
self.session.struct_span_err(span, "visibility must resolve to a module")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
.emit()
|
|
|
|
}
|
|
|
|
|
2019-07-11 18:29:28 -05:00
|
|
|
/// Lookup typo candidate in scope for a macro or import.
|
|
|
|
fn early_lookup_typo_candidate(
|
2019-07-11 13:13:11 -05:00
|
|
|
&mut self,
|
2021-03-13 13:23:18 -06:00
|
|
|
scope_set: ScopeSet<'a>,
|
2019-07-11 18:29:28 -05:00
|
|
|
parent_scope: &ParentScope<'a>,
|
2019-07-14 15:04:51 -05:00
|
|
|
ident: Ident,
|
2019-07-11 18:29:28 -05:00
|
|
|
filter_fn: &impl Fn(Res) -> bool,
|
|
|
|
) -> Option<TypoSuggestion> {
|
|
|
|
let mut suggestions = Vec::new();
|
2021-01-07 06:23:25 -06:00
|
|
|
let ctxt = ident.span.ctxt();
|
|
|
|
self.visit_scopes(scope_set, parent_scope, ctxt, |this, scope, use_prelude, _| {
|
2019-07-11 18:29:28 -05:00
|
|
|
match scope {
|
2019-10-03 17:53:20 -05:00
|
|
|
Scope::DeriveHelpers(expn_id) => {
|
|
|
|
let res = Res::NonMacroAttr(NonMacroAttrKind::DeriveHelper);
|
|
|
|
if filter_fn(res) {
|
|
|
|
suggestions.extend(
|
|
|
|
this.helper_attrs
|
|
|
|
.get(&expn_id)
|
|
|
|
.into_iter()
|
|
|
|
.flatten()
|
2021-08-04 16:29:30 -05:00
|
|
|
.map(|ident| TypoSuggestion::typo_from_res(ident.name, res)),
|
2019-10-03 17:53:20 -05:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2019-10-03 17:44:57 -05:00
|
|
|
Scope::DeriveHelpersCompat => {
|
2020-11-18 16:45:10 -06:00
|
|
|
let res = Res::NonMacroAttr(NonMacroAttrKind::DeriveHelperCompat);
|
2019-07-11 18:29:28 -05:00
|
|
|
if filter_fn(res) {
|
2019-08-12 17:39:10 -05:00
|
|
|
for derive in parent_scope.derives {
|
|
|
|
let parent_scope = &ParentScope { derives: &[], ..*parent_scope };
|
2019-07-11 18:29:28 -05:00
|
|
|
if let Ok((Some(ext), _)) = this.resolve_macro_path(
|
2019-08-05 13:18:50 -05:00
|
|
|
derive,
|
|
|
|
Some(MacroKind::Derive),
|
|
|
|
parent_scope,
|
|
|
|
false,
|
|
|
|
false,
|
2019-07-11 18:29:28 -05:00
|
|
|
) {
|
|
|
|
suggestions.extend(
|
|
|
|
ext.helper_attrs
|
|
|
|
.iter()
|
2021-08-04 16:29:30 -05:00
|
|
|
.map(|name| TypoSuggestion::typo_from_res(*name, res)),
|
2019-07-11 18:29:28 -05:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-03-13 17:06:36 -05:00
|
|
|
Scope::MacroRules(macro_rules_scope) => {
|
2020-11-06 07:11:21 -06:00
|
|
|
if let MacroRulesScope::Binding(macro_rules_binding) = macro_rules_scope.get() {
|
2020-03-13 17:06:36 -05:00
|
|
|
let res = macro_rules_binding.binding.res();
|
2019-07-11 18:29:28 -05:00
|
|
|
if filter_fn(res) {
|
2021-08-04 16:29:30 -05:00
|
|
|
suggestions.push(TypoSuggestion::typo_from_res(
|
|
|
|
macro_rules_binding.ident.name,
|
|
|
|
res,
|
|
|
|
))
|
2019-07-11 18:29:28 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Scope::CrateRoot => {
|
2019-07-14 15:04:51 -05:00
|
|
|
let root_ident = Ident::new(kw::PathRoot, ident.span);
|
2019-07-11 18:29:28 -05:00
|
|
|
let root_module = this.resolve_crate_root(root_ident);
|
2019-08-15 17:18:14 -05:00
|
|
|
this.add_module_candidates(root_module, &mut suggestions, filter_fn);
|
2019-07-11 18:29:28 -05:00
|
|
|
}
|
2021-03-13 13:23:18 -06:00
|
|
|
Scope::Module(module, _) => {
|
2019-08-15 17:18:14 -05:00
|
|
|
this.add_module_candidates(module, &mut suggestions, filter_fn);
|
2019-07-11 18:29:28 -05:00
|
|
|
}
|
2019-11-03 11:28:20 -06:00
|
|
|
Scope::RegisteredAttrs => {
|
|
|
|
let res = Res::NonMacroAttr(NonMacroAttrKind::Registered);
|
|
|
|
if filter_fn(res) {
|
|
|
|
suggestions.extend(
|
|
|
|
this.registered_attrs
|
|
|
|
.iter()
|
2021-08-04 16:29:30 -05:00
|
|
|
.map(|ident| TypoSuggestion::typo_from_res(ident.name, res)),
|
2019-11-03 11:28:20 -06:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2019-07-11 18:29:28 -05:00
|
|
|
Scope::MacroUsePrelude => {
|
2019-07-14 15:04:51 -05:00
|
|
|
suggestions.extend(this.macro_use_prelude.iter().filter_map(
|
|
|
|
|(name, binding)| {
|
|
|
|
let res = binding.res();
|
2021-08-04 16:29:30 -05:00
|
|
|
filter_fn(res).then_some(TypoSuggestion::typo_from_res(*name, res))
|
2019-07-14 15:04:51 -05:00
|
|
|
},
|
|
|
|
));
|
2019-07-11 18:29:28 -05:00
|
|
|
}
|
|
|
|
Scope::BuiltinAttrs => {
|
2020-12-13 10:34:04 -06:00
|
|
|
let res = Res::NonMacroAttr(NonMacroAttrKind::Builtin(kw::Empty));
|
2019-07-11 18:29:28 -05:00
|
|
|
if filter_fn(res) {
|
|
|
|
suggestions.extend(
|
|
|
|
BUILTIN_ATTRIBUTES
|
|
|
|
.iter()
|
2021-11-12 06:15:14 -06:00
|
|
|
.map(|attr| TypoSuggestion::typo_from_res(attr.name, res)),
|
2019-07-14 15:04:51 -05:00
|
|
|
);
|
2019-07-11 18:29:28 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
Scope::ExternPrelude => {
|
2019-07-14 15:04:51 -05:00
|
|
|
suggestions.extend(this.extern_prelude.iter().filter_map(|(ident, _)| {
|
2022-04-15 12:27:53 -05:00
|
|
|
let res = Res::Def(DefKind::Mod, CRATE_DEF_ID.to_def_id());
|
2021-08-04 16:29:30 -05:00
|
|
|
filter_fn(res).then_some(TypoSuggestion::typo_from_res(ident.name, res))
|
2019-07-14 15:04:51 -05:00
|
|
|
}));
|
2019-07-11 18:29:28 -05:00
|
|
|
}
|
|
|
|
Scope::ToolPrelude => {
|
2019-07-14 15:04:51 -05:00
|
|
|
let res = Res::NonMacroAttr(NonMacroAttrKind::Tool);
|
2019-11-03 11:28:20 -06:00
|
|
|
suggestions.extend(
|
|
|
|
this.registered_tools
|
|
|
|
.iter()
|
2021-08-04 16:29:30 -05:00
|
|
|
.map(|ident| TypoSuggestion::typo_from_res(ident.name, res)),
|
2019-07-14 15:04:51 -05:00
|
|
|
);
|
2019-07-11 18:29:28 -05:00
|
|
|
}
|
|
|
|
Scope::StdLibPrelude => {
|
2019-07-14 15:04:51 -05:00
|
|
|
if let Some(prelude) = this.prelude {
|
2019-06-20 03:52:31 -05:00
|
|
|
let mut tmp_suggestions = Vec::new();
|
2019-08-15 17:18:14 -05:00
|
|
|
this.add_module_candidates(prelude, &mut tmp_suggestions, filter_fn);
|
2019-06-20 03:52:31 -05:00
|
|
|
suggestions.extend(
|
|
|
|
tmp_suggestions
|
|
|
|
.into_iter()
|
2019-08-12 18:13:36 -05:00
|
|
|
.filter(|s| use_prelude || this.is_builtin_macro(s.res)),
|
2019-06-20 03:52:31 -05:00
|
|
|
);
|
2019-07-11 13:13:11 -05:00
|
|
|
}
|
|
|
|
}
|
2019-07-11 18:29:28 -05:00
|
|
|
Scope::BuiltinTypes => {
|
2021-02-02 12:16:35 -06:00
|
|
|
suggestions.extend(PrimTy::ALL.iter().filter_map(|prim_ty| {
|
2019-07-11 18:29:28 -05:00
|
|
|
let res = Res::PrimTy(*prim_ty);
|
2021-08-04 16:29:30 -05:00
|
|
|
filter_fn(res).then_some(TypoSuggestion::typo_from_res(prim_ty.name(), res))
|
2019-07-11 18:29:28 -05:00
|
|
|
}))
|
|
|
|
}
|
2019-07-11 13:13:11 -05:00
|
|
|
}
|
|
|
|
|
2019-07-11 18:29:28 -05:00
|
|
|
None::<()>
|
|
|
|
});
|
|
|
|
|
|
|
|
// Make sure error reporting is deterministic.
|
2021-12-14 15:32:21 -06:00
|
|
|
suggestions.sort_by(|a, b| a.candidate.as_str().partial_cmp(b.candidate.as_str()).unwrap());
|
2019-07-11 18:29:28 -05:00
|
|
|
|
|
|
|
match find_best_match_for_name(
|
Move lev_distance to rustc_ast, make non-generic
rustc_ast currently has a few dependencies on rustc_lexer. Ideally, an AST
would not have any dependency its lexer, for minimizing unnecessarily
design-time dependencies. Breaking this dependency would also have practical
benefits, since modifying rustc_lexer would not trigger a rebuild of rustc_ast.
This commit does not remove the rustc_ast --> rustc_lexer dependency,
but it does remove one of the sources of this dependency, which is the
code that handles fuzzy matching between symbol names for making suggestions
in diagnostics. Since that code depends only on Symbol, it is easy to move
it to rustc_span. It might even be best to move it to a separate crate,
since other tools such as Cargo use the same algorithm, and have simply
contain a duplicate of the code.
This changes the signature of find_best_match_for_name so that it is no
longer generic over its input. I checked the optimized binaries, and this
function was duplicated at nearly every call site, because most call sites
used short-lived iterator chains, generic over Map and such. But there's
no good reason for a function like this to be generic, since all it does
is immediately convert the generic input (the Iterator impl) to a concrete
Vec<Symbol>. This has all of the costs of generics (duplicated method bodies)
with no benefit.
Changing find_best_match_for_name to be non-generic removed about 10KB of
code from the optimized binary. I know it's a drop in the bucket, but we have
to start reducing binary size, and beginning to tame over-use of generics
is part of that.
2020-11-12 13:24:10 -06:00
|
|
|
&suggestions.iter().map(|suggestion| suggestion.candidate).collect::<Vec<Symbol>>(),
|
2020-07-08 05:03:37 -05:00
|
|
|
ident.name,
|
2019-07-11 18:29:28 -05:00
|
|
|
None,
|
|
|
|
) {
|
|
|
|
Some(found) if found != ident.name => {
|
|
|
|
suggestions.into_iter().find(|suggestion| suggestion.candidate == found)
|
2019-12-22 16:42:04 -06:00
|
|
|
}
|
2019-07-11 18:29:28 -05:00
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-11 13:13:11 -05:00
|
|
|
fn lookup_import_candidates_from_module<FilterFn>(
|
|
|
|
&mut self,
|
|
|
|
lookup_ident: Ident,
|
|
|
|
namespace: Namespace,
|
2020-05-30 12:11:14 -05:00
|
|
|
parent_scope: &ParentScope<'a>,
|
2019-07-11 13:13:11 -05:00
|
|
|
start_module: Module<'a>,
|
|
|
|
crate_name: Ident,
|
|
|
|
filter_fn: FilterFn,
|
|
|
|
) -> Vec<ImportSuggestion>
|
|
|
|
where
|
|
|
|
FilterFn: Fn(Res) -> bool,
|
|
|
|
{
|
|
|
|
let mut candidates = Vec::new();
|
|
|
|
let mut seen_modules = FxHashSet::default();
|
2021-04-11 17:44:54 -05:00
|
|
|
let mut worklist = vec![(start_module, Vec::<ast::PathSegment>::new(), true)];
|
2020-06-21 11:31:49 -05:00
|
|
|
let mut worklist_via_import = vec![];
|
2019-07-11 13:13:11 -05:00
|
|
|
|
2021-04-11 17:44:54 -05:00
|
|
|
while let Some((in_module, path_segments, accessible)) = match worklist.pop() {
|
|
|
|
None => worklist_via_import.pop(),
|
|
|
|
Some(x) => Some(x),
|
|
|
|
} {
|
2021-09-26 11:29:53 -05:00
|
|
|
let in_module_is_extern = !in_module.def_id().is_local();
|
2019-07-11 13:13:11 -05:00
|
|
|
// We have to visit module children in deterministic order to avoid
|
|
|
|
// instabilities in reported imports (#43552).
|
2019-10-05 15:02:34 -05:00
|
|
|
in_module.for_each_child(self, |this, ident, ns, name_binding| {
|
2020-06-21 11:31:49 -05:00
|
|
|
// avoid non-importable candidates
|
2019-07-11 13:13:11 -05:00
|
|
|
if !name_binding.is_importable() {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-06-13 12:58:46 -05:00
|
|
|
let child_accessible =
|
|
|
|
accessible && this.is_accessible_from(name_binding.vis, parent_scope.module);
|
|
|
|
|
|
|
|
// do not venture inside inaccessible items of other crates
|
|
|
|
if in_module_is_extern && !child_accessible {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-06-23 14:25:23 -05:00
|
|
|
let via_import = name_binding.is_import() && !name_binding.is_extern_crate();
|
|
|
|
|
|
|
|
// There is an assumption elsewhere that paths of variants are in the enum's
|
|
|
|
// declaration and not imported. With this assumption, the variant component is
|
|
|
|
// chopped and the rest of the path is assumed to be the enum's own path. For
|
|
|
|
// errors where a variant is used as the type instead of the enum, this causes
|
|
|
|
// funny looking invalid suggestions, i.e `foo` instead of `foo::MyEnum`.
|
2020-06-21 11:31:49 -05:00
|
|
|
if via_import && name_binding.is_possibly_imported_variant() {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-10-21 06:23:34 -05:00
|
|
|
// #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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-11 13:13:11 -05:00
|
|
|
// collect results based on the filter function
|
2020-05-30 12:11:14 -05:00
|
|
|
// avoid suggesting anything from the same module in which we are resolving
|
2021-10-21 17:36:51 -05:00
|
|
|
// avoid suggesting anything with a hygienic name
|
2020-05-30 12:11:14 -05:00
|
|
|
if ident.name == lookup_ident.name
|
|
|
|
&& ns == namespace
|
|
|
|
&& !ptr::eq(in_module, parent_scope.module)
|
2021-10-21 17:36:51 -05:00
|
|
|
&& !ident.span.normalize_to_macros_2_0().from_expansion()
|
2020-05-30 12:11:14 -05:00
|
|
|
{
|
2019-07-11 13:13:11 -05:00
|
|
|
let res = name_binding.res();
|
|
|
|
if filter_fn(res) {
|
|
|
|
// create the path
|
|
|
|
let mut segms = path_segments.clone();
|
|
|
|
if lookup_ident.span.rust_2018() {
|
|
|
|
// crate-local absolute paths start with `crate::` in edition 2018
|
|
|
|
// FIXME: may also be stabilized for Rust 2015 (Issues #45477, #44660)
|
|
|
|
segms.insert(0, ast::PathSegment::from_ident(crate_name));
|
|
|
|
}
|
|
|
|
|
|
|
|
segms.push(ast::PathSegment::from_ident(ident));
|
2020-08-21 17:51:23 -05:00
|
|
|
let path = Path { span: name_binding.span, segments: segms, tokens: None };
|
2020-06-13 12:58:46 -05:00
|
|
|
let did = match res {
|
2022-04-25 14:08:45 -05:00
|
|
|
Res::Def(DefKind::Ctor(..), did) => this.opt_parent(did),
|
2020-06-13 12:58:46 -05:00
|
|
|
_ => res.opt_def_id(),
|
|
|
|
};
|
|
|
|
|
|
|
|
if child_accessible {
|
|
|
|
// Remove invisible match if exists
|
|
|
|
if let Some(idx) = candidates
|
|
|
|
.iter()
|
|
|
|
.position(|v: &ImportSuggestion| v.did == did && !v.accessible)
|
|
|
|
{
|
|
|
|
candidates.remove(idx);
|
2020-06-05 04:57:37 -05:00
|
|
|
}
|
2019-07-11 13:13:11 -05:00
|
|
|
}
|
2020-06-13 12:58:46 -05:00
|
|
|
|
|
|
|
if candidates.iter().all(|v: &ImportSuggestion| v.did != did) {
|
2021-10-25 19:04:35 -05:00
|
|
|
// 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()
|
2021-12-24 00:57:21 -06:00
|
|
|
&& this.cstore().item_attrs_untracked(did, this.session).any(
|
|
|
|
|attr| {
|
2021-10-25 19:04:35 -05:00
|
|
|
if attr.has_name(sym::rustc_diagnostic_item) {
|
|
|
|
[sym::TryInto, sym::TryFrom, sym::FromIterator]
|
|
|
|
.map(|x| Some(x))
|
|
|
|
.contains(&attr.value_str())
|
|
|
|
} else {
|
|
|
|
false
|
|
|
|
}
|
2021-12-24 00:57:21 -06:00
|
|
|
},
|
|
|
|
);
|
2021-10-25 19:04:35 -05:00
|
|
|
|
|
|
|
requires_note.then(|| {
|
|
|
|
format!(
|
|
|
|
"'{}' is included in the prelude starting in Edition 2021",
|
|
|
|
path_names_to_string(&path)
|
|
|
|
)
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
|
|
|
|
2020-06-13 12:58:46 -05:00
|
|
|
candidates.push(ImportSuggestion {
|
|
|
|
did,
|
|
|
|
descr: res.descr(),
|
|
|
|
path,
|
|
|
|
accessible: child_accessible,
|
2021-10-25 19:04:35 -05:00
|
|
|
note,
|
2020-06-13 12:58:46 -05:00
|
|
|
});
|
|
|
|
}
|
2019-07-11 13:13:11 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// collect submodules to explore
|
|
|
|
if let Some(module) = name_binding.module() {
|
|
|
|
// form the path
|
|
|
|
let mut path_segments = path_segments.clone();
|
|
|
|
path_segments.push(ast::PathSegment::from_ident(ident));
|
|
|
|
|
|
|
|
let is_extern_crate_that_also_appears_in_prelude =
|
|
|
|
name_binding.is_extern_crate() && lookup_ident.span.rust_2018();
|
|
|
|
|
2020-06-13 12:58:46 -05:00
|
|
|
if !is_extern_crate_that_also_appears_in_prelude {
|
|
|
|
// add the module to the lookup
|
2021-09-26 11:29:53 -05:00
|
|
|
if seen_modules.insert(module.def_id()) {
|
2020-06-21 11:31:49 -05:00
|
|
|
if via_import { &mut worklist_via_import } else { &mut worklist }
|
2021-04-11 17:44:54 -05:00
|
|
|
.push((module, path_segments, child_accessible));
|
2019-07-11 13:13:11 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-06-13 12:58:46 -05:00
|
|
|
// If only some candidates are accessible, take just them
|
|
|
|
if !candidates.iter().all(|v: &ImportSuggestion| !v.accessible) {
|
|
|
|
candidates = candidates.into_iter().filter(|x| x.accessible).collect();
|
|
|
|
}
|
|
|
|
|
2019-07-11 13:13:11 -05:00
|
|
|
candidates
|
|
|
|
}
|
|
|
|
|
|
|
|
/// When name resolution fails, this method can be used to look up candidate
|
|
|
|
/// entities with the expected name. It allows filtering them using the
|
|
|
|
/// supplied predicate (which should be used to only accept the types of
|
|
|
|
/// definitions expected, e.g., traits). The lookup spans across all crates.
|
|
|
|
///
|
|
|
|
/// N.B., the method does not look into imports, but this is not a problem,
|
|
|
|
/// since we report the definitions (thus, the de-aliased imports).
|
2022-05-20 18:51:09 -05:00
|
|
|
pub(crate) fn lookup_import_candidates<FilterFn>(
|
2019-07-11 13:13:11 -05:00
|
|
|
&mut self,
|
|
|
|
lookup_ident: Ident,
|
|
|
|
namespace: Namespace,
|
2020-05-30 11:54:44 -05:00
|
|
|
parent_scope: &ParentScope<'a>,
|
2019-07-11 13:13:11 -05:00
|
|
|
filter_fn: FilterFn,
|
|
|
|
) -> Vec<ImportSuggestion>
|
|
|
|
where
|
|
|
|
FilterFn: Fn(Res) -> bool,
|
|
|
|
{
|
|
|
|
let mut suggestions = self.lookup_import_candidates_from_module(
|
2019-08-10 18:20:18 -05:00
|
|
|
lookup_ident,
|
|
|
|
namespace,
|
2020-05-30 11:54:44 -05:00
|
|
|
parent_scope,
|
2019-08-10 18:20:18 -05:00
|
|
|
self.graph_root,
|
|
|
|
Ident::with_dummy_span(kw::Crate),
|
|
|
|
&filter_fn,
|
2019-07-11 13:13:11 -05:00
|
|
|
);
|
|
|
|
|
|
|
|
if lookup_ident.span.rust_2018() {
|
|
|
|
let extern_prelude_names = self.extern_prelude.clone();
|
|
|
|
for (ident, _) in extern_prelude_names.into_iter() {
|
2019-09-05 09:05:58 -05:00
|
|
|
if ident.span.from_expansion() {
|
|
|
|
// Idents are adjusted to the root context before being
|
|
|
|
// resolved in the extern prelude, so reporting this to the
|
|
|
|
// user is no help. This skips the injected
|
|
|
|
// `extern crate std` in the 2018 edition, which would
|
|
|
|
// otherwise cause duplicate suggestions.
|
|
|
|
continue;
|
|
|
|
}
|
2020-07-05 02:39:15 -05:00
|
|
|
if let Some(crate_id) = self.crate_loader.maybe_process_path_extern(ident.name) {
|
2021-09-11 18:06:27 -05:00
|
|
|
let crate_root = self.expect_module(crate_id.as_def_id());
|
2019-07-11 13:13:11 -05:00
|
|
|
suggestions.extend(self.lookup_import_candidates_from_module(
|
|
|
|
lookup_ident,
|
|
|
|
namespace,
|
2020-05-30 11:54:44 -05:00
|
|
|
parent_scope,
|
2019-07-11 13:13:11 -05:00
|
|
|
crate_root,
|
|
|
|
ident,
|
|
|
|
&filter_fn,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
suggestions
|
|
|
|
}
|
|
|
|
|
2022-05-20 18:51:09 -05:00
|
|
|
pub(crate) fn unresolved_macro_suggestions(
|
2019-07-11 18:29:28 -05:00
|
|
|
&mut self,
|
2022-01-23 14:41:46 -06:00
|
|
|
err: &mut Diagnostic,
|
2019-07-11 18:29:28 -05:00
|
|
|
macro_kind: MacroKind,
|
|
|
|
parent_scope: &ParentScope<'a>,
|
|
|
|
ident: Ident,
|
2019-07-11 13:13:11 -05:00
|
|
|
) {
|
2019-07-11 18:29:28 -05:00
|
|
|
let is_expected = &|res: Res| res.macro_kind() == Some(macro_kind);
|
|
|
|
let suggestion = self.early_lookup_typo_candidate(
|
2019-08-05 13:18:50 -05:00
|
|
|
ScopeSet::Macro(macro_kind),
|
|
|
|
parent_scope,
|
|
|
|
ident,
|
|
|
|
is_expected,
|
2019-07-11 18:29:28 -05:00
|
|
|
);
|
2019-10-14 19:20:50 -05:00
|
|
|
self.add_typo_suggestion(err, suggestion, ident.span);
|
2019-07-11 13:13:11 -05:00
|
|
|
|
2020-10-26 20:02:48 -05:00
|
|
|
let import_suggestions =
|
2021-08-22 07:06:45 -05:00
|
|
|
self.lookup_import_candidates(ident, Namespace::MacroNS, parent_scope, is_expected);
|
2021-09-17 17:07:41 -05:00
|
|
|
show_candidates(
|
2022-05-30 11:49:17 -05:00
|
|
|
&self.session,
|
|
|
|
&self.source_span,
|
2021-09-17 17:07:41 -05:00
|
|
|
err,
|
|
|
|
None,
|
|
|
|
&import_suggestions,
|
2022-04-29 21:40:36 -05:00
|
|
|
Instead::No,
|
|
|
|
FoundUse::Yes,
|
|
|
|
IsPattern::No,
|
2022-04-23 18:41:36 -05:00
|
|
|
vec![],
|
2021-09-17 17:07:41 -05:00
|
|
|
);
|
2020-08-06 00:53:09 -05:00
|
|
|
|
2020-07-07 20:04:10 -05:00
|
|
|
if macro_kind == MacroKind::Derive && (ident.name == sym::Send || ident.name == sym::Sync) {
|
2019-07-11 18:29:28 -05:00
|
|
|
let msg = format!("unsafe traits like `{}` should be implemented explicitly", ident);
|
|
|
|
err.span_note(ident.span, &msg);
|
2021-08-22 08:02:35 -05:00
|
|
|
return;
|
2019-07-11 18:29:28 -05:00
|
|
|
}
|
2020-03-13 17:36:46 -05:00
|
|
|
if self.macro_names.contains(&ident.normalize_to_macros_2_0()) {
|
2019-07-11 18:29:28 -05:00
|
|
|
err.help("have you added the `#[macro_use]` on the module/import?");
|
2021-08-22 08:02:35 -05:00
|
|
|
return;
|
|
|
|
}
|
2022-07-04 06:46:59 -05:00
|
|
|
if ident.name == kw::Default
|
|
|
|
&& let ModuleKind::Def(DefKind::Enum, def_id, _) = parent_scope.module.kind
|
|
|
|
&& let Some(span) = self.opt_span(def_id)
|
|
|
|
{
|
2022-07-06 23:57:01 -05:00
|
|
|
let source_map = self.session.source_map();
|
|
|
|
let head_span = source_map.guess_head_span(span);
|
|
|
|
if let Ok(head) = source_map.span_to_snippet(head_span) {
|
|
|
|
err.span_suggestion(head_span, "consider adding a derive", format!("#[derive(Default)]\n{head}"), Applicability::MaybeIncorrect);
|
|
|
|
} else {
|
|
|
|
err.span_help(
|
|
|
|
head_span,
|
|
|
|
"consider adding `#[derive(Default)]` to this enum",
|
|
|
|
);
|
|
|
|
}
|
2022-07-04 06:46:59 -05:00
|
|
|
}
|
2021-08-22 08:02:35 -05:00
|
|
|
for ns in [Namespace::MacroNS, Namespace::TypeNS, Namespace::ValueNS] {
|
|
|
|
if let Ok(binding) = self.early_resolve_ident_in_lexical_scope(
|
|
|
|
ident,
|
|
|
|
ScopeSet::All(ns, false),
|
|
|
|
&parent_scope,
|
2022-03-23 16:32:00 -05:00
|
|
|
None,
|
2021-08-22 08:02:35 -05:00
|
|
|
false,
|
2022-04-08 15:50:56 -05:00
|
|
|
None,
|
2021-08-22 08:02:35 -05:00
|
|
|
) {
|
2021-08-22 11:22:06 -05:00
|
|
|
let desc = match binding.res() {
|
|
|
|
Res::Def(DefKind::Macro(MacroKind::Bang), _) => {
|
|
|
|
"a function-like macro".to_string()
|
|
|
|
}
|
|
|
|
Res::Def(DefKind::Macro(MacroKind::Attr), _) | Res::NonMacroAttr(..) => {
|
|
|
|
format!("an attribute: `#[{}]`", ident)
|
|
|
|
}
|
|
|
|
Res::Def(DefKind::Macro(MacroKind::Derive), _) => {
|
|
|
|
format!("a derive macro: `#[derive({})]`", ident)
|
|
|
|
}
|
|
|
|
Res::ToolMod => {
|
|
|
|
// Don't confuse the user with tool modules.
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
Res::Def(DefKind::Trait, _) if macro_kind == MacroKind::Derive => {
|
|
|
|
"only a trait, without a derive macro".to_string()
|
|
|
|
}
|
|
|
|
res => format!(
|
2021-08-22 11:15:07 -05:00
|
|
|
"{} {}, not {} {}",
|
|
|
|
res.article(),
|
|
|
|
res.descr(),
|
|
|
|
macro_kind.article(),
|
|
|
|
macro_kind.descr_expected(),
|
|
|
|
),
|
2021-08-22 08:02:35 -05:00
|
|
|
};
|
|
|
|
if let crate::NameBindingKind::Import { import, .. } = binding.kind {
|
|
|
|
if !import.span.is_dummy() {
|
|
|
|
err.span_note(
|
|
|
|
import.span,
|
2021-08-22 10:16:24 -05:00
|
|
|
&format!("`{}` is imported here, but it is {}", ident, desc),
|
2021-08-22 08:02:35 -05:00
|
|
|
);
|
2021-08-22 09:33:21 -05:00
|
|
|
// Silence the 'unused import' warning we might get,
|
|
|
|
// since this diagnostic already covers that import.
|
|
|
|
self.record_use(ident, binding, false);
|
2021-08-22 08:02:35 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2021-08-22 10:16:24 -05:00
|
|
|
err.note(&format!("`{}` is in scope, but it is {}", ident, desc));
|
2021-08-22 08:02:35 -05:00
|
|
|
return;
|
|
|
|
}
|
2019-07-11 13:13:11 -05:00
|
|
|
}
|
|
|
|
}
|
2019-10-14 19:20:50 -05:00
|
|
|
|
2022-05-20 18:51:09 -05:00
|
|
|
pub(crate) fn add_typo_suggestion(
|
2019-10-14 19:20:50 -05:00
|
|
|
&self,
|
2022-01-23 14:41:46 -06:00
|
|
|
err: &mut Diagnostic,
|
2019-10-14 19:20:50 -05:00
|
|
|
suggestion: Option<TypoSuggestion>,
|
|
|
|
span: Span,
|
|
|
|
) -> bool {
|
2020-07-09 11:05:40 -05:00
|
|
|
let suggestion = match suggestion {
|
|
|
|
None => return false,
|
2020-02-01 13:40:55 -06:00
|
|
|
// We shouldn't suggest underscore.
|
2020-07-09 11:05:40 -05:00
|
|
|
Some(suggestion) if suggestion.candidate == kw::Underscore => return false,
|
|
|
|
Some(suggestion) => suggestion,
|
|
|
|
};
|
|
|
|
let def_span = suggestion.res.opt_def_id().and_then(|def_id| match def_id.krate {
|
|
|
|
LOCAL_CRATE => self.opt_span(def_id),
|
2022-07-23 07:42:54 -05:00
|
|
|
_ => Some(self.cstore().get_span_untracked(def_id, self.session)),
|
2020-07-09 11:05:40 -05:00
|
|
|
});
|
2020-08-09 23:02:58 -05:00
|
|
|
if let Some(def_span) = def_span {
|
|
|
|
if span.overlaps(def_span) {
|
2021-01-29 04:38:49 -06:00
|
|
|
// Don't suggest typo suggestion for itself like in the following:
|
2020-08-09 23:02:58 -05:00
|
|
|
// error[E0423]: expected function, tuple struct or tuple variant, found struct `X`
|
|
|
|
// --> $DIR/issue-64792-bad-unicode-ctor.rs:3:14
|
|
|
|
// |
|
|
|
|
// LL | struct X {}
|
|
|
|
// | ----------- `X` defined here
|
|
|
|
// LL |
|
|
|
|
// LL | const Y: X = X("ö");
|
|
|
|
// | -------------^^^^^^- similarly named constant `Y` defined here
|
|
|
|
// |
|
|
|
|
// help: use struct literal syntax instead
|
|
|
|
// |
|
|
|
|
// LL | const Y: X = X {};
|
|
|
|
// | ^^^^
|
|
|
|
// help: a constant with a similar name exists
|
|
|
|
// |
|
|
|
|
// LL | const Y: X = Y("ö");
|
|
|
|
// | ^
|
|
|
|
return false;
|
|
|
|
}
|
2021-08-04 16:29:30 -05:00
|
|
|
let prefix = match suggestion.target {
|
|
|
|
SuggestionTarget::SimilarlyNamed => "similarly named ",
|
|
|
|
SuggestionTarget::SingleItem => "",
|
|
|
|
};
|
|
|
|
|
2020-07-09 11:05:40 -05:00
|
|
|
err.span_label(
|
2020-08-09 23:02:58 -05:00
|
|
|
self.session.source_map().guess_head_span(def_span),
|
2020-07-09 11:05:40 -05:00
|
|
|
&format!(
|
2021-08-04 16:29:30 -05:00
|
|
|
"{}{} `{}` defined here",
|
|
|
|
prefix,
|
2020-07-09 11:05:40 -05:00
|
|
|
suggestion.res.descr(),
|
2022-07-18 00:25:34 -05:00
|
|
|
suggestion.candidate,
|
2020-01-07 14:22:47 -06:00
|
|
|
),
|
2020-07-09 11:05:40 -05:00
|
|
|
);
|
2019-10-14 19:20:50 -05:00
|
|
|
}
|
2021-08-04 16:29:30 -05:00
|
|
|
let msg = match suggestion.target {
|
|
|
|
SuggestionTarget::SimilarlyNamed => format!(
|
|
|
|
"{} {} with a similar name exists",
|
|
|
|
suggestion.res.article(),
|
|
|
|
suggestion.res.descr()
|
|
|
|
),
|
|
|
|
SuggestionTarget::SingleItem => {
|
|
|
|
format!("maybe you meant this {}", suggestion.res.descr())
|
|
|
|
}
|
|
|
|
};
|
2022-06-13 01:48:40 -05:00
|
|
|
err.span_suggestion(span, &msg, suggestion.candidate, Applicability::MaybeIncorrect);
|
2020-07-09 11:05:40 -05:00
|
|
|
true
|
2019-10-14 19:20:50 -05:00
|
|
|
}
|
2020-01-12 04:29:00 -06:00
|
|
|
|
|
|
|
fn binding_description(&self, b: &NameBinding<'_>, ident: Ident, from_prelude: bool) -> String {
|
|
|
|
let res = b.res();
|
2022-07-11 02:51:19 -05:00
|
|
|
if b.span.is_dummy() || !self.session.source_map().is_span_accessible(b.span) {
|
2020-10-27 09:55:26 -05:00
|
|
|
// These already contain the "built-in" prefix or look bad with it.
|
2020-10-26 20:02:48 -05:00
|
|
|
let add_built_in =
|
|
|
|
!matches!(b.res(), Res::NonMacroAttr(..) | Res::PrimTy(..) | Res::ToolMod);
|
2020-01-12 04:29:00 -06:00
|
|
|
let (built_in, from) = if from_prelude {
|
|
|
|
("", " from prelude")
|
|
|
|
} else if b.is_extern_crate()
|
|
|
|
&& !b.is_import()
|
2021-12-14 23:13:11 -06:00
|
|
|
&& self.session.opts.externs.get(ident.as_str()).is_some()
|
2020-01-12 04:29:00 -06:00
|
|
|
{
|
|
|
|
("", " passed with `--extern`")
|
|
|
|
} else if add_built_in {
|
|
|
|
(" built-in", "")
|
|
|
|
} else {
|
|
|
|
("", "")
|
|
|
|
};
|
|
|
|
|
2020-11-07 16:38:11 -06:00
|
|
|
let a = if built_in.is_empty() { res.article() } else { "a" };
|
|
|
|
format!("{a}{built_in} {thing}{from}", thing = res.descr())
|
2020-01-12 04:29:00 -06:00
|
|
|
} else {
|
|
|
|
let introduced = if b.is_import() { "imported" } else { "defined" };
|
2020-11-07 16:38:11 -06:00
|
|
|
format!("the {thing} {introduced} here", thing = res.descr())
|
2020-01-12 04:29:00 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-08 15:52:18 -05:00
|
|
|
fn report_ambiguity_error(&self, ambiguity_error: &AmbiguityError<'_>) {
|
2020-01-12 04:29:00 -06:00
|
|
|
let AmbiguityError { kind, ident, b1, b2, misc1, misc2 } = *ambiguity_error;
|
|
|
|
let (b1, b2, misc1, misc2, swapped) = if b2.span.is_dummy() && !b1.span.is_dummy() {
|
|
|
|
// We have to print the span-less alternative first, otherwise formatting looks bad.
|
|
|
|
(b2, b1, misc2, misc1, true)
|
|
|
|
} else {
|
|
|
|
(b1, b2, misc1, misc2, false)
|
|
|
|
};
|
|
|
|
|
2021-10-20 08:56:10 -05:00
|
|
|
let mut err = struct_span_err!(self.session, ident.span, E0659, "`{ident}` is ambiguous");
|
2020-01-12 04:29:00 -06:00
|
|
|
err.span_label(ident.span, "ambiguous name");
|
2021-10-20 08:56:10 -05:00
|
|
|
err.note(&format!("ambiguous because of {}", kind.descr()));
|
2020-01-12 04:29:00 -06:00
|
|
|
|
|
|
|
let mut could_refer_to = |b: &NameBinding<'_>, misc: AmbiguityErrorMisc, also: &str| {
|
|
|
|
let what = self.binding_description(b, ident, misc == AmbiguityErrorMisc::FromPrelude);
|
2020-11-07 16:38:11 -06:00
|
|
|
let note_msg = format!("`{ident}` could{also} refer to {what}");
|
2020-01-12 04:29:00 -06:00
|
|
|
|
|
|
|
let thing = b.res().descr();
|
|
|
|
let mut help_msgs = Vec::new();
|
|
|
|
if b.is_glob_import()
|
|
|
|
&& (kind == AmbiguityKind::GlobVsGlob
|
|
|
|
|| kind == AmbiguityKind::GlobVsExpanded
|
|
|
|
|| kind == AmbiguityKind::GlobVsOuter && swapped != also.is_empty())
|
|
|
|
{
|
|
|
|
help_msgs.push(format!(
|
2020-11-07 16:38:11 -06:00
|
|
|
"consider adding an explicit import of `{ident}` to disambiguate"
|
2020-01-12 04:29:00 -06:00
|
|
|
))
|
|
|
|
}
|
|
|
|
if b.is_extern_crate() && ident.span.rust_2018() {
|
2020-11-07 16:38:11 -06:00
|
|
|
help_msgs.push(format!("use `::{ident}` to refer to this {thing} unambiguously"))
|
2020-01-12 04:29:00 -06:00
|
|
|
}
|
|
|
|
if misc == AmbiguityErrorMisc::SuggestCrate {
|
2020-11-07 16:38:11 -06:00
|
|
|
help_msgs
|
|
|
|
.push(format!("use `crate::{ident}` to refer to this {thing} unambiguously"))
|
2020-01-12 04:29:00 -06:00
|
|
|
} else if misc == AmbiguityErrorMisc::SuggestSelf {
|
2020-11-07 16:38:11 -06:00
|
|
|
help_msgs
|
|
|
|
.push(format!("use `self::{ident}` to refer to this {thing} unambiguously"))
|
2020-01-12 04:29:00 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
err.span_note(b.span, ¬e_msg);
|
|
|
|
for (i, help_msg) in help_msgs.iter().enumerate() {
|
|
|
|
let or = if i == 0 { "" } else { "or " };
|
|
|
|
err.help(&format!("{}{}", or, help_msg));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
could_refer_to(b1, misc1, "");
|
|
|
|
could_refer_to(b2, misc2, " also");
|
|
|
|
err.emit();
|
|
|
|
}
|
|
|
|
|
2020-03-07 09:39:16 -06:00
|
|
|
/// If the binding refers to a tuple struct constructor with fields,
|
|
|
|
/// returns the span of its fields.
|
|
|
|
fn ctor_fields_span(&self, binding: &NameBinding<'_>) -> Option<Span> {
|
|
|
|
if let NameBindingKind::Res(
|
2020-01-12 04:29:00 -06:00
|
|
|
Res::Def(DefKind::Ctor(CtorOf::Struct, CtorKind::Fn), ctor_def_id),
|
|
|
|
_,
|
|
|
|
) = binding.kind
|
|
|
|
{
|
2022-04-25 14:08:45 -05:00
|
|
|
let def_id = self.parent(ctor_def_id);
|
2020-08-05 06:59:26 -05:00
|
|
|
let fields = self.field_names.get(&def_id)?;
|
2021-02-04 15:44:24 -06:00
|
|
|
return fields.iter().map(|name| name.span).reduce(Span::to); // None for `struct Foo()`
|
2020-03-07 09:39:16 -06:00
|
|
|
}
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
2022-04-08 15:52:18 -05:00
|
|
|
fn report_privacy_error(&self, privacy_error: &PrivacyError<'_>) {
|
2020-03-07 09:39:16 -06:00
|
|
|
let PrivacyError { ident, binding, .. } = *privacy_error;
|
2020-01-12 04:29:00 -06:00
|
|
|
|
2020-03-07 12:18:29 -06:00
|
|
|
let res = binding.res();
|
2020-03-07 09:39:16 -06:00
|
|
|
let ctor_fields_span = self.ctor_fields_span(binding);
|
2020-03-07 12:18:29 -06:00
|
|
|
let plain_descr = res.descr().to_string();
|
|
|
|
let nonimport_descr =
|
|
|
|
if ctor_fields_span.is_some() { plain_descr + " constructor" } else { plain_descr };
|
|
|
|
let import_descr = nonimport_descr.clone() + " import";
|
|
|
|
let get_descr =
|
|
|
|
|b: &NameBinding<'_>| if b.is_import() { &import_descr } else { &nonimport_descr };
|
|
|
|
|
|
|
|
// Print the primary message.
|
|
|
|
let descr = get_descr(binding);
|
2020-03-07 09:39:16 -06:00
|
|
|
let mut err =
|
|
|
|
struct_span_err!(self.session, ident.span, E0603, "{} `{}` is private", descr, ident);
|
2020-03-22 17:36:54 -05:00
|
|
|
err.span_label(ident.span, &format!("private {}", descr));
|
2020-03-07 09:39:16 -06:00
|
|
|
if let Some(span) = ctor_fields_span {
|
|
|
|
err.span_label(span, "a constructor is private if any of the fields is private");
|
|
|
|
}
|
|
|
|
|
2020-03-07 12:18:29 -06:00
|
|
|
// Print the whole import chain to make it easier to see what happens.
|
|
|
|
let first_binding = binding;
|
|
|
|
let mut next_binding = Some(binding);
|
|
|
|
let mut next_ident = ident;
|
|
|
|
while let Some(binding) = next_binding {
|
|
|
|
let name = next_ident;
|
|
|
|
next_binding = match binding.kind {
|
|
|
|
_ if res == Res::Err => None,
|
|
|
|
NameBindingKind::Import { binding, import, .. } => match import.kind {
|
|
|
|
_ if binding.span.is_dummy() => None,
|
|
|
|
ImportKind::Single { source, .. } => {
|
|
|
|
next_ident = source;
|
|
|
|
Some(binding)
|
|
|
|
}
|
|
|
|
ImportKind::Glob { .. } | ImportKind::MacroUse => Some(binding),
|
|
|
|
ImportKind::ExternCrate { .. } => None,
|
|
|
|
},
|
|
|
|
_ => None,
|
|
|
|
};
|
|
|
|
|
|
|
|
let first = ptr::eq(binding, first_binding);
|
|
|
|
let msg = format!(
|
|
|
|
"{and_refers_to}the {item} `{name}`{which} is defined here{dots}",
|
|
|
|
and_refers_to = if first { "" } else { "...and refers to " },
|
2020-11-07 16:38:11 -06:00
|
|
|
item = get_descr(binding),
|
2020-03-07 12:18:29 -06:00
|
|
|
which = if first { "" } else { " which" },
|
|
|
|
dots = if next_binding.is_some() { "..." } else { "" },
|
|
|
|
);
|
2020-03-09 13:42:37 -05:00
|
|
|
let def_span = self.session.source_map().guess_head_span(binding.span);
|
2020-03-07 12:18:29 -06:00
|
|
|
let mut note_span = MultiSpan::from_span(def_span);
|
2021-11-07 21:53:26 -06:00
|
|
|
if !first && binding.vis.is_public() {
|
2022-03-26 02:27:43 -05:00
|
|
|
note_span.push_span_label(def_span, "consider importing it directly");
|
2020-03-07 12:18:29 -06:00
|
|
|
}
|
|
|
|
err.span_note(note_span, &msg);
|
|
|
|
}
|
2020-01-12 04:29:00 -06:00
|
|
|
|
|
|
|
err.emit();
|
|
|
|
}
|
2021-09-28 23:55:24 -05:00
|
|
|
|
2022-05-20 18:51:09 -05:00
|
|
|
pub(crate) fn find_similarly_named_module_or_crate(
|
2021-09-28 23:55:24 -05:00
|
|
|
&mut self,
|
|
|
|
ident: Symbol,
|
|
|
|
current_module: &Module<'a>,
|
|
|
|
) -> Option<Symbol> {
|
|
|
|
let mut candidates = self
|
|
|
|
.extern_prelude
|
|
|
|
.iter()
|
|
|
|
.map(|(ident, _)| ident.name)
|
|
|
|
.chain(
|
|
|
|
self.module_map
|
|
|
|
.iter()
|
|
|
|
.filter(|(_, module)| {
|
|
|
|
current_module.is_ancestor_of(module) && !ptr::eq(current_module, *module)
|
|
|
|
})
|
2022-02-03 16:12:25 -06:00
|
|
|
.flat_map(|(_, module)| module.kind.name()),
|
2021-09-28 23:55:24 -05:00
|
|
|
)
|
|
|
|
.filter(|c| !c.to_string().is_empty())
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
candidates.sort();
|
|
|
|
candidates.dedup();
|
|
|
|
match find_best_match_for_name(&candidates, ident, None) {
|
|
|
|
Some(sugg) if sugg == ident => None,
|
|
|
|
sugg => sugg,
|
|
|
|
}
|
|
|
|
}
|
2022-04-08 15:51:55 -05:00
|
|
|
|
2022-05-20 18:51:09 -05:00
|
|
|
pub(crate) fn report_path_resolution_error(
|
2022-04-08 15:51:55 -05:00
|
|
|
&mut self,
|
|
|
|
path: &[Segment],
|
|
|
|
opt_ns: Option<Namespace>, // `None` indicates a module path in import
|
|
|
|
parent_scope: &ParentScope<'a>,
|
|
|
|
ribs: Option<&PerNS<Vec<Rib<'a>>>>,
|
2022-04-30 10:01:01 -05:00
|
|
|
ignore_binding: Option<&'a NameBinding<'a>>,
|
2022-04-08 15:51:55 -05:00
|
|
|
module: Option<ModuleOrUniformRoot<'a>>,
|
|
|
|
i: usize,
|
|
|
|
ident: Ident,
|
|
|
|
) -> (String, Option<Suggestion>) {
|
|
|
|
let is_last = i == path.len() - 1;
|
|
|
|
let ns = if is_last { opt_ns.unwrap_or(TypeNS) } else { TypeNS };
|
|
|
|
let module_res = match module {
|
|
|
|
Some(ModuleOrUniformRoot::Module(module)) => module.res(),
|
|
|
|
_ => None,
|
|
|
|
};
|
|
|
|
if module_res == self.graph_root.res() {
|
|
|
|
let is_mod = |res| matches!(res, Res::Def(DefKind::Mod, _));
|
|
|
|
let mut candidates = self.lookup_import_candidates(ident, TypeNS, parent_scope, is_mod);
|
|
|
|
candidates
|
|
|
|
.sort_by_cached_key(|c| (c.path.segments.len(), pprust::path_to_string(&c.path)));
|
|
|
|
if let Some(candidate) = candidates.get(0) {
|
|
|
|
(
|
|
|
|
String::from("unresolved import"),
|
|
|
|
Some((
|
|
|
|
vec![(ident.span, pprust::path_to_string(&candidate.path))],
|
|
|
|
String::from("a similar path exists"),
|
|
|
|
Applicability::MaybeIncorrect,
|
|
|
|
)),
|
|
|
|
)
|
|
|
|
} else if self.session.edition() == Edition::Edition2015 {
|
2022-05-21 21:48:35 -05:00
|
|
|
(
|
|
|
|
format!("maybe a missing crate `{ident}`?"),
|
|
|
|
Some((
|
|
|
|
vec![],
|
|
|
|
format!(
|
|
|
|
"consider adding `extern crate {ident}` to use the `{ident}` crate"
|
|
|
|
),
|
|
|
|
Applicability::MaybeIncorrect,
|
|
|
|
)),
|
|
|
|
)
|
2022-04-08 15:51:55 -05:00
|
|
|
} else {
|
2022-05-21 21:48:35 -05:00
|
|
|
(format!("could not find `{ident}` in the crate root"), None)
|
2022-04-08 15:51:55 -05:00
|
|
|
}
|
2022-04-08 15:52:07 -05:00
|
|
|
} else if i > 0 {
|
2022-04-08 15:51:55 -05:00
|
|
|
let parent = path[i - 1].ident.name;
|
|
|
|
let parent = match parent {
|
|
|
|
// ::foo is mounted at the crate root for 2015, and is the extern
|
|
|
|
// prelude for 2018+
|
|
|
|
kw::PathRoot if self.session.edition() > Edition::Edition2015 => {
|
|
|
|
"the list of imported crates".to_owned()
|
|
|
|
}
|
|
|
|
kw::PathRoot | kw::Crate => "the crate root".to_owned(),
|
2022-05-21 21:48:35 -05:00
|
|
|
_ => format!("`{parent}`"),
|
2022-04-08 15:51:55 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
let mut msg = format!("could not find `{}` in {}", ident, parent);
|
|
|
|
if ns == TypeNS || ns == ValueNS {
|
|
|
|
let ns_to_try = if ns == TypeNS { ValueNS } else { TypeNS };
|
|
|
|
let binding = if let Some(module) = module {
|
|
|
|
self.resolve_ident_in_module(
|
|
|
|
module,
|
|
|
|
ident,
|
|
|
|
ns_to_try,
|
|
|
|
parent_scope,
|
2022-04-08 15:52:07 -05:00
|
|
|
None,
|
2022-04-30 10:01:01 -05:00
|
|
|
ignore_binding,
|
2022-04-08 15:51:55 -05:00
|
|
|
).ok()
|
|
|
|
} else if let Some(ribs) = ribs
|
|
|
|
&& let Some(TypeNS | ValueNS) = opt_ns
|
|
|
|
{
|
|
|
|
match self.resolve_ident_in_lexical_scope(
|
|
|
|
ident,
|
|
|
|
ns_to_try,
|
|
|
|
parent_scope,
|
2022-04-30 08:26:36 -05:00
|
|
|
None,
|
2022-04-08 15:51:55 -05:00
|
|
|
&ribs[ns_to_try],
|
2022-04-30 10:01:01 -05:00
|
|
|
ignore_binding,
|
2022-04-08 15:51:55 -05:00
|
|
|
) {
|
|
|
|
// we found a locally-imported or available item/module
|
|
|
|
Some(LexicalScopeBinding::Item(binding)) => Some(binding),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
let scopes = ScopeSet::All(ns_to_try, opt_ns.is_none());
|
|
|
|
self.early_resolve_ident_in_lexical_scope(
|
|
|
|
ident,
|
|
|
|
scopes,
|
|
|
|
parent_scope,
|
2022-04-08 15:52:07 -05:00
|
|
|
None,
|
|
|
|
false,
|
2022-04-30 10:01:01 -05:00
|
|
|
ignore_binding,
|
2022-04-08 15:51:55 -05:00
|
|
|
).ok()
|
|
|
|
};
|
|
|
|
if let Some(binding) = binding {
|
|
|
|
let mut found = |what| {
|
|
|
|
msg = format!(
|
|
|
|
"expected {}, found {} `{}` in {}",
|
|
|
|
ns.descr(),
|
|
|
|
what,
|
|
|
|
ident,
|
|
|
|
parent
|
|
|
|
)
|
|
|
|
};
|
|
|
|
if binding.module().is_some() {
|
|
|
|
found("module")
|
|
|
|
} else {
|
|
|
|
match binding.res() {
|
|
|
|
Res::Def(kind, id) => found(kind.descr(id)),
|
|
|
|
_ => found(ns_to_try.descr()),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
(msg, None)
|
2022-06-19 21:24:28 -05:00
|
|
|
} else if ident.name == kw::SelfUpper {
|
|
|
|
("`Self` is only available in impls, traits, and type definitions".to_string(), None)
|
2022-04-08 15:52:07 -05:00
|
|
|
} else if ident.name.as_str().chars().next().map_or(false, |c| c.is_ascii_uppercase()) {
|
|
|
|
// Check whether the name refers to an item in the value namespace.
|
|
|
|
let binding = if let Some(ribs) = ribs {
|
|
|
|
self.resolve_ident_in_lexical_scope(
|
|
|
|
ident,
|
|
|
|
ValueNS,
|
|
|
|
parent_scope,
|
2022-04-30 08:26:36 -05:00
|
|
|
None,
|
2022-04-08 15:52:07 -05:00
|
|
|
&ribs[ValueNS],
|
2022-04-30 10:01:01 -05:00
|
|
|
ignore_binding,
|
2022-04-08 15:52:07 -05:00
|
|
|
)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
|
|
|
let match_span = match binding {
|
|
|
|
// Name matches a local variable. For example:
|
|
|
|
// ```
|
|
|
|
// fn f() {
|
|
|
|
// let Foo: &str = "";
|
|
|
|
// println!("{}", Foo::Bar); // Name refers to local
|
|
|
|
// // variable `Foo`.
|
|
|
|
// }
|
|
|
|
// ```
|
|
|
|
Some(LexicalScopeBinding::Res(Res::Local(id))) => {
|
|
|
|
Some(*self.pat_span_map.get(&id).unwrap())
|
|
|
|
}
|
|
|
|
// Name matches item from a local name binding
|
|
|
|
// created by `use` declaration. For example:
|
|
|
|
// ```
|
|
|
|
// pub Foo: &str = "";
|
|
|
|
//
|
|
|
|
// mod submod {
|
|
|
|
// use super::Foo;
|
|
|
|
// println!("{}", Foo::Bar); // Name refers to local
|
|
|
|
// // binding `Foo`.
|
|
|
|
// }
|
|
|
|
// ```
|
|
|
|
Some(LexicalScopeBinding::Item(name_binding)) => Some(name_binding.span),
|
|
|
|
_ => None,
|
|
|
|
};
|
|
|
|
let suggestion = if let Some(span) = match_span {
|
|
|
|
Some((
|
|
|
|
vec![(span, String::from(""))],
|
|
|
|
format!("`{}` is defined here, but is not a type", ident),
|
|
|
|
Applicability::MaybeIncorrect,
|
|
|
|
))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
|
|
|
|
|
|
|
(format!("use of undeclared type `{}`", ident), suggestion)
|
|
|
|
} else {
|
|
|
|
let suggestion = if ident.name == sym::alloc {
|
|
|
|
Some((
|
|
|
|
vec![],
|
|
|
|
String::from("add `extern crate alloc` to use the `alloc` crate"),
|
|
|
|
Applicability::MaybeIncorrect,
|
|
|
|
))
|
|
|
|
} else {
|
|
|
|
self.find_similarly_named_module_or_crate(ident.name, &parent_scope.module).map(
|
|
|
|
|sugg| {
|
|
|
|
(
|
|
|
|
vec![(ident.span, sugg.to_string())],
|
|
|
|
String::from("there is a crate or module with a similar name"),
|
|
|
|
Applicability::MaybeIncorrect,
|
|
|
|
)
|
|
|
|
},
|
|
|
|
)
|
|
|
|
};
|
|
|
|
(format!("use of undeclared crate or module `{}`", ident), suggestion)
|
2022-04-08 15:51:55 -05:00
|
|
|
}
|
|
|
|
}
|
2019-02-04 04:25:29 -06:00
|
|
|
}
|
2018-09-20 10:15:52 -05:00
|
|
|
|
2019-06-14 11:39:39 -05:00
|
|
|
impl<'a, 'b> ImportResolver<'a, 'b> {
|
2019-02-04 04:24:09 -06:00
|
|
|
/// Adds suggestions for a path that cannot be resolved.
|
2018-09-20 10:15:52 -05:00
|
|
|
pub(crate) fn make_path_suggestion(
|
|
|
|
&mut self,
|
|
|
|
span: Span,
|
2018-11-11 08:35:23 -06:00
|
|
|
mut path: Vec<Segment>,
|
2018-10-27 12:23:54 -05:00
|
|
|
parent_scope: &ParentScope<'b>,
|
2022-07-28 04:17:55 -05:00
|
|
|
) -> Option<(Vec<Segment>, Option<String>)> {
|
2018-09-20 10:15:52 -05:00
|
|
|
debug!("make_path_suggestion: span={:?} path={:?}", span, path);
|
|
|
|
|
|
|
|
match (path.get(0), path.get(1)) {
|
2018-11-11 08:35:23 -06:00
|
|
|
// `{{root}}::ident::...` on both editions.
|
|
|
|
// On 2015 `{{root}}` is usually added implicitly.
|
2019-05-11 09:41:37 -05:00
|
|
|
(Some(fst), Some(snd))
|
2018-11-18 05:41:06 -06:00
|
|
|
if fst.ident.name == kw::PathRoot && !snd.ident.is_path_segment_keyword() => {}
|
2019-02-04 04:24:09 -06:00
|
|
|
// `ident::...` on 2018.
|
2018-11-17 18:25:59 -06:00
|
|
|
(Some(fst), _)
|
|
|
|
if fst.ident.span.rust_2018() && !fst.ident.is_path_segment_keyword() =>
|
|
|
|
{
|
2018-11-11 08:35:23 -06:00
|
|
|
// Insert a placeholder that's later replaced by `self`/`super`/etc.
|
2021-10-17 15:20:30 -05:00
|
|
|
path.insert(0, Segment::from_ident(Ident::empty()));
|
2018-11-11 08:35:23 -06:00
|
|
|
}
|
|
|
|
_ => return None,
|
2018-09-20 10:15:52 -05:00
|
|
|
}
|
2018-11-11 08:35:23 -06:00
|
|
|
|
2022-03-23 16:32:00 -05:00
|
|
|
self.make_missing_self_suggestion(path.clone(), parent_scope)
|
|
|
|
.or_else(|| self.make_missing_crate_suggestion(path.clone(), parent_scope))
|
|
|
|
.or_else(|| self.make_missing_super_suggestion(path.clone(), parent_scope))
|
|
|
|
.or_else(|| self.make_external_crate_suggestion(path, parent_scope))
|
2018-09-20 10:15:52 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Suggest a missing `self::` if that resolves to an correct module.
|
|
|
|
///
|
2020-04-18 11:39:40 -05:00
|
|
|
/// ```text
|
2018-09-20 10:15:52 -05:00
|
|
|
/// |
|
|
|
|
/// LL | use foo::Bar;
|
2018-11-13 17:52:26 -06:00
|
|
|
/// | ^^^ did you mean `self::foo`?
|
2018-09-20 10:15:52 -05:00
|
|
|
/// ```
|
|
|
|
fn make_missing_self_suggestion(
|
|
|
|
&mut self,
|
2018-10-27 12:23:54 -05:00
|
|
|
mut path: Vec<Segment>,
|
|
|
|
parent_scope: &ParentScope<'b>,
|
2022-07-28 04:17:55 -05:00
|
|
|
) -> Option<(Vec<Segment>, Option<String>)> {
|
2018-09-20 10:15:52 -05:00
|
|
|
// Replace first ident with `self` and check if that is valid.
|
2019-05-11 09:41:37 -05:00
|
|
|
path[0].ident.name = kw::SelfLower;
|
2022-04-08 15:50:56 -05:00
|
|
|
let result = self.r.maybe_resolve_path(&path, None, parent_scope);
|
2018-09-20 10:15:52 -05:00
|
|
|
debug!("make_missing_self_suggestion: path={:?} result={:?}", path, result);
|
2022-07-28 04:17:55 -05:00
|
|
|
if let PathResult::Module(..) = result { Some((path, None)) } else { None }
|
2018-09-20 10:15:52 -05:00
|
|
|
}
|
|
|
|
|
2019-02-04 04:24:09 -06:00
|
|
|
/// Suggests a missing `crate::` if that resolves to an correct module.
|
2018-09-20 10:15:52 -05:00
|
|
|
///
|
2020-05-01 15:28:15 -05:00
|
|
|
/// ```text
|
2018-09-20 10:15:52 -05:00
|
|
|
/// |
|
|
|
|
/// LL | use foo::Bar;
|
2018-11-13 17:52:26 -06:00
|
|
|
/// | ^^^ did you mean `crate::foo`?
|
2018-09-20 10:15:52 -05:00
|
|
|
/// ```
|
|
|
|
fn make_missing_crate_suggestion(
|
|
|
|
&mut self,
|
2018-10-27 12:23:54 -05:00
|
|
|
mut path: Vec<Segment>,
|
|
|
|
parent_scope: &ParentScope<'b>,
|
2022-07-28 04:17:55 -05:00
|
|
|
) -> Option<(Vec<Segment>, Option<String>)> {
|
2018-09-20 10:15:52 -05:00
|
|
|
// Replace first ident with `crate` and check if that is valid.
|
2019-05-11 09:41:37 -05:00
|
|
|
path[0].ident.name = kw::Crate;
|
2022-04-08 15:50:56 -05:00
|
|
|
let result = self.r.maybe_resolve_path(&path, None, parent_scope);
|
2018-09-20 10:15:52 -05:00
|
|
|
debug!("make_missing_crate_suggestion: path={:?} result={:?}", path, result);
|
|
|
|
if let PathResult::Module(..) = result {
|
2018-10-18 12:09:49 -05:00
|
|
|
Some((
|
|
|
|
path,
|
2022-07-28 04:17:55 -05:00
|
|
|
Some(
|
2018-10-18 12:09:49 -05:00
|
|
|
"`use` statements changed in Rust 2018; read more at \
|
|
|
|
<https://doc.rust-lang.org/edition-guide/rust-2018/module-system/path-\
|
|
|
|
clarity.html>"
|
|
|
|
.to_string(),
|
2022-07-28 04:17:55 -05:00
|
|
|
),
|
2018-10-18 12:09:49 -05:00
|
|
|
))
|
2018-09-20 10:15:52 -05:00
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-04 04:24:09 -06:00
|
|
|
/// Suggests a missing `super::` if that resolves to an correct module.
|
2018-09-20 10:15:52 -05:00
|
|
|
///
|
2020-04-18 11:39:40 -05:00
|
|
|
/// ```text
|
2018-09-20 10:15:52 -05:00
|
|
|
/// |
|
|
|
|
/// LL | use foo::Bar;
|
2018-11-13 17:52:26 -06:00
|
|
|
/// | ^^^ did you mean `super::foo`?
|
2018-09-20 10:15:52 -05:00
|
|
|
/// ```
|
|
|
|
fn make_missing_super_suggestion(
|
|
|
|
&mut self,
|
2018-10-27 12:23:54 -05:00
|
|
|
mut path: Vec<Segment>,
|
|
|
|
parent_scope: &ParentScope<'b>,
|
2022-07-28 04:17:55 -05:00
|
|
|
) -> Option<(Vec<Segment>, Option<String>)> {
|
2018-09-20 10:15:52 -05:00
|
|
|
// Replace first ident with `crate` and check if that is valid.
|
2019-05-11 09:41:37 -05:00
|
|
|
path[0].ident.name = kw::Super;
|
2022-04-08 15:50:56 -05:00
|
|
|
let result = self.r.maybe_resolve_path(&path, None, parent_scope);
|
2018-09-20 10:15:52 -05:00
|
|
|
debug!("make_missing_super_suggestion: path={:?} result={:?}", path, result);
|
2022-07-28 04:17:55 -05:00
|
|
|
if let PathResult::Module(..) = result { Some((path, None)) } else { None }
|
2018-09-20 10:15:52 -05:00
|
|
|
}
|
|
|
|
|
2019-02-04 04:24:09 -06:00
|
|
|
/// Suggests a missing external crate name if that resolves to an correct module.
|
2018-09-20 10:15:52 -05:00
|
|
|
///
|
2020-04-18 11:39:40 -05:00
|
|
|
/// ```text
|
2018-09-20 10:15:52 -05:00
|
|
|
/// |
|
|
|
|
/// LL | use foobar::Baz;
|
2018-11-13 17:52:26 -06:00
|
|
|
/// | ^^^^^^ did you mean `baz::foobar`?
|
2018-09-20 10:15:52 -05:00
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// Used when importing a submodule of an external crate but missing that crate's
|
|
|
|
/// name as the first part of path.
|
|
|
|
fn make_external_crate_suggestion(
|
|
|
|
&mut self,
|
2018-10-27 12:23:54 -05:00
|
|
|
mut path: Vec<Segment>,
|
|
|
|
parent_scope: &ParentScope<'b>,
|
2022-07-28 04:17:55 -05:00
|
|
|
) -> Option<(Vec<Segment>, Option<String>)> {
|
2018-11-17 18:25:59 -06:00
|
|
|
if path[1].ident.span.rust_2015() {
|
2018-11-11 08:35:23 -06:00
|
|
|
return None;
|
|
|
|
}
|
2018-09-20 10:15:52 -05:00
|
|
|
|
2021-12-14 15:32:21 -06:00
|
|
|
// Sort extern crate names in *reverse* order to get
|
2020-03-06 05:13:55 -06:00
|
|
|
// 1) some consistent ordering for emitted diagnostics, and
|
2018-11-11 08:35:23 -06:00
|
|
|
// 2) `std` suggestions before `core` suggestions.
|
|
|
|
let mut extern_crate_names =
|
2019-08-08 06:06:42 -05:00
|
|
|
self.r.extern_prelude.iter().map(|(ident, _)| ident.name).collect::<Vec<_>>();
|
2021-12-14 15:32:21 -06:00
|
|
|
extern_crate_names.sort_by(|a, b| b.as_str().partial_cmp(a.as_str()).unwrap());
|
2018-09-20 10:15:52 -05:00
|
|
|
|
2018-11-11 08:35:23 -06:00
|
|
|
for name in extern_crate_names.into_iter() {
|
|
|
|
// Replace first ident with a crate name and check if that is valid.
|
|
|
|
path[0].ident.name = name;
|
2022-04-08 15:50:56 -05:00
|
|
|
let result = self.r.maybe_resolve_path(&path, None, parent_scope);
|
2018-09-28 17:31:54 -05:00
|
|
|
debug!(
|
|
|
|
"make_external_crate_suggestion: name={:?} path={:?} result={:?}",
|
|
|
|
name, path, result
|
|
|
|
);
|
|
|
|
if let PathResult::Module(..) = result {
|
2022-07-28 04:17:55 -05:00
|
|
|
return Some((path, None));
|
2018-09-20 10:15:52 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
None
|
|
|
|
}
|
2019-04-07 12:56:41 -05:00
|
|
|
|
|
|
|
/// Suggests importing a macro from the root of the crate rather than a module within
|
|
|
|
/// the crate.
|
|
|
|
///
|
2020-05-01 15:28:15 -05:00
|
|
|
/// ```text
|
2019-04-07 12:56:41 -05:00
|
|
|
/// help: a macro with this name exists at the root of the crate
|
|
|
|
/// |
|
|
|
|
/// LL | use issue_59764::makro;
|
|
|
|
/// | ^^^^^^^^^^^^^^^^^^
|
|
|
|
/// |
|
|
|
|
/// = note: this could be because a macro annotated with `#[macro_export]` will be exported
|
|
|
|
/// at the root of the crate instead of the module where it is defined
|
|
|
|
/// ```
|
|
|
|
pub(crate) fn check_for_module_export_macro(
|
2019-07-29 13:19:50 -05:00
|
|
|
&mut self,
|
2020-03-07 10:02:32 -06:00
|
|
|
import: &'b Import<'b>,
|
2019-04-07 12:56:41 -05:00
|
|
|
module: ModuleOrUniformRoot<'b>,
|
|
|
|
ident: Ident,
|
2022-07-28 04:17:55 -05:00
|
|
|
) -> Option<(Option<Suggestion>, Option<String>)> {
|
2021-10-15 20:45:14 -05:00
|
|
|
let ModuleOrUniformRoot::Module(mut crate_module) = module else {
|
2019-04-07 12:56:41 -05:00
|
|
|
return None;
|
|
|
|
};
|
|
|
|
|
|
|
|
while let Some(parent) = crate_module.parent {
|
|
|
|
crate_module = parent;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ModuleOrUniformRoot::same_def(ModuleOrUniformRoot::Module(crate_module), module) {
|
|
|
|
// Don't make a suggestion if the import was already from the root of the
|
|
|
|
// crate.
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
2019-07-29 13:19:50 -05:00
|
|
|
let resolutions = self.r.resolutions(crate_module).borrow();
|
2019-09-09 15:04:26 -05:00
|
|
|
let resolution = resolutions.get(&self.r.new_key(ident, MacroNS))?;
|
2019-04-07 12:56:41 -05:00
|
|
|
let binding = resolution.borrow().binding()?;
|
2019-04-20 11:36:05 -05:00
|
|
|
if let Res::Def(DefKind::Macro(MacroKind::Bang), _) = binding.res() {
|
2019-04-07 13:34:21 -05:00
|
|
|
let module_name = crate_module.kind.name().unwrap();
|
2020-03-07 10:02:32 -06:00
|
|
|
let import_snippet = match import.kind {
|
2020-03-07 09:49:13 -06:00
|
|
|
ImportKind::Single { source, target, .. } if source != target => {
|
2019-04-07 13:34:21 -05:00
|
|
|
format!("{} as {}", source, target)
|
2019-12-22 16:42:04 -06:00
|
|
|
}
|
2019-04-07 13:34:21 -05:00
|
|
|
_ => format!("{}", ident),
|
|
|
|
};
|
2019-04-07 16:18:13 -05:00
|
|
|
|
2019-04-11 18:53:32 -05:00
|
|
|
let mut corrections: Vec<(Span, String)> = Vec::new();
|
2020-03-07 10:02:32 -06:00
|
|
|
if !import.is_nested() {
|
2019-04-11 18:53:32 -05:00
|
|
|
// Assume this is the easy case of `use issue_59764::foo::makro;` and just remove
|
|
|
|
// intermediate segments.
|
2020-03-07 10:02:32 -06:00
|
|
|
corrections.push((import.span, format!("{}::{}", module_name, import_snippet)));
|
2019-04-11 18:53:32 -05:00
|
|
|
} else {
|
2019-04-07 16:18:13 -05:00
|
|
|
// Find the binding span (and any trailing commas and spaces).
|
|
|
|
// ie. `use a::b::{c, d, e};`
|
|
|
|
// ^^^
|
|
|
|
let (found_closing_brace, binding_span) = find_span_of_binding_until_next_binding(
|
2019-08-08 06:06:42 -05:00
|
|
|
self.r.session,
|
2020-03-07 10:02:32 -06:00
|
|
|
import.span,
|
|
|
|
import.use_span,
|
2019-12-22 16:42:04 -06:00
|
|
|
);
|
|
|
|
debug!(
|
2019-04-07 16:18:13 -05:00
|
|
|
"check_for_module_export_macro: found_closing_brace={:?} binding_span={:?}",
|
|
|
|
found_closing_brace, binding_span
|
|
|
|
);
|
|
|
|
|
|
|
|
let mut removal_span = binding_span;
|
|
|
|
if found_closing_brace {
|
|
|
|
// If the binding span ended with a closing brace, as in the below example:
|
|
|
|
// ie. `use a::b::{c, d};`
|
|
|
|
// ^
|
|
|
|
// Then expand the span of characters to remove to include the previous
|
|
|
|
// binding's trailing comma.
|
|
|
|
// ie. `use a::b::{c, d};`
|
|
|
|
// ^^^
|
|
|
|
if let Some(previous_span) =
|
|
|
|
extend_span_to_previous_binding(self.r.session, binding_span)
|
|
|
|
{
|
|
|
|
debug!("check_for_module_export_macro: previous_span={:?}", previous_span);
|
|
|
|
removal_span = removal_span.with_lo(previous_span.lo());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
debug!("check_for_module_export_macro: removal_span={:?}", removal_span);
|
|
|
|
|
2019-04-11 18:53:32 -05:00
|
|
|
// Remove the `removal_span`.
|
|
|
|
corrections.push((removal_span, "".to_string()));
|
|
|
|
|
2022-03-30 14:14:15 -05:00
|
|
|
// Find the span after the crate name and if it has nested imports immediately
|
2019-04-07 16:18:13 -05:00
|
|
|
// after the crate name already.
|
|
|
|
// ie. `use a::b::{c, d};`
|
|
|
|
// ^^^^^^^^^
|
|
|
|
// or `use a::{b, c, d}};`
|
|
|
|
// ^^^^^^^^^^^
|
|
|
|
let (has_nested, after_crate_name) = find_span_immediately_after_crate_name(
|
2019-08-08 06:06:42 -05:00
|
|
|
self.r.session,
|
|
|
|
module_name,
|
2020-03-07 10:02:32 -06:00
|
|
|
import.use_span,
|
2019-12-22 16:42:04 -06:00
|
|
|
);
|
|
|
|
debug!(
|
2019-04-07 16:18:13 -05:00
|
|
|
"check_for_module_export_macro: has_nested={:?} after_crate_name={:?}",
|
|
|
|
has_nested, after_crate_name
|
|
|
|
);
|
|
|
|
|
2019-08-08 06:06:42 -05:00
|
|
|
let source_map = self.r.session.source_map();
|
2019-04-07 16:18:13 -05:00
|
|
|
|
2019-04-11 18:53:32 -05:00
|
|
|
// Add the import to the start, with a `{` if required.
|
|
|
|
let start_point = source_map.start_point(after_crate_name);
|
|
|
|
if let Ok(start_snippet) = source_map.span_to_snippet(start_point) {
|
|
|
|
corrections.push((
|
|
|
|
start_point,
|
|
|
|
if has_nested {
|
|
|
|
// In this case, `start_snippet` must equal '{'.
|
2020-03-07 10:02:32 -06:00
|
|
|
format!("{}{}, ", start_snippet, import_snippet)
|
2019-04-11 18:53:32 -05:00
|
|
|
} else {
|
|
|
|
// In this case, add a `{`, then the moved import, then whatever
|
|
|
|
// was there before.
|
2020-03-07 10:02:32 -06:00
|
|
|
format!("{{{}, {}", import_snippet, start_snippet)
|
2019-04-11 18:53:32 -05:00
|
|
|
},
|
|
|
|
));
|
2019-04-07 16:18:13 -05:00
|
|
|
}
|
|
|
|
|
2019-04-11 18:53:32 -05:00
|
|
|
// Add a `};` to the end if nested, matching the `{` added at the start.
|
|
|
|
if !has_nested {
|
|
|
|
corrections.push((source_map.end_point(after_crate_name), "};".to_string()));
|
2019-04-07 16:18:13 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-07 12:56:41 -05:00
|
|
|
let suggestion = Some((
|
2019-04-11 18:53:32 -05:00
|
|
|
corrections,
|
2019-04-07 12:56:41 -05:00
|
|
|
String::from("a macro with this name exists at the root of the crate"),
|
|
|
|
Applicability::MaybeIncorrect,
|
|
|
|
));
|
2022-07-28 04:17:55 -05:00
|
|
|
Some((suggestion, Some("this could be because a macro annotated with `#[macro_export]` will be exported \
|
|
|
|
at the root of the crate instead of the module where it is defined"
|
|
|
|
.to_string())))
|
2019-04-07 12:56:41 -05:00
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
2018-09-20 10:15:52 -05:00
|
|
|
}
|
2019-04-07 16:18:13 -05:00
|
|
|
|
|
|
|
/// Given a `binding_span` of a binding within a use statement:
|
|
|
|
///
|
2022-04-15 17:04:34 -05:00
|
|
|
/// ```ignore (illustrative)
|
2019-04-07 16:18:13 -05:00
|
|
|
/// use foo::{a, b, c};
|
2022-04-15 17:04:34 -05:00
|
|
|
/// // ^
|
2019-04-07 16:18:13 -05:00
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// then return the span until the next binding or the end of the statement:
|
|
|
|
///
|
2022-04-15 17:04:34 -05:00
|
|
|
/// ```ignore (illustrative)
|
2019-04-07 16:18:13 -05:00
|
|
|
/// use foo::{a, b, c};
|
2022-04-15 17:04:34 -05:00
|
|
|
/// // ^^^
|
2019-04-07 16:18:13 -05:00
|
|
|
/// ```
|
2022-04-08 15:52:18 -05:00
|
|
|
fn find_span_of_binding_until_next_binding(
|
2019-04-07 16:18:13 -05:00
|
|
|
sess: &Session,
|
|
|
|
binding_span: Span,
|
|
|
|
use_span: Span,
|
|
|
|
) -> (bool, Span) {
|
|
|
|
let source_map = sess.source_map();
|
|
|
|
|
|
|
|
// Find the span of everything after the binding.
|
|
|
|
// ie. `a, e};` or `a};`
|
|
|
|
let binding_until_end = binding_span.with_hi(use_span.hi());
|
|
|
|
|
|
|
|
// Find everything after the binding but not including the binding.
|
|
|
|
// ie. `, e};` or `};`
|
|
|
|
let after_binding_until_end = binding_until_end.with_lo(binding_span.hi());
|
|
|
|
|
|
|
|
// Keep characters in the span until we encounter something that isn't a comma or
|
|
|
|
// whitespace.
|
|
|
|
// ie. `, ` or ``.
|
|
|
|
//
|
|
|
|
// Also note whether a closing brace character was encountered. If there
|
|
|
|
// was, then later go backwards to remove any trailing commas that are left.
|
|
|
|
let mut found_closing_brace = false;
|
|
|
|
let after_binding_until_next_binding =
|
|
|
|
source_map.span_take_while(after_binding_until_end, |&ch| {
|
|
|
|
if ch == '}' {
|
|
|
|
found_closing_brace = true;
|
|
|
|
}
|
|
|
|
ch == ' ' || ch == ','
|
|
|
|
});
|
|
|
|
|
|
|
|
// Combine the two spans.
|
|
|
|
// ie. `a, ` or `a`.
|
|
|
|
//
|
|
|
|
// Removing these would leave `issue_52891::{d, e};` or `issue_52891::{d, e, };`
|
|
|
|
let span = binding_span.with_hi(after_binding_until_next_binding.hi());
|
|
|
|
|
|
|
|
(found_closing_brace, span)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Given a `binding_span`, return the span through to the comma or opening brace of the previous
|
|
|
|
/// binding.
|
|
|
|
///
|
2022-04-15 17:04:34 -05:00
|
|
|
/// ```ignore (illustrative)
|
2019-04-07 16:18:13 -05:00
|
|
|
/// use foo::a::{a, b, c};
|
2022-04-15 17:04:34 -05:00
|
|
|
/// // ^^--- binding span
|
|
|
|
/// // |
|
|
|
|
/// // returned span
|
2019-04-07 16:18:13 -05:00
|
|
|
///
|
|
|
|
/// use foo::{a, b, c};
|
2022-04-15 17:04:34 -05:00
|
|
|
/// // --- binding span
|
2019-04-07 16:18:13 -05:00
|
|
|
/// ```
|
2022-04-08 15:52:18 -05:00
|
|
|
fn extend_span_to_previous_binding(sess: &Session, binding_span: Span) -> Option<Span> {
|
2019-04-07 16:18:13 -05:00
|
|
|
let source_map = sess.source_map();
|
|
|
|
|
|
|
|
// `prev_source` will contain all of the source that came before the span.
|
|
|
|
// Then split based on a command and take the first (ie. closest to our span)
|
|
|
|
// snippet. In the example, this is a space.
|
|
|
|
let prev_source = source_map.span_to_prev_source(binding_span).ok()?;
|
|
|
|
|
|
|
|
let prev_comma = prev_source.rsplit(',').collect::<Vec<_>>();
|
|
|
|
let prev_starting_brace = prev_source.rsplit('{').collect::<Vec<_>>();
|
|
|
|
if prev_comma.len() <= 1 || prev_starting_brace.len() <= 1 {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
let prev_comma = prev_comma.first().unwrap();
|
|
|
|
let prev_starting_brace = prev_starting_brace.first().unwrap();
|
|
|
|
|
|
|
|
// If the amount of source code before the comma is greater than
|
|
|
|
// the amount of source code before the starting brace then we've only
|
|
|
|
// got one item in the nested item (eg. `issue_52891::{self}`).
|
|
|
|
if prev_comma.len() > prev_starting_brace.len() {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
Some(binding_span.with_lo(BytePos(
|
|
|
|
// Take away the number of bytes for the characters we've found and an
|
|
|
|
// extra for the comma.
|
|
|
|
binding_span.lo().0 - (prev_comma.as_bytes().len() as u32) - 1,
|
|
|
|
)))
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Given a `use_span` of a binding within a use statement, returns the highlighted span and if
|
|
|
|
/// it is a nested use tree.
|
|
|
|
///
|
2022-04-15 17:04:34 -05:00
|
|
|
/// ```ignore (illustrative)
|
2019-04-07 16:18:13 -05:00
|
|
|
/// use foo::a::{b, c};
|
2022-04-15 17:04:34 -05:00
|
|
|
/// // ^^^^^^^^^^ -- false
|
2019-04-07 16:18:13 -05:00
|
|
|
///
|
|
|
|
/// use foo::{a, b, c};
|
2022-04-15 17:04:34 -05:00
|
|
|
/// // ^^^^^^^^^^ -- true
|
2019-04-07 16:18:13 -05:00
|
|
|
///
|
|
|
|
/// use foo::{a, b::{c, d}};
|
2022-04-15 17:04:34 -05:00
|
|
|
/// // ^^^^^^^^^^^^^^^ -- true
|
2019-04-07 16:18:13 -05:00
|
|
|
/// ```
|
|
|
|
fn find_span_immediately_after_crate_name(
|
|
|
|
sess: &Session,
|
|
|
|
module_name: Symbol,
|
|
|
|
use_span: Span,
|
|
|
|
) -> (bool, Span) {
|
|
|
|
debug!(
|
|
|
|
"find_span_immediately_after_crate_name: module_name={:?} use_span={:?}",
|
|
|
|
module_name, use_span
|
|
|
|
);
|
|
|
|
let source_map = sess.source_map();
|
|
|
|
|
2019-04-11 18:19:02 -05:00
|
|
|
// Using `use issue_59764::foo::{baz, makro};` as an example throughout..
|
|
|
|
let mut num_colons = 0;
|
|
|
|
// Find second colon.. `use issue_59764:`
|
|
|
|
let until_second_colon = source_map.span_take_while(use_span, |c| {
|
|
|
|
if *c == ':' {
|
|
|
|
num_colons += 1;
|
|
|
|
}
|
2020-10-26 20:02:48 -05:00
|
|
|
!matches!(c, ':' if num_colons == 2)
|
2019-04-11 18:19:02 -05:00
|
|
|
});
|
|
|
|
// Find everything after the second colon.. `foo::{baz, makro};`
|
|
|
|
let from_second_colon = use_span.with_lo(until_second_colon.hi() + BytePos(1));
|
|
|
|
|
|
|
|
let mut found_a_non_whitespace_character = false;
|
|
|
|
// Find the first non-whitespace character in `from_second_colon`.. `f`
|
|
|
|
let after_second_colon = source_map.span_take_while(from_second_colon, |c| {
|
|
|
|
if found_a_non_whitespace_character {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if !c.is_whitespace() {
|
|
|
|
found_a_non_whitespace_character = true;
|
|
|
|
}
|
|
|
|
true
|
|
|
|
});
|
|
|
|
|
|
|
|
// Find the first `{` in from_second_colon.. `foo::{`
|
|
|
|
let next_left_bracket = source_map.span_through_char(from_second_colon, '{');
|
|
|
|
|
|
|
|
(next_left_bracket == after_second_colon, from_second_colon)
|
2019-04-07 16:18:13 -05:00
|
|
|
}
|
2019-07-11 13:13:11 -05:00
|
|
|
|
2022-04-29 21:40:36 -05:00
|
|
|
/// 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,
|
|
|
|
}
|
|
|
|
|
2019-07-11 13:13:11 -05:00
|
|
|
/// 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
|
2022-04-08 15:52:18 -05:00
|
|
|
fn show_candidates(
|
2021-09-17 17:07:41 -05:00
|
|
|
session: &Session,
|
2022-05-30 11:49:17 -05:00
|
|
|
source_span: &IndexVec<LocalDefId, Span>,
|
2022-01-23 14:41:46 -06:00
|
|
|
err: &mut Diagnostic,
|
2019-07-11 13:13:11 -05:00
|
|
|
// This is `None` if all placement locations are inside expansions
|
2020-05-04 17:12:06 -05:00
|
|
|
use_placement_span: Option<Span>,
|
2019-07-11 13:13:11 -05:00
|
|
|
candidates: &[ImportSuggestion],
|
2022-04-29 21:40:36 -05:00
|
|
|
instead: Instead,
|
|
|
|
found_use: FoundUse,
|
|
|
|
is_pattern: IsPattern,
|
2022-04-23 18:41:36 -05:00
|
|
|
path: Vec<Segment>,
|
2019-07-11 13:13:11 -05:00
|
|
|
) {
|
2020-01-22 01:01:21 -06:00
|
|
|
if candidates.is_empty() {
|
|
|
|
return;
|
|
|
|
}
|
2020-05-04 17:12:06 -05:00
|
|
|
|
2021-10-25 19:04:35 -05:00
|
|
|
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();
|
2021-09-10 18:15:40 -05:00
|
|
|
|
|
|
|
candidates.iter().for_each(|c| {
|
|
|
|
(if c.accessible { &mut accessible_path_strings } else { &mut inaccessible_path_strings })
|
2021-10-25 19:04:35 -05:00
|
|
|
.push((path_names_to_string(&c.path), c.descr, c.did, &c.note))
|
2021-09-10 18:15:40 -05:00
|
|
|
});
|
|
|
|
|
2019-07-11 13:13:11 -05:00
|
|
|
// we want consistent results across executions, but candidates are produced
|
|
|
|
// by iterating through a hash map, so make sure they are ordered:
|
2021-09-10 18:15:40 -05:00
|
|
|
for path_strings in [&mut accessible_path_strings, &mut inaccessible_path_strings] {
|
2021-09-17 17:07:41 -05:00
|
|
|
path_strings.sort_by(|a, b| a.0.cmp(&b.0));
|
2021-09-10 18:15:40 -05:00
|
|
|
let core_path_strings =
|
2021-09-17 17:07:41 -05:00
|
|
|
path_strings.drain_filter(|p| p.0.starts_with("core::")).collect::<Vec<_>>();
|
2021-09-10 18:15:40 -05:00
|
|
|
path_strings.extend(core_path_strings);
|
2021-09-17 17:07:41 -05:00
|
|
|
path_strings.dedup_by(|a, b| a.0 == b.0);
|
2021-09-10 18:15:40 -05:00
|
|
|
}
|
2020-06-02 13:16:23 -05:00
|
|
|
|
2021-09-10 18:15:40 -05:00
|
|
|
if !accessible_path_strings.is_empty() {
|
2021-11-17 13:37:46 -06:00
|
|
|
let (determiner, kind, name) = if accessible_path_strings.len() == 1 {
|
|
|
|
("this", accessible_path_strings[0].1, format!(" `{}`", accessible_path_strings[0].0))
|
2021-09-10 18:15:40 -05:00
|
|
|
} else {
|
2021-11-17 13:37:46 -06:00
|
|
|
("one of these", "items", String::new())
|
2021-09-10 18:15:40 -05:00
|
|
|
};
|
2020-06-02 13:16:23 -05:00
|
|
|
|
2022-04-29 21:40:36 -05:00
|
|
|
let instead = if let Instead::Yes = instead { " instead" } else { "" };
|
|
|
|
let mut msg = if let IsPattern::Yes = is_pattern {
|
2021-11-17 13:37:46 -06:00
|
|
|
format!(
|
2022-05-02 21:07:47 -05:00
|
|
|
"if you meant to match on {}{}{}, use the full path in the pattern",
|
|
|
|
kind, instead, name
|
2021-11-17 13:37:46 -06:00
|
|
|
)
|
|
|
|
} else {
|
2022-05-02 21:07:47 -05:00
|
|
|
format!("consider importing {} {}{}", determiner, kind, instead)
|
2021-11-17 13:37:46 -06:00
|
|
|
};
|
2019-07-11 13:13:11 -05:00
|
|
|
|
2022-02-03 16:12:25 -06:00
|
|
|
for note in accessible_path_strings.iter().flat_map(|cand| cand.3.as_ref()) {
|
2021-10-25 19:04:35 -05:00
|
|
|
err.note(note);
|
|
|
|
}
|
|
|
|
|
2022-04-29 21:40:36 -05:00
|
|
|
if let (IsPattern::Yes, Some(span)) = (is_pattern, use_placement_span) {
|
2021-11-17 13:37:46 -06:00
|
|
|
err.span_suggestions(
|
|
|
|
span,
|
|
|
|
&msg,
|
|
|
|
accessible_path_strings.into_iter().map(|a| a.0),
|
|
|
|
Applicability::MaybeIncorrect,
|
|
|
|
);
|
|
|
|
} else if let Some(span) = use_placement_span {
|
2021-09-10 18:15:40 -05:00
|
|
|
for candidate in &mut accessible_path_strings {
|
|
|
|
// produce an additional newline to separate the new use statement
|
|
|
|
// from the directly following item.
|
2022-04-29 21:40:36 -05:00
|
|
|
let additional_newline = if let FoundUse::Yes = found_use { "" } else { "\n" };
|
2021-09-10 18:15:40 -05:00
|
|
|
candidate.0 = format!("use {};\n{}", &candidate.0, additional_newline);
|
|
|
|
}
|
2019-07-11 13:13:11 -05:00
|
|
|
|
2021-09-10 18:15:40 -05:00
|
|
|
err.span_suggestions(
|
|
|
|
span,
|
|
|
|
&msg,
|
|
|
|
accessible_path_strings.into_iter().map(|a| a.0),
|
2021-11-17 13:37:46 -06:00
|
|
|
Applicability::MaybeIncorrect,
|
2021-09-10 18:15:40 -05:00
|
|
|
);
|
2022-04-23 18:41:36 -05:00
|
|
|
if let [first, .., last] = &path[..] {
|
|
|
|
err.span_suggestion_verbose(
|
|
|
|
first.ident.span.until(last.ident.span),
|
2022-05-02 21:07:47 -05:00
|
|
|
&format!("if you import `{}`, refer to it directly", last.ident),
|
2022-06-13 01:48:40 -05:00
|
|
|
"",
|
2022-04-23 18:41:36 -05:00
|
|
|
Applicability::Unspecified,
|
|
|
|
);
|
|
|
|
}
|
2021-09-10 18:15:40 -05:00
|
|
|
} else {
|
|
|
|
msg.push(':');
|
2020-06-02 13:16:23 -05:00
|
|
|
|
2021-09-10 18:15:40 -05:00
|
|
|
for candidate in accessible_path_strings {
|
|
|
|
msg.push('\n');
|
|
|
|
msg.push_str(&candidate.0);
|
|
|
|
}
|
|
|
|
|
|
|
|
err.note(&msg);
|
2019-07-11 13:13:11 -05:00
|
|
|
}
|
|
|
|
} else {
|
2021-09-10 18:15:40 -05:00
|
|
|
assert!(!inaccessible_path_strings.is_empty());
|
|
|
|
|
2022-04-29 21:40:36 -05:00
|
|
|
let prefix =
|
|
|
|
if let IsPattern::Yes = is_pattern { "you might have meant to match on " } else { "" };
|
2021-09-17 17:07:41 -05:00
|
|
|
if inaccessible_path_strings.len() == 1 {
|
2021-10-25 19:04:35 -05:00
|
|
|
let (name, descr, def_id, note) = &inaccessible_path_strings[0];
|
2021-11-17 13:37:46 -06:00
|
|
|
let msg = format!(
|
|
|
|
"{}{} `{}`{} exists but is inaccessible",
|
|
|
|
prefix,
|
|
|
|
descr,
|
|
|
|
name,
|
2022-04-29 21:40:36 -05:00
|
|
|
if let IsPattern::Yes = is_pattern { ", which" } else { "" }
|
2021-11-17 13:37:46 -06:00
|
|
|
);
|
2021-09-17 17:07:41 -05:00
|
|
|
|
|
|
|
if let Some(local_def_id) = def_id.and_then(|did| did.as_local()) {
|
2022-05-30 11:49:17 -05:00
|
|
|
let span = source_span[local_def_id];
|
2021-09-17 17:07:41 -05:00
|
|
|
let span = session.source_map().guess_head_span(span);
|
|
|
|
let mut multi_span = MultiSpan::from_span(span);
|
2022-06-29 07:16:43 -05:00
|
|
|
multi_span.push_span_label(span, "not accessible");
|
2021-09-17 17:07:41 -05:00
|
|
|
err.span_note(multi_span, &msg);
|
|
|
|
} else {
|
|
|
|
err.note(&msg);
|
|
|
|
}
|
2021-10-25 19:04:35 -05:00
|
|
|
if let Some(note) = (*note).as_deref() {
|
|
|
|
err.note(note);
|
|
|
|
}
|
2021-09-10 18:15:40 -05:00
|
|
|
} else {
|
2021-10-25 19:04:35 -05:00
|
|
|
let (_, descr_first, _, _) = &inaccessible_path_strings[0];
|
2021-09-17 17:07:41 -05:00
|
|
|
let descr = if inaccessible_path_strings
|
|
|
|
.iter()
|
|
|
|
.skip(1)
|
2021-10-25 19:04:35 -05:00
|
|
|
.all(|(_, descr, _, _)| descr == descr_first)
|
2021-09-17 17:07:41 -05:00
|
|
|
{
|
2022-07-20 04:19:57 -05:00
|
|
|
descr_first
|
2021-09-17 17:07:41 -05:00
|
|
|
} else {
|
2022-07-20 04:19:57 -05:00
|
|
|
"item"
|
2021-09-17 17:07:41 -05:00
|
|
|
};
|
2022-07-05 20:01:53 -05:00
|
|
|
let plural_descr =
|
2022-07-20 04:48:11 -05:00
|
|
|
if descr.ends_with('s') { format!("{}es", descr) } else { format!("{}s", descr) };
|
2021-09-10 18:15:40 -05:00
|
|
|
|
2022-07-05 20:01:53 -05:00
|
|
|
let mut msg = format!("{}these {} exist but are inaccessible", prefix, plural_descr);
|
2021-09-17 17:07:41 -05:00
|
|
|
let mut has_colon = false;
|
2020-06-02 13:16:23 -05:00
|
|
|
|
2021-09-17 17:07:41 -05:00
|
|
|
let mut spans = Vec::new();
|
2021-10-25 19:04:35 -05:00
|
|
|
for (name, _, def_id, _) in &inaccessible_path_strings {
|
2021-09-17 17:07:41 -05:00
|
|
|
if let Some(local_def_id) = def_id.and_then(|did| did.as_local()) {
|
2022-05-30 11:49:17 -05:00
|
|
|
let span = source_span[local_def_id];
|
2021-09-17 17:07:41 -05:00
|
|
|
let span = session.source_map().guess_head_span(span);
|
|
|
|
spans.push((name, span));
|
|
|
|
} else {
|
|
|
|
if !has_colon {
|
|
|
|
msg.push(':');
|
|
|
|
has_colon = true;
|
|
|
|
}
|
|
|
|
msg.push('\n');
|
|
|
|
msg.push_str(name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut multi_span = MultiSpan::from_spans(spans.iter().map(|(_, sp)| *sp).collect());
|
|
|
|
for (name, span) in spans {
|
|
|
|
multi_span.push_span_label(span, format!("`{}`: not accessible", name));
|
|
|
|
}
|
2020-06-02 13:16:23 -05:00
|
|
|
|
2022-02-03 16:12:25 -06:00
|
|
|
for note in inaccessible_path_strings.iter().flat_map(|cand| cand.3.as_ref()) {
|
2021-10-25 19:04:35 -05:00
|
|
|
err.note(note);
|
|
|
|
}
|
|
|
|
|
2021-09-17 17:07:41 -05:00
|
|
|
err.span_note(multi_span, &msg);
|
|
|
|
}
|
2019-07-11 13:13:11 -05:00
|
|
|
}
|
|
|
|
}
|
2022-04-08 15:52:18 -05:00
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
struct UsePlacementFinder {
|
|
|
|
target_module: NodeId,
|
|
|
|
first_legal_span: Option<Span>,
|
|
|
|
first_use_span: Option<Span>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl UsePlacementFinder {
|
2022-04-29 21:40:36 -05:00
|
|
|
fn check(krate: &Crate, target_module: NodeId) -> (Option<Span>, FoundUse) {
|
2022-04-08 15:52:18 -05:00
|
|
|
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 {
|
2022-04-29 21:40:36 -05:00
|
|
|
(Some(use_span), FoundUse::Yes)
|
2022-04-08 15:52:18 -05:00
|
|
|
} else {
|
2022-04-29 21:40:36 -05:00
|
|
|
(finder.first_legal_span, FoundUse::No)
|
2022-04-08 15:52:18 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'tcx> visit::Visitor<'tcx> for UsePlacementFinder {
|
|
|
|
fn visit_crate(&mut self, c: &Crate) {
|
|
|
|
if self.target_module == CRATE_NODE_ID {
|
|
|
|
let inject = c.spans.inject_use_span;
|
|
|
|
if is_span_suitable_for_use_injection(inject) {
|
|
|
|
self.first_legal_span = Some(inject);
|
|
|
|
}
|
|
|
|
self.first_use_span = search_for_any_use_in_items(&c.items);
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
visit::walk_crate(self, c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn visit_item(&mut self, item: &'tcx ast::Item) {
|
|
|
|
if self.target_module == item.id {
|
|
|
|
if let ItemKind::Mod(_, ModKind::Loaded(items, _inline, mod_spans)) = &item.kind {
|
|
|
|
let inject = mod_spans.inject_use_span;
|
|
|
|
if is_span_suitable_for_use_injection(inject) {
|
|
|
|
self.first_legal_span = Some(inject);
|
|
|
|
}
|
|
|
|
self.first_use_span = search_for_any_use_in_items(items);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
visit::walk_item(self, item);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn search_for_any_use_in_items(items: &[P<ast::Item>]) -> Option<Span> {
|
|
|
|
for item in items {
|
|
|
|
if let ItemKind::Use(..) = item.kind {
|
|
|
|
if is_span_suitable_for_use_injection(item.span) {
|
|
|
|
return Some(item.span.shrink_to_lo());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn is_span_suitable_for_use_injection(s: Span) -> bool {
|
|
|
|
// don't suggest placing a use before the prelude
|
|
|
|
// import or other generated ones
|
|
|
|
!s.from_expansion()
|
|
|
|
}
|
2022-04-16 21:01:17 -05:00
|
|
|
|
|
|
|
/// Convert the given number into the corresponding ordinal
|
2022-05-20 18:51:09 -05:00
|
|
|
pub(crate) fn ordinalize(v: usize) -> String {
|
2022-04-16 21:01:17 -05:00
|
|
|
let suffix = match ((11..=13).contains(&(v % 100)), v % 10) {
|
|
|
|
(false, 1) => "st",
|
|
|
|
(false, 2) => "nd",
|
|
|
|
(false, 3) => "rd",
|
|
|
|
_ => "th",
|
|
|
|
};
|
|
|
|
format!("{v}{suffix}")
|
|
|
|
}
|