use rustc::hir::def::Namespace::*;
use rustc::hir::def_id::{CRATE_DEF_INDEX, LOCAL_CRATE, DefId};
use rustc::hir::{Freevar, FreevarMap, TraitCandidate, TraitMap, GlobMap};
-use rustc::session::config::nightly_options;
use rustc::ty;
use rustc::util::nodemap::{NodeMap, NodeSet, FxHashMap, FxHashSet, DefIdMap};
use rustc::{bug, span_bug};
}
}
- /// resolve_hir_path, but takes a callback in case there was an error
+ /// Like `resolve_hir_path`, but takes a callback in case there was an error.
fn resolve_hir_path_cb<F>(
&mut self,
path: &ast::Path,
let span = path.span;
let segments = &path.segments;
let path = Segment::from_path(&path);
- // FIXME (Manishearth): Intra doc links won't get warned of epoch changes
+ // FIXME(Manishearth): intra-doc links won't get warned of epoch changes.
let def = match self.resolve_path_without_parent_scope(&path, Some(namespace), true,
span, CrateLint::No) {
PathResult::Module(ModuleOrUniformRoot::Module(module)) =>
source: PathSource<'_>,
crate_lint: CrateLint)
-> PathResolution {
- let ident_span = path.last().map_or(span, |ident| ident.ident.span);
let ns = source.namespace();
let is_expected = &|def| source.is_expected(def);
- let is_enum_variant = &|def| if let Def::Variant(..) = def { true } else { false };
- // Base error is amended with one short label and possibly some longer helps/notes.
let report_errors = |this: &mut Self, def: Option<Def>| {
- // Make the base error.
- let expected = source.descr_expected();
- let path_str = Segment::names_to_string(path);
- let item_str = path.last().unwrap().ident;
- let code = source.error_code(def.is_some());
- let (base_msg, fallback_label, base_span) = if let Some(def) = def {
- (format!("expected {}, found {} `{}`", expected, def.kind_name(), path_str),
- format!("not a {}", expected),
- span)
- } 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 == keywords::PathRoot.name() {
- (String::new(), "the crate root".to_string())
- } else {
- let mod_path = &path[..path.len() - 1];
- let mod_prefix = match this.resolve_path_without_parent_scope(
- mod_path, Some(TypeNS), false, span, CrateLint::No
- ) {
- PathResult::Module(ModuleOrUniformRoot::Module(module)) =>
- module.def(),
- _ => None,
- }.map_or(String::new(), |def| format!("{} ", def.kind_name()));
- (mod_prefix, format!("`{}`", Segment::names_to_string(mod_path)))
- };
- (format!("cannot find {} `{}` in {}{}", expected, item_str, mod_prefix, mod_str),
- format!("not found in {}", mod_str),
- item_span)
- };
-
- let code = DiagnosticId::Error(code.into());
- let mut err = this.session.struct_span_err_with_code(base_span, &base_msg, code);
-
- // Emit help message for fake-self from other languages like `this`(javascript)
- if ["this", "my"].contains(&&*item_str.as_str())
- && this.self_value_is_available(path[0].ident.span, span) {
- err.span_suggestion(
- span,
- "did you mean",
- "self".to_string(),
- Applicability::MaybeIncorrect,
- );
- }
-
- // Emit special messages for unresolved `Self` and `self`.
- if is_self_type(path, ns) {
- __diagnostic_used!(E0411);
- err.code(DiagnosticId::Error("E0411".into()));
- err.span_label(span, format!("`Self` is only available in impls, traits, \
- and type definitions"));
- return (err, Vec::new());
- }
- if is_self_value(path, ns) {
- debug!("smart_resolve_path_fragment E0424 source:{:?}", source);
-
- __diagnostic_used!(E0424);
- err.code(DiagnosticId::Error("E0424".into()));
- err.span_label(span, match source {
- PathSource::Pat => {
- format!("`self` value is a keyword \
- and may not be bound to \
- variables or shadowed")
- }
- _ => {
- format!("`self` value is a keyword \
- only available in methods \
- with `self` parameter")
- }
- });
- return (err, Vec::new());
- }
-
- // Try to lookup the name in more relaxed fashion for better error reporting.
- let ident = path.last().unwrap().ident;
- let candidates = this.lookup_import_candidates(ident, ns, is_expected);
- if candidates.is_empty() && is_expected(Def::Enum(DefId::local(CRATE_DEF_INDEX))) {
- let enum_candidates =
- this.lookup_import_candidates(ident, ns, is_enum_variant);
- let mut enum_candidates = enum_candidates.iter()
- .map(|suggestion| {
- import_candidate_to_enum_paths(&suggestion)
- }).collect::<Vec<_>>();
- enum_candidates.sort();
-
- if !enum_candidates.is_empty() {
- // 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 def.is_none() {
- let others = match enum_candidates.len() {
- 1 => String::new(),
- 2 => " and 1 other".to_owned(),
- n => format!(" and {} others", n)
- };
- format!("there is an enum variant `{}`{}; ",
- enum_candidates[0].0, others)
- } else {
- String::new()
- };
- let msg = format!("{}try using the variant's enum", preamble);
-
- err.span_suggestions(
- span,
- &msg,
- enum_candidates.into_iter()
- .map(|(_variant_path, enum_ty_path)| enum_ty_path)
- // variants reƫxported in prelude doesn't mean `prelude::v1` is the
- // type name! FIXME: is there a more principled way to do this that
- // would work for other reƫxports?
- .filter(|enum_ty_path| enum_ty_path != "std::prelude::v1")
- // also say `Option` rather than `std::prelude::v1::Option`
- .map(|enum_ty_path| {
- // FIXME #56861: DRYer prelude filtering
- enum_ty_path.trim_start_matches("std::prelude::v1::").to_owned()
- }),
- Applicability::MachineApplicable,
- );
- }
- }
- if path.len() == 1 && this.self_type_is_available(span) {
- if let Some(candidate) = this.lookup_assoc_candidate(ident, ns, is_expected) {
- let self_is_available = this.self_value_is_available(path[0].ident.span, span);
- match candidate {
- AssocSuggestion::Field => {
- err.span_suggestion(
- span,
- "try",
- format!("self.{}", path_str),
- Applicability::MachineApplicable,
- );
- if !self_is_available {
- err.span_label(span, format!("`self` value is a keyword \
- only available in \
- methods with `self` parameter"));
- }
- }
- AssocSuggestion::MethodWithSelf if self_is_available => {
- err.span_suggestion(
- span,
- "try",
- format!("self.{}", path_str),
- Applicability::MachineApplicable,
- );
- }
- AssocSuggestion::MethodWithSelf | AssocSuggestion::AssocItem => {
- err.span_suggestion(
- span,
- "try",
- format!("Self::{}", path_str),
- Applicability::MachineApplicable,
- );
- }
- }
- return (err, candidates);
- }
- }
-
- let mut levenshtein_worked = false;
-
- // Try Levenshtein algorithm.
- let suggestion = this.lookup_typo_candidate(path, ns, is_expected, span);
- if let Some(suggestion) = suggestion {
- let msg = format!(
- "{} {} with a similar name exists",
- suggestion.article, suggestion.kind
- );
- err.span_suggestion(
- ident_span,
- &msg,
- suggestion.candidate.to_string(),
- Applicability::MaybeIncorrect,
- );
-
- levenshtein_worked = true;
- }
-
- // Try context dependent help if relaxed lookup didn't work.
- if let Some(def) = def {
- match (def, source) {
- (Def::Macro(..), _) => {
- err.span_suggestion(
- span,
- "use `!` to invoke the macro",
- format!("{}!", path_str),
- Applicability::MaybeIncorrect,
- );
- return (err, candidates);
- }
- (Def::TyAlias(..), PathSource::Trait(_)) => {
- err.span_label(span, "type aliases cannot be used as traits");
- if nightly_options::is_nightly_build() {
- err.note("did you mean to use a trait alias?");
- }
- return (err, candidates);
- }
- (Def::Mod(..), PathSource::Expr(Some(parent))) => match parent.node {
- ExprKind::Field(_, ident) => {
- err.span_suggestion(
- parent.span,
- "use the path separator to refer to an item",
- format!("{}::{}", path_str, ident),
- Applicability::MaybeIncorrect,
- );
- return (err, candidates);
- }
- ExprKind::MethodCall(ref segment, ..) => {
- let span = parent.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,
- );
- return (err, candidates);
- }
- _ => {}
- },
- (Def::Enum(..), PathSource::TupleStruct)
- | (Def::Enum(..), PathSource::Expr(..)) => {
- if let Some(variants) = this.collect_enum_variants(def) {
- err.note(&format!("did you mean to use one \
- of the following variants?\n{}",
- variants.iter()
- .map(|suggestion| path_names_to_string(suggestion))
- .map(|suggestion| format!("- `{}`", suggestion))
- .collect::<Vec<_>>()
- .join("\n")));
-
- } else {
- err.note("did you mean to use one of the enum's variants?");
- }
- return (err, candidates);
- },
- (Def::Struct(def_id), _) if ns == ValueNS => {
- if let Some((ctor_def, ctor_vis))
- = this.struct_constructors.get(&def_id).cloned() {
- let accessible_ctor = this.is_accessible(ctor_vis);
- if is_expected(ctor_def) && !accessible_ctor {
- err.span_label(span, format!("constructor is not visible \
- here due to private fields"));
- }
- } else {
- // 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 `{`.
- let sm = this.session.source_map();
- let mut sp = span;
- loop {
- sp = sm.next_point(sp);
- match sm.span_to_snippet(sp) {
- Ok(ref snippet) => {
- if snippet.chars().any(|c| { !c.is_whitespace() }) {
- break;
- }
- }
- _ => break,
- }
- }
- let followed_by_brace = match sm.span_to_snippet(sp) {
- Ok(ref snippet) if snippet == "{" => true,
- _ => false,
- };
- // In case this could be a struct literal that needs to be surrounded
- // by parenthesis, find the appropriate span.
- 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 == "}" {
- let sp = span.to(sp);
- if let Ok(snippet) = sm.span_to_snippet(sp) {
- closing_brace = Some((sp, snippet));
- }
- break;
- }
- }
- _ => break,
- }
- i += 1;
- if i > 100 { // The bigger the span the more likely we're
- break; // incorrect. Bound it to 100 chars long.
- }
- }
- match source {
- PathSource::Expr(Some(parent)) => {
- match parent.node {
- ExprKind::MethodCall(ref path_assignment, _) => {
- err.span_suggestion(
- sm.start_point(parent.span)
- .to(path_assignment.ident.span),
- "use `::` to access an associated function",
- format!("{}::{}",
- path_str,
- path_assignment.ident),
- Applicability::MaybeIncorrect
- );
- return (err, candidates);
- },
- _ => {
- err.span_label(
- span,
- format!("did you mean `{} {{ /* fields */ }}`?",
- path_str),
- );
- return (err, candidates);
- },
- }
- },
- PathSource::Expr(None) if followed_by_brace == true => {
- if let Some((sp, snippet)) = closing_brace {
- err.span_suggestion(
- sp,
- "surround the struct literal with parenthesis",
- format!("({})", snippet),
- Applicability::MaybeIncorrect,
- );
- } else {
- err.span_label(
- span,
- format!("did you mean `({} {{ /* fields */ }})`?",
- path_str),
- );
- }
- return (err, candidates);
- },
- _ => {
- err.span_label(
- span,
- format!("did you mean `{} {{ /* fields */ }}`?",
- path_str),
- );
- return (err, candidates);
- },
- }
- }
- return (err, candidates);
- }
- (Def::Union(..), _) |
- (Def::Variant(..), _) |
- (Def::VariantCtor(_, CtorKind::Fictive), _) if ns == ValueNS => {
- err.span_label(span, format!("did you mean `{} {{ /* fields */ }}`?",
- path_str));
- return (err, candidates);
- }
- (Def::SelfTy(..), _) if ns == ValueNS => {
- err.span_label(span, fallback_label);
- err.note("can't use `Self` as a constructor, you must use the \
- implemented struct");
- return (err, candidates);
- }
- (Def::TyAlias(_), _) | (Def::AssociatedTy(..), _) if ns == ValueNS => {
- err.note("can't use a type alias as a constructor");
- return (err, candidates);
- }
- _ => {}
- }
- }
-
- // Fallback label.
- if !levenshtein_worked {
- err.span_label(base_span, fallback_label);
- this.type_ascription_suggestion(&mut err, base_span);
- }
- (err, candidates)
- };
- let report_errors = |this: &mut Self, def: Option<Def>| {
- let (err, candidates) = report_errors(this, def);
+ let (err, candidates) = this.smart_resolve_report_errors(path, span, source, def);
let def_id = this.current_module.normal_ancestor_id;
let node_id = this.definitions.as_local_node_id(def_id).unwrap();
let better = def.is_some();
debug!("self.current_type_ascription {:?}", self.current_type_ascription);
if let Some(sp) = self.current_type_ascription.last() {
let mut sp = *sp;
- loop { // try to find the `:`, bail on first non-':'/non-whitespace
+ loop {
+ // Try to find the `:`; bail on first non-':' / non-whitespace.
sp = cm.next_point(sp);
if let Ok(snippet) = cm.span_to_snippet(sp.to(cm.next_point(sp))) {
debug!("snippet {:?}", snippet);
(variant_path_string, enum_path_string)
}
-
/// When an entity with a given name is not available in scope, we search for
/// entities with that name in all crates. This method allows outputting the
/// results of this search in a programmer-friendly way