4 use rustc::hir::def::*;
5 use rustc::hir::def::Namespace::*;
6 use rustc::hir::def_id::{CRATE_DEF_INDEX, DefId};
7 use rustc::session::config::nightly_options;
8 use syntax::ast::{ExprKind};
9 use syntax::symbol::keywords;
12 use crate::errors::{Applicability, DiagnosticBuilder, DiagnosticId};
13 use crate::macros::ParentScope;
14 use crate::resolve_imports::ImportResolver;
15 use crate::{import_candidate_to_enum_paths, is_self_type, is_self_value, path_names_to_string};
16 use crate::{AssocSuggestion, CrateLint, ImportSuggestion, ModuleOrUniformRoot, PathResult,
17 PathSource, Resolver, Segment};
19 impl<'a> Resolver<'a> {
20 /// Handles error reporting for `smart_resolve_path_fragment` function.
21 /// Creates base error and amends it with one short label and possibly some longer helps/notes.
22 pub(crate) fn smart_resolve_report_errors(
26 source: PathSource<'_>,
28 ) -> (DiagnosticBuilder<'a>, Vec<ImportSuggestion>) {
29 let ident_span = path.last().map_or(span, |ident| ident.ident.span);
30 let ns = source.namespace();
31 let is_expected = &|def| source.is_expected(def);
32 let is_enum_variant = &|def| if let Def::Variant(..) = def { true } else { false };
34 // Make the base error.
35 let expected = source.descr_expected();
36 let path_str = Segment::names_to_string(path);
37 let item_str = path.last().unwrap().ident;
38 let code = source.error_code(def.is_some());
39 let (base_msg, fallback_label, base_span) = if let Some(def) = def {
40 (format!("expected {}, found {} `{}`", expected, def.kind_name(), path_str),
41 format!("not a {}", expected),
44 let item_span = path.last().unwrap().ident.span;
45 let (mod_prefix, mod_str) = if path.len() == 1 {
46 (String::new(), "this scope".to_string())
47 } else if path.len() == 2 && path[0].ident.name == keywords::PathRoot.name() {
48 (String::new(), "the crate root".to_string())
50 let mod_path = &path[..path.len() - 1];
51 let mod_prefix = match self.resolve_path_without_parent_scope(
52 mod_path, Some(TypeNS), false, span, CrateLint::No
54 PathResult::Module(ModuleOrUniformRoot::Module(module)) =>
57 }.map_or(String::new(), |def| format!("{} ", def.kind_name()));
58 (mod_prefix, format!("`{}`", Segment::names_to_string(mod_path)))
60 (format!("cannot find {} `{}` in {}{}", expected, item_str, mod_prefix, mod_str),
61 format!("not found in {}", mod_str),
65 let code = DiagnosticId::Error(code.into());
66 let mut err = self.session.struct_span_err_with_code(base_span, &base_msg, code);
68 // Emit help message for fake-self from other languages (e.g., `this` in Javascript).
69 if ["this", "my"].contains(&&*item_str.as_str())
70 && self.self_value_is_available(path[0].ident.span, span) {
75 Applicability::MaybeIncorrect,
79 // Emit special messages for unresolved `Self` and `self`.
80 if is_self_type(path, ns) {
81 __diagnostic_used!(E0411);
82 err.code(DiagnosticId::Error("E0411".into()));
83 err.span_label(span, format!("`Self` is only available in impls, traits, \
84 and type definitions"));
85 return (err, Vec::new());
87 if is_self_value(path, ns) {
88 debug!("smart_resolve_path_fragment: E0424, source={:?}", source);
90 __diagnostic_used!(E0424);
91 err.code(DiagnosticId::Error("E0424".into()));
92 err.span_label(span, match source {
94 format!("`self` value is a keyword \
95 and may not be bound to \
96 variables or shadowed")
99 format!("`self` value is a keyword \
100 only available in methods \
101 with `self` parameter")
104 return (err, Vec::new());
107 // Try to lookup name in more relaxed fashion for better error reporting.
108 let ident = path.last().unwrap().ident;
109 let candidates = self.lookup_import_candidates(ident, ns, is_expected);
110 if candidates.is_empty() && is_expected(Def::Enum(DefId::local(CRATE_DEF_INDEX))) {
111 let enum_candidates =
112 self.lookup_import_candidates(ident, ns, is_enum_variant);
113 let mut enum_candidates = enum_candidates.iter()
115 import_candidate_to_enum_paths(&suggestion)
116 }).collect::<Vec<_>>();
117 enum_candidates.sort();
119 if !enum_candidates.is_empty() {
120 // Contextualize for E0412 "cannot find type", but don't belabor the point
121 // (that it's a variant) for E0573 "expected type, found variant".
122 let preamble = if def.is_none() {
123 let others = match enum_candidates.len() {
125 2 => " and 1 other".to_owned(),
126 n => format!(" and {} others", n)
128 format!("there is an enum variant `{}`{}; ",
129 enum_candidates[0].0, others)
133 let msg = format!("{}try using the variant's enum", preamble);
135 err.span_suggestions(
138 enum_candidates.into_iter()
139 .map(|(_variant_path, enum_ty_path)| enum_ty_path)
140 // Variants re-exported in prelude doesn't mean `prelude::v1` is the
142 // FIXME: is there a more principled way to do this that
143 // would work for other re-exports?
144 .filter(|enum_ty_path| enum_ty_path != "std::prelude::v1")
145 // Also write `Option` rather than `std::prelude::v1::Option`.
146 .map(|enum_ty_path| {
147 // FIXME #56861: DRY-er prelude filtering.
148 enum_ty_path.trim_start_matches("std::prelude::v1::").to_owned()
150 Applicability::MachineApplicable,
154 if path.len() == 1 && self.self_type_is_available(span) {
155 if let Some(candidate) = self.lookup_assoc_candidate(ident, ns, is_expected) {
156 let self_is_available = self.self_value_is_available(path[0].ident.span, span);
158 AssocSuggestion::Field => {
162 format!("self.{}", path_str),
163 Applicability::MachineApplicable,
165 if !self_is_available {
166 err.span_label(span, format!("`self` value is a keyword \
168 methods with `self` parameter"));
171 AssocSuggestion::MethodWithSelf if self_is_available => {
175 format!("self.{}", path_str),
176 Applicability::MachineApplicable,
179 AssocSuggestion::MethodWithSelf | AssocSuggestion::AssocItem => {
183 format!("Self::{}", path_str),
184 Applicability::MachineApplicable,
188 return (err, candidates);
192 let mut levenshtein_worked = false;
194 // Try Levenshtein algorithm.
195 let suggestion = self.lookup_typo_candidate(path, ns, is_expected, span);
196 if let Some(suggestion) = suggestion {
198 "{} {} with a similar name exists",
199 suggestion.article, suggestion.kind
204 suggestion.candidate.to_string(),
205 Applicability::MaybeIncorrect,
208 levenshtein_worked = true;
211 // Try context-dependent help if relaxed lookup didn't work.
212 if let Some(def) = def {
213 if self.smart_resolve_context_dependent_help(&mut err,
219 return (err, candidates);
224 if !levenshtein_worked {
225 err.span_label(base_span, fallback_label);
226 self.type_ascription_suggestion(&mut err, base_span);
231 /// Provides context-dependent help for errors reported by the `smart_resolve_path_fragment`
233 /// Returns `true` if able to provide context-dependent help.
234 fn smart_resolve_context_dependent_help(
236 err: &mut DiagnosticBuilder<'a>,
238 source: PathSource<'_>,
241 fallback_label: &str,
243 let ns = source.namespace();
244 let is_expected = &|def| source.is_expected(def);
246 match (def, source) {
247 (Def::Macro(..), _) => {
250 "use `!` to invoke the macro",
251 format!("{}!", path_str),
252 Applicability::MaybeIncorrect,
255 (Def::TyAlias(..), PathSource::Trait(_)) => {
256 err.span_label(span, "type aliases cannot be used as traits");
257 if nightly_options::is_nightly_build() {
258 err.note("did you mean to use a trait alias?");
261 (Def::Mod(..), PathSource::Expr(Some(parent))) => match parent.node {
262 ExprKind::Field(_, ident) => {
265 "use the path separator to refer to an item",
266 format!("{}::{}", path_str, ident),
267 Applicability::MaybeIncorrect,
270 ExprKind::MethodCall(ref segment, ..) => {
271 let span = parent.span.with_hi(segment.ident.span.hi());
274 "use the path separator to refer to an item",
275 format!("{}::{}", path_str, segment.ident),
276 Applicability::MaybeIncorrect,
281 (Def::Enum(..), PathSource::TupleStruct)
282 | (Def::Enum(..), PathSource::Expr(..)) => {
283 if let Some(variants) = self.collect_enum_variants(def) {
284 err.note(&format!("did you mean to use one \
285 of the following variants?\n{}",
287 .map(|suggestion| path_names_to_string(suggestion))
288 .map(|suggestion| format!("- `{}`", suggestion))
292 err.note("did you mean to use one of the enum's variants?");
295 (Def::Struct(def_id), _) if ns == ValueNS => {
296 if let Some((ctor_def, ctor_vis))
297 = self.struct_constructors.get(&def_id).cloned() {
298 let accessible_ctor = self.is_accessible(ctor_vis);
299 if is_expected(ctor_def) && !accessible_ctor {
300 err.span_label(span, format!("constructor is not visible \
301 here due to private fields"));
304 // HACK(estebank): find a better way to figure out that this was a
305 // parser issue where a struct literal is being used on an expression
306 // where a brace being opened means a block is being started. Look
307 // ahead for the next text to see if `span` is followed by a `{`.
308 let sm = self.session.source_map();
311 sp = sm.next_point(sp);
312 match sm.span_to_snippet(sp) {
314 if snippet.chars().any(|c| { !c.is_whitespace() }) {
321 let followed_by_brace = match sm.span_to_snippet(sp) {
322 Ok(ref snippet) if snippet == "{" => true,
325 // In case this could be a struct literal that needs to be surrounded
326 // by parenthesis, find the appropriate span.
328 let mut closing_brace = None;
330 sp = sm.next_point(sp);
331 match sm.span_to_snippet(sp) {
334 let sp = span.to(sp);
335 if let Ok(snippet) = sm.span_to_snippet(sp) {
336 closing_brace = Some((sp, snippet));
344 // The bigger the span, the more likely we're incorrect --
345 // bound it to 100 chars long.
351 PathSource::Expr(Some(parent)) => {
353 ExprKind::MethodCall(ref path_assignment, _) => {
355 sm.start_point(parent.span)
356 .to(path_assignment.ident.span),
357 "use `::` to access an associated function",
360 path_assignment.ident),
361 Applicability::MaybeIncorrect
367 format!("did you mean `{} {{ /* fields */ }}`?",
373 PathSource::Expr(None) if followed_by_brace == true => {
374 if let Some((sp, snippet)) = closing_brace {
377 "surround the struct literal with parenthesis",
378 format!("({})", snippet),
379 Applicability::MaybeIncorrect,
384 format!("did you mean `({} {{ /* fields */ }})`?",
392 format!("did you mean `{} {{ /* fields */ }}`?",
399 (Def::Union(..), _) |
400 (Def::Variant(..), _) |
401 (Def::VariantCtor(_, CtorKind::Fictive), _) if ns == ValueNS => {
402 err.span_label(span, format!("did you mean `{} {{ /* fields */ }}`?",
405 (Def::SelfTy(..), _) if ns == ValueNS => {
406 err.span_label(span, fallback_label);
407 err.note("can't use `Self` as a constructor, you must use the \
408 implemented struct");
410 (Def::TyAlias(_), _) | (Def::AssociatedTy(..), _) if ns == ValueNS => {
411 err.note("can't use a type alias as a constructor");
419 impl<'a, 'b:'a> ImportResolver<'a, 'b> {
420 /// Adds suggestions for a path that cannot be resolved.
421 pub(crate) fn make_path_suggestion(
424 mut path: Vec<Segment>,
425 parent_scope: &ParentScope<'b>,
426 ) -> Option<(Vec<Segment>, Option<String>)> {
427 debug!("make_path_suggestion: span={:?} path={:?}", span, path);
429 match (path.get(0), path.get(1)) {
430 // `{{root}}::ident::...` on both editions.
431 // On 2015 `{{root}}` is usually added implicitly.
432 (Some(fst), Some(snd)) if fst.ident.name == keywords::PathRoot.name() &&
433 !snd.ident.is_path_segment_keyword() => {}
434 // `ident::...` on 2018.
435 (Some(fst), _) if fst.ident.span.rust_2018() &&
436 !fst.ident.is_path_segment_keyword() => {
437 // Insert a placeholder that's later replaced by `self`/`super`/etc.
438 path.insert(0, Segment::from_ident(keywords::Invalid.ident()));
443 self.make_missing_self_suggestion(span, path.clone(), parent_scope)
444 .or_else(|| self.make_missing_crate_suggestion(span, path.clone(), parent_scope))
445 .or_else(|| self.make_missing_super_suggestion(span, path.clone(), parent_scope))
446 .or_else(|| self.make_external_crate_suggestion(span, path, parent_scope))
449 /// Suggest a missing `self::` if that resolves to an correct module.
453 /// LL | use foo::Bar;
454 /// | ^^^ did you mean `self::foo`?
456 fn make_missing_self_suggestion(
459 mut path: Vec<Segment>,
460 parent_scope: &ParentScope<'b>,
461 ) -> Option<(Vec<Segment>, Option<String>)> {
462 // Replace first ident with `self` and check if that is valid.
463 path[0].ident.name = keywords::SelfLower.name();
464 let result = self.resolve_path(&path, None, parent_scope, false, span, CrateLint::No);
465 debug!("make_missing_self_suggestion: path={:?} result={:?}", path, result);
466 if let PathResult::Module(..) = result {
473 /// Suggests a missing `crate::` if that resolves to an correct module.
477 /// LL | use foo::Bar;
478 /// | ^^^ did you mean `crate::foo`?
480 fn make_missing_crate_suggestion(
483 mut path: Vec<Segment>,
484 parent_scope: &ParentScope<'b>,
485 ) -> Option<(Vec<Segment>, Option<String>)> {
486 // Replace first ident with `crate` and check if that is valid.
487 path[0].ident.name = keywords::Crate.name();
488 let result = self.resolve_path(&path, None, parent_scope, false, span, CrateLint::No);
489 debug!("make_missing_crate_suggestion: path={:?} result={:?}", path, result);
490 if let PathResult::Module(..) = result {
494 "`use` statements changed in Rust 2018; read more at \
495 <https://doc.rust-lang.org/edition-guide/rust-2018/module-system/path-\
496 clarity.html>".to_string()
504 /// Suggests a missing `super::` if that resolves to an correct module.
508 /// LL | use foo::Bar;
509 /// | ^^^ did you mean `super::foo`?
511 fn make_missing_super_suggestion(
514 mut path: Vec<Segment>,
515 parent_scope: &ParentScope<'b>,
516 ) -> Option<(Vec<Segment>, Option<String>)> {
517 // Replace first ident with `crate` and check if that is valid.
518 path[0].ident.name = keywords::Super.name();
519 let result = self.resolve_path(&path, None, parent_scope, false, span, CrateLint::No);
520 debug!("make_missing_super_suggestion: path={:?} result={:?}", path, result);
521 if let PathResult::Module(..) = result {
528 /// Suggests a missing external crate name if that resolves to an correct module.
532 /// LL | use foobar::Baz;
533 /// | ^^^^^^ did you mean `baz::foobar`?
536 /// Used when importing a submodule of an external crate but missing that crate's
537 /// name as the first part of path.
538 fn make_external_crate_suggestion(
541 mut path: Vec<Segment>,
542 parent_scope: &ParentScope<'b>,
543 ) -> Option<(Vec<Segment>, Option<String>)> {
544 if path[1].ident.span.rust_2015() {
548 // Sort extern crate names in reverse order to get
549 // 1) some consistent ordering for emitted dignostics, and
550 // 2) `std` suggestions before `core` suggestions.
551 let mut extern_crate_names =
552 self.resolver.extern_prelude.iter().map(|(ident, _)| ident.name).collect::<Vec<_>>();
553 extern_crate_names.sort_by_key(|name| Reverse(name.as_str()));
555 for name in extern_crate_names.into_iter() {
556 // Replace first ident with a crate name and check if that is valid.
557 path[0].ident.name = name;
558 let result = self.resolve_path(&path, None, parent_scope, false, span, CrateLint::No);
559 debug!("make_external_crate_suggestion: name={:?} path={:?} result={:?}",
561 if let PathResult::Module(..) = result {
562 return Some((path, None));