@@ -81,6 +81,8 @@ use rustc_front::hir::{ItemFn, ItemForeignMod, ItemImpl, ItemMod, ItemStatic, It
8181use rustc_front:: hir:: { ItemStruct , ItemTrait , ItemTy , ItemUse } ;
8282use rustc_front:: hir:: Local ;
8383use rustc_front:: hir:: { Pat , PatKind , Path , PrimTy } ;
84+ use rustc_front:: hir:: { PathSegment , PathParameters } ;
85+ use rustc_front:: hir:: HirVec ;
8486use rustc_front:: hir:: { TraitRef , Ty , TyBool , TyChar , TyFloat , TyInt } ;
8587use rustc_front:: hir:: { TyRptr , TyStr , TyUint , TyPath , TyPtr } ;
8688use rustc_front:: util:: walk_pat;
@@ -117,6 +119,12 @@ enum SuggestionType {
117119 NotFound ,
118120}
119121
122+ /// Candidates for a name resolution failure
123+ pub struct SuggestedCandidates {
124+ name : String ,
125+ candidates : Vec < Path > ,
126+ }
127+
120128pub enum ResolutionError < ' a > {
121129 /// error E0401: can't use type parameters from outer function
122130 TypeParametersFromOuterFunction ,
@@ -127,7 +135,7 @@ pub enum ResolutionError<'a> {
127135 /// error E0404: is not a trait
128136 IsNotATrait ( & ' a str ) ,
129137 /// error E0405: use of undeclared trait name
130- UndeclaredTraitName ( & ' a str ) ,
138+ UndeclaredTraitName ( & ' a str , SuggestedCandidates ) ,
131139 /// error E0406: undeclared associated type
132140 UndeclaredAssociatedType ,
133141 /// error E0407: method is not a member of trait
@@ -145,7 +153,7 @@ pub enum ResolutionError<'a> {
145153 /// error E0411: use of `Self` outside of an impl or trait
146154 SelfUsedOutsideImplOrTrait ,
147155 /// error E0412: use of undeclared
148- UseOfUndeclared ( & ' a str , & ' a str ) ,
156+ UseOfUndeclared ( & ' a str , & ' a str , SuggestedCandidates ) ,
149157 /// error E0413: declaration shadows an enum variant or unit-like struct in scope
150158 DeclarationShadowsEnumVariantOrUnitLikeStruct ( Name ) ,
151159 /// error E0414: only irrefutable patterns allowed here
@@ -248,12 +256,14 @@ fn resolve_struct_error<'b, 'a: 'b, 'tcx: 'a>(resolver: &'b Resolver<'a, 'tcx>,
248256 ResolutionError :: IsNotATrait ( name) => {
249257 struct_span_err ! ( resolver. session, span, E0404 , "`{}` is not a trait" , name)
250258 }
251- ResolutionError :: UndeclaredTraitName ( name) => {
252- struct_span_err ! ( resolver. session,
253- span,
254- E0405 ,
255- "use of undeclared trait name `{}`" ,
256- name)
259+ ResolutionError :: UndeclaredTraitName ( name, candidates) => {
260+ let mut err = struct_span_err ! ( resolver. session,
261+ span,
262+ E0405 ,
263+ "trait `{}` is not in scope" ,
264+ name) ;
265+ show_candidates ( & mut err, span, & candidates) ;
266+ err
257267 }
258268 ResolutionError :: UndeclaredAssociatedType => {
259269 struct_span_err ! ( resolver. session, span, E0406 , "undeclared associated type" )
@@ -313,13 +323,15 @@ fn resolve_struct_error<'b, 'a: 'b, 'tcx: 'a>(resolver: &'b Resolver<'a, 'tcx>,
313323 E0411 ,
314324 "use of `Self` outside of an impl or trait" )
315325 }
316- ResolutionError :: UseOfUndeclared ( kind, name) => {
317- struct_span_err ! ( resolver. session,
318- span,
319- E0412 ,
320- "use of undeclared {} `{}`" ,
321- kind,
322- name)
326+ ResolutionError :: UseOfUndeclared ( kind, name, candidates) => {
327+ let mut err = struct_span_err ! ( resolver. session,
328+ span,
329+ E0412 ,
330+ "{} `{}` is undefined or not in scope" ,
331+ kind,
332+ name) ;
333+ show_candidates ( & mut err, span, & candidates) ;
334+ err
323335 }
324336 ResolutionError :: DeclarationShadowsEnumVariantOrUnitLikeStruct ( name) => {
325337 struct_span_err ! ( resolver. session,
@@ -839,6 +851,7 @@ pub struct ModuleS<'a> {
839851pub type Module < ' a > = & ' a ModuleS < ' a > ;
840852
841853impl < ' a > ModuleS < ' a > {
854+
842855 fn new ( parent_link : ParentLink < ' a > , def : Option < Def > , external : bool , is_public : bool ) -> Self {
843856 ModuleS {
844857 parent_link : parent_link,
@@ -1970,10 +1983,28 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
19701983 Err ( ( ) )
19711984 }
19721985 } else {
1973- resolve_error ( self ,
1974- trait_path. span ,
1975- ResolutionError :: UndeclaredTraitName ( & path_names_to_string ( trait_path,
1976- path_depth) ) ) ;
1986+
1987+ // find possible candidates
1988+ let trait_name = trait_path. segments . last ( ) . unwrap ( ) . identifier . name ;
1989+ let candidates =
1990+ self . lookup_candidates (
1991+ trait_name,
1992+ TypeNS ,
1993+ |def| match def {
1994+ Def :: Trait ( _) => true ,
1995+ _ => false ,
1996+ } ,
1997+ ) ;
1998+
1999+ // create error object
2000+ let name = & path_names_to_string ( trait_path, path_depth) ;
2001+ let error =
2002+ ResolutionError :: UndeclaredTraitName (
2003+ name,
2004+ candidates,
2005+ ) ;
2006+
2007+ resolve_error ( self , trait_path. span , error) ;
19772008 Err ( ( ) )
19782009 }
19792010 }
@@ -2297,13 +2328,33 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
22972328 ty. span ,
22982329 ResolutionError :: SelfUsedOutsideImplOrTrait ) ;
22992330 } else {
2300- resolve_error ( self ,
2301- ty. span ,
2302- ResolutionError :: UseOfUndeclared (
2303- kind,
2304- & path_names_to_string ( path,
2305- 0 ) )
2306- ) ;
2331+ let segment = path. segments . last ( ) ;
2332+ let segment = segment. expect ( "missing name in path" ) ;
2333+ let type_name = segment. identifier . name ;
2334+
2335+ let candidates =
2336+ self . lookup_candidates (
2337+ type_name,
2338+ TypeNS ,
2339+ |def| match def {
2340+ Def :: Trait ( _) |
2341+ Def :: Enum ( _) |
2342+ Def :: Struct ( _) |
2343+ Def :: TyAlias ( _) => true ,
2344+ _ => false ,
2345+ } ,
2346+ ) ;
2347+
2348+ // create error object
2349+ let name = & path_names_to_string ( path, 0 ) ;
2350+ let error =
2351+ ResolutionError :: UseOfUndeclared (
2352+ kind,
2353+ name,
2354+ candidates,
2355+ ) ;
2356+
2357+ resolve_error ( self , ty. span , error) ;
23072358 }
23082359 }
23092360 }
@@ -3458,6 +3509,99 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
34583509 found_traits
34593510 }
34603511
3512+ /// When name resolution fails, this method can be used to look up candidate
3513+ /// entities with the expected name. It allows filtering them using the
3514+ /// supplied predicate (which should be used to only accept the types of
3515+ /// definitions expected e.g. traits). The lookup spans across all crates.
3516+ ///
3517+ /// NOTE: The method does not look into imports, but this is not a problem,
3518+ /// since we report the definitions (thus, the de-aliased imports).
3519+ fn lookup_candidates < FilterFn > ( & mut self ,
3520+ lookup_name : Name ,
3521+ namespace : Namespace ,
3522+ filter_fn : FilterFn ) -> SuggestedCandidates
3523+ where FilterFn : Fn ( Def ) -> bool {
3524+
3525+ let mut lookup_results = Vec :: new ( ) ;
3526+ let mut worklist = Vec :: new ( ) ;
3527+ worklist. push ( ( self . graph_root , Vec :: new ( ) , false ) ) ;
3528+
3529+ while let Some ( ( in_module,
3530+ path_segments,
3531+ in_module_is_extern) ) = worklist. pop ( ) {
3532+ build_reduced_graph:: populate_module_if_necessary ( self , & in_module) ;
3533+
3534+ in_module. for_each_child ( |name, ns, name_binding| {
3535+
3536+ // avoid imports entirely
3537+ if name_binding. is_import ( ) { return ; }
3538+
3539+ // collect results based on the filter function
3540+ if let Some ( def) = name_binding. def ( ) {
3541+ if name == lookup_name && ns == namespace && filter_fn ( def) {
3542+ // create the path
3543+ let ident = hir:: Ident :: from_name ( name) ;
3544+ let params = PathParameters :: none ( ) ;
3545+ let segment = PathSegment {
3546+ identifier : ident,
3547+ parameters : params,
3548+ } ;
3549+ let span = name_binding. span . unwrap_or ( syntax:: codemap:: DUMMY_SP ) ;
3550+ let mut segms = path_segments. clone ( ) ;
3551+ segms. push ( segment) ;
3552+ let segms = HirVec :: from_vec ( segms) ;
3553+ let path = Path {
3554+ span : span,
3555+ global : true ,
3556+ segments : segms,
3557+ } ;
3558+ // the entity is accessible in the following cases:
3559+ // 1. if it's defined in the same crate, it's always
3560+ // accessible (since private entities can be made public)
3561+ // 2. if it's defined in another crate, it's accessible
3562+ // only if both the module is public and the entity is
3563+ // declared as public (due to pruning, we don't explore
3564+ // outside crate private modules => no need to check this)
3565+ if !in_module_is_extern || name_binding. is_public ( ) {
3566+ lookup_results. push ( path) ;
3567+ }
3568+ }
3569+ }
3570+
3571+ // collect submodules to explore
3572+ if let Some ( module) = name_binding. module ( ) {
3573+ // form the path
3574+ let path_segments = match module. parent_link {
3575+ NoParentLink => path_segments. clone ( ) ,
3576+ ModuleParentLink ( _, name) => {
3577+ let mut paths = path_segments. clone ( ) ;
3578+ let ident = hir:: Ident :: from_name ( name) ;
3579+ let params = PathParameters :: none ( ) ;
3580+ let segm = PathSegment {
3581+ identifier : ident,
3582+ parameters : params,
3583+ } ;
3584+ paths. push ( segm) ;
3585+ paths
3586+ }
3587+ _ => unreachable ! ( ) ,
3588+ } ;
3589+
3590+ if !in_module_is_extern || name_binding. is_public ( ) {
3591+ // add the module to the lookup
3592+ let is_extern = in_module_is_extern || module. is_extern_crate ;
3593+ worklist. push ( ( module, path_segments, is_extern) ) ;
3594+ }
3595+ }
3596+ } )
3597+ }
3598+
3599+ SuggestedCandidates {
3600+ name : lookup_name. as_str ( ) . to_string ( ) ,
3601+ candidates : lookup_results,
3602+ }
3603+ }
3604+
34613605 fn record_def ( & mut self , node_id : NodeId , resolution : PathResolution ) {
34623606 debug ! ( "(recording def) recording {:?} for {}" , resolution, node_id) ;
34633607 assert ! ( match resolution. last_private {
@@ -3513,6 +3657,67 @@ fn path_names_to_string(path: &Path, depth: usize) -> String {
35133657 names_to_string ( & names[ ..] )
35143658}
35153659
3660+ /// When an entity with a given name is not available in scope, we search for
3661+ /// entities with that name in all crates. This method allows outputting the
3662+ /// results of this search in a programmer-friendly way
3663+ fn show_candidates ( session : & mut DiagnosticBuilder ,
3664+ span : syntax:: codemap:: Span ,
3665+ candidates : & SuggestedCandidates ) {
3666+
3667+ let paths = & candidates. candidates ;
3668+
3669+ if paths. len ( ) > 0 {
3670+ // don't show more than MAX_CANDIDATES results, so
3671+ // we're consistent with the trait suggestions
3672+ const MAX_CANDIDATES : usize = 5 ;
3673+
3674+ // we want consistent results across executions, but candidates are produced
3675+ // by iterating through a hash map, so make sure they are ordered:
3676+ let mut path_strings: Vec < _ > = paths. into_iter ( )
3677+ . map ( |p| path_names_to_string ( & p, 0 ) )
3678+ . collect ( ) ;
3679+ path_strings. sort ( ) ;
3680+
3681+ // behave differently based on how many candidates we have:
3682+ if !paths. is_empty ( ) {
3683+ if paths. len ( ) == 1 {
3684+ session. fileline_help (
3685+ span,
3686+ & format ! ( "you can to import it into scope: `use {};`." ,
3687+ & path_strings[ 0 ] ) ,
3688+ ) ;
3689+ } else {
3690+ session. fileline_help ( span, "you can import several candidates \
3691+ into scope (`use ...;`):") ;
3692+ let count = path_strings. len ( ) as isize - MAX_CANDIDATES as isize + 1 ;
3693+
3694+ for ( idx, path_string) in path_strings. iter ( ) . enumerate ( ) {
3695+ if idx == MAX_CANDIDATES - 1 && count > 1 {
3696+ session. fileline_help (
3697+ span,
3698+ & format ! ( " and {} other candidates" , count) . to_string ( ) ,
3699+ ) ;
3700+ break ;
3701+ } else {
3702+ session. fileline_help (
3703+ span,
3704+ & format ! ( " `{}`" , path_string) . to_string ( ) ,
3705+ ) ;
3706+ }
3707+ }
3708+ }
3709+ }
3710+ } else {
3711+ // nothing found:
3712+ session. fileline_help (
3713+ span,
3714+ & format ! ( "no candidates by the name of `{}` found in your \
3715+ project; maybe you misspelled the name or forgot to import \
3716+ an external crate?", candidates. name. to_string( ) ) ,
3717+ ) ;
3718+ } ;
3719+ }
3720+
35163721/// A somewhat inefficient routine to obtain the name of a module.
35173722fn module_to_string ( module : Module ) -> String {
35183723 let mut names = Vec :: new ( ) ;
0 commit comments