use {CrateLint, PathResult, Segment}; use macros::ParentScope; use syntax::symbol::keywords; use syntax_pos::Span; use resolve_imports::ImportResolver; use std::cmp::Reverse; impl<'a, 'b:'a> ImportResolver<'a, 'b> { /// Add suggestions for a path that cannot be resolved. pub(crate) fn make_path_suggestion( &mut self, span: Span, mut path: Vec, parent_scope: &ParentScope<'b>, ) -> Option<(Vec, Option)> { debug!("make_path_suggestion: span={:?} path={:?}", span, path); match (path.get(0), path.get(1)) { // `{{root}}::ident::...` on both editions. // On 2015 `{{root}}` is usually added implicitly. (Some(fst), Some(snd)) if fst.ident.name == keywords::PathRoot.name() && !snd.ident.is_path_segment_keyword() => {} // `ident::...` on 2018 (Some(fst), _) if fst.ident.span.rust_2018() && !fst.ident.is_path_segment_keyword() => { // Insert a placeholder that's later replaced by `self`/`super`/etc. path.insert(0, Segment::from_ident(keywords::Invalid.ident())); } _ => return None, } self.make_missing_self_suggestion(span, path.clone(), parent_scope) .or_else(|| self.make_missing_crate_suggestion(span, path.clone(), parent_scope)) .or_else(|| self.make_missing_super_suggestion(span, path.clone(), parent_scope)) .or_else(|| self.make_external_crate_suggestion(span, path, parent_scope)) } /// Suggest a missing `self::` if that resolves to an correct module. /// /// ``` /// | /// LL | use foo::Bar; /// | ^^^ did you mean `self::foo`? /// ``` fn make_missing_self_suggestion( &mut self, span: Span, mut path: Vec, parent_scope: &ParentScope<'b>, ) -> Option<(Vec, Option)> { // Replace first ident with `self` and check if that is valid. path[0].ident.name = keywords::SelfLower.name(); let result = self.resolve_path(&path, None, parent_scope, false, span, CrateLint::No); debug!("make_missing_self_suggestion: path={:?} result={:?}", path, result); if let PathResult::Module(..) = result { Some((path, None)) } else { None } } /// Suggest a missing `crate::` if that resolves to an correct module. /// /// ``` /// | /// LL | use foo::Bar; /// | ^^^ did you mean `crate::foo`? /// ``` fn make_missing_crate_suggestion( &mut self, span: Span, mut path: Vec, parent_scope: &ParentScope<'b>, ) -> Option<(Vec, Option)> { // Replace first ident with `crate` and check if that is valid. path[0].ident.name = keywords::Crate.name(); let result = self.resolve_path(&path, None, parent_scope, false, span, CrateLint::No); debug!("make_missing_crate_suggestion: path={:?} result={:?}", path, result); if let PathResult::Module(..) = result { Some(( path, Some( "`use` statements changed in Rust 2018; read more at \ ".to_string() ), )) } else { None } } /// Suggest a missing `super::` if that resolves to an correct module. /// /// ``` /// | /// LL | use foo::Bar; /// | ^^^ did you mean `super::foo`? /// ``` fn make_missing_super_suggestion( &mut self, span: Span, mut path: Vec, parent_scope: &ParentScope<'b>, ) -> Option<(Vec, Option)> { // Replace first ident with `crate` and check if that is valid. path[0].ident.name = keywords::Super.name(); let result = self.resolve_path(&path, None, parent_scope, false, span, CrateLint::No); debug!("make_missing_super_suggestion: path={:?} result={:?}", path, result); if let PathResult::Module(..) = result { Some((path, None)) } else { None } } /// Suggest a missing external crate name if that resolves to an correct module. /// /// ``` /// | /// LL | use foobar::Baz; /// | ^^^^^^ did you mean `baz::foobar`? /// ``` /// /// Used when importing a submodule of an external crate but missing that crate's /// name as the first part of path. fn make_external_crate_suggestion( &mut self, span: Span, mut path: Vec, parent_scope: &ParentScope<'b>, ) -> Option<(Vec, Option)> { if path[1].ident.span.rust_2015() { return None; } // Sort extern crate names in reverse order to get // 1) some consistent ordering for emitted dignostics and // 2) `std` suggestions before `core` suggestions. let mut extern_crate_names = self.resolver.extern_prelude.iter().map(|(ident, _)| ident.name).collect::>(); extern_crate_names.sort_by_key(|name| Reverse(name.as_str())); for name in extern_crate_names.into_iter() { // Replace first ident with a crate name and check if that is valid. path[0].ident.name = name; let result = self.resolve_path(&path, None, parent_scope, false, span, CrateLint::No); debug!("make_external_crate_suggestion: name={:?} path={:?} result={:?}", name, path, result); if let PathResult::Module(..) = result { return Some((path, None)); } } None } }