3 use errors::{Applicability, DiagnosticBuilder, DiagnosticId};
5 use rustc::hir::def::*;
6 use rustc::hir::def::Namespace::*;
7 use rustc::hir::def_id::{CRATE_DEF_INDEX, DefId};
8 use rustc::session::config::nightly_options;
9 use syntax::ast::{ExprKind};
10 use syntax::symbol::keywords;
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)
111 .filter(|ImportSuggestion { did, .. }| {
112 match (did, def.and_then(|def| def.opt_def_id())) {
113 (Some(suggestion_did), Some(actual_did)) => *suggestion_did != actual_did,
117 .collect::<Vec<_>>();
118 if candidates.is_empty() && is_expected(Def::Enum(DefId::local(CRATE_DEF_INDEX))) {
119 let enum_candidates =
120 self.lookup_import_candidates(ident, ns, is_enum_variant);
121 let mut enum_candidates = enum_candidates.iter()
123 import_candidate_to_enum_paths(&suggestion)
124 }).collect::<Vec<_>>();
125 enum_candidates.sort();
127 if !enum_candidates.is_empty() {
128 // Contextualize for E0412 "cannot find type", but don't belabor the point
129 // (that it's a variant) for E0573 "expected type, found variant".
130 let preamble = if def.is_none() {
131 let others = match enum_candidates.len() {
133 2 => " and 1 other".to_owned(),
134 n => format!(" and {} others", n)
136 format!("there is an enum variant `{}`{}; ",
137 enum_candidates[0].0, others)
141 let msg = format!("{}try using the variant's enum", preamble);
143 err.span_suggestions(
146 enum_candidates.into_iter()
147 .map(|(_variant_path, enum_ty_path)| enum_ty_path)
148 // Variants re-exported in prelude doesn't mean `prelude::v1` is the
150 // FIXME: is there a more principled way to do this that
151 // would work for other re-exports?
152 .filter(|enum_ty_path| enum_ty_path != "std::prelude::v1")
153 // Also write `Option` rather than `std::prelude::v1::Option`.
154 .map(|enum_ty_path| {
155 // FIXME #56861: DRY-er prelude filtering.
156 enum_ty_path.trim_start_matches("std::prelude::v1::").to_owned()
158 Applicability::MachineApplicable,
162 if path.len() == 1 && self.self_type_is_available(span) {
163 if let Some(candidate) = self.lookup_assoc_candidate(ident, ns, is_expected) {
164 let self_is_available = self.self_value_is_available(path[0].ident.span, span);
166 AssocSuggestion::Field => {
170 format!("self.{}", path_str),
171 Applicability::MachineApplicable,
173 if !self_is_available {
174 err.span_label(span, format!("`self` value is a keyword \
176 methods with `self` parameter"));
179 AssocSuggestion::MethodWithSelf if self_is_available => {
183 format!("self.{}", path_str),
184 Applicability::MachineApplicable,
187 AssocSuggestion::MethodWithSelf | AssocSuggestion::AssocItem => {
191 format!("Self::{}", path_str),
192 Applicability::MachineApplicable,
196 return (err, candidates);
200 let mut levenshtein_worked = false;
202 // Try Levenshtein algorithm.
203 let suggestion = self.lookup_typo_candidate(path, ns, is_expected, span);
204 if let Some(suggestion) = suggestion {
206 "{} {} with a similar name exists",
207 suggestion.article, suggestion.kind
212 suggestion.candidate.to_string(),
213 Applicability::MaybeIncorrect,
216 levenshtein_worked = true;
219 // Try context-dependent help if relaxed lookup didn't work.
220 if let Some(def) = def {
221 if self.smart_resolve_context_dependent_help(&mut err,
227 return (err, candidates);
232 if !levenshtein_worked {
233 err.span_label(base_span, fallback_label);
234 self.type_ascription_suggestion(&mut err, base_span);
239 /// Provides context-dependent help for errors reported by the `smart_resolve_path_fragment`
241 /// Returns `true` if able to provide context-dependent help.
242 fn smart_resolve_context_dependent_help(
244 err: &mut DiagnosticBuilder<'a>,
246 source: PathSource<'_>,
249 fallback_label: &str,
251 let ns = source.namespace();
252 let is_expected = &|def| source.is_expected(def);
254 match (def, source) {
255 (Def::Macro(..), _) => {
258 "use `!` to invoke the macro",
259 format!("{}!", path_str),
260 Applicability::MaybeIncorrect,
263 (Def::TyAlias(..), PathSource::Trait(_)) => {
264 err.span_label(span, "type aliases cannot be used as traits");
265 if nightly_options::is_nightly_build() {
266 err.note("did you mean to use a trait alias?");
269 (Def::Mod(..), PathSource::Expr(Some(parent))) => match parent.node {
270 ExprKind::Field(_, ident) => {
273 "use the path separator to refer to an item",
274 format!("{}::{}", path_str, ident),
275 Applicability::MaybeIncorrect,
278 ExprKind::MethodCall(ref segment, ..) => {
279 let span = parent.span.with_hi(segment.ident.span.hi());
282 "use the path separator to refer to an item",
283 format!("{}::{}", path_str, segment.ident),
284 Applicability::MaybeIncorrect,
289 (Def::Enum(..), PathSource::TupleStruct)
290 | (Def::Enum(..), PathSource::Expr(..)) => {
291 if let Some(variants) = self.collect_enum_variants(def) {
292 err.note(&format!("did you mean to use one \
293 of the following variants?\n{}",
295 .map(|suggestion| path_names_to_string(suggestion))
296 .map(|suggestion| format!("- `{}`", suggestion))
300 err.note("did you mean to use one of the enum's variants?");
303 (Def::Struct(def_id), _) if ns == ValueNS => {
304 if let Some((ctor_def, ctor_vis))
305 = self.struct_constructors.get(&def_id).cloned() {
306 let accessible_ctor = self.is_accessible(ctor_vis);
307 if is_expected(ctor_def) && !accessible_ctor {
308 err.span_label(span, format!("constructor is not visible \
309 here due to private fields"));
312 // HACK(estebank): find a better way to figure out that this was a
313 // parser issue where a struct literal is being used on an expression
314 // where a brace being opened means a block is being started. Look
315 // ahead for the next text to see if `span` is followed by a `{`.
316 let sm = self.session.source_map();
319 sp = sm.next_point(sp);
320 match sm.span_to_snippet(sp) {
322 if snippet.chars().any(|c| { !c.is_whitespace() }) {
329 let followed_by_brace = match sm.span_to_snippet(sp) {
330 Ok(ref snippet) if snippet == "{" => true,
333 // In case this could be a struct literal that needs to be surrounded
334 // by parenthesis, find the appropriate span.
336 let mut closing_brace = None;
338 sp = sm.next_point(sp);
339 match sm.span_to_snippet(sp) {
342 let sp = span.to(sp);
343 if let Ok(snippet) = sm.span_to_snippet(sp) {
344 closing_brace = Some((sp, snippet));
352 // The bigger the span, the more likely we're incorrect --
353 // bound it to 100 chars long.
359 PathSource::Expr(Some(parent)) => {
361 ExprKind::MethodCall(ref path_assignment, _) => {
363 sm.start_point(parent.span)
364 .to(path_assignment.ident.span),
365 "use `::` to access an associated function",
368 path_assignment.ident),
369 Applicability::MaybeIncorrect
375 format!("did you mean `{} {{ /* fields */ }}`?",
381 PathSource::Expr(None) if followed_by_brace == true => {
382 if let Some((sp, snippet)) = closing_brace {
385 "surround the struct literal with parenthesis",
386 format!("({})", snippet),
387 Applicability::MaybeIncorrect,
392 format!("did you mean `({} {{ /* fields */ }})`?",
400 format!("did you mean `{} {{ /* fields */ }}`?",
407 (Def::Union(..), _) |
408 (Def::Variant(..), _) |
409 (Def::VariantCtor(_, CtorKind::Fictive), _) if ns == ValueNS => {
410 err.span_label(span, format!("did you mean `{} {{ /* fields */ }}`?",
413 (Def::SelfTy(..), _) if ns == ValueNS => {
414 err.span_label(span, fallback_label);
415 err.note("can't use `Self` as a constructor, you must use the \
416 implemented struct");
418 (Def::TyAlias(_), _) | (Def::AssociatedTy(..), _) if ns == ValueNS => {
419 err.note("can't use a type alias as a constructor");
427 impl<'a, 'b:'a> ImportResolver<'a, 'b> {
428 /// Adds suggestions for a path that cannot be resolved.
429 pub(crate) fn make_path_suggestion(
432 mut path: Vec<Segment>,
433 parent_scope: &ParentScope<'b>,
434 ) -> Option<(Vec<Segment>, Option<String>)> {
435 debug!("make_path_suggestion: span={:?} path={:?}", span, path);
437 match (path.get(0), path.get(1)) {
438 // `{{root}}::ident::...` on both editions.
439 // On 2015 `{{root}}` is usually added implicitly.
440 (Some(fst), Some(snd)) if fst.ident.name == keywords::PathRoot.name() &&
441 !snd.ident.is_path_segment_keyword() => {}
442 // `ident::...` on 2018.
443 (Some(fst), _) if fst.ident.span.rust_2018() &&
444 !fst.ident.is_path_segment_keyword() => {
445 // Insert a placeholder that's later replaced by `self`/`super`/etc.
446 path.insert(0, Segment::from_ident(keywords::Invalid.ident()));
451 self.make_missing_self_suggestion(span, path.clone(), parent_scope)
452 .or_else(|| self.make_missing_crate_suggestion(span, path.clone(), parent_scope))
453 .or_else(|| self.make_missing_super_suggestion(span, path.clone(), parent_scope))
454 .or_else(|| self.make_external_crate_suggestion(span, path, parent_scope))
457 /// Suggest a missing `self::` if that resolves to an correct module.
461 /// LL | use foo::Bar;
462 /// | ^^^ did you mean `self::foo`?
464 fn make_missing_self_suggestion(
467 mut path: Vec<Segment>,
468 parent_scope: &ParentScope<'b>,
469 ) -> Option<(Vec<Segment>, Option<String>)> {
470 // Replace first ident with `self` and check if that is valid.
471 path[0].ident.name = keywords::SelfLower.name();
472 let result = self.resolve_path(&path, None, parent_scope, false, span, CrateLint::No);
473 debug!("make_missing_self_suggestion: path={:?} result={:?}", path, result);
474 if let PathResult::Module(..) = result {
481 /// Suggests a missing `crate::` if that resolves to an correct module.
485 /// LL | use foo::Bar;
486 /// | ^^^ did you mean `crate::foo`?
488 fn make_missing_crate_suggestion(
491 mut path: Vec<Segment>,
492 parent_scope: &ParentScope<'b>,
493 ) -> Option<(Vec<Segment>, Option<String>)> {
494 // Replace first ident with `crate` and check if that is valid.
495 path[0].ident.name = keywords::Crate.name();
496 let result = self.resolve_path(&path, None, parent_scope, false, span, CrateLint::No);
497 debug!("make_missing_crate_suggestion: path={:?} result={:?}", path, result);
498 if let PathResult::Module(..) = result {
502 "`use` statements changed in Rust 2018; read more at \
503 <https://doc.rust-lang.org/edition-guide/rust-2018/module-system/path-\
504 clarity.html>".to_string()
512 /// Suggests a missing `super::` if that resolves to an correct module.
516 /// LL | use foo::Bar;
517 /// | ^^^ did you mean `super::foo`?
519 fn make_missing_super_suggestion(
522 mut path: Vec<Segment>,
523 parent_scope: &ParentScope<'b>,
524 ) -> Option<(Vec<Segment>, Option<String>)> {
525 // Replace first ident with `crate` and check if that is valid.
526 path[0].ident.name = keywords::Super.name();
527 let result = self.resolve_path(&path, None, parent_scope, false, span, CrateLint::No);
528 debug!("make_missing_super_suggestion: path={:?} result={:?}", path, result);
529 if let PathResult::Module(..) = result {
536 /// Suggests a missing external crate name if that resolves to an correct module.
540 /// LL | use foobar::Baz;
541 /// | ^^^^^^ did you mean `baz::foobar`?
544 /// Used when importing a submodule of an external crate but missing that crate's
545 /// name as the first part of path.
546 fn make_external_crate_suggestion(
549 mut path: Vec<Segment>,
550 parent_scope: &ParentScope<'b>,
551 ) -> Option<(Vec<Segment>, Option<String>)> {
552 if path[1].ident.span.rust_2015() {
556 // Sort extern crate names in reverse order to get
557 // 1) some consistent ordering for emitted dignostics, and
558 // 2) `std` suggestions before `core` suggestions.
559 let mut extern_crate_names =
560 self.resolver.extern_prelude.iter().map(|(ident, _)| ident.name).collect::<Vec<_>>();
561 extern_crate_names.sort_by_key(|name| Reverse(name.as_str()));
563 for name in extern_crate_names.into_iter() {
564 // Replace first ident with a crate name and check if that is valid.
565 path[0].ident.name = name;
566 let result = self.resolve_path(&path, None, parent_scope, false, span, CrateLint::No);
567 debug!("make_external_crate_suggestion: name={:?} path={:?} result={:?}",
569 if let PathResult::Module(..) = result {
570 return Some((path, None));