@@ -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,
@@ -1969,10 +1982,28 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
19691982 Err ( ( ) )
19701983 }
19711984 } else {
1972- resolve_error ( self ,
1973- trait_path. span ,
1974- ResolutionError :: UndeclaredTraitName ( & path_names_to_string ( trait_path,
1975- path_depth) ) ) ;
1985+
1986+ // find possible candidates
1987+ let trait_name = trait_path. segments . last ( ) . unwrap ( ) . identifier . name ;
1988+ let candidates =
1989+ self . lookup_candidates (
1990+ trait_name,
1991+ TypeNS ,
1992+ |def| match def {
1993+ Def :: Trait ( _) => true ,
1994+ _ => false ,
1995+ } ,
1996+ ) ;
1997+
1998+ // create error object
1999+ let name = & path_names_to_string ( trait_path, path_depth) ;
2000+ let error =
2001+ ResolutionError :: UndeclaredTraitName (
2002+ name,
2003+ candidates,
2004+ ) ;
2005+
2006+ resolve_error ( self , trait_path. span , error) ;
19762007 Err ( ( ) )
19772008 }
19782009 }
@@ -2296,13 +2327,33 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
22962327 ty. span ,
22972328 ResolutionError :: SelfUsedOutsideImplOrTrait ) ;
22982329 } else {
2299- resolve_error ( self ,
2300- ty. span ,
2301- ResolutionError :: UseOfUndeclared (
2302- kind,
2303- & path_names_to_string ( path,
2304- 0 ) )
2305- ) ;
2330+ let segment = path. segments . last ( ) ;
2331+ let segment = segment. expect ( "missing name in path" ) ;
2332+ let type_name = segment. identifier . name ;
2333+
2334+ let candidates =
2335+ self . lookup_candidates (
2336+ type_name,
2337+ TypeNS ,
2338+ |def| match def {
2339+ Def :: Trait ( _) |
2340+ Def :: Enum ( _) |
2341+ Def :: Struct ( _) |
2342+ Def :: TyAlias ( _) => true ,
2343+ _ => false ,
2344+ } ,
2345+ ) ;
2346+
2347+ // create error object
2348+ let name = & path_names_to_string ( path, 0 ) ;
2349+ let error =
2350+ ResolutionError :: UseOfUndeclared (
2351+ kind,
2352+ name,
2353+ candidates,
2354+ ) ;
2355+
2356+ resolve_error ( self , ty. span , error) ;
23062357 }
23072358 }
23082359 }
@@ -3457,6 +3508,99 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
34573508 found_traits
34583509 }
34593510
3511+ /// When name resolution fails, this method can be used to look up candidate
3512+ /// entities with the expected name. It allows filtering them using the
3513+ /// supplied predicate (which should be used to only accept the types of
3514+ /// definitions expected e.g. traits). The lookup spans across all crates.
3515+ ///
3516+ /// NOTE: The method does not look into imports, but this is not a problem,
3517+ /// since we report the definitions (thus, the de-aliased imports).
3518+ fn lookup_candidates < FilterFn > ( & mut self ,
3519+ lookup_name : Name ,
3520+ namespace : Namespace ,
3521+ filter_fn : FilterFn ) -> SuggestedCandidates
3522+ where FilterFn : Fn ( Def ) -> bool {
3523+
3524+ let mut lookup_results = Vec :: new ( ) ;
3525+ let mut worklist = Vec :: new ( ) ;
3526+ worklist. push ( ( self . graph_root , Vec :: new ( ) , false ) ) ;
3527+
3528+ while let Some ( ( in_module,
3529+ path_segments,
3530+ in_module_is_extern) ) = worklist. pop ( ) {
3531+ build_reduced_graph:: populate_module_if_necessary ( self , & in_module) ;
3532+
3533+ in_module. for_each_child ( |name, ns, name_binding| {
3534+
3535+ // avoid imports entirely
3536+ if name_binding. is_import ( ) { return ; }
3537+
3538+ // collect results based on the filter function
3539+ if let Some ( def) = name_binding. def ( ) {
3540+ if name == lookup_name && ns == namespace && filter_fn ( def) {
3541+ // create the path
3542+ let ident = hir:: Ident :: from_name ( name) ;
3543+ let params = PathParameters :: none ( ) ;
3544+ let segment = PathSegment {
3545+ identifier : ident,
3546+ parameters : params,
3547+ } ;
3548+ let span = name_binding. span . unwrap_or ( syntax:: codemap:: DUMMY_SP ) ;
3549+ let mut segms = path_segments. clone ( ) ;
3550+ segms. push ( segment) ;
3551+ let segms = HirVec :: from_vec ( segms) ;
3552+ let path = Path {
3553+ span : span,
3554+ global : true ,
3555+ segments : segms,
3556+ } ;
3557+ // the entity is accessible in the following cases:
3558+ // 1. if it's defined in the same crate, it's always
3559+ // accessible (since private entities can be made public)
3560+ // 2. if it's defined in another crate, it's accessible
3561+ // only if both the module is public and the entity is
3562+ // declared as public (due to pruning, we don't explore
3563+ // outside crate private modules => no need to check this)
3564+ if !in_module_is_extern || name_binding. is_public ( ) {
3565+ lookup_results. push ( path) ;
3566+ }
3567+ }
3568+ }
3569+
3570+ // collect submodules to explore
3571+ if let Some ( module) = name_binding. module ( ) {
3572+ // form the path
3573+ let path_segments = match module. parent_link {
3574+ NoParentLink => path_segments. clone ( ) ,
3575+ ModuleParentLink ( _, name) => {
3576+ let mut paths = path_segments. clone ( ) ;
3577+ let ident = hir:: Ident :: from_name ( name) ;
3578+ let params = PathParameters :: none ( ) ;
3579+ let segm = PathSegment {
3580+ identifier : ident,
3581+ parameters : params,
3582+ } ;
3583+ paths. push ( segm) ;
3584+ paths
3585+ }
3586+ _ => unreachable ! ( ) ,
3587+ } ;
3588+
3589+ if !in_module_is_extern || name_binding. is_public ( ) {
3590+ // add the module to the lookup
3591+ let is_extern = in_module_is_extern || module. is_extern_crate ;
3592+ worklist. push ( ( module, path_segments, is_extern) ) ;
3593+ }
3594+ }
3595+ } )
3596+ }
3597+
3598+ SuggestedCandidates {
3599+ name : lookup_name. as_str ( ) . to_string ( ) ,
3600+ candidates : lookup_results,
3601+ }
3602+ }
3603+
34603604 fn record_def ( & mut self , node_id : NodeId , resolution : PathResolution ) {
34613605 debug ! ( "(recording def) recording {:?} for {}" , resolution, node_id) ;
34623606 assert ! ( match resolution. last_private {
@@ -3512,6 +3656,67 @@ fn path_names_to_string(path: &Path, depth: usize) -> String {
35123656 names_to_string ( & names[ ..] )
35133657}
35143658
3659+ /// When an entity with a given name is not available in scope, we search for
3660+ /// entities with that name in all crates. This method allows outputting the
3661+ /// results of this search in a programmer-friendly way
3662+ fn show_candidates ( session : & mut DiagnosticBuilder ,
3663+ span : syntax:: codemap:: Span ,
3664+ candidates : & SuggestedCandidates ) {
3665+
3666+ let paths = & candidates. candidates ;
3667+
3668+ if paths. len ( ) > 0 {
3669+ // don't show more than MAX_CANDIDATES results, so
3670+ // we're consistent with the trait suggestions
3671+ const MAX_CANDIDATES : usize = 5 ;
3672+
3673+ // we want consistent results across executions, but candidates are produced
3674+ // by iterating through a hash map, so make sure they are ordered:
3675+ let mut path_strings: Vec < _ > = paths. into_iter ( )
3676+ . map ( |p| path_names_to_string ( & p, 0 ) )
3677+ . collect ( ) ;
3678+ path_strings. sort ( ) ;
3679+
3680+ // behave differently based on how many candidates we have:
3681+ if !paths. is_empty ( ) {
3682+ if paths. len ( ) == 1 {
3683+ session. fileline_help (
3684+ span,
3685+ & format ! ( "you can to import it into scope: `use {};`." ,
3686+ & path_strings[ 0 ] ) ,
3687+ ) ;
3688+ } else {
3689+ session. fileline_help ( span, "you can import several candidates \
3690+ into scope (`use ...;`):") ;
3691+ let count = path_strings. len ( ) as isize - MAX_CANDIDATES as isize + 1 ;
3692+
3693+ for ( idx, path_string) in path_strings. iter ( ) . enumerate ( ) {
3694+ if idx == MAX_CANDIDATES - 1 && count > 1 {
3695+ session. fileline_help (
3696+ span,
3697+ & format ! ( " and {} other candidates" , count) . to_string ( ) ,
3698+ ) ;
3699+ break ;
3700+ } else {
3701+ session. fileline_help (
3702+ span,
3703+ & format ! ( " `{}`" , path_string) . to_string ( ) ,
3704+ ) ;
3705+ }
3706+ }
3707+ }
3708+ }
3709+ } else {
3710+ // nothing found:
3711+ session. fileline_help (
3712+ span,
3713+ & format ! ( "no candidates by the name of `{}` found in your \
3714+ project; maybe you misspelled the name or forgot to import \
3715+ an external crate?", candidates. name. to_string( ) ) ,
3716+ ) ;
3717+ } ;
3718+ }
3719+
35153720/// A somewhat inefficient routine to obtain the name of a module.
35163721fn module_to_string < ' a > ( module : Module < ' a > ) -> String {
35173722 let mut names = Vec :: new ( ) ;
0 commit comments