2020-06-25 14:16:38 +00:00
|
|
|
use crate::diagnostics::{ImportSuggestion, LabelSuggestion, TypoSuggestion};
|
2020-02-24 18:44:55 +00:00
|
|
|
use crate::late::lifetimes::{ElisionFailureInfo, LifetimeContext};
|
2020-09-23 00:35:03 +00:00
|
|
|
use crate::late::{AliasPossibility, LateResolutionVisitor, RibKind};
|
2019-12-22 22:42:04 +00:00
|
|
|
use crate::path_names_to_string;
|
|
|
|
use crate::{CrateLint, Module, ModuleKind, ModuleOrUniformRoot};
|
|
|
|
use crate::{PathResult, PathSource, Segment};
|
2019-08-07 23:39:02 +00:00
|
|
|
|
2020-08-14 00:04:33 +00:00
|
|
|
use rustc_ast::visit::FnKind;
|
2021-05-06 12:33:23 +00:00
|
|
|
use rustc_ast::{
|
2021-09-25 15:53:37 +00:00
|
|
|
self as ast, AssocItemKind, Expr, ExprKind, GenericParam, GenericParamKind, Item, ItemKind,
|
|
|
|
NodeId, Path, Ty, TyKind,
|
2021-05-06 12:33:23 +00:00
|
|
|
};
|
2020-09-24 06:01:17 +00:00
|
|
|
use rustc_ast_pretty::pprust::path_segment_to_string;
|
2019-12-24 04:02:53 +00:00
|
|
|
use rustc_data_structures::fx::FxHashSet;
|
2021-08-10 10:53:43 +00:00
|
|
|
use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder};
|
2020-02-24 18:44:55 +00:00
|
|
|
use rustc_hir as hir;
|
2020-01-05 01:37:57 +00:00
|
|
|
use rustc_hir::def::Namespace::{self, *};
|
2020-09-29 15:52:43 +00:00
|
|
|
use rustc_hir::def::{self, CtorKind, CtorOf, DefKind};
|
2020-08-10 04:14:59 +00:00
|
|
|
use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE};
|
2020-01-05 01:37:57 +00:00
|
|
|
use rustc_hir::PrimTy;
|
2020-09-15 13:38:41 +00:00
|
|
|
use rustc_session::parse::feature_err;
|
2021-03-07 22:19:50 +00:00
|
|
|
use rustc_span::edition::Edition;
|
2019-12-31 17:15:40 +00: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 19:24:10 +00:00
|
|
|
use rustc_span::lev_distance::find_best_match_for_name;
|
2020-08-10 23:42:57 +00:00
|
|
|
use rustc_span::symbol::{kw, sym, Ident, Symbol};
|
2020-09-23 02:19:33 +00:00
|
|
|
use rustc_span::{BytePos, MultiSpan, Span, DUMMY_SP};
|
2019-08-07 23:39:02 +00:00
|
|
|
|
2021-03-08 23:32:41 +00:00
|
|
|
use std::iter;
|
2022-01-11 21:56:10 +00:00
|
|
|
use std::ops::Deref;
|
2021-03-08 23:32:41 +00:00
|
|
|
|
2020-08-14 06:05:01 +00:00
|
|
|
use tracing::debug;
|
2019-11-11 21:46:56 +00:00
|
|
|
|
2019-08-07 23:39:02 +00:00
|
|
|
type Res = def::Res<ast::NodeId>;
|
|
|
|
|
|
|
|
/// A field or associated item from self type suggested in case of resolution failure.
|
|
|
|
enum AssocSuggestion {
|
|
|
|
Field,
|
|
|
|
MethodWithSelf,
|
2020-10-26 23:28:56 +00:00
|
|
|
AssocFn,
|
|
|
|
AssocType,
|
|
|
|
AssocConst,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl AssocSuggestion {
|
|
|
|
fn action(&self) -> &'static str {
|
|
|
|
match self {
|
|
|
|
AssocSuggestion::Field => "use the available field",
|
|
|
|
AssocSuggestion::MethodWithSelf => "call the method with the fully-qualified path",
|
|
|
|
AssocSuggestion::AssocFn => "call the associated function",
|
|
|
|
AssocSuggestion::AssocConst => "use the associated `const`",
|
|
|
|
AssocSuggestion::AssocType => "use the associated type",
|
|
|
|
}
|
|
|
|
}
|
2019-08-07 23:39:02 +00:00
|
|
|
}
|
|
|
|
|
2020-02-24 18:44:55 +00:00
|
|
|
crate enum MissingLifetimeSpot<'tcx> {
|
|
|
|
Generics(&'tcx hir::Generics<'tcx>),
|
|
|
|
HigherRanked { span: Span, span_type: ForLifetimeSpanType },
|
2020-08-11 20:02:14 +00:00
|
|
|
Static,
|
2020-02-24 18:44:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
crate enum ForLifetimeSpanType {
|
|
|
|
BoundEmpty,
|
|
|
|
BoundTail,
|
|
|
|
TypeEmpty,
|
|
|
|
TypeTail,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ForLifetimeSpanType {
|
|
|
|
crate fn descr(&self) -> &'static str {
|
|
|
|
match self {
|
|
|
|
Self::BoundEmpty | Self::BoundTail => "bound",
|
|
|
|
Self::TypeEmpty | Self::TypeTail => "type",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
crate fn suggestion(&self, sugg: &str) -> String {
|
|
|
|
match self {
|
|
|
|
Self::BoundEmpty | Self::TypeEmpty => format!("for<{}> ", sugg),
|
|
|
|
Self::BoundTail | Self::TypeTail => format!(", {}", sugg),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'tcx> Into<MissingLifetimeSpot<'tcx>> for &'tcx hir::Generics<'tcx> {
|
|
|
|
fn into(self) -> MissingLifetimeSpot<'tcx> {
|
|
|
|
MissingLifetimeSpot::Generics(self)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-07 23:39:02 +00:00
|
|
|
fn is_self_type(path: &[Segment], namespace: Namespace) -> bool {
|
|
|
|
namespace == TypeNS && path.len() == 1 && path[0].ident.name == kw::SelfUpper
|
|
|
|
}
|
|
|
|
|
|
|
|
fn is_self_value(path: &[Segment], namespace: Namespace) -> bool {
|
|
|
|
namespace == ValueNS && path.len() == 1 && path[0].ident.name == kw::SelfLower
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Gets the stringified path for an enum from an `ImportSuggestion` for an enum variant.
|
|
|
|
fn import_candidate_to_enum_paths(suggestion: &ImportSuggestion) -> (String, String) {
|
|
|
|
let variant_path = &suggestion.path;
|
|
|
|
let variant_path_string = path_names_to_string(variant_path);
|
|
|
|
|
|
|
|
let path_len = suggestion.path.segments.len();
|
|
|
|
let enum_path = ast::Path {
|
|
|
|
span: suggestion.path.span,
|
|
|
|
segments: suggestion.path.segments[0..path_len - 1].to_vec(),
|
2020-08-21 22:51:23 +00:00
|
|
|
tokens: None,
|
2019-08-07 23:39:02 +00:00
|
|
|
};
|
|
|
|
let enum_path_string = path_names_to_string(&enum_path);
|
|
|
|
|
|
|
|
(variant_path_string, enum_path_string)
|
|
|
|
}
|
|
|
|
|
2020-09-08 22:14:09 +00:00
|
|
|
impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
|
2020-08-10 04:14:59 +00:00
|
|
|
fn def_span(&self, def_id: DefId) -> Option<Span> {
|
|
|
|
match def_id.krate {
|
|
|
|
LOCAL_CRATE => self.r.opt_span(def_id),
|
|
|
|
_ => Some(
|
|
|
|
self.r
|
|
|
|
.session
|
|
|
|
.source_map()
|
|
|
|
.guess_head_span(self.r.cstore().get_span_untracked(def_id, self.r.session)),
|
|
|
|
),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-07 23:39:02 +00:00
|
|
|
/// Handles error reporting for `smart_resolve_path_fragment` function.
|
|
|
|
/// Creates base error and amends it with one short label and possibly some longer helps/notes.
|
|
|
|
pub(crate) fn smart_resolve_report_errors(
|
|
|
|
&mut self,
|
|
|
|
path: &[Segment],
|
|
|
|
span: Span,
|
|
|
|
source: PathSource<'_>,
|
|
|
|
res: Option<Res>,
|
|
|
|
) -> (DiagnosticBuilder<'a>, Vec<ImportSuggestion>) {
|
|
|
|
let ident_span = path.last().map_or(span, |ident| ident.ident.span);
|
|
|
|
let ns = source.namespace();
|
|
|
|
let is_expected = &|res| source.is_expected(res);
|
2020-07-10 07:36:02 +00:00
|
|
|
let is_enum_variant = &|res| matches!(res, Res::Def(DefKind::Variant, _));
|
2019-08-07 23:39:02 +00:00
|
|
|
|
|
|
|
// Make the base error.
|
2021-03-07 22:19:50 +00:00
|
|
|
let mut expected = source.descr_expected();
|
2019-08-07 23:39:02 +00:00
|
|
|
let path_str = Segment::names_to_string(path);
|
|
|
|
let item_str = path.last().unwrap().ident;
|
2019-10-19 17:13:56 +00:00
|
|
|
let (base_msg, fallback_label, base_span, could_be_expr) = if let Some(res) = res {
|
2019-12-22 22:42:04 +00:00
|
|
|
(
|
|
|
|
format!("expected {}, found {} `{}`", expected, res.descr(), path_str),
|
2019-08-07 23:39:02 +00:00
|
|
|
format!("not a {}", expected),
|
2019-10-18 17:33:25 +00:00
|
|
|
span,
|
|
|
|
match res {
|
2019-10-19 17:13:56 +00:00
|
|
|
Res::Def(DefKind::Fn, _) => {
|
|
|
|
// Verify whether this is a fn call or an Fn used as a type.
|
2019-12-22 22:42:04 +00:00
|
|
|
self.r
|
|
|
|
.session
|
|
|
|
.source_map()
|
|
|
|
.span_to_snippet(span)
|
|
|
|
.map(|snippet| snippet.ends_with(')'))
|
|
|
|
.unwrap_or(false)
|
2019-10-19 17:13:56 +00:00
|
|
|
}
|
2020-04-17 00:38:52 +00:00
|
|
|
Res::Def(
|
|
|
|
DefKind::Ctor(..) | DefKind::AssocFn | DefKind::Const | DefKind::AssocConst,
|
|
|
|
_,
|
|
|
|
)
|
2019-12-22 22:42:04 +00:00
|
|
|
| Res::SelfCtor(_)
|
|
|
|
| Res::PrimTy(_)
|
|
|
|
| Res::Local(_) => true,
|
2019-10-18 17:33:25 +00:00
|
|
|
_ => false,
|
2019-12-22 22:42:04 +00:00
|
|
|
},
|
|
|
|
)
|
2019-08-07 23:39:02 +00:00
|
|
|
} else {
|
|
|
|
let item_span = path.last().unwrap().ident.span;
|
|
|
|
let (mod_prefix, mod_str) = if path.len() == 1 {
|
|
|
|
(String::new(), "this scope".to_string())
|
|
|
|
} else if path.len() == 2 && path[0].ident.name == kw::PathRoot {
|
2021-03-07 22:19:50 +00:00
|
|
|
if self.r.session.edition() > Edition::Edition2015 {
|
|
|
|
// In edition 2018 onwards, the `::foo` syntax may only pull from the extern prelude
|
|
|
|
// which overrides all other expectations of item type
|
|
|
|
expected = "crate";
|
|
|
|
(String::new(), "the list of imported crates".to_string())
|
|
|
|
} else {
|
|
|
|
(String::new(), "the crate root".to_string())
|
|
|
|
}
|
|
|
|
} else if path.len() == 2 && path[0].ident.name == kw::Crate {
|
2019-08-07 23:39:02 +00:00
|
|
|
(String::new(), "the crate root".to_string())
|
|
|
|
} else {
|
|
|
|
let mod_path = &path[..path.len() - 1];
|
2019-12-22 22:42:04 +00:00
|
|
|
let mod_prefix =
|
|
|
|
match self.resolve_path(mod_path, Some(TypeNS), false, span, CrateLint::No) {
|
|
|
|
PathResult::Module(ModuleOrUniformRoot::Module(module)) => module.res(),
|
|
|
|
_ => None,
|
|
|
|
}
|
2021-03-26 19:32:37 +00:00
|
|
|
.map_or_else(String::new, |res| format!("{} ", res.descr()));
|
2019-08-07 23:39:02 +00:00
|
|
|
(mod_prefix, format!("`{}`", Segment::names_to_string(mod_path)))
|
|
|
|
};
|
2019-12-22 22:42:04 +00:00
|
|
|
(
|
|
|
|
format!("cannot find {} `{}` in {}{}", expected, item_str, mod_prefix, mod_str),
|
2020-05-02 01:24:14 +00:00
|
|
|
if path_str == "async" && expected.starts_with("struct") {
|
2020-11-30 21:11:29 +00:00
|
|
|
"`async` blocks are only allowed in Rust 2018 or later".to_string()
|
2020-05-02 01:24:14 +00:00
|
|
|
} else {
|
|
|
|
format!("not found in {}", mod_str)
|
|
|
|
},
|
2019-10-18 17:33:25 +00:00
|
|
|
item_span,
|
2019-12-22 22:42:04 +00:00
|
|
|
false,
|
|
|
|
)
|
2019-08-07 23:39:02 +00:00
|
|
|
};
|
|
|
|
|
2019-12-31 20:25:16 +00:00
|
|
|
let code = source.error_code(res.is_some());
|
2019-08-08 11:06:42 +00:00
|
|
|
let mut err = self.r.session.struct_span_err_with_code(base_span, &base_msg, code);
|
2019-08-07 23:39:02 +00:00
|
|
|
|
2021-09-02 18:34:03 +00:00
|
|
|
if let Some(span) = self.diagnostic_metadata.current_block_could_be_bare_struct_literal {
|
|
|
|
err.multipart_suggestion(
|
|
|
|
"you might have meant to write a `struct` literal",
|
|
|
|
vec![
|
|
|
|
(span.shrink_to_lo(), "{ SomeStruct ".to_string()),
|
|
|
|
(span.shrink_to_hi(), "}".to_string()),
|
|
|
|
],
|
|
|
|
Applicability::HasPlaceholders,
|
|
|
|
);
|
|
|
|
}
|
2020-08-26 03:28:25 +00:00
|
|
|
match (source, self.diagnostic_metadata.in_if_condition) {
|
|
|
|
(PathSource::Expr(_), Some(Expr { span, kind: ExprKind::Assign(..), .. })) => {
|
|
|
|
err.span_suggestion_verbose(
|
|
|
|
span.shrink_to_lo(),
|
|
|
|
"you might have meant to use pattern matching",
|
|
|
|
"let ".to_string(),
|
|
|
|
Applicability::MaybeIncorrect,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
|
2020-08-14 00:52:33 +00:00
|
|
|
let is_assoc_fn = self.self_type_is_available(span);
|
2019-08-07 23:39:02 +00:00
|
|
|
// Emit help message for fake-self from other languages (e.g., `this` in Javascript).
|
2021-12-15 05:13:11 +00:00
|
|
|
if ["this", "my"].contains(&item_str.as_str()) && is_assoc_fn {
|
2020-07-09 20:01:20 +00:00
|
|
|
err.span_suggestion_short(
|
2019-08-07 23:39:02 +00:00
|
|
|
span,
|
2020-07-09 20:01:20 +00:00
|
|
|
"you might have meant to use `self` here instead",
|
2019-08-07 23:39:02 +00:00
|
|
|
"self".to_string(),
|
|
|
|
Applicability::MaybeIncorrect,
|
|
|
|
);
|
2020-08-14 00:04:33 +00:00
|
|
|
if !self.self_value_is_available(path[0].ident.span, span) {
|
|
|
|
if let Some((FnKind::Fn(_, _, sig, ..), fn_span)) =
|
|
|
|
&self.diagnostic_metadata.current_function
|
|
|
|
{
|
2020-08-14 00:52:33 +00:00
|
|
|
let (span, sugg) = if let Some(param) = sig.decl.inputs.get(0) {
|
|
|
|
(param.span.shrink_to_lo(), "&self, ")
|
2020-08-14 00:04:33 +00:00
|
|
|
} else {
|
2020-08-14 00:52:33 +00:00
|
|
|
(
|
2020-08-14 00:04:33 +00:00
|
|
|
self.r
|
|
|
|
.session
|
|
|
|
.source_map()
|
|
|
|
.span_through_char(*fn_span, '(')
|
|
|
|
.shrink_to_hi(),
|
2020-08-14 00:52:33 +00:00
|
|
|
"&self",
|
|
|
|
)
|
|
|
|
};
|
|
|
|
err.span_suggestion_verbose(
|
|
|
|
span,
|
2020-08-14 17:16:34 +00:00
|
|
|
"if you meant to use `self`, you are also missing a `self` receiver \
|
|
|
|
argument",
|
2020-08-14 00:52:33 +00:00
|
|
|
sugg.to_string(),
|
|
|
|
Applicability::MaybeIncorrect,
|
|
|
|
);
|
2020-08-14 00:04:33 +00:00
|
|
|
}
|
|
|
|
}
|
2019-08-07 23:39:02 +00:00
|
|
|
}
|
|
|
|
|
2022-01-11 21:56:10 +00:00
|
|
|
self.detect_assoct_type_constraint_meant_as_path(base_span, &mut err);
|
|
|
|
|
2019-08-07 23:39:02 +00:00
|
|
|
// Emit special messages for unresolved `Self` and `self`.
|
|
|
|
if is_self_type(path, ns) {
|
2020-01-09 10:18:47 +00:00
|
|
|
err.code(rustc_errors::error_code!(E0411));
|
2019-10-18 02:00:19 +00:00
|
|
|
err.span_label(
|
|
|
|
span,
|
2020-02-28 23:35:24 +00:00
|
|
|
"`Self` is only available in impls, traits, and type definitions".to_string(),
|
2019-10-18 02:00:19 +00:00
|
|
|
);
|
2019-08-07 23:39:02 +00:00
|
|
|
return (err, Vec::new());
|
|
|
|
}
|
|
|
|
if is_self_value(path, ns) {
|
|
|
|
debug!("smart_resolve_path_fragment: E0424, source={:?}", source);
|
|
|
|
|
2020-01-09 10:18:47 +00:00
|
|
|
err.code(rustc_errors::error_code!(E0424));
|
2019-08-07 23:39:02 +00:00
|
|
|
err.span_label(span, match source {
|
2020-02-28 23:35:24 +00:00
|
|
|
PathSource::Pat => "`self` value is a keyword and may not be bound to variables or shadowed"
|
|
|
|
.to_string(),
|
|
|
|
_ => "`self` value is a keyword only available in methods with a `self` parameter"
|
|
|
|
.to_string(),
|
2019-08-07 23:39:02 +00:00
|
|
|
});
|
2020-05-17 22:28:12 +00:00
|
|
|
if let Some((fn_kind, span)) = &self.diagnostic_metadata.current_function {
|
|
|
|
// The current function has a `self' parameter, but we were unable to resolve
|
|
|
|
// a reference to `self`. This can only happen if the `self` identifier we
|
|
|
|
// are resolving came from a different hygiene context.
|
2021-01-11 19:45:33 +00:00
|
|
|
if fn_kind.decl().inputs.get(0).map_or(false, |p| p.is_self()) {
|
2020-05-17 22:28:12 +00:00
|
|
|
err.span_label(*span, "this function has a `self` parameter, but a macro invocation can only access identifiers it receives from parameters");
|
|
|
|
} else {
|
2020-08-14 00:52:33 +00:00
|
|
|
let doesnt = if is_assoc_fn {
|
|
|
|
let (span, sugg) = fn_kind
|
|
|
|
.decl()
|
|
|
|
.inputs
|
|
|
|
.get(0)
|
|
|
|
.map(|p| (p.span.shrink_to_lo(), "&self, "))
|
|
|
|
.unwrap_or_else(|| {
|
2021-12-11 16:32:15 +00:00
|
|
|
// Try to look for the "(" after the function name, if possible.
|
|
|
|
// This avoids placing the suggestion into the visibility specifier.
|
|
|
|
let span = fn_kind
|
|
|
|
.ident()
|
|
|
|
.map_or(*span, |ident| span.with_lo(ident.span.hi()));
|
2020-08-14 00:52:33 +00:00
|
|
|
(
|
|
|
|
self.r
|
|
|
|
.session
|
|
|
|
.source_map()
|
2021-12-11 16:32:15 +00:00
|
|
|
.span_through_char(span, '(')
|
2020-08-14 00:52:33 +00:00
|
|
|
.shrink_to_hi(),
|
|
|
|
"&self",
|
|
|
|
)
|
|
|
|
});
|
|
|
|
err.span_suggestion_verbose(
|
|
|
|
span,
|
|
|
|
"add a `self` receiver parameter to make the associated `fn` a method",
|
|
|
|
sugg.to_string(),
|
|
|
|
Applicability::MaybeIncorrect,
|
|
|
|
);
|
|
|
|
"doesn't"
|
|
|
|
} else {
|
|
|
|
"can't"
|
|
|
|
};
|
|
|
|
if let Some(ident) = fn_kind.ident() {
|
|
|
|
err.span_label(
|
|
|
|
ident.span,
|
|
|
|
&format!("this function {} have a `self` parameter", doesnt),
|
|
|
|
);
|
|
|
|
}
|
2020-05-17 22:28:12 +00:00
|
|
|
}
|
2019-10-18 03:26:21 +00:00
|
|
|
}
|
2019-08-07 23:39:02 +00:00
|
|
|
return (err, Vec::new());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try to lookup name in more relaxed fashion for better error reporting.
|
|
|
|
let ident = path.last().unwrap().ident;
|
2019-12-22 22:42:04 +00:00
|
|
|
let candidates = self
|
|
|
|
.r
|
2020-05-30 16:54:44 +00:00
|
|
|
.lookup_import_candidates(ident, ns, &self.parent_scope, is_expected)
|
2021-11-06 17:42:07 +00:00
|
|
|
.into_iter()
|
2019-08-07 23:39:02 +00:00
|
|
|
.filter(|ImportSuggestion { did, .. }| {
|
|
|
|
match (did, res.and_then(|res| res.opt_def_id())) {
|
|
|
|
(Some(suggestion_did), Some(actual_did)) => *suggestion_did != actual_did,
|
|
|
|
_ => true,
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
let crate_def_id = DefId::local(CRATE_DEF_INDEX);
|
|
|
|
if candidates.is_empty() && is_expected(Res::Def(DefKind::Enum, crate_def_id)) {
|
2021-02-14 03:52:12 +00:00
|
|
|
let mut enum_candidates: Vec<_> = self
|
|
|
|
.r
|
|
|
|
.lookup_import_candidates(ident, ns, &self.parent_scope, is_enum_variant)
|
|
|
|
.into_iter()
|
|
|
|
.map(|suggestion| import_candidate_to_enum_paths(&suggestion))
|
2021-03-09 18:40:01 +00:00
|
|
|
.filter(|(_, enum_ty_path)| !enum_ty_path.starts_with("std::prelude::"))
|
2021-02-14 03:52:12 +00:00
|
|
|
.collect();
|
2019-08-07 23:39:02 +00:00
|
|
|
if !enum_candidates.is_empty() {
|
2020-07-09 20:49:55 +00:00
|
|
|
if let (PathSource::Type, Some(span)) =
|
|
|
|
(source, self.diagnostic_metadata.current_type_ascription.last())
|
|
|
|
{
|
|
|
|
if self
|
|
|
|
.r
|
|
|
|
.session
|
|
|
|
.parse_sess
|
|
|
|
.type_ascription_path_suggestions
|
|
|
|
.borrow()
|
|
|
|
.contains(span)
|
|
|
|
{
|
|
|
|
// Already reported this issue on the lhs of the type ascription.
|
|
|
|
err.delay_as_bug();
|
|
|
|
return (err, candidates);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
enum_candidates.sort();
|
|
|
|
|
2019-08-07 23:39:02 +00:00
|
|
|
// Contextualize for E0412 "cannot find type", but don't belabor the point
|
|
|
|
// (that it's a variant) for E0573 "expected type, found variant".
|
|
|
|
let preamble = if res.is_none() {
|
|
|
|
let others = match enum_candidates.len() {
|
|
|
|
1 => String::new(),
|
|
|
|
2 => " and 1 other".to_owned(),
|
2019-12-22 22:42:04 +00:00
|
|
|
n => format!(" and {} others", n),
|
2019-08-07 23:39:02 +00:00
|
|
|
};
|
2019-12-22 22:42:04 +00:00
|
|
|
format!("there is an enum variant `{}`{}; ", enum_candidates[0].0, others)
|
2019-08-07 23:39:02 +00:00
|
|
|
} else {
|
|
|
|
String::new()
|
|
|
|
};
|
|
|
|
let msg = format!("{}try using the variant's enum", preamble);
|
|
|
|
|
|
|
|
err.span_suggestions(
|
|
|
|
span,
|
|
|
|
&msg,
|
2021-02-14 03:52:12 +00:00
|
|
|
enum_candidates.into_iter().map(|(_variant_path, enum_ty_path)| enum_ty_path),
|
2019-08-07 23:39:02 +00:00
|
|
|
Applicability::MachineApplicable,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if path.len() == 1 && self.self_type_is_available(span) {
|
|
|
|
if let Some(candidate) = self.lookup_assoc_candidate(ident, ns, is_expected) {
|
|
|
|
let self_is_available = self.self_value_is_available(path[0].ident.span, span);
|
|
|
|
match candidate {
|
|
|
|
AssocSuggestion::Field => {
|
|
|
|
if self_is_available {
|
|
|
|
err.span_suggestion(
|
|
|
|
span,
|
|
|
|
"you might have meant to use the available field",
|
|
|
|
format!("self.{}", path_str),
|
|
|
|
Applicability::MachineApplicable,
|
|
|
|
);
|
|
|
|
} else {
|
2019-12-22 22:42:04 +00:00
|
|
|
err.span_label(span, "a field by this name exists in `Self`");
|
2019-08-07 23:39:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
AssocSuggestion::MethodWithSelf if self_is_available => {
|
|
|
|
err.span_suggestion(
|
|
|
|
span,
|
2020-10-26 23:28:56 +00:00
|
|
|
"you might have meant to call the method",
|
2019-08-07 23:39:02 +00:00
|
|
|
format!("self.{}", path_str),
|
|
|
|
Applicability::MachineApplicable,
|
|
|
|
);
|
|
|
|
}
|
2020-10-26 23:28:56 +00:00
|
|
|
AssocSuggestion::MethodWithSelf
|
|
|
|
| AssocSuggestion::AssocFn
|
|
|
|
| AssocSuggestion::AssocConst
|
|
|
|
| AssocSuggestion::AssocType => {
|
2019-08-07 23:39:02 +00:00
|
|
|
err.span_suggestion(
|
|
|
|
span,
|
2020-10-26 23:28:56 +00:00
|
|
|
&format!("you might have meant to {}", candidate.action()),
|
2019-08-07 23:39:02 +00:00
|
|
|
format!("Self::{}", path_str),
|
|
|
|
Applicability::MachineApplicable,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return (err, candidates);
|
|
|
|
}
|
2019-11-30 17:59:51 +00:00
|
|
|
|
2019-12-23 10:56:34 +00:00
|
|
|
// If the first argument in call is `self` suggest calling a method.
|
|
|
|
if let Some((call_span, args_span)) = self.call_has_self_arg(source) {
|
|
|
|
let mut args_snippet = String::new();
|
2019-12-21 18:13:12 +00:00
|
|
|
if let Some(args_span) = args_span {
|
|
|
|
if let Ok(snippet) = self.r.session.source_map().span_to_snippet(args_span) {
|
|
|
|
args_snippet = snippet;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-30 17:59:51 +00:00
|
|
|
err.span_suggestion(
|
2019-12-22 21:48:45 +00:00
|
|
|
call_span,
|
2019-12-09 15:05:45 +00:00
|
|
|
&format!("try calling `{}` as a method", ident),
|
2019-12-21 18:13:12 +00:00
|
|
|
format!("self.{}({})", path_str, args_snippet),
|
2019-11-30 17:59:51 +00:00
|
|
|
Applicability::MachineApplicable,
|
|
|
|
);
|
|
|
|
return (err, candidates);
|
|
|
|
}
|
2019-08-07 23:39:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Try Levenshtein algorithm.
|
2019-10-15 00:20:50 +00:00
|
|
|
let typo_sugg = self.lookup_typo_candidate(path, ns, is_expected, span);
|
2019-08-07 23:39:02 +00:00
|
|
|
// Try context-dependent help if relaxed lookup didn't work.
|
|
|
|
if let Some(res) = res {
|
2019-10-15 00:20:50 +00:00
|
|
|
if self.smart_resolve_context_dependent_help(
|
|
|
|
&mut err,
|
|
|
|
span,
|
|
|
|
source,
|
|
|
|
res,
|
|
|
|
&path_str,
|
|
|
|
&fallback_label,
|
|
|
|
) {
|
2020-08-10 03:29:39 +00:00
|
|
|
// We do this to avoid losing a secondary span when we override the main error span.
|
|
|
|
self.r.add_typo_suggestion(&mut err, typo_sugg, ident_span);
|
2019-08-07 23:39:02 +00:00
|
|
|
return (err, candidates);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-29 08:12:03 +00:00
|
|
|
let is_macro = base_span.from_expansion() && base_span.desugaring_kind().is_none();
|
2020-09-23 02:25:27 +00:00
|
|
|
if !self.type_ascription_suggestion(&mut err, base_span) {
|
|
|
|
let mut fallback = false;
|
2020-10-09 23:51:54 +00:00
|
|
|
if let (
|
|
|
|
PathSource::Trait(AliasPossibility::Maybe),
|
|
|
|
Some(Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, _)),
|
2021-03-29 08:12:03 +00:00
|
|
|
false,
|
|
|
|
) = (source, res, is_macro)
|
2020-10-09 23:51:54 +00:00
|
|
|
{
|
2020-09-23 02:19:33 +00:00
|
|
|
if let Some(bounds @ [_, .., _]) = self.diagnostic_metadata.current_trait_object {
|
2020-09-23 02:25:27 +00:00
|
|
|
fallback = true;
|
2020-09-23 02:19:33 +00:00
|
|
|
let spans: Vec<Span> = bounds
|
|
|
|
.iter()
|
|
|
|
.map(|bound| bound.span())
|
|
|
|
.filter(|&sp| sp != base_span)
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
let start_span = bounds.iter().map(|bound| bound.span()).next().unwrap();
|
|
|
|
// `end_span` is the end of the poly trait ref (Foo + 'baz + Bar><)
|
|
|
|
let end_span = bounds.iter().map(|bound| bound.span()).last().unwrap();
|
|
|
|
// `last_bound_span` is the last bound of the poly trait ref (Foo + >'baz< + Bar)
|
|
|
|
let last_bound_span = spans.last().cloned().unwrap();
|
|
|
|
let mut multi_span: MultiSpan = spans.clone().into();
|
|
|
|
for sp in spans {
|
|
|
|
let msg = if sp == last_bound_span {
|
|
|
|
format!(
|
|
|
|
"...because of {} bound{}",
|
|
|
|
if bounds.len() <= 2 { "this" } else { "these" },
|
|
|
|
if bounds.len() <= 2 { "" } else { "s" },
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
String::new()
|
|
|
|
};
|
|
|
|
multi_span.push_span_label(sp, msg);
|
|
|
|
}
|
|
|
|
multi_span.push_span_label(
|
|
|
|
base_span,
|
|
|
|
"expected this type to be a trait...".to_string(),
|
|
|
|
);
|
2020-09-23 00:35:03 +00:00
|
|
|
err.span_help(
|
2020-09-23 02:19:33 +00:00
|
|
|
multi_span,
|
|
|
|
"`+` is used to constrain a \"trait object\" type with lifetimes or \
|
|
|
|
auto-traits; structs and enums can't be bound in that way",
|
2020-09-23 00:35:03 +00:00
|
|
|
);
|
2020-09-23 02:19:33 +00:00
|
|
|
if bounds.iter().all(|bound| match bound {
|
|
|
|
ast::GenericBound::Outlives(_) => true,
|
|
|
|
ast::GenericBound::Trait(tr, _) => tr.span == base_span,
|
|
|
|
}) {
|
|
|
|
let mut sugg = vec![];
|
|
|
|
if base_span != start_span {
|
|
|
|
sugg.push((start_span.until(base_span), String::new()));
|
|
|
|
}
|
|
|
|
if base_span != end_span {
|
|
|
|
sugg.push((base_span.shrink_to_hi().to(end_span), String::new()));
|
|
|
|
}
|
|
|
|
|
|
|
|
err.multipart_suggestion(
|
|
|
|
"if you meant to use a type and not a trait here, remove the bounds",
|
|
|
|
sugg,
|
|
|
|
Applicability::MaybeIncorrect,
|
|
|
|
);
|
|
|
|
}
|
2020-09-23 00:35:03 +00:00
|
|
|
}
|
|
|
|
}
|
2020-09-24 06:01:17 +00:00
|
|
|
|
|
|
|
fallback |= self.restrict_assoc_type_in_where_clause(span, &mut err);
|
|
|
|
|
2020-09-23 02:25:27 +00:00
|
|
|
if !self.r.add_typo_suggestion(&mut err, typo_sugg, ident_span) {
|
|
|
|
fallback = true;
|
|
|
|
match self.diagnostic_metadata.current_let_binding {
|
|
|
|
Some((pat_sp, Some(ty_sp), None))
|
|
|
|
if ty_sp.contains(base_span) && could_be_expr =>
|
|
|
|
{
|
|
|
|
err.span_suggestion_short(
|
|
|
|
pat_sp.between(ty_sp),
|
|
|
|
"use `=` if you meant to assign",
|
|
|
|
" = ".to_string(),
|
|
|
|
Applicability::MaybeIncorrect,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
_ => {}
|
2019-10-18 17:33:25 +00:00
|
|
|
}
|
2021-08-04 21:29:30 +00:00
|
|
|
|
|
|
|
// If the trait has a single item (which wasn't matched by Levenshtein), suggest it
|
|
|
|
let suggestion = self.get_single_associated_item(&path, span, &source, is_expected);
|
|
|
|
self.r.add_typo_suggestion(&mut err, suggestion, ident_span);
|
2020-09-23 02:25:27 +00:00
|
|
|
}
|
|
|
|
if fallback {
|
|
|
|
// Fallback label.
|
|
|
|
err.span_label(base_span, fallback_label);
|
2019-10-18 17:33:25 +00:00
|
|
|
}
|
2019-08-07 23:39:02 +00:00
|
|
|
}
|
2020-12-14 12:13:40 +00:00
|
|
|
if let Some(err_code) = &err.code {
|
|
|
|
if err_code == &rustc_errors::error_code!(E0425) {
|
|
|
|
for label_rib in &self.label_ribs {
|
2021-01-21 01:57:47 +00:00
|
|
|
for (label_ident, node_id) in &label_rib.bindings {
|
2020-12-14 12:13:40 +00:00
|
|
|
if format!("'{}", ident) == label_ident.to_string() {
|
2021-01-20 01:51:48 +00:00
|
|
|
err.span_label(label_ident.span, "a label with a similar name exists");
|
|
|
|
if let PathSource::Expr(Some(Expr {
|
|
|
|
kind: ExprKind::Break(None, Some(_)),
|
|
|
|
..
|
|
|
|
})) = source
|
|
|
|
{
|
|
|
|
err.span_suggestion(
|
|
|
|
span,
|
|
|
|
"use the similarly named label",
|
|
|
|
label_ident.name.to_string(),
|
|
|
|
Applicability::MaybeIncorrect,
|
|
|
|
);
|
2021-01-21 01:57:47 +00:00
|
|
|
// Do not lint against unused label when we suggest them.
|
|
|
|
self.diagnostic_metadata.unused_labels.remove(node_id);
|
2021-01-20 01:51:48 +00:00
|
|
|
}
|
2020-12-14 12:13:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-03-15 12:11:02 +00:00
|
|
|
} else if err_code == &rustc_errors::error_code!(E0412) {
|
|
|
|
if let Some(correct) = Self::likely_rust_type(path) {
|
|
|
|
err.span_suggestion(
|
|
|
|
span,
|
|
|
|
"perhaps you intended to use this type",
|
|
|
|
correct.to_string(),
|
|
|
|
Applicability::MaybeIncorrect,
|
|
|
|
);
|
|
|
|
}
|
2020-12-14 12:13:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-07 23:39:02 +00:00
|
|
|
(err, candidates)
|
|
|
|
}
|
|
|
|
|
2022-01-11 21:56:10 +00:00
|
|
|
fn detect_assoct_type_constraint_meant_as_path(
|
|
|
|
&self,
|
|
|
|
base_span: Span,
|
|
|
|
err: &mut DiagnosticBuilder<'_>,
|
|
|
|
) {
|
|
|
|
let Some(ty) = self.diagnostic_metadata.current_type_path else { return; };
|
|
|
|
let TyKind::Path(_, path) = &ty.kind else { return; };
|
|
|
|
for segment in &path.segments {
|
|
|
|
let Some(params) = &segment.args else { continue; };
|
|
|
|
let ast::GenericArgs::AngleBracketed(ref params) = params.deref() else { continue; };
|
|
|
|
for param in ¶ms.args {
|
|
|
|
let ast::AngleBracketedArg::Constraint(constraint) = param else { continue; };
|
|
|
|
let ast::AssocConstraintKind::Bound { bounds } = &constraint.kind else {
|
|
|
|
continue;
|
|
|
|
};
|
|
|
|
for bound in bounds {
|
|
|
|
let ast::GenericBound::Trait(trait_ref, ast::TraitBoundModifier::None)
|
|
|
|
= bound else
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
};
|
|
|
|
if base_span == trait_ref.span {
|
|
|
|
err.span_suggestion_verbose(
|
|
|
|
constraint.ident.span.between(trait_ref.span),
|
|
|
|
"you might have meant to write a path instead of an associated type bound",
|
|
|
|
"::".to_string(),
|
|
|
|
Applicability::MachineApplicable,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-04 21:29:30 +00:00
|
|
|
fn get_single_associated_item(
|
|
|
|
&mut self,
|
|
|
|
path: &[Segment],
|
|
|
|
span: Span,
|
|
|
|
source: &PathSource<'_>,
|
|
|
|
filter_fn: &impl Fn(Res) -> bool,
|
|
|
|
) -> Option<TypoSuggestion> {
|
|
|
|
if let crate::PathSource::TraitItem(_) = source {
|
|
|
|
let mod_path = &path[..path.len() - 1];
|
|
|
|
if let PathResult::Module(ModuleOrUniformRoot::Module(module)) =
|
|
|
|
self.resolve_path(mod_path, None, false, span, CrateLint::No)
|
|
|
|
{
|
|
|
|
let resolutions = self.r.resolutions(module).borrow();
|
|
|
|
let targets: Vec<_> =
|
|
|
|
resolutions
|
|
|
|
.iter()
|
|
|
|
.filter_map(|(key, resolution)| {
|
|
|
|
resolution.borrow().binding.map(|binding| binding.res()).and_then(
|
|
|
|
|res| if filter_fn(res) { Some((key, res)) } else { None },
|
|
|
|
)
|
|
|
|
})
|
|
|
|
.collect();
|
|
|
|
if targets.len() == 1 {
|
|
|
|
let target = targets[0];
|
|
|
|
return Some(TypoSuggestion::single_item_from_res(
|
|
|
|
target.0.ident.name,
|
|
|
|
target.1,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
2020-09-24 06:01:17 +00:00
|
|
|
/// Given `where <T as Bar>::Baz: String`, suggest `where T: Bar<Baz = String>`.
|
|
|
|
fn restrict_assoc_type_in_where_clause(
|
|
|
|
&mut self,
|
|
|
|
span: Span,
|
|
|
|
err: &mut DiagnosticBuilder<'_>,
|
|
|
|
) -> bool {
|
|
|
|
// Detect that we are actually in a `where` predicate.
|
|
|
|
let (bounded_ty, bounds, where_span) =
|
|
|
|
if let Some(ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate {
|
|
|
|
bounded_ty,
|
|
|
|
bound_generic_params,
|
|
|
|
bounds,
|
|
|
|
span,
|
|
|
|
})) = self.diagnostic_metadata.current_where_predicate
|
|
|
|
{
|
|
|
|
if !bound_generic_params.is_empty() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
(bounded_ty, bounds, span)
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
|
|
|
|
// Confirm that the target is an associated type.
|
|
|
|
let (ty, position, path) = if let ast::TyKind::Path(
|
|
|
|
Some(ast::QSelf { ty, position, .. }),
|
|
|
|
path,
|
|
|
|
) = &bounded_ty.kind
|
|
|
|
{
|
|
|
|
// use this to verify that ident is a type param.
|
2022-02-15 04:58:25 +00:00
|
|
|
let Ok(Some(partial_res)) = self.resolve_qpath_anywhere(
|
2020-09-24 06:01:17 +00:00
|
|
|
bounded_ty.id,
|
|
|
|
None,
|
|
|
|
&Segment::from_path(path),
|
|
|
|
Namespace::TypeNS,
|
|
|
|
span,
|
|
|
|
true,
|
|
|
|
CrateLint::No,
|
2022-02-15 04:58:25 +00:00
|
|
|
) else {
|
2020-09-24 06:01:17 +00:00
|
|
|
return false;
|
|
|
|
};
|
|
|
|
if !(matches!(
|
|
|
|
partial_res.base_res(),
|
|
|
|
hir::def::Res::Def(hir::def::DefKind::AssocTy, _)
|
|
|
|
) && partial_res.unresolved_segments() == 0)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
(ty, position, path)
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
|
2020-10-09 23:51:54 +00:00
|
|
|
if let ast::TyKind::Path(None, type_param_path) = &ty.peel_refs().kind {
|
2020-09-24 06:01:17 +00:00
|
|
|
// Confirm that the `SelfTy` is a type parameter.
|
2022-02-15 04:58:25 +00:00
|
|
|
let Ok(Some(partial_res)) = self.resolve_qpath_anywhere(
|
2020-09-24 06:01:17 +00:00
|
|
|
bounded_ty.id,
|
|
|
|
None,
|
|
|
|
&Segment::from_path(type_param_path),
|
|
|
|
Namespace::TypeNS,
|
|
|
|
span,
|
|
|
|
true,
|
|
|
|
CrateLint::No,
|
2022-02-15 04:58:25 +00:00
|
|
|
) else {
|
2020-09-24 06:01:17 +00:00
|
|
|
return false;
|
|
|
|
};
|
|
|
|
if !(matches!(
|
|
|
|
partial_res.base_res(),
|
|
|
|
hir::def::Res::Def(hir::def::DefKind::TyParam, _)
|
|
|
|
) && partial_res.unresolved_segments() == 0)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if let (
|
2020-10-09 23:51:54 +00:00
|
|
|
[ast::PathSegment { ident: constrain_ident, args: None, .. }],
|
2020-09-24 06:01:17 +00:00
|
|
|
[ast::GenericBound::Trait(poly_trait_ref, ast::TraitBoundModifier::None)],
|
|
|
|
) = (&type_param_path.segments[..], &bounds[..])
|
|
|
|
{
|
2020-10-09 23:51:54 +00:00
|
|
|
if let [ast::PathSegment { ident, args: None, .. }] =
|
2020-09-24 06:01:17 +00:00
|
|
|
&poly_trait_ref.trait_ref.path.segments[..]
|
|
|
|
{
|
2020-10-09 23:51:54 +00:00
|
|
|
if ident.span == span {
|
2020-09-24 06:01:17 +00:00
|
|
|
err.span_suggestion_verbose(
|
|
|
|
*where_span,
|
2020-10-09 23:51:54 +00:00
|
|
|
&format!("constrain the associated type to `{}`", ident),
|
2020-09-24 06:01:17 +00:00
|
|
|
format!(
|
|
|
|
"{}: {}<{} = {}>",
|
2020-10-09 23:51:54 +00:00
|
|
|
self.r
|
|
|
|
.session
|
|
|
|
.source_map()
|
|
|
|
.span_to_snippet(ty.span) // Account for `<&'a T as Foo>::Bar`.
|
|
|
|
.unwrap_or_else(|_| constrain_ident.to_string()),
|
2020-09-24 06:01:17 +00:00
|
|
|
path.segments[..*position]
|
|
|
|
.iter()
|
|
|
|
.map(|segment| path_segment_to_string(segment))
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
.join("::"),
|
|
|
|
path.segments[*position..]
|
|
|
|
.iter()
|
|
|
|
.map(|segment| path_segment_to_string(segment))
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
.join("::"),
|
2020-10-09 23:51:54 +00:00
|
|
|
ident,
|
2020-09-24 06:01:17 +00:00
|
|
|
),
|
|
|
|
Applicability::MaybeIncorrect,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
false
|
|
|
|
}
|
|
|
|
|
2019-12-23 10:56:34 +00:00
|
|
|
/// Check if the source is call expression and the first argument is `self`. If true,
|
|
|
|
/// return the span of whole call and the span for all arguments expect the first one (`self`).
|
|
|
|
fn call_has_self_arg(&self, source: PathSource<'_>) -> Option<(Span, Option<Span>)> {
|
|
|
|
let mut has_self_arg = None;
|
2020-08-10 00:48:32 +00:00
|
|
|
if let PathSource::Expr(Some(parent)) = source {
|
|
|
|
match &parent.kind {
|
2020-02-28 13:20:33 +00:00
|
|
|
ExprKind::Call(_, args) if !args.is_empty() => {
|
2019-12-23 10:56:34 +00:00
|
|
|
let mut expr_kind = &args[0].kind;
|
|
|
|
loop {
|
|
|
|
match expr_kind {
|
|
|
|
ExprKind::Path(_, arg_name) if arg_name.segments.len() == 1 => {
|
|
|
|
if arg_name.segments[0].ident.name == kw::SelfLower {
|
2020-08-10 00:48:32 +00:00
|
|
|
let call_span = parent.span;
|
2019-12-23 10:56:34 +00:00
|
|
|
let tail_args_span = if args.len() > 1 {
|
|
|
|
Some(Span::new(
|
|
|
|
args[1].span.lo(),
|
|
|
|
args.last().unwrap().span.hi(),
|
|
|
|
call_span.ctxt(),
|
2021-04-18 12:27:04 +00:00
|
|
|
None,
|
2019-12-23 10:56:34 +00:00
|
|
|
))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
|
|
|
has_self_arg = Some((call_span, tail_args_span));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
ExprKind::AddrOf(_, _, expr) => expr_kind = &expr.kind,
|
|
|
|
_ => break,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
};
|
2020-03-20 14:03:11 +00:00
|
|
|
has_self_arg
|
2019-12-23 10:56:34 +00:00
|
|
|
}
|
|
|
|
|
2020-04-22 18:58:21 +00:00
|
|
|
fn followed_by_brace(&self, span: Span) -> (bool, Option<Span>) {
|
2019-08-07 23:39:02 +00:00
|
|
|
// HACK(estebank): find a better way to figure out that this was a
|
|
|
|
// parser issue where a struct literal is being used on an expression
|
|
|
|
// where a brace being opened means a block is being started. Look
|
|
|
|
// ahead for the next text to see if `span` is followed by a `{`.
|
2019-08-08 11:06:42 +00:00
|
|
|
let sm = self.r.session.source_map();
|
2019-08-07 23:39:02 +00:00
|
|
|
let mut sp = span;
|
|
|
|
loop {
|
|
|
|
sp = sm.next_point(sp);
|
|
|
|
match sm.span_to_snippet(sp) {
|
|
|
|
Ok(ref snippet) => {
|
2019-12-22 22:42:04 +00:00
|
|
|
if snippet.chars().any(|c| !c.is_whitespace()) {
|
2019-08-07 23:39:02 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => break,
|
|
|
|
}
|
|
|
|
}
|
2020-10-27 01:02:48 +00:00
|
|
|
let followed_by_brace = matches!(sm.span_to_snippet(sp), Ok(ref snippet) if snippet == "{");
|
2019-08-07 23:39:02 +00:00
|
|
|
// In case this could be a struct literal that needs to be surrounded
|
2020-04-22 18:58:21 +00:00
|
|
|
// by parentheses, find the appropriate span.
|
2019-08-07 23:39:02 +00:00
|
|
|
let mut i = 0;
|
|
|
|
let mut closing_brace = None;
|
|
|
|
loop {
|
|
|
|
sp = sm.next_point(sp);
|
|
|
|
match sm.span_to_snippet(sp) {
|
|
|
|
Ok(ref snippet) => {
|
|
|
|
if snippet == "}" {
|
2020-04-22 18:58:21 +00:00
|
|
|
closing_brace = Some(span.to(sp));
|
2019-08-07 23:39:02 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => break,
|
|
|
|
}
|
|
|
|
i += 1;
|
|
|
|
// The bigger the span, the more likely we're incorrect --
|
|
|
|
// bound it to 100 chars long.
|
|
|
|
if i > 100 {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2020-03-20 14:03:11 +00:00
|
|
|
(followed_by_brace, closing_brace)
|
2019-08-07 23:39:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Provides context-dependent help for errors reported by the `smart_resolve_path_fragment`
|
|
|
|
/// function.
|
|
|
|
/// Returns `true` if able to provide context-dependent help.
|
|
|
|
fn smart_resolve_context_dependent_help(
|
|
|
|
&mut self,
|
|
|
|
err: &mut DiagnosticBuilder<'a>,
|
|
|
|
span: Span,
|
|
|
|
source: PathSource<'_>,
|
|
|
|
res: Res,
|
|
|
|
path_str: &str,
|
|
|
|
fallback_label: &str,
|
|
|
|
) -> bool {
|
|
|
|
let ns = source.namespace();
|
|
|
|
let is_expected = &|res| source.is_expected(res);
|
|
|
|
|
2019-09-26 13:39:48 +00:00
|
|
|
let path_sep = |err: &mut DiagnosticBuilder<'_>, expr: &Expr| match expr.kind {
|
2019-08-07 23:39:02 +00:00
|
|
|
ExprKind::Field(_, ident) => {
|
|
|
|
err.span_suggestion(
|
|
|
|
expr.span,
|
|
|
|
"use the path separator to refer to an item",
|
|
|
|
format!("{}::{}", path_str, ident),
|
|
|
|
Applicability::MaybeIncorrect,
|
|
|
|
);
|
|
|
|
true
|
|
|
|
}
|
|
|
|
ExprKind::MethodCall(ref segment, ..) => {
|
|
|
|
let span = expr.span.with_hi(segment.ident.span.hi());
|
|
|
|
err.span_suggestion(
|
|
|
|
span,
|
|
|
|
"use the path separator to refer to an item",
|
|
|
|
format!("{}::{}", path_str, segment.ident),
|
|
|
|
Applicability::MaybeIncorrect,
|
|
|
|
);
|
|
|
|
true
|
|
|
|
}
|
|
|
|
_ => false,
|
|
|
|
};
|
|
|
|
|
2021-05-02 09:46:29 +00:00
|
|
|
let find_span = |source: &PathSource<'_>, err: &mut DiagnosticBuilder<'_>| {
|
|
|
|
match source {
|
|
|
|
PathSource::Expr(Some(Expr { span, kind: ExprKind::Call(_, _), .. }))
|
|
|
|
| PathSource::TupleStruct(span, _) => {
|
|
|
|
// We want the main underline to cover the suggested code as well for
|
|
|
|
// cleaner output.
|
|
|
|
err.set_span(*span);
|
|
|
|
*span
|
|
|
|
}
|
|
|
|
_ => span,
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-09-22 18:27:55 +00:00
|
|
|
let mut bad_struct_syntax_suggestion = |def_id: DefId| {
|
2019-08-07 23:39:02 +00:00
|
|
|
let (followed_by_brace, closing_brace) = self.followed_by_brace(span);
|
2020-07-10 00:42:07 +00:00
|
|
|
|
2019-08-07 23:39:02 +00:00
|
|
|
match source {
|
2020-07-10 00:42:07 +00:00
|
|
|
PathSource::Expr(Some(
|
|
|
|
parent @ Expr { kind: ExprKind::Field(..) | ExprKind::MethodCall(..), .. },
|
2020-07-09 20:49:55 +00:00
|
|
|
)) if path_sep(err, &parent) => {}
|
|
|
|
PathSource::Expr(
|
|
|
|
None
|
|
|
|
| Some(Expr {
|
|
|
|
kind:
|
|
|
|
ExprKind::Path(..)
|
|
|
|
| ExprKind::Binary(..)
|
|
|
|
| ExprKind::Unary(..)
|
|
|
|
| ExprKind::If(..)
|
|
|
|
| ExprKind::While(..)
|
|
|
|
| ExprKind::ForLoop(..)
|
|
|
|
| ExprKind::Match(..),
|
|
|
|
..
|
|
|
|
}),
|
|
|
|
) if followed_by_brace => {
|
2020-04-22 18:58:21 +00:00
|
|
|
if let Some(sp) = closing_brace {
|
2020-08-10 04:24:05 +00:00
|
|
|
err.span_label(span, fallback_label);
|
2020-04-22 18:58:21 +00:00
|
|
|
err.multipart_suggestion(
|
|
|
|
"surround the struct literal with parentheses",
|
|
|
|
vec![
|
|
|
|
(sp.shrink_to_lo(), "(".to_string()),
|
|
|
|
(sp.shrink_to_hi(), ")".to_string()),
|
|
|
|
],
|
2019-08-07 23:39:02 +00:00
|
|
|
Applicability::MaybeIncorrect,
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
err.span_label(
|
2020-04-22 18:58:21 +00:00
|
|
|
span, // Note the parentheses surrounding the suggestion below
|
|
|
|
format!(
|
|
|
|
"you might want to surround a struct literal with parentheses: \
|
|
|
|
`({} {{ /* fields */ }})`?",
|
|
|
|
path_str
|
|
|
|
),
|
2019-08-07 23:39:02 +00:00
|
|
|
);
|
|
|
|
}
|
2019-12-22 22:42:04 +00:00
|
|
|
}
|
2020-09-08 22:14:09 +00:00
|
|
|
PathSource::Expr(_) | PathSource::TupleStruct(..) | PathSource::Pat => {
|
2021-05-02 09:46:29 +00:00
|
|
|
let span = find_span(&source, err);
|
2020-08-10 04:14:59 +00:00
|
|
|
if let Some(span) = self.def_span(def_id) {
|
2020-07-10 00:42:07 +00:00
|
|
|
err.span_label(span, &format!("`{}` defined here", path_str));
|
|
|
|
}
|
|
|
|
let (tail, descr, applicability) = match source {
|
2020-09-08 22:14:09 +00:00
|
|
|
PathSource::Pat | PathSource::TupleStruct(..) => {
|
2020-07-10 00:42:07 +00:00
|
|
|
("", "pattern", Applicability::MachineApplicable)
|
|
|
|
}
|
|
|
|
_ => (": val", "literal", Applicability::HasPlaceholders),
|
|
|
|
};
|
|
|
|
let (fields, applicability) = match self.r.field_names.get(&def_id) {
|
|
|
|
Some(fields) => (
|
|
|
|
fields
|
|
|
|
.iter()
|
|
|
|
.map(|f| format!("{}{}", f.node, tail))
|
|
|
|
.collect::<Vec<String>>()
|
|
|
|
.join(", "),
|
|
|
|
applicability,
|
|
|
|
),
|
|
|
|
None => ("/* fields */".to_string(), Applicability::HasPlaceholders),
|
|
|
|
};
|
|
|
|
let pad = match self.r.field_names.get(&def_id) {
|
|
|
|
Some(fields) if fields.is_empty() => "",
|
|
|
|
_ => " ",
|
|
|
|
};
|
|
|
|
err.span_suggestion(
|
|
|
|
span,
|
|
|
|
&format!("use struct {} syntax instead", descr),
|
2020-11-07 22:38:11 +00:00
|
|
|
format!("{path_str} {{{pad}{fields}{pad}}}"),
|
2020-07-10 00:42:07 +00:00
|
|
|
applicability,
|
|
|
|
);
|
2019-09-22 18:27:55 +00:00
|
|
|
}
|
2020-08-10 04:24:05 +00:00
|
|
|
_ => {
|
|
|
|
err.span_label(span, fallback_label);
|
|
|
|
}
|
2019-08-07 23:39:02 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
match (res, source) {
|
2022-01-19 02:27:15 +00:00
|
|
|
(
|
|
|
|
Res::Def(DefKind::Macro(MacroKind::Bang), _),
|
|
|
|
PathSource::Expr(Some(Expr {
|
|
|
|
kind: ExprKind::Index(..) | ExprKind::Call(..), ..
|
|
|
|
}))
|
|
|
|
| PathSource::Struct,
|
|
|
|
) => {
|
2020-08-10 04:24:05 +00:00
|
|
|
err.span_label(span, fallback_label);
|
2020-03-22 22:36:54 +00:00
|
|
|
err.span_suggestion_verbose(
|
|
|
|
span.shrink_to_hi(),
|
2019-08-07 23:39:02 +00:00
|
|
|
"use `!` to invoke the macro",
|
2020-03-22 22:36:54 +00:00
|
|
|
"!".to_string(),
|
2019-08-07 23:39:02 +00:00
|
|
|
Applicability::MaybeIncorrect,
|
|
|
|
);
|
|
|
|
if path_str == "try" && span.rust_2015() {
|
2020-11-30 21:11:29 +00:00
|
|
|
err.note("if you want the `try` keyword, you need Rust 2018 or later");
|
2019-08-07 23:39:02 +00:00
|
|
|
}
|
|
|
|
}
|
2022-01-19 02:27:15 +00:00
|
|
|
(Res::Def(DefKind::Macro(MacroKind::Bang), _), _) => {
|
|
|
|
err.span_label(span, fallback_label);
|
|
|
|
}
|
2020-04-22 18:58:21 +00:00
|
|
|
(Res::Def(DefKind::TyAlias, def_id), PathSource::Trait(_)) => {
|
2019-08-07 23:39:02 +00:00
|
|
|
err.span_label(span, "type aliases cannot be used as traits");
|
2020-10-10 18:27:52 +00:00
|
|
|
if self.r.session.is_nightly_build() {
|
2020-04-22 18:58:21 +00:00
|
|
|
let msg = "you might have meant to use `#![feature(trait_alias)]` instead of a \
|
|
|
|
`type` alias";
|
2020-08-10 04:14:59 +00:00
|
|
|
if let Some(span) = self.def_span(def_id) {
|
2021-03-31 23:37:29 +00:00
|
|
|
if let Ok(snip) = self.r.session.source_map().span_to_snippet(span) {
|
|
|
|
// The span contains a type alias so we should be able to
|
|
|
|
// replace `type` with `trait`.
|
|
|
|
let snip = snip.replacen("type", "trait", 1);
|
|
|
|
err.span_suggestion(span, msg, snip, Applicability::MaybeIncorrect);
|
|
|
|
} else {
|
|
|
|
err.span_help(span, msg);
|
|
|
|
}
|
2020-04-22 18:58:21 +00:00
|
|
|
} else {
|
|
|
|
err.help(msg);
|
|
|
|
}
|
2019-08-07 23:39:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
(Res::Def(DefKind::Mod, _), PathSource::Expr(Some(parent))) => {
|
|
|
|
if !path_sep(err, &parent) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2020-07-10 00:42:07 +00:00
|
|
|
(
|
|
|
|
Res::Def(DefKind::Enum, def_id),
|
2020-09-08 22:14:09 +00:00
|
|
|
PathSource::TupleStruct(..) | PathSource::Expr(..),
|
2020-07-10 00:42:07 +00:00
|
|
|
) => {
|
2020-07-09 20:49:55 +00:00
|
|
|
if self
|
|
|
|
.diagnostic_metadata
|
|
|
|
.current_type_ascription
|
|
|
|
.last()
|
|
|
|
.map(|sp| {
|
|
|
|
self.r
|
|
|
|
.session
|
|
|
|
.parse_sess
|
|
|
|
.type_ascription_path_suggestions
|
|
|
|
.borrow()
|
|
|
|
.contains(&sp)
|
|
|
|
})
|
|
|
|
.unwrap_or(false)
|
|
|
|
{
|
|
|
|
err.delay_as_bug();
|
|
|
|
// We already suggested changing `:` into `::` during parsing.
|
|
|
|
return false;
|
|
|
|
}
|
2019-08-07 23:39:02 +00:00
|
|
|
|
2020-09-29 15:52:43 +00:00
|
|
|
self.suggest_using_enum_variant(err, source, def_id, span);
|
2019-09-20 16:22:43 +00:00
|
|
|
}
|
2021-09-27 07:28:38 +00:00
|
|
|
(Res::Def(DefKind::Struct, def_id), source) if ns == ValueNS => {
|
2020-10-26 14:36:48 +00:00
|
|
|
let (ctor_def, ctor_vis, fields) =
|
|
|
|
if let Some(struct_ctor) = self.r.struct_constructors.get(&def_id).cloned() {
|
2021-09-27 07:28:38 +00:00
|
|
|
if let PathSource::Expr(Some(parent)) = source {
|
|
|
|
if let ExprKind::Field(..) | ExprKind::MethodCall(..) = parent.kind {
|
|
|
|
bad_struct_syntax_suggestion(def_id);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2020-10-26 14:36:48 +00:00
|
|
|
struct_ctor
|
|
|
|
} else {
|
|
|
|
bad_struct_syntax_suggestion(def_id);
|
|
|
|
return true;
|
|
|
|
};
|
|
|
|
|
|
|
|
let is_accessible = self.r.is_accessible_from(ctor_vis, self.parent_scope.module);
|
|
|
|
if !is_expected(ctor_def) || is_accessible {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
let field_spans = match source {
|
|
|
|
// e.g. `if let Enum::TupleVariant(field1, field2) = _`
|
|
|
|
PathSource::TupleStruct(_, pattern_spans) => {
|
|
|
|
err.set_primary_message(
|
|
|
|
"cannot match against a tuple struct which contains private fields",
|
|
|
|
);
|
|
|
|
|
|
|
|
// Use spans of the tuple struct pattern.
|
|
|
|
Some(Vec::from(pattern_spans))
|
|
|
|
}
|
|
|
|
// e.g. `let _ = Enum::TupleVariant(field1, field2);`
|
|
|
|
_ if source.is_call() => {
|
|
|
|
err.set_primary_message(
|
|
|
|
"cannot initialize a tuple struct which contains private fields",
|
|
|
|
);
|
|
|
|
|
|
|
|
// Use spans of the tuple struct definition.
|
|
|
|
self.r
|
|
|
|
.field_names
|
|
|
|
.get(&def_id)
|
|
|
|
.map(|fields| fields.iter().map(|f| f.span).collect::<Vec<_>>())
|
|
|
|
}
|
|
|
|
_ => None,
|
|
|
|
};
|
|
|
|
|
|
|
|
if let Some(spans) =
|
|
|
|
field_spans.filter(|spans| spans.len() > 0 && fields.len() == spans.len())
|
2019-12-22 22:42:04 +00:00
|
|
|
{
|
2021-03-08 23:32:41 +00:00
|
|
|
let non_visible_spans: Vec<Span> = iter::zip(&fields, &spans)
|
2020-10-26 14:36:48 +00:00
|
|
|
.filter(|(vis, _)| {
|
|
|
|
!self.r.is_accessible_from(**vis, self.parent_scope.module)
|
|
|
|
})
|
|
|
|
.map(|(_, span)| *span)
|
|
|
|
.collect();
|
2020-09-08 22:14:09 +00:00
|
|
|
|
2020-10-26 14:36:48 +00:00
|
|
|
if non_visible_spans.len() > 0 {
|
|
|
|
let mut m: rustc_span::MultiSpan = non_visible_spans.clone().into();
|
|
|
|
non_visible_spans
|
|
|
|
.into_iter()
|
|
|
|
.for_each(|s| m.push_span_label(s, "private field".to_string()));
|
|
|
|
err.span_note(m, "constructor is not visible here due to private fields");
|
2019-08-07 23:39:02 +00:00
|
|
|
}
|
2020-10-26 14:36:48 +00:00
|
|
|
|
|
|
|
return true;
|
2019-08-07 23:39:02 +00:00
|
|
|
}
|
2020-10-26 14:36:48 +00:00
|
|
|
|
|
|
|
err.span_label(
|
|
|
|
span,
|
|
|
|
"constructor is not visible here due to private fields".to_string(),
|
|
|
|
);
|
2019-08-07 23:39:02 +00:00
|
|
|
}
|
2020-04-17 00:38:52 +00:00
|
|
|
(
|
|
|
|
Res::Def(
|
|
|
|
DefKind::Union | DefKind::Variant | DefKind::Ctor(_, CtorKind::Fictive),
|
|
|
|
def_id,
|
|
|
|
),
|
|
|
|
_,
|
|
|
|
) if ns == ValueNS => {
|
2019-09-22 18:27:55 +00:00
|
|
|
bad_struct_syntax_suggestion(def_id);
|
2019-08-07 23:39:02 +00:00
|
|
|
}
|
2021-05-02 09:46:29 +00:00
|
|
|
(Res::Def(DefKind::Ctor(_, CtorKind::Const), def_id), _) if ns == ValueNS => {
|
|
|
|
match source {
|
|
|
|
PathSource::Expr(_) | PathSource::TupleStruct(..) | PathSource::Pat => {
|
|
|
|
let span = find_span(&source, err);
|
|
|
|
if let Some(span) = self.def_span(def_id) {
|
|
|
|
err.span_label(span, &format!("`{}` defined here", path_str));
|
|
|
|
}
|
|
|
|
err.span_suggestion(
|
|
|
|
span,
|
2021-07-21 20:43:19 +00:00
|
|
|
&"use this syntax instead",
|
2022-02-03 22:12:25 +00:00
|
|
|
path_str.to_string(),
|
2021-05-02 09:46:29 +00:00
|
|
|
Applicability::MaybeIncorrect,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
_ => return false,
|
|
|
|
}
|
|
|
|
}
|
2019-09-22 18:27:55 +00:00
|
|
|
(Res::Def(DefKind::Ctor(_, CtorKind::Fn), def_id), _) if ns == ValueNS => {
|
2020-08-10 04:14:59 +00:00
|
|
|
if let Some(span) = self.def_span(def_id) {
|
2019-09-22 18:27:55 +00:00
|
|
|
err.span_label(span, &format!("`{}` defined here", path_str));
|
|
|
|
}
|
2021-03-26 19:32:37 +00:00
|
|
|
let fields = self.r.field_names.get(&def_id).map_or_else(
|
|
|
|
|| "/* fields */".to_string(),
|
|
|
|
|fields| vec!["_"; fields.len()].join(", "),
|
|
|
|
);
|
2020-08-10 00:52:29 +00:00
|
|
|
err.span_suggestion(
|
|
|
|
span,
|
|
|
|
"use the tuple variant pattern syntax instead",
|
|
|
|
format!("{}({})", path_str, fields),
|
|
|
|
Applicability::HasPlaceholders,
|
|
|
|
);
|
2019-09-21 15:36:12 +00:00
|
|
|
}
|
2022-02-09 11:03:27 +00:00
|
|
|
(Res::SelfTy { .. }, _) if ns == ValueNS => {
|
2019-08-07 23:39:02 +00:00
|
|
|
err.span_label(span, fallback_label);
|
|
|
|
err.note("can't use `Self` as a constructor, you must use the implemented struct");
|
|
|
|
}
|
2020-04-17 00:38:52 +00:00
|
|
|
(Res::Def(DefKind::TyAlias | DefKind::AssocTy, _), _) if ns == ValueNS => {
|
2019-08-07 23:39:02 +00:00
|
|
|
err.note("can't use a type alias as a constructor");
|
|
|
|
}
|
|
|
|
_ => return false,
|
|
|
|
}
|
|
|
|
true
|
|
|
|
}
|
|
|
|
|
2021-09-25 15:53:37 +00:00
|
|
|
/// Given the target `ident` and `kind`, search for the similarly named associated item
|
|
|
|
/// in `self.current_trait_ref`.
|
|
|
|
crate fn find_similarly_named_assoc_item(
|
|
|
|
&mut self,
|
|
|
|
ident: Symbol,
|
|
|
|
kind: &AssocItemKind,
|
|
|
|
) -> Option<Symbol> {
|
2022-01-18 19:26:13 +00:00
|
|
|
let Some((module, _)) = &self.current_trait_ref else {
|
2021-09-25 15:53:37 +00:00
|
|
|
return None;
|
|
|
|
};
|
|
|
|
if ident == kw::Underscore {
|
|
|
|
// We do nothing for `_`.
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
let resolutions = self.r.resolutions(module);
|
|
|
|
let targets = resolutions
|
|
|
|
.borrow()
|
|
|
|
.iter()
|
|
|
|
.filter_map(|(key, res)| res.borrow().binding.map(|binding| (key, binding.res())))
|
|
|
|
.filter(|(_, res)| match (kind, res) {
|
|
|
|
(AssocItemKind::Const(..), Res::Def(DefKind::AssocConst, _)) => true,
|
|
|
|
(AssocItemKind::Fn(_), Res::Def(DefKind::AssocFn, _)) => true,
|
|
|
|
(AssocItemKind::TyAlias(..), Res::Def(DefKind::AssocTy, _)) => true,
|
|
|
|
_ => false,
|
|
|
|
})
|
|
|
|
.map(|(key, _)| key.ident.name)
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
|
|
|
find_best_match_for_name(&targets, ident, None)
|
|
|
|
}
|
|
|
|
|
2019-12-22 22:42:04 +00:00
|
|
|
fn lookup_assoc_candidate<FilterFn>(
|
|
|
|
&mut self,
|
|
|
|
ident: Ident,
|
|
|
|
ns: Namespace,
|
|
|
|
filter_fn: FilterFn,
|
|
|
|
) -> Option<AssocSuggestion>
|
|
|
|
where
|
|
|
|
FilterFn: Fn(Res) -> bool,
|
2019-08-07 23:39:02 +00:00
|
|
|
{
|
|
|
|
fn extract_node_id(t: &Ty) -> Option<NodeId> {
|
2019-09-26 16:25:31 +00:00
|
|
|
match t.kind {
|
2019-08-07 23:39:02 +00:00
|
|
|
TyKind::Path(None, _) => Some(t.id),
|
|
|
|
TyKind::Rptr(_, ref mut_ty) => extract_node_id(&mut_ty.ty),
|
|
|
|
// This doesn't handle the remaining `Ty` variants as they are not
|
|
|
|
// that commonly the self_type, it might be interesting to provide
|
|
|
|
// support for those in future.
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fields are generally expected in the same contexts as locals.
|
|
|
|
if filter_fn(Res::Local(ast::DUMMY_NODE_ID)) {
|
2019-12-22 22:42:04 +00:00
|
|
|
if let Some(node_id) =
|
|
|
|
self.diagnostic_metadata.current_self_type.as_ref().and_then(extract_node_id)
|
2019-10-19 17:13:56 +00:00
|
|
|
{
|
2019-08-07 23:39:02 +00:00
|
|
|
// Look for a field with the same name in the current self_type.
|
2019-08-08 11:06:42 +00:00
|
|
|
if let Some(resolution) = self.r.partial_res_map.get(&node_id) {
|
2019-08-07 23:39:02 +00:00
|
|
|
match resolution.base_res() {
|
2020-04-17 00:38:52 +00:00
|
|
|
Res::Def(DefKind::Struct | DefKind::Union, did)
|
2019-12-22 22:42:04 +00:00
|
|
|
if resolution.unresolved_segments() == 0 =>
|
|
|
|
{
|
2019-08-08 11:06:42 +00:00
|
|
|
if let Some(field_names) = self.r.field_names.get(&did) {
|
2019-12-22 22:42:04 +00:00
|
|
|
if field_names
|
|
|
|
.iter()
|
|
|
|
.any(|&field_name| ident.name == field_name.node)
|
|
|
|
{
|
2019-08-07 23:39:02 +00:00
|
|
|
return Some(AssocSuggestion::Field);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-26 23:28:56 +00:00
|
|
|
if let Some(items) = self.diagnostic_metadata.current_trait_assoc_items {
|
2021-02-15 23:30:06 +00:00
|
|
|
for assoc_item in items {
|
2020-10-26 23:28:56 +00:00
|
|
|
if assoc_item.ident == ident {
|
|
|
|
return Some(match &assoc_item.kind {
|
|
|
|
ast::AssocItemKind::Const(..) => AssocSuggestion::AssocConst,
|
2021-11-07 08:43:49 +00:00
|
|
|
ast::AssocItemKind::Fn(box ast::Fn { sig, .. }) if sig.decl.has_self() => {
|
2020-10-26 23:28:56 +00:00
|
|
|
AssocSuggestion::MethodWithSelf
|
|
|
|
}
|
|
|
|
ast::AssocItemKind::Fn(..) => AssocSuggestion::AssocFn,
|
|
|
|
ast::AssocItemKind::TyAlias(..) => AssocSuggestion::AssocType,
|
|
|
|
ast::AssocItemKind::MacCall(_) => continue,
|
|
|
|
});
|
|
|
|
}
|
2019-08-07 23:39:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Look for associated items in the current trait.
|
|
|
|
if let Some((module, _)) = self.current_trait_ref {
|
2019-08-08 11:06:42 +00:00
|
|
|
if let Ok(binding) = self.r.resolve_ident_in_module(
|
2019-12-22 22:42:04 +00:00
|
|
|
ModuleOrUniformRoot::Module(module),
|
|
|
|
ident,
|
|
|
|
ns,
|
|
|
|
&self.parent_scope,
|
|
|
|
false,
|
|
|
|
module.span,
|
|
|
|
) {
|
2019-08-07 23:39:02 +00:00
|
|
|
let res = binding.res();
|
|
|
|
if filter_fn(res) {
|
2020-10-26 23:28:56 +00:00
|
|
|
if self.r.has_self.contains(&res.def_id()) {
|
|
|
|
return Some(AssocSuggestion::MethodWithSelf);
|
2019-08-07 23:39:02 +00:00
|
|
|
} else {
|
2020-10-26 23:28:56 +00:00
|
|
|
match res {
|
|
|
|
Res::Def(DefKind::AssocFn, _) => return Some(AssocSuggestion::AssocFn),
|
|
|
|
Res::Def(DefKind::AssocConst, _) => {
|
|
|
|
return Some(AssocSuggestion::AssocConst);
|
|
|
|
}
|
|
|
|
Res::Def(DefKind::AssocTy, _) => {
|
|
|
|
return Some(AssocSuggestion::AssocType);
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
2019-08-07 23:39:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
fn lookup_typo_candidate(
|
|
|
|
&mut self,
|
|
|
|
path: &[Segment],
|
|
|
|
ns: Namespace,
|
|
|
|
filter_fn: &impl Fn(Res) -> bool,
|
|
|
|
span: Span,
|
|
|
|
) -> Option<TypoSuggestion> {
|
|
|
|
let mut names = Vec::new();
|
|
|
|
if path.len() == 1 {
|
|
|
|
// Search in lexical scope.
|
|
|
|
// Walk backwards up the ribs in scope and collect candidates.
|
|
|
|
for rib in self.ribs[ns].iter().rev() {
|
|
|
|
// Locals and type parameters
|
2021-10-21 13:09:24 +00:00
|
|
|
for (ident, &res) in &rib.bindings {
|
2019-08-07 23:39:02 +00:00
|
|
|
if filter_fn(res) {
|
2021-08-04 21:29:30 +00:00
|
|
|
names.push(TypoSuggestion::typo_from_res(ident.name, res));
|
2019-08-07 23:39:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// Items in scope
|
|
|
|
if let RibKind::ModuleRibKind(module) = rib.kind {
|
|
|
|
// Items from this module
|
2019-08-15 22:18:14 +00:00
|
|
|
self.r.add_module_candidates(module, &mut names, &filter_fn);
|
2019-08-07 23:39:02 +00:00
|
|
|
|
|
|
|
if let ModuleKind::Block(..) = module.kind {
|
|
|
|
// We can see through blocks
|
|
|
|
} else {
|
|
|
|
// Items from the prelude
|
|
|
|
if !module.no_implicit_prelude {
|
2019-08-08 11:06:42 +00:00
|
|
|
let extern_prelude = self.r.extern_prelude.clone();
|
|
|
|
names.extend(extern_prelude.iter().flat_map(|(ident, _)| {
|
2020-07-05 07:39:15 +00:00
|
|
|
self.r.crate_loader.maybe_process_path_extern(ident.name).and_then(
|
|
|
|
|crate_id| {
|
2019-08-07 23:39:02 +00:00
|
|
|
let crate_mod = Res::Def(
|
|
|
|
DefKind::Mod,
|
2019-12-22 22:42:04 +00:00
|
|
|
DefId { krate: crate_id, index: CRATE_DEF_INDEX },
|
2019-08-07 23:39:02 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
if filter_fn(crate_mod) {
|
2021-08-04 21:29:30 +00:00
|
|
|
Some(TypoSuggestion::typo_from_res(
|
|
|
|
ident.name, crate_mod,
|
|
|
|
))
|
2019-08-07 23:39:02 +00:00
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
2020-07-05 07:39:15 +00:00
|
|
|
},
|
|
|
|
)
|
2019-08-07 23:39:02 +00:00
|
|
|
}));
|
|
|
|
|
2019-08-08 11:06:42 +00:00
|
|
|
if let Some(prelude) = self.r.prelude {
|
2019-08-15 22:18:14 +00:00
|
|
|
self.r.add_module_candidates(prelude, &mut names, &filter_fn);
|
2019-08-07 23:39:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Add primitive types to the mix
|
|
|
|
if filter_fn(Res::PrimTy(PrimTy::Bool)) {
|
2021-08-04 21:29:30 +00:00
|
|
|
names.extend(PrimTy::ALL.iter().map(|prim_ty| {
|
|
|
|
TypoSuggestion::typo_from_res(prim_ty.name(), Res::PrimTy(*prim_ty))
|
|
|
|
}))
|
2019-08-07 23:39:02 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Search in module.
|
|
|
|
let mod_path = &path[..path.len() - 1];
|
2021-11-07 09:33:27 +00:00
|
|
|
if let PathResult::Module(ModuleOrUniformRoot::Module(module)) =
|
2019-12-22 22:42:04 +00:00
|
|
|
self.resolve_path(mod_path, Some(TypeNS), false, span, CrateLint::No)
|
|
|
|
{
|
2021-11-07 09:33:27 +00:00
|
|
|
self.r.add_module_candidates(module, &mut names, &filter_fn);
|
2019-08-07 23:39:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let name = path[path.len() - 1].ident.name;
|
|
|
|
// Make sure error reporting is deterministic.
|
2021-12-14 21:32:21 +00:00
|
|
|
names.sort_by(|a, b| a.candidate.as_str().partial_cmp(b.candidate.as_str()).unwrap());
|
2019-08-07 23:39:02 +00: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 19:24:10 +00:00
|
|
|
&names.iter().map(|suggestion| suggestion.candidate).collect::<Vec<Symbol>>(),
|
2020-07-08 10:03:37 +00:00
|
|
|
name,
|
2019-08-07 23:39:02 +00:00
|
|
|
None,
|
|
|
|
) {
|
2019-12-22 22:42:04 +00:00
|
|
|
Some(found) if found != name => {
|
|
|
|
names.into_iter().find(|suggestion| suggestion.candidate == found)
|
|
|
|
}
|
2019-08-07 23:39:02 +00:00
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-15 12:11:02 +00:00
|
|
|
// Returns the name of the Rust type approximately corresponding to
|
|
|
|
// a type name in another programming language.
|
|
|
|
fn likely_rust_type(path: &[Segment]) -> Option<Symbol> {
|
|
|
|
let name = path[path.len() - 1].ident.as_str();
|
|
|
|
// Common Java types
|
2021-12-15 05:13:11 +00:00
|
|
|
Some(match name {
|
2021-03-15 12:11:02 +00:00
|
|
|
"byte" => sym::u8, // In Java, bytes are signed, but in practice one almost always wants unsigned bytes.
|
|
|
|
"short" => sym::i16,
|
|
|
|
"boolean" => sym::bool,
|
|
|
|
"int" => sym::i32,
|
|
|
|
"long" => sym::i64,
|
|
|
|
"float" => sym::f32,
|
|
|
|
"double" => sym::f64,
|
|
|
|
_ => return None,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2019-08-07 23:39:02 +00:00
|
|
|
/// Only used in a specific case of type ascription suggestions
|
|
|
|
fn get_colon_suggestion_span(&self, start: Span) -> Span {
|
2020-02-22 14:07:05 +00:00
|
|
|
let sm = self.r.session.source_map();
|
|
|
|
start.to(sm.next_point(start))
|
2019-08-07 23:39:02 +00:00
|
|
|
}
|
|
|
|
|
2020-08-10 03:29:39 +00:00
|
|
|
fn type_ascription_suggestion(&self, err: &mut DiagnosticBuilder<'_>, base_span: Span) -> bool {
|
2020-02-22 14:07:05 +00:00
|
|
|
let sm = self.r.session.source_map();
|
|
|
|
let base_snippet = sm.span_to_snippet(base_span);
|
2020-07-09 20:49:55 +00:00
|
|
|
if let Some(&sp) = self.diagnostic_metadata.current_type_ascription.last() {
|
|
|
|
if let Ok(snippet) = sm.span_to_snippet(sp) {
|
|
|
|
let len = snippet.trim_end().len() as u32;
|
|
|
|
if snippet.trim() == ":" {
|
|
|
|
let colon_sp =
|
|
|
|
sp.with_lo(sp.lo() + BytePos(len - 1)).with_hi(sp.lo() + BytePos(len));
|
|
|
|
let mut show_label = true;
|
|
|
|
if sm.is_multiline(sp) {
|
|
|
|
err.span_suggestion_short(
|
|
|
|
colon_sp,
|
|
|
|
"maybe you meant to write `;` here",
|
|
|
|
";".to_string(),
|
|
|
|
Applicability::MaybeIncorrect,
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
let after_colon_sp =
|
|
|
|
self.get_colon_suggestion_span(colon_sp.shrink_to_hi());
|
|
|
|
if snippet.len() == 1 {
|
|
|
|
// `foo:bar`
|
|
|
|
err.span_suggestion(
|
|
|
|
colon_sp,
|
|
|
|
"maybe you meant to write a path separator here",
|
|
|
|
"::".to_string(),
|
2019-08-07 23:39:02 +00:00
|
|
|
Applicability::MaybeIncorrect,
|
|
|
|
);
|
2020-07-09 20:49:55 +00:00
|
|
|
show_label = false;
|
|
|
|
if !self
|
|
|
|
.r
|
|
|
|
.session
|
|
|
|
.parse_sess
|
|
|
|
.type_ascription_path_suggestions
|
|
|
|
.borrow_mut()
|
|
|
|
.insert(colon_sp)
|
2019-08-07 23:39:02 +00:00
|
|
|
{
|
2020-07-09 20:49:55 +00:00
|
|
|
err.delay_as_bug();
|
2019-08-07 23:39:02 +00:00
|
|
|
}
|
2020-07-09 20:49:55 +00:00
|
|
|
}
|
|
|
|
if let Ok(base_snippet) = base_snippet {
|
|
|
|
let mut sp = after_colon_sp;
|
|
|
|
for _ in 0..100 {
|
|
|
|
// Try to find an assignment
|
|
|
|
sp = sm.next_point(sp);
|
|
|
|
let snippet = sm.span_to_snippet(sp.to(sm.next_point(sp)));
|
|
|
|
match snippet {
|
|
|
|
Ok(ref x) if x.as_str() == "=" => {
|
|
|
|
err.span_suggestion(
|
|
|
|
base_span,
|
|
|
|
"maybe you meant to write an assignment here",
|
|
|
|
format!("let {}", base_snippet),
|
|
|
|
Applicability::MaybeIncorrect,
|
|
|
|
);
|
|
|
|
show_label = false;
|
|
|
|
break;
|
2019-08-07 23:39:02 +00:00
|
|
|
}
|
2020-07-09 20:49:55 +00:00
|
|
|
Ok(ref x) if x.as_str() == "\n" => break,
|
|
|
|
Err(_) => break,
|
|
|
|
Ok(_) => {}
|
2019-08-07 23:39:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-07-09 20:49:55 +00:00
|
|
|
if show_label {
|
|
|
|
err.span_label(
|
|
|
|
base_span,
|
|
|
|
"expecting a type here because of type ascription",
|
|
|
|
);
|
|
|
|
}
|
2020-08-10 03:29:39 +00:00
|
|
|
return show_label;
|
2019-08-07 23:39:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-08-10 03:29:39 +00:00
|
|
|
false
|
2019-08-07 23:39:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn find_module(&mut self, def_id: DefId) -> Option<(Module<'a>, ImportSuggestion)> {
|
|
|
|
let mut result = None;
|
|
|
|
let mut seen_modules = FxHashSet::default();
|
2019-08-08 11:06:42 +00:00
|
|
|
let mut worklist = vec![(self.r.graph_root, Vec::new())];
|
2019-08-07 23:39:02 +00:00
|
|
|
|
|
|
|
while let Some((in_module, path_segments)) = worklist.pop() {
|
|
|
|
// abort if the module is already found
|
2019-12-22 22:42:04 +00:00
|
|
|
if result.is_some() {
|
|
|
|
break;
|
|
|
|
}
|
2019-08-07 23:39:02 +00:00
|
|
|
|
2019-10-05 20:02:34 +00:00
|
|
|
in_module.for_each_child(self.r, |_, ident, _, name_binding| {
|
2019-08-07 23:39:02 +00:00
|
|
|
// abort if the module is already found or if name_binding is private external
|
|
|
|
if result.is_some() || !name_binding.vis.is_visible_locally() {
|
2019-12-22 22:42:04 +00:00
|
|
|
return;
|
2019-08-07 23:39:02 +00:00
|
|
|
}
|
|
|
|
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));
|
2021-09-26 16:29:53 +00:00
|
|
|
let module_def_id = module.def_id();
|
2019-08-07 23:39:02 +00:00
|
|
|
if module_def_id == def_id {
|
2020-08-21 22:51:23 +00:00
|
|
|
let path =
|
|
|
|
Path { span: name_binding.span, segments: path_segments, tokens: None };
|
2020-05-04 22:12:06 +00:00
|
|
|
result = Some((
|
|
|
|
module,
|
2020-06-13 17:58:46 +00:00
|
|
|
ImportSuggestion {
|
|
|
|
did: Some(def_id),
|
|
|
|
descr: "module",
|
|
|
|
path,
|
|
|
|
accessible: true,
|
2021-10-26 00:04:35 +00:00
|
|
|
note: None,
|
2020-06-13 17:58:46 +00:00
|
|
|
},
|
2020-05-04 22:12:06 +00:00
|
|
|
));
|
2019-08-07 23:39:02 +00:00
|
|
|
} else {
|
|
|
|
// add the module to the lookup
|
|
|
|
if seen_modules.insert(module_def_id) {
|
|
|
|
worklist.push((module, path_segments));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
result
|
|
|
|
}
|
|
|
|
|
2020-09-29 15:52:43 +00:00
|
|
|
fn collect_enum_ctors(&mut self, def_id: DefId) -> Option<Vec<(Path, DefId, CtorKind)>> {
|
2019-08-07 23:39:02 +00:00
|
|
|
self.find_module(def_id).map(|(enum_module, enum_import_suggestion)| {
|
|
|
|
let mut variants = Vec::new();
|
2019-10-05 20:02:34 +00:00
|
|
|
enum_module.for_each_child(self.r, |_, ident, _, name_binding| {
|
2020-09-29 15:52:43 +00:00
|
|
|
if let Res::Def(DefKind::Ctor(CtorOf::Variant, kind), def_id) = name_binding.res() {
|
2019-08-07 23:39:02 +00:00
|
|
|
let mut segms = enum_import_suggestion.path.segments.clone();
|
|
|
|
segms.push(ast::PathSegment::from_ident(ident));
|
2020-09-29 15:52:43 +00:00
|
|
|
let path = Path { span: name_binding.span, segments: segms, tokens: None };
|
|
|
|
variants.push((path, def_id, kind));
|
2019-08-07 23:39:02 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
variants
|
|
|
|
})
|
|
|
|
}
|
2020-01-22 07:01:21 +00:00
|
|
|
|
2020-09-29 15:52:43 +00:00
|
|
|
/// Adds a suggestion for using an enum's variant when an enum is used instead.
|
|
|
|
fn suggest_using_enum_variant(
|
|
|
|
&mut self,
|
|
|
|
err: &mut DiagnosticBuilder<'a>,
|
|
|
|
source: PathSource<'_>,
|
|
|
|
def_id: DefId,
|
|
|
|
span: Span,
|
|
|
|
) {
|
2022-02-18 23:48:49 +00:00
|
|
|
let Some(variants) = self.collect_enum_ctors(def_id) else {
|
|
|
|
err.note("you might have meant to use one of the enum's variants");
|
|
|
|
return;
|
2020-09-29 15:52:43 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
let suggest_only_tuple_variants =
|
|
|
|
matches!(source, PathSource::TupleStruct(..)) || source.is_call();
|
2020-10-12 15:43:49 +00:00
|
|
|
if suggest_only_tuple_variants {
|
2020-09-29 15:52:43 +00:00
|
|
|
// Suggest only tuple variants regardless of whether they have fields and do not
|
2021-10-17 10:04:01 +00:00
|
|
|
// suggest path with added parentheses.
|
2021-11-06 17:42:07 +00:00
|
|
|
let suggestable_variants = variants
|
2020-09-29 15:52:43 +00:00
|
|
|
.iter()
|
|
|
|
.filter(|(.., kind)| *kind == CtorKind::Fn)
|
|
|
|
.map(|(variant, ..)| path_names_to_string(variant))
|
2020-10-12 15:43:49 +00:00
|
|
|
.collect::<Vec<_>>();
|
2020-09-29 15:52:43 +00:00
|
|
|
|
2020-10-12 15:43:49 +00:00
|
|
|
let non_suggestable_variant_count = variants.len() - suggestable_variants.len();
|
2020-09-29 15:52:43 +00:00
|
|
|
|
2020-10-12 15:52:51 +00:00
|
|
|
let source_msg = if source.is_call() {
|
|
|
|
"to construct"
|
|
|
|
} else if matches!(source, PathSource::TupleStruct(..)) {
|
|
|
|
"to match against"
|
|
|
|
} else {
|
|
|
|
unreachable!()
|
|
|
|
};
|
|
|
|
|
2020-10-12 15:43:49 +00:00
|
|
|
if !suggestable_variants.is_empty() {
|
|
|
|
let msg = if non_suggestable_variant_count == 0 && suggestable_variants.len() == 1 {
|
2020-10-12 15:52:51 +00:00
|
|
|
format!("try {} the enum's variant", source_msg)
|
2020-10-12 15:43:49 +00:00
|
|
|
} else {
|
2020-10-12 15:52:51 +00:00
|
|
|
format!("try {} one of the enum's variants", source_msg)
|
2020-10-12 15:43:49 +00:00
|
|
|
};
|
2020-09-29 15:52:43 +00:00
|
|
|
|
2020-10-12 15:43:49 +00:00
|
|
|
err.span_suggestions(
|
|
|
|
span,
|
2020-10-12 15:52:51 +00:00
|
|
|
&msg,
|
2021-11-06 17:42:07 +00:00
|
|
|
suggestable_variants.into_iter(),
|
2020-10-12 15:43:49 +00:00
|
|
|
Applicability::MaybeIncorrect,
|
|
|
|
);
|
|
|
|
}
|
2020-09-29 15:52:43 +00:00
|
|
|
|
|
|
|
// If the enum has no tuple variants..
|
|
|
|
if non_suggestable_variant_count == variants.len() {
|
|
|
|
err.help(&format!("the enum has no tuple variants {}", source_msg));
|
|
|
|
}
|
|
|
|
|
|
|
|
// If there are also non-tuple variants..
|
|
|
|
if non_suggestable_variant_count == 1 {
|
|
|
|
err.help(&format!(
|
|
|
|
"you might have meant {} the enum's non-tuple variant",
|
|
|
|
source_msg
|
|
|
|
));
|
|
|
|
} else if non_suggestable_variant_count >= 1 {
|
|
|
|
err.help(&format!(
|
|
|
|
"you might have meant {} one of the enum's non-tuple variants",
|
|
|
|
source_msg
|
|
|
|
));
|
|
|
|
}
|
|
|
|
} else {
|
2020-10-12 15:43:49 +00:00
|
|
|
let needs_placeholder = |def_id: DefId, kind: CtorKind| {
|
2021-01-11 19:45:33 +00:00
|
|
|
let has_no_fields = self.r.field_names.get(&def_id).map_or(false, |f| f.is_empty());
|
2020-10-12 15:43:49 +00:00
|
|
|
match kind {
|
|
|
|
CtorKind::Const => false,
|
|
|
|
CtorKind::Fn | CtorKind::Fictive if has_no_fields => false,
|
|
|
|
_ => true,
|
2020-09-29 15:52:43 +00:00
|
|
|
}
|
2020-10-12 15:43:49 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
let mut suggestable_variants = variants
|
|
|
|
.iter()
|
|
|
|
.filter(|(_, def_id, kind)| !needs_placeholder(*def_id, *kind))
|
|
|
|
.map(|(variant, _, kind)| (path_names_to_string(variant), kind))
|
|
|
|
.map(|(variant, kind)| match kind {
|
|
|
|
CtorKind::Const => variant,
|
|
|
|
CtorKind::Fn => format!("({}())", variant),
|
|
|
|
CtorKind::Fictive => format!("({} {{}})", variant),
|
|
|
|
})
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
|
|
|
if !suggestable_variants.is_empty() {
|
|
|
|
let msg = if suggestable_variants.len() == 1 {
|
|
|
|
"you might have meant to use the following enum variant"
|
|
|
|
} else {
|
|
|
|
"you might have meant to use one of the following enum variants"
|
|
|
|
};
|
|
|
|
|
|
|
|
err.span_suggestions(
|
|
|
|
span,
|
|
|
|
msg,
|
|
|
|
suggestable_variants.drain(..),
|
|
|
|
Applicability::MaybeIncorrect,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-11-06 17:42:07 +00:00
|
|
|
let suggestable_variants_with_placeholders = variants
|
2020-10-12 15:43:49 +00:00
|
|
|
.iter()
|
|
|
|
.filter(|(_, def_id, kind)| needs_placeholder(*def_id, *kind))
|
|
|
|
.map(|(variant, _, kind)| (path_names_to_string(variant), kind))
|
|
|
|
.filter_map(|(variant, kind)| match kind {
|
|
|
|
CtorKind::Fn => Some(format!("({}(/* fields */))", variant)),
|
|
|
|
CtorKind::Fictive => Some(format!("({} {{ /* fields */ }})", variant)),
|
|
|
|
_ => None,
|
|
|
|
})
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
|
|
|
if !suggestable_variants_with_placeholders.is_empty() {
|
|
|
|
let msg = match (
|
|
|
|
suggestable_variants.is_empty(),
|
|
|
|
suggestable_variants_with_placeholders.len(),
|
|
|
|
) {
|
|
|
|
(true, 1) => "the following enum variant is available",
|
|
|
|
(true, _) => "the following enum variants are available",
|
|
|
|
(false, 1) => "alternatively, the following enum variant is available",
|
|
|
|
(false, _) => "alternatively, the following enum variants are also available",
|
|
|
|
};
|
|
|
|
|
|
|
|
err.span_suggestions(
|
|
|
|
span,
|
|
|
|
msg,
|
2021-11-06 17:42:07 +00:00
|
|
|
suggestable_variants_with_placeholders.into_iter(),
|
2020-10-12 15:43:49 +00:00
|
|
|
Applicability::HasPlaceholders,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
if def_id.is_local() {
|
|
|
|
if let Some(span) = self.def_span(def_id) {
|
|
|
|
err.span_note(span, "the enum is defined here");
|
2020-09-29 15:52:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-22 07:01:21 +00:00
|
|
|
crate fn report_missing_type_error(
|
|
|
|
&self,
|
|
|
|
path: &[Segment],
|
|
|
|
) -> Option<(Span, &'static str, String, Applicability)> {
|
2020-06-13 19:08:32 +00:00
|
|
|
let (ident, span) = match path {
|
2020-06-17 23:29:03 +00:00
|
|
|
[segment] if !segment.has_generic_args => {
|
|
|
|
(segment.ident.to_string(), segment.ident.span)
|
|
|
|
}
|
2020-01-22 07:01:21 +00:00
|
|
|
_ => return None,
|
|
|
|
};
|
2020-06-13 19:08:32 +00:00
|
|
|
let mut iter = ident.chars().map(|c| c.is_uppercase());
|
|
|
|
let single_uppercase_char =
|
|
|
|
matches!(iter.next(), Some(true)) && matches!(iter.next(), None);
|
|
|
|
if !self.diagnostic_metadata.currently_processing_generics && !single_uppercase_char {
|
|
|
|
return None;
|
|
|
|
}
|
2021-05-05 16:59:37 +00:00
|
|
|
match (self.diagnostic_metadata.current_item, single_uppercase_char, self.diagnostic_metadata.currently_processing_generics) {
|
|
|
|
(Some(Item { kind: ItemKind::Fn(..), ident, .. }), _, _) if ident.name == sym::main => {
|
2020-01-22 07:01:21 +00:00
|
|
|
// Ignore `fn main()` as we don't want to suggest `fn main<T>()`
|
|
|
|
}
|
2020-06-13 19:08:32 +00:00
|
|
|
(
|
|
|
|
Some(Item {
|
|
|
|
kind:
|
|
|
|
kind @ ItemKind::Fn(..)
|
|
|
|
| kind @ ItemKind::Enum(..)
|
|
|
|
| kind @ ItemKind::Struct(..)
|
|
|
|
| kind @ ItemKind::Union(..),
|
|
|
|
..
|
|
|
|
}),
|
2021-05-05 16:59:37 +00:00
|
|
|
true, _
|
2020-06-13 19:08:32 +00:00
|
|
|
)
|
2021-05-05 16:59:37 +00:00
|
|
|
// Without the 2nd `true`, we'd suggest `impl <T>` for `impl T` when a type `T` isn't found
|
|
|
|
| (Some(Item { kind: kind @ ItemKind::Impl(..), .. }), true, true)
|
|
|
|
| (Some(Item { kind, .. }), false, _) => {
|
2020-01-22 07:01:21 +00:00
|
|
|
// Likely missing type parameter.
|
|
|
|
if let Some(generics) = kind.generics() {
|
2020-06-13 19:08:32 +00:00
|
|
|
if span.overlaps(generics.span) {
|
|
|
|
// Avoid the following:
|
|
|
|
// error[E0405]: cannot find trait `A` in this scope
|
|
|
|
// --> $DIR/typo-suggestion-named-underscore.rs:CC:LL
|
|
|
|
// |
|
|
|
|
// L | fn foo<T: A>(x: T) {} // Shouldn't suggest underscore
|
|
|
|
// | ^- help: you might be missing a type parameter: `, A`
|
|
|
|
// | |
|
|
|
|
// | not found in this scope
|
|
|
|
return None;
|
|
|
|
}
|
2020-01-22 07:01:21 +00:00
|
|
|
let msg = "you might be missing a type parameter";
|
|
|
|
let (span, sugg) = if let [.., param] = &generics.params[..] {
|
|
|
|
let span = if let [.., bound] = ¶m.bounds[..] {
|
|
|
|
bound.span()
|
2021-05-06 12:33:23 +00:00
|
|
|
} else if let GenericParam {
|
|
|
|
kind: GenericParamKind::Const { ty, kw_span: _, default }, ..
|
|
|
|
} = param {
|
|
|
|
default.as_ref().map(|def| def.value.span).unwrap_or(ty.span)
|
2020-01-22 07:01:21 +00:00
|
|
|
} else {
|
|
|
|
param.ident.span
|
|
|
|
};
|
|
|
|
(span, format!(", {}", ident))
|
|
|
|
} else {
|
|
|
|
(generics.span, format!("<{}>", ident))
|
|
|
|
};
|
|
|
|
// Do not suggest if this is coming from macro expansion.
|
2021-11-20 18:46:36 +00:00
|
|
|
if span.can_be_used_for_suggestions() {
|
2020-01-22 07:01:21 +00:00
|
|
|
return Some((
|
|
|
|
span.shrink_to_hi(),
|
|
|
|
msg,
|
|
|
|
sugg,
|
|
|
|
Applicability::MaybeIncorrect,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
None
|
|
|
|
}
|
2020-06-25 14:16:38 +00:00
|
|
|
|
|
|
|
/// Given the target `label`, search the `rib_index`th label rib for similarly named labels,
|
|
|
|
/// optionally returning the closest match and whether it is reachable.
|
|
|
|
crate fn suggestion_for_label_in_rib(
|
|
|
|
&self,
|
|
|
|
rib_index: usize,
|
|
|
|
label: Ident,
|
|
|
|
) -> Option<LabelSuggestion> {
|
|
|
|
// Are ribs from this `rib_index` within scope?
|
|
|
|
let within_scope = self.is_label_valid_from_rib(rib_index);
|
|
|
|
|
|
|
|
let rib = &self.label_ribs[rib_index];
|
|
|
|
let names = rib
|
|
|
|
.bindings
|
|
|
|
.iter()
|
|
|
|
.filter(|(id, _)| id.span.ctxt() == label.span.ctxt())
|
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 19:24:10 +00:00
|
|
|
.map(|(id, _)| id.name)
|
|
|
|
.collect::<Vec<Symbol>>();
|
2020-06-25 14:16:38 +00:00
|
|
|
|
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 19:24:10 +00:00
|
|
|
find_best_match_for_name(&names, label.name, None).map(|symbol| {
|
2020-06-25 14:16:38 +00:00
|
|
|
// Upon finding a similar name, get the ident that it was from - the span
|
|
|
|
// contained within helps make a useful diagnostic. In addition, determine
|
|
|
|
// whether this candidate is within scope.
|
|
|
|
let (ident, _) = rib.bindings.iter().find(|(ident, _)| ident.name == symbol).unwrap();
|
|
|
|
(*ident, within_scope)
|
|
|
|
})
|
|
|
|
}
|
2019-08-07 23:39:02 +00:00
|
|
|
}
|
2020-02-24 18:44:55 +00:00
|
|
|
|
|
|
|
impl<'tcx> LifetimeContext<'_, 'tcx> {
|
|
|
|
crate fn report_missing_lifetime_specifiers(
|
|
|
|
&self,
|
2021-05-07 17:44:32 +00:00
|
|
|
spans: Vec<Span>,
|
2020-02-24 18:44:55 +00:00
|
|
|
count: usize,
|
|
|
|
) -> DiagnosticBuilder<'tcx> {
|
|
|
|
struct_span_err!(
|
|
|
|
self.tcx.sess,
|
2021-05-07 17:44:32 +00:00
|
|
|
spans,
|
2020-02-24 18:44:55 +00:00
|
|
|
E0106,
|
|
|
|
"missing lifetime specifier{}",
|
|
|
|
pluralize!(count)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
crate fn emit_undeclared_lifetime_error(&self, lifetime_ref: &hir::Lifetime) {
|
|
|
|
let mut err = struct_span_err!(
|
|
|
|
self.tcx.sess,
|
|
|
|
lifetime_ref.span,
|
|
|
|
E0261,
|
|
|
|
"use of undeclared lifetime name `{}`",
|
|
|
|
lifetime_ref
|
|
|
|
);
|
|
|
|
err.span_label(lifetime_ref.span, "undeclared lifetime");
|
2020-07-08 23:12:59 +00:00
|
|
|
let mut suggests_in_band = false;
|
2021-11-03 04:19:06 +00:00
|
|
|
let mut suggested_spans = vec![];
|
2020-02-24 18:44:55 +00:00
|
|
|
for missing in &self.missing_named_lifetime_spots {
|
|
|
|
match missing {
|
|
|
|
MissingLifetimeSpot::Generics(generics) => {
|
2020-12-24 01:55:21 +00:00
|
|
|
let (span, sugg) = if let Some(param) = generics.params.iter().find(|p| {
|
2021-01-09 17:00:45 +00:00
|
|
|
!matches!(
|
|
|
|
p.kind,
|
2021-11-18 05:25:27 +00:00
|
|
|
hir::GenericParamKind::Type { synthetic: true, .. }
|
|
|
|
| hir::GenericParamKind::Lifetime {
|
|
|
|
kind: hir::LifetimeParamKind::Elided,
|
|
|
|
}
|
2021-01-09 17:00:45 +00:00
|
|
|
)
|
2020-12-24 01:55:21 +00:00
|
|
|
}) {
|
2020-02-24 18:44:55 +00:00
|
|
|
(param.span.shrink_to_lo(), format!("{}, ", lifetime_ref))
|
|
|
|
} else {
|
2020-07-08 23:12:59 +00:00
|
|
|
suggests_in_band = true;
|
2020-02-24 18:44:55 +00:00
|
|
|
(generics.span, format!("<{}>", lifetime_ref))
|
|
|
|
};
|
2021-11-03 04:19:06 +00:00
|
|
|
if suggested_spans.contains(&span) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
suggested_spans.push(span);
|
2021-11-20 18:46:36 +00:00
|
|
|
if span.can_be_used_for_suggestions() {
|
2021-03-03 02:24:13 +00:00
|
|
|
err.span_suggestion(
|
|
|
|
span,
|
|
|
|
&format!("consider introducing lifetime `{}` here", lifetime_ref),
|
|
|
|
sugg,
|
|
|
|
Applicability::MaybeIncorrect,
|
|
|
|
);
|
|
|
|
}
|
2020-02-24 18:44:55 +00:00
|
|
|
}
|
|
|
|
MissingLifetimeSpot::HigherRanked { span, span_type } => {
|
|
|
|
err.span_suggestion(
|
|
|
|
*span,
|
|
|
|
&format!(
|
|
|
|
"consider making the {} lifetime-generic with a new `{}` lifetime",
|
|
|
|
span_type.descr(),
|
|
|
|
lifetime_ref
|
|
|
|
),
|
|
|
|
span_type.suggestion(&lifetime_ref.to_string()),
|
|
|
|
Applicability::MaybeIncorrect,
|
|
|
|
);
|
|
|
|
err.note(
|
|
|
|
"for more information on higher-ranked polymorphism, visit \
|
2021-03-03 02:24:13 +00:00
|
|
|
https://doc.rust-lang.org/nomicon/hrtb.html",
|
2020-02-24 18:44:55 +00:00
|
|
|
);
|
|
|
|
}
|
2020-08-11 20:02:14 +00:00
|
|
|
_ => {}
|
2020-02-24 18:44:55 +00:00
|
|
|
}
|
|
|
|
}
|
2020-10-10 18:27:52 +00:00
|
|
|
if self.tcx.sess.is_nightly_build()
|
2020-07-08 23:12:59 +00:00
|
|
|
&& !self.tcx.features().in_band_lifetimes
|
|
|
|
&& suggests_in_band
|
|
|
|
{
|
|
|
|
err.help(
|
2020-07-09 22:24:18 +00:00
|
|
|
"if you want to experiment with in-band lifetime bindings, \
|
2021-03-03 02:24:13 +00:00
|
|
|
add `#![feature(in_band_lifetimes)]` to the crate attributes",
|
2020-07-08 23:12:59 +00:00
|
|
|
);
|
|
|
|
}
|
2020-02-24 18:44:55 +00:00
|
|
|
err.emit();
|
|
|
|
}
|
|
|
|
|
2021-10-31 16:11:28 +00:00
|
|
|
/// Returns whether to add `'static` lifetime to the suggested lifetime list.
|
|
|
|
crate fn report_elision_failure(
|
|
|
|
&mut self,
|
|
|
|
db: &mut DiagnosticBuilder<'_>,
|
|
|
|
params: &[ElisionFailureInfo],
|
|
|
|
) -> bool {
|
|
|
|
let mut m = String::new();
|
|
|
|
let len = params.len();
|
|
|
|
|
|
|
|
let elided_params: Vec<_> =
|
|
|
|
params.iter().cloned().filter(|info| info.lifetime_count > 0).collect();
|
|
|
|
|
|
|
|
let elided_len = elided_params.len();
|
|
|
|
|
|
|
|
for (i, info) in elided_params.into_iter().enumerate() {
|
|
|
|
let ElisionFailureInfo { parent, index, lifetime_count: n, have_bound_regions, span } =
|
|
|
|
info;
|
|
|
|
|
|
|
|
db.span_label(span, "");
|
|
|
|
let help_name = if let Some(ident) =
|
|
|
|
parent.and_then(|body| self.tcx.hir().body(body).params[index].pat.simple_ident())
|
|
|
|
{
|
|
|
|
format!("`{}`", ident)
|
|
|
|
} else {
|
|
|
|
format!("argument {}", index + 1)
|
|
|
|
};
|
|
|
|
|
|
|
|
m.push_str(
|
|
|
|
&(if n == 1 {
|
|
|
|
help_name
|
|
|
|
} else {
|
|
|
|
format!(
|
|
|
|
"one of {}'s {} {}lifetimes",
|
|
|
|
help_name,
|
|
|
|
n,
|
|
|
|
if have_bound_regions { "free " } else { "" }
|
|
|
|
)
|
|
|
|
})[..],
|
|
|
|
);
|
|
|
|
|
|
|
|
if elided_len == 2 && i == 0 {
|
|
|
|
m.push_str(" or ");
|
|
|
|
} else if i + 2 == elided_len {
|
|
|
|
m.push_str(", or ");
|
|
|
|
} else if i != elided_len - 1 {
|
|
|
|
m.push_str(", ");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len == 0 {
|
|
|
|
db.help(
|
|
|
|
"this function's return type contains a borrowed value, \
|
|
|
|
but there is no value for it to be borrowed from",
|
|
|
|
);
|
|
|
|
true
|
|
|
|
} else if elided_len == 0 {
|
|
|
|
db.help(
|
|
|
|
"this function's return type contains a borrowed value with \
|
|
|
|
an elided lifetime, but the lifetime cannot be derived from \
|
|
|
|
the arguments",
|
|
|
|
);
|
|
|
|
true
|
|
|
|
} else if elided_len == 1 {
|
|
|
|
db.help(&format!(
|
|
|
|
"this function's return type contains a borrowed value, \
|
|
|
|
but the signature does not say which {} it is borrowed from",
|
|
|
|
m
|
|
|
|
));
|
|
|
|
false
|
|
|
|
} else {
|
|
|
|
db.help(&format!(
|
|
|
|
"this function's return type contains a borrowed value, \
|
|
|
|
but the signature does not say whether it is borrowed from {}",
|
|
|
|
m
|
|
|
|
));
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-11 13:04:57 +00:00
|
|
|
crate fn report_elided_lifetime_in_ty(&self, lifetime_refs: &[&hir::Lifetime]) {
|
2021-10-31 17:39:39 +00:00
|
|
|
let Some(missing_lifetime) = lifetime_refs.iter().find(|lt| {
|
2021-11-26 22:07:21 +00:00
|
|
|
lt.name == hir::LifetimeName::Implicit(true)
|
2021-10-31 17:39:39 +00:00
|
|
|
}) else { return };
|
|
|
|
|
|
|
|
let mut spans: Vec<_> = lifetime_refs.iter().map(|lt| lt.span).collect();
|
|
|
|
spans.sort();
|
|
|
|
let mut spans_dedup = spans.clone();
|
|
|
|
spans_dedup.dedup();
|
|
|
|
let spans_with_counts: Vec<_> = spans_dedup
|
|
|
|
.into_iter()
|
|
|
|
.map(|sp| (sp, spans.iter().filter(|nsp| *nsp == &sp).count()))
|
|
|
|
.collect();
|
2021-07-11 13:04:57 +00:00
|
|
|
|
2021-10-31 17:39:39 +00:00
|
|
|
self.tcx.struct_span_lint_hir(
|
|
|
|
rustc_session::lint::builtin::ELIDED_LIFETIMES_IN_PATHS,
|
|
|
|
missing_lifetime.hir_id,
|
|
|
|
spans,
|
|
|
|
|lint| {
|
|
|
|
let mut db = lint.build("hidden lifetime parameters in types are deprecated");
|
|
|
|
self.add_missing_lifetime_specifiers_label(
|
|
|
|
&mut db,
|
|
|
|
spans_with_counts,
|
|
|
|
&FxHashSet::from_iter([kw::UnderscoreLifetime]),
|
|
|
|
Vec::new(),
|
|
|
|
&[],
|
|
|
|
);
|
|
|
|
db.emit()
|
|
|
|
},
|
|
|
|
);
|
2021-07-11 13:04:57 +00:00
|
|
|
}
|
|
|
|
|
2021-08-22 14:20:58 +00:00
|
|
|
// FIXME(const_generics): This patches over an ICE caused by non-'static lifetimes in const
|
2020-07-05 03:43:48 +00:00
|
|
|
// generics. We are disallowing this until we can decide on how we want to handle non-'static
|
|
|
|
// lifetimes in const generics. See issue #74052 for discussion.
|
|
|
|
crate fn emit_non_static_lt_in_const_generic_error(&self, lifetime_ref: &hir::Lifetime) {
|
|
|
|
let mut err = struct_span_err!(
|
|
|
|
self.tcx.sess,
|
|
|
|
lifetime_ref.span,
|
|
|
|
E0771,
|
|
|
|
"use of non-static lifetime `{}` in const generic",
|
|
|
|
lifetime_ref
|
|
|
|
);
|
|
|
|
err.note(
|
|
|
|
"for more information, see issue #74052 \
|
|
|
|
<https://github.com/rust-lang/rust/issues/74052>",
|
|
|
|
);
|
|
|
|
err.emit();
|
|
|
|
}
|
|
|
|
|
2020-02-24 18:44:55 +00:00
|
|
|
crate fn is_trait_ref_fn_scope(&mut self, trait_ref: &'tcx hir::PolyTraitRef<'tcx>) -> bool {
|
|
|
|
if let def::Res::Def(_, did) = trait_ref.trait_ref.path.res {
|
|
|
|
if [
|
|
|
|
self.tcx.lang_items().fn_once_trait(),
|
|
|
|
self.tcx.lang_items().fn_trait(),
|
|
|
|
self.tcx.lang_items().fn_mut_trait(),
|
|
|
|
]
|
|
|
|
.contains(&Some(did))
|
|
|
|
{
|
|
|
|
let (span, span_type) = match &trait_ref.bound_generic_params {
|
|
|
|
[] => (trait_ref.span.shrink_to_lo(), ForLifetimeSpanType::BoundEmpty),
|
|
|
|
[.., bound] => (bound.span.shrink_to_hi(), ForLifetimeSpanType::BoundTail),
|
|
|
|
};
|
|
|
|
self.missing_named_lifetime_spots
|
|
|
|
.push(MissingLifetimeSpot::HigherRanked { span, span_type });
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
false
|
|
|
|
}
|
|
|
|
|
|
|
|
crate fn add_missing_lifetime_specifiers_label(
|
|
|
|
&self,
|
|
|
|
err: &mut DiagnosticBuilder<'_>,
|
2021-05-12 09:36:38 +00:00
|
|
|
mut spans_with_counts: Vec<(Span, usize)>,
|
2020-08-10 23:42:57 +00:00
|
|
|
lifetime_names: &FxHashSet<Symbol>,
|
|
|
|
lifetime_spans: Vec<Span>,
|
2020-02-24 18:44:55 +00:00
|
|
|
params: &[ElisionFailureInfo],
|
|
|
|
) {
|
2021-05-09 20:31:49 +00:00
|
|
|
let snippets: Vec<Option<String>> = spans_with_counts
|
2021-05-07 17:44:32 +00:00
|
|
|
.iter()
|
2021-05-09 20:31:49 +00:00
|
|
|
.map(|(span, _)| self.tcx.sess.source_map().span_to_snippet(*span).ok())
|
2021-05-07 17:44:32 +00:00
|
|
|
.collect();
|
2020-02-24 18:44:55 +00:00
|
|
|
|
2021-05-12 09:36:38 +00:00
|
|
|
// Empty generics are marked with a span of "<", but since from now on
|
|
|
|
// that information is in the snippets it can be removed from the spans.
|
|
|
|
for ((span, _), snippet) in spans_with_counts.iter_mut().zip(&snippets) {
|
|
|
|
if snippet.as_deref() == Some("<") {
|
|
|
|
*span = span.shrink_to_hi();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for &(span, count) in &spans_with_counts {
|
2021-05-07 17:44:32 +00:00
|
|
|
err.span_label(
|
2021-05-12 09:36:38 +00:00
|
|
|
span,
|
2021-05-07 17:44:32 +00:00
|
|
|
format!(
|
|
|
|
"expected {} lifetime parameter{}",
|
2021-05-12 09:36:38 +00:00
|
|
|
if count == 1 { "named".to_string() } else { count.to_string() },
|
|
|
|
pluralize!(count),
|
2021-05-07 17:44:32 +00:00
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
2020-08-10 23:42:57 +00:00
|
|
|
|
2021-05-07 17:44:32 +00:00
|
|
|
let suggest_existing =
|
|
|
|
|err: &mut DiagnosticBuilder<'_>,
|
|
|
|
name: &str,
|
2021-05-09 20:31:49 +00:00
|
|
|
formatters: Vec<Option<Box<dyn Fn(&str) -> String>>>| {
|
2021-05-07 17:44:32 +00:00
|
|
|
if let Some(MissingLifetimeSpot::HigherRanked { span: for_span, span_type }) =
|
|
|
|
self.missing_named_lifetime_spots.iter().rev().next()
|
|
|
|
{
|
|
|
|
// When we have `struct S<'a>(&'a dyn Fn(&X) -> &X);` we want to not only suggest
|
|
|
|
// using `'a`, but also introduce the concept of HRLTs by suggesting
|
|
|
|
// `struct S<'a>(&'a dyn for<'b> Fn(&X) -> &'b X);`. (#72404)
|
|
|
|
let mut introduce_suggestion = vec![];
|
|
|
|
|
|
|
|
let a_to_z_repeat_n = |n| {
|
|
|
|
(b'a'..=b'z').map(move |c| {
|
|
|
|
let mut s = '\''.to_string();
|
|
|
|
s.extend(std::iter::repeat(char::from(c)).take(n));
|
|
|
|
s
|
|
|
|
})
|
|
|
|
};
|
2020-08-10 23:42:57 +00:00
|
|
|
|
2021-05-07 17:44:32 +00:00
|
|
|
// If all single char lifetime names are present, we wrap around and double the chars.
|
|
|
|
let lt_name = (1..)
|
|
|
|
.flat_map(a_to_z_repeat_n)
|
|
|
|
.find(|lt| !lifetime_names.contains(&Symbol::intern(<)))
|
|
|
|
.unwrap();
|
|
|
|
let msg = format!(
|
|
|
|
"consider making the {} lifetime-generic with a new `{}` lifetime",
|
|
|
|
span_type.descr(),
|
|
|
|
lt_name,
|
|
|
|
);
|
|
|
|
err.note(
|
|
|
|
"for more information on higher-ranked polymorphism, visit \
|
2020-08-10 23:42:57 +00:00
|
|
|
https://doc.rust-lang.org/nomicon/hrtb.html",
|
2021-05-07 17:44:32 +00:00
|
|
|
);
|
|
|
|
let for_sugg = span_type.suggestion(<_name);
|
|
|
|
for param in params {
|
|
|
|
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(param.span)
|
|
|
|
{
|
|
|
|
if snippet.starts_with('&') && !snippet.starts_with("&'") {
|
|
|
|
introduce_suggestion
|
|
|
|
.push((param.span, format!("&{} {}", lt_name, &snippet[1..])));
|
|
|
|
} else if let Some(stripped) = snippet.strip_prefix("&'_ ") {
|
|
|
|
introduce_suggestion
|
|
|
|
.push((param.span, format!("&{} {}", lt_name, stripped)));
|
|
|
|
}
|
2020-08-10 23:42:57 +00:00
|
|
|
}
|
|
|
|
}
|
2021-05-07 17:44:32 +00:00
|
|
|
introduce_suggestion.push((*for_span, for_sugg));
|
2021-05-09 20:31:49 +00:00
|
|
|
for ((span, _), formatter) in spans_with_counts.iter().zip(formatters.iter()) {
|
2021-05-07 17:44:32 +00:00
|
|
|
if let Some(formatter) = formatter {
|
2021-05-09 20:31:49 +00:00
|
|
|
introduce_suggestion.push((*span, formatter(<_name)));
|
2021-05-07 17:44:32 +00:00
|
|
|
}
|
|
|
|
}
|
2021-08-10 10:53:43 +00:00
|
|
|
err.multipart_suggestion_verbose(
|
2021-05-07 17:44:32 +00:00
|
|
|
&msg,
|
|
|
|
introduce_suggestion,
|
|
|
|
Applicability::MaybeIncorrect,
|
|
|
|
);
|
2020-08-10 23:42:57 +00:00
|
|
|
}
|
|
|
|
|
2021-05-09 20:31:49 +00:00
|
|
|
let spans_suggs: Vec<_> = formatters
|
|
|
|
.into_iter()
|
|
|
|
.zip(spans_with_counts.iter())
|
2021-12-27 22:10:59 +00:00
|
|
|
.filter_map(|(formatter, (span, _))| {
|
|
|
|
if let Some(formatter) = formatter {
|
|
|
|
Some((*span, formatter(name)))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
2021-05-10 12:59:54 +00:00
|
|
|
})
|
2021-05-09 20:31:49 +00:00
|
|
|
.collect();
|
2021-12-10 23:58:13 +00:00
|
|
|
if spans_suggs.is_empty() {
|
|
|
|
// If all the spans come from macros, we cannot extract snippets and then
|
|
|
|
// `formatters` only contains None and `spans_suggs` is empty.
|
|
|
|
return;
|
|
|
|
}
|
2021-08-10 10:53:43 +00:00
|
|
|
err.multipart_suggestion_verbose(
|
2021-05-07 17:44:32 +00:00
|
|
|
&format!(
|
|
|
|
"consider using the `{}` lifetime",
|
|
|
|
lifetime_names.iter().next().unwrap()
|
|
|
|
),
|
|
|
|
spans_suggs,
|
|
|
|
Applicability::MaybeIncorrect,
|
|
|
|
);
|
|
|
|
};
|
2021-05-09 20:31:49 +00:00
|
|
|
let suggest_new = |err: &mut DiagnosticBuilder<'_>, suggs: Vec<Option<String>>| {
|
2020-04-17 04:33:42 +00:00
|
|
|
for missing in self.missing_named_lifetime_spots.iter().rev() {
|
|
|
|
let mut introduce_suggestion = vec![];
|
|
|
|
let msg;
|
|
|
|
let should_break;
|
|
|
|
introduce_suggestion.push(match missing {
|
|
|
|
MissingLifetimeSpot::Generics(generics) => {
|
2020-08-10 20:26:16 +00:00
|
|
|
if generics.span == DUMMY_SP {
|
|
|
|
// Account for malformed generics in the HIR. This shouldn't happen,
|
|
|
|
// but if we make a mistake elsewhere, mainly by keeping something in
|
|
|
|
// `missing_named_lifetime_spots` that we shouldn't, like associated
|
|
|
|
// `const`s or making a mistake in the AST lowering we would provide
|
|
|
|
// non-sensical suggestions. Guard against that by skipping these.
|
|
|
|
// (#74264)
|
|
|
|
continue;
|
|
|
|
}
|
2020-04-17 04:33:42 +00:00
|
|
|
msg = "consider introducing a named lifetime parameter".to_string();
|
|
|
|
should_break = true;
|
2020-10-27 01:02:48 +00:00
|
|
|
if let Some(param) = generics.params.iter().find(|p| {
|
2021-01-09 17:00:45 +00:00
|
|
|
!matches!(
|
|
|
|
p.kind,
|
2021-11-18 05:25:27 +00:00
|
|
|
hir::GenericParamKind::Type { synthetic: true, .. }
|
|
|
|
| hir::GenericParamKind::Lifetime {
|
|
|
|
kind: hir::LifetimeParamKind::Elided
|
|
|
|
}
|
2021-01-09 17:00:45 +00:00
|
|
|
)
|
2020-04-17 04:33:42 +00:00
|
|
|
}) {
|
|
|
|
(param.span.shrink_to_lo(), "'a, ".to_string())
|
|
|
|
} else {
|
|
|
|
(generics.span, "<'a>".to_string())
|
2020-02-24 18:44:55 +00:00
|
|
|
}
|
|
|
|
}
|
2020-04-17 04:33:42 +00:00
|
|
|
MissingLifetimeSpot::HigherRanked { span, span_type } => {
|
|
|
|
msg = format!(
|
|
|
|
"consider making the {} lifetime-generic with a new `'a` lifetime",
|
|
|
|
span_type.descr(),
|
|
|
|
);
|
|
|
|
should_break = false;
|
|
|
|
err.note(
|
|
|
|
"for more information on higher-ranked polymorphism, visit \
|
|
|
|
https://doc.rust-lang.org/nomicon/hrtb.html",
|
|
|
|
);
|
|
|
|
(*span, span_type.suggestion("'a"))
|
|
|
|
}
|
2020-08-11 20:02:14 +00:00
|
|
|
MissingLifetimeSpot::Static => {
|
2021-05-07 17:44:32 +00:00
|
|
|
let mut spans_suggs = Vec::new();
|
2021-05-09 20:31:49 +00:00
|
|
|
for ((span, count), snippet) in
|
|
|
|
spans_with_counts.iter().copied().zip(snippets.iter())
|
2021-05-07 17:44:32 +00:00
|
|
|
{
|
|
|
|
let (span, sugg) = match snippet.as_deref() {
|
|
|
|
Some("&") => (span.shrink_to_hi(), "'static ".to_owned()),
|
|
|
|
Some("'_") => (span, "'static".to_owned()),
|
|
|
|
Some(snippet) if !snippet.ends_with('>') => {
|
|
|
|
if snippet == "" {
|
|
|
|
(
|
|
|
|
span,
|
2020-08-11 20:02:14 +00:00
|
|
|
std::iter::repeat("'static")
|
|
|
|
.take(count)
|
|
|
|
.collect::<Vec<_>>()
|
2021-05-07 17:44:32 +00:00
|
|
|
.join(", "),
|
|
|
|
)
|
2021-05-12 09:36:38 +00:00
|
|
|
} else if snippet == "<" || snippet == "(" {
|
|
|
|
(
|
|
|
|
span.shrink_to_hi(),
|
|
|
|
std::iter::repeat("'static")
|
|
|
|
.take(count)
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
.join(", "),
|
|
|
|
)
|
2021-05-07 17:44:32 +00:00
|
|
|
} else {
|
|
|
|
(
|
|
|
|
span.shrink_to_hi(),
|
|
|
|
format!(
|
|
|
|
"<{}>",
|
|
|
|
std::iter::repeat("'static")
|
|
|
|
.take(count)
|
|
|
|
.collect::<Vec<_>>()
|
2021-05-12 09:36:38 +00:00
|
|
|
.join(", "),
|
2021-05-07 17:44:32 +00:00
|
|
|
),
|
|
|
|
)
|
|
|
|
}
|
2020-08-11 20:02:14 +00:00
|
|
|
}
|
2021-05-07 17:44:32 +00:00
|
|
|
_ => continue,
|
|
|
|
};
|
|
|
|
spans_suggs.push((span, sugg.to_string()));
|
|
|
|
}
|
2021-08-10 10:53:43 +00:00
|
|
|
err.multipart_suggestion_verbose(
|
2020-08-11 20:02:14 +00:00
|
|
|
"consider using the `'static` lifetime",
|
2021-05-07 17:44:32 +00:00
|
|
|
spans_suggs,
|
2020-08-11 20:02:14 +00:00
|
|
|
Applicability::MaybeIncorrect,
|
|
|
|
);
|
|
|
|
continue;
|
|
|
|
}
|
2020-04-17 04:33:42 +00:00
|
|
|
});
|
2021-08-18 12:00:07 +00:00
|
|
|
|
|
|
|
struct Lifetime(Span, String);
|
|
|
|
impl Lifetime {
|
|
|
|
fn is_unnamed(&self) -> bool {
|
|
|
|
self.1.starts_with('&') && !self.1.starts_with("&'")
|
|
|
|
}
|
|
|
|
fn is_underscore(&self) -> bool {
|
|
|
|
self.1.starts_with("&'_ ")
|
|
|
|
}
|
|
|
|
fn is_named(&self) -> bool {
|
|
|
|
self.1.starts_with("&'")
|
|
|
|
}
|
|
|
|
fn suggestion(&self, sugg: String) -> Option<(Span, String)> {
|
|
|
|
Some(
|
|
|
|
match (
|
|
|
|
self.is_unnamed(),
|
|
|
|
self.is_underscore(),
|
|
|
|
self.is_named(),
|
2021-08-30 22:54:48 +00:00
|
|
|
sugg.starts_with('&'),
|
2021-08-18 12:00:07 +00:00
|
|
|
) {
|
|
|
|
(true, _, _, false) => (self.span_unnamed_borrow(), sugg),
|
|
|
|
(true, _, _, true) => {
|
|
|
|
(self.span_unnamed_borrow(), sugg[1..].to_string())
|
|
|
|
}
|
|
|
|
(_, true, _, false) => {
|
|
|
|
(self.span_underscore_borrow(), sugg.trim().to_string())
|
|
|
|
}
|
|
|
|
(_, true, _, true) => {
|
|
|
|
(self.span_underscore_borrow(), sugg[1..].trim().to_string())
|
|
|
|
}
|
|
|
|
(_, _, true, false) => {
|
|
|
|
(self.span_named_borrow(), sugg.trim().to_string())
|
|
|
|
}
|
|
|
|
(_, _, true, true) => {
|
|
|
|
(self.span_named_borrow(), sugg[1..].trim().to_string())
|
|
|
|
}
|
|
|
|
_ => return None,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
}
|
|
|
|
fn span_unnamed_borrow(&self) -> Span {
|
|
|
|
let lo = self.0.lo() + BytePos(1);
|
|
|
|
self.0.with_lo(lo).with_hi(lo)
|
|
|
|
}
|
|
|
|
fn span_named_borrow(&self) -> Span {
|
|
|
|
let lo = self.0.lo() + BytePos(1);
|
|
|
|
self.0.with_lo(lo)
|
|
|
|
}
|
|
|
|
fn span_underscore_borrow(&self) -> Span {
|
|
|
|
let lo = self.0.lo() + BytePos(1);
|
|
|
|
let hi = lo + BytePos(2);
|
|
|
|
self.0.with_lo(lo).with_hi(hi)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-17 04:33:42 +00:00
|
|
|
for param in params {
|
|
|
|
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(param.span) {
|
2021-08-18 12:00:07 +00:00
|
|
|
if let Some((span, sugg)) =
|
|
|
|
Lifetime(param.span, snippet).suggestion("'a ".to_string())
|
|
|
|
{
|
|
|
|
introduce_suggestion.push((span, sugg));
|
2020-04-17 04:33:42 +00:00
|
|
|
}
|
2020-02-28 12:36:45 +00:00
|
|
|
}
|
|
|
|
}
|
2021-08-18 11:14:33 +00:00
|
|
|
for (span, sugg) in spans_with_counts.iter().copied().zip(suggs.iter()).filter_map(
|
2021-08-18 12:00:07 +00:00
|
|
|
|((span, _), sugg)| match &sugg {
|
|
|
|
Some(sugg) => Some((span, sugg.to_string())),
|
2021-08-18 11:14:33 +00:00
|
|
|
_ => None,
|
|
|
|
},
|
|
|
|
) {
|
2021-08-18 12:00:07 +00:00
|
|
|
let (span, sugg) = self
|
|
|
|
.tcx
|
|
|
|
.sess
|
|
|
|
.source_map()
|
|
|
|
.span_to_snippet(span)
|
|
|
|
.ok()
|
|
|
|
.and_then(|snippet| Lifetime(span, snippet).suggestion(sugg.clone()))
|
|
|
|
.unwrap_or((span, sugg));
|
|
|
|
introduce_suggestion.push((span, sugg.to_string()));
|
2021-05-07 17:44:32 +00:00
|
|
|
}
|
2021-08-10 10:53:43 +00:00
|
|
|
err.multipart_suggestion_verbose(
|
2021-05-07 17:44:32 +00:00
|
|
|
&msg,
|
|
|
|
introduce_suggestion,
|
|
|
|
Applicability::MaybeIncorrect,
|
|
|
|
);
|
2020-04-17 04:33:42 +00:00
|
|
|
if should_break {
|
|
|
|
break;
|
2020-02-24 18:44:55 +00:00
|
|
|
}
|
|
|
|
}
|
2020-04-17 04:33:42 +00:00
|
|
|
};
|
|
|
|
|
2020-09-15 20:36:43 +00:00
|
|
|
let lifetime_names: Vec<_> = lifetime_names.iter().collect();
|
2021-05-07 17:44:32 +00:00
|
|
|
match &lifetime_names[..] {
|
|
|
|
[name] => {
|
|
|
|
let mut suggs: Vec<Option<Box<dyn Fn(&str) -> String>>> = Vec::new();
|
2021-05-09 20:31:49 +00:00
|
|
|
for (snippet, (_, count)) in snippets.iter().zip(spans_with_counts.iter().copied())
|
|
|
|
{
|
|
|
|
suggs.push(match snippet.as_deref() {
|
|
|
|
Some("&") => Some(Box::new(|name| format!("&{} ", name))),
|
|
|
|
Some("'_") => Some(Box::new(|n| n.to_string())),
|
|
|
|
Some("") => Some(Box::new(move |n| format!("{}, ", n).repeat(count))),
|
2021-05-12 09:36:38 +00:00
|
|
|
Some("<") => Some(Box::new(move |n| {
|
|
|
|
std::iter::repeat(n).take(count).collect::<Vec<_>>().join(", ")
|
|
|
|
})),
|
2021-05-09 20:31:49 +00:00
|
|
|
Some(snippet) if !snippet.ends_with('>') => Some(Box::new(move |name| {
|
|
|
|
format!(
|
|
|
|
"{}<{}>",
|
|
|
|
snippet,
|
|
|
|
std::iter::repeat(name.to_string())
|
|
|
|
.take(count)
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
.join(", ")
|
|
|
|
)
|
|
|
|
})),
|
|
|
|
_ => None,
|
|
|
|
});
|
2021-05-07 17:44:32 +00:00
|
|
|
}
|
2021-12-15 03:39:23 +00:00
|
|
|
suggest_existing(err, name.as_str(), suggs);
|
2021-05-07 17:44:32 +00:00
|
|
|
}
|
|
|
|
[] => {
|
2021-05-09 20:31:49 +00:00
|
|
|
let mut suggs = Vec::new();
|
|
|
|
for (snippet, (_, count)) in
|
|
|
|
snippets.iter().cloned().zip(spans_with_counts.iter().copied())
|
|
|
|
{
|
|
|
|
suggs.push(match snippet.as_deref() {
|
|
|
|
Some("&") => Some("&'a ".to_string()),
|
|
|
|
Some("'_") => Some("'a".to_string()),
|
|
|
|
Some("") => {
|
|
|
|
Some(std::iter::repeat("'a, ").take(count).collect::<Vec<_>>().join(""))
|
2021-05-07 17:44:32 +00:00
|
|
|
}
|
2021-05-12 09:36:38 +00:00
|
|
|
Some("<") => {
|
|
|
|
Some(std::iter::repeat("'a").take(count).collect::<Vec<_>>().join(", "))
|
|
|
|
}
|
2021-05-09 20:31:49 +00:00
|
|
|
Some(snippet) => Some(format!(
|
|
|
|
"{}<{}>",
|
|
|
|
snippet,
|
|
|
|
std::iter::repeat("'a").take(count).collect::<Vec<_>>().join(", "),
|
|
|
|
)),
|
|
|
|
None => None,
|
|
|
|
});
|
2020-08-10 19:30:31 +00:00
|
|
|
}
|
2021-05-09 20:31:49 +00:00
|
|
|
suggest_new(err, suggs);
|
2020-04-17 04:33:42 +00:00
|
|
|
}
|
2021-05-07 17:44:32 +00:00
|
|
|
lts if lts.len() > 1 => {
|
2020-08-10 23:42:57 +00:00
|
|
|
err.span_note(lifetime_spans, "these named lifetimes are available to use");
|
2021-05-07 17:44:32 +00:00
|
|
|
|
|
|
|
let mut spans_suggs: Vec<_> = Vec::new();
|
2021-05-09 20:31:49 +00:00
|
|
|
for ((span, _), snippet) in spans_with_counts.iter().copied().zip(snippets.iter()) {
|
|
|
|
match snippet.as_deref() {
|
|
|
|
Some("") => spans_suggs.push((span, "'lifetime, ".to_string())),
|
2021-08-12 18:54:29 +00:00
|
|
|
Some("&") => spans_suggs
|
|
|
|
.push((span.with_lo(span.lo() + BytePos(1)), "'lifetime ".to_string())),
|
2021-05-09 20:31:49 +00:00
|
|
|
_ => {}
|
2021-05-07 17:44:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if spans_suggs.len() > 0 {
|
2020-04-17 17:46:22 +00:00
|
|
|
// This happens when we have `Foo<T>` where we point at the space before `T`,
|
|
|
|
// but this can be confusing so we give a suggestion with placeholders.
|
2021-08-10 10:53:43 +00:00
|
|
|
err.multipart_suggestion_verbose(
|
2020-04-17 17:46:22 +00:00
|
|
|
"consider using one of the available lifetimes here",
|
2021-05-07 17:44:32 +00:00
|
|
|
spans_suggs,
|
2020-04-17 17:46:22 +00:00
|
|
|
Applicability::HasPlaceholders,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2021-05-07 17:44:32 +00:00
|
|
|
_ => unreachable!(),
|
2020-02-24 18:44:55 +00:00
|
|
|
}
|
|
|
|
}
|
2020-09-15 13:38:41 +00:00
|
|
|
|
2020-11-17 09:55:13 +00:00
|
|
|
/// Non-static lifetimes are prohibited in anonymous constants under `min_const_generics`.
|
2021-08-27 16:04:57 +00:00
|
|
|
/// This function will emit an error if `generic_const_exprs` is not enabled, the body identified by
|
2020-09-15 13:38:41 +00:00
|
|
|
/// `body_id` is an anonymous constant and `lifetime_ref` is non-static.
|
|
|
|
crate fn maybe_emit_forbidden_non_static_lifetime_error(
|
|
|
|
&self,
|
|
|
|
body_id: hir::BodyId,
|
|
|
|
lifetime_ref: &'tcx hir::Lifetime,
|
|
|
|
) {
|
|
|
|
let is_anon_const = matches!(
|
|
|
|
self.tcx.def_kind(self.tcx.hir().body_owner_def_id(body_id)),
|
|
|
|
hir::def::DefKind::AnonConst
|
|
|
|
);
|
|
|
|
let is_allowed_lifetime = matches!(
|
|
|
|
lifetime_ref.name,
|
2021-11-26 22:07:21 +00:00
|
|
|
hir::LifetimeName::Implicit(_)
|
2021-07-11 13:04:57 +00:00
|
|
|
| hir::LifetimeName::Static
|
|
|
|
| hir::LifetimeName::Underscore
|
2020-09-15 13:38:41 +00:00
|
|
|
);
|
|
|
|
|
2020-11-17 09:55:13 +00:00
|
|
|
if !self.tcx.lazy_normalization() && is_anon_const && !is_allowed_lifetime {
|
2020-09-15 13:38:41 +00:00
|
|
|
feature_err(
|
|
|
|
&self.tcx.sess.parse_sess,
|
2021-08-27 16:04:57 +00:00
|
|
|
sym::generic_const_exprs,
|
2020-09-15 13:38:41 +00:00
|
|
|
lifetime_ref.span,
|
|
|
|
"a non-static lifetime is not allowed in a `const`",
|
|
|
|
)
|
|
|
|
.emit();
|
|
|
|
}
|
|
|
|
}
|
2020-02-24 18:44:55 +00:00
|
|
|
}
|