1010use std:: collections:: VecDeque ;
1111
1212use rustc_hir:: def:: { DefKind , Res } ;
13- use rustc_hir:: def_id:: { DefId , LocalDefId , CRATE_DEF_INDEX } ;
14- use rustc_hir:: ItemKind ;
13+ use rustc_hir:: def_id:: { DefId , LocalDefId , CRATE_DEF_INDEX , LOCAL_CRATE } ;
14+ use rustc_hir:: { ItemKind , UseKind } ;
1515use rustc_middle:: ty:: TyCtxt ;
1616
1717/// Attempts to resolve a simple path (in the form of a string) to a `DefId`.
@@ -196,12 +196,9 @@ fn resolve_in_foreign_module(
196196 return resolve_in_foreign_module ( tcx, inner_mod_id, segments) ;
197197 }
198198 }
199- Res :: Def ( DefKind :: Struct , type_id ) | Res :: Def ( DefKind :: Enum , type_id) => {
199+ Res :: Def ( DefKind :: Struct | DefKind :: Enum | DefKind :: Union , type_id) => {
200200 if first == child. ident . as_str ( ) && segments. len ( ) == 2 {
201- let maybe_resolved = resolve_in_inherent_impls ( tcx, type_id, & segments[ 1 ] ) ;
202- if maybe_resolved. is_some ( ) {
203- return maybe_resolved;
204- }
201+ return resolve_in_type ( tcx, type_id, & segments[ 1 ] ) ;
205202 }
206203 }
207204 _ => { }
@@ -215,30 +212,34 @@ fn resolve_in_foreign_module(
215212 None
216213}
217214
215+ /// Generates a more friendly string representation of a local module's name
216+ /// (the default representation for the crate root is the empty string).
217+ fn module_to_string ( tcx : TyCtxt , current_module : LocalDefId ) -> String {
218+ let def_id = current_module. to_def_id ( ) ;
219+ if def_id. is_crate_root ( ) {
220+ format ! ( "module `{}`" , tcx. crate_name( LOCAL_CRATE ) )
221+ } else {
222+ format ! ( "module `{}`" , tcx. def_path_str( def_id) )
223+ }
224+ }
225+
218226/// Resolves a path relative to a local module.
219227fn resolve_relative (
220228 tcx : TyCtxt ,
221229 current_module : LocalDefId ,
222230 mut segments : Segments ,
223231) -> Option < DefId > {
224- let current_module_string = || -> String {
225- let def_id = current_module. to_def_id ( ) ;
226- if def_id. is_crate_root ( ) {
227- "crate root" . to_string ( )
228- } else {
229- format ! ( "module `{}`" , tcx. def_path_str( def_id) )
230- }
231- } ;
232232 tracing:: debug!(
233233 "Resolving `{}` in local {}" ,
234234 segments_to_string( & segments) ,
235- current_module_string ( )
235+ module_to_string ( tcx , current_module )
236236 ) ;
237237
238238 let first = segments. front ( ) . or_else ( || {
239239 tracing:: debug!( "Unable to resolve the empty path" ) ;
240240 None
241241 } ) ?;
242+ let mut glob_imports = Vec :: new ( ) ;
242243 for item_id in tcx. hir ( ) . module_items ( current_module) {
243244 let item = tcx. hir ( ) . item ( item_id) ;
244245 let def_id = item. owner_id . def_id . to_def_id ( ) ;
@@ -247,7 +248,7 @@ fn resolve_relative(
247248 if first == item. ident . as_str ( ) && segments. len ( ) == 1 {
248249 tracing:: debug!(
249250 "Resolved `{first}` as a function in local {}" ,
250- current_module_string ( )
251+ module_to_string ( tcx , current_module )
251252 ) ;
252253 return Some ( def_id) ;
253254 }
@@ -258,41 +259,139 @@ fn resolve_relative(
258259 return resolve_relative ( tcx, def_id. expect_local ( ) , segments) ;
259260 }
260261 }
261- ItemKind :: Enum ( ..) | ItemKind :: Struct ( ..) => {
262+ ItemKind :: Enum ( ..) | ItemKind :: Struct ( ..) | ItemKind :: Union ( .. ) => {
262263 if first == item. ident . as_str ( ) && segments. len ( ) == 2 {
263- let maybe_resolved = resolve_in_inherent_impls ( tcx, def_id, & segments[ 1 ] ) ;
264- if maybe_resolved. is_some ( ) {
265- return maybe_resolved;
264+ return resolve_in_type ( tcx, def_id, & segments[ 1 ] ) ;
265+ }
266+ }
267+ ItemKind :: Use ( use_path, UseKind :: Single ) => {
268+ if first == item. ident . as_str ( ) {
269+ segments. pop_front ( ) ;
270+ return resolve_in_use ( tcx, use_path, segments) ;
271+ }
272+ }
273+ ItemKind :: Use ( use_path, UseKind :: Glob ) => {
274+ // Do not immediately try to resolve the path using this glob,
275+ // since paths resolved via non-globs take precedence.
276+ glob_imports. push ( use_path) ;
277+ }
278+ ItemKind :: ExternCrate ( orig_name_opt) => {
279+ if first == item. ident . as_str ( ) {
280+ if let Some ( orig_name) = orig_name_opt {
281+ segments[ 0 ] = orig_name. to_string ( ) ;
266282 }
283+ return resolve_external ( tcx, segments) ;
267284 }
268285 }
269286 _ => ( ) ,
270287 }
271288 }
272289
273- tracing:: debug!( "Unable to resolve `{first}` as an item in local {}" , current_module_string( ) ) ;
290+ resolve_in_glob_uses ( tcx, current_module, glob_imports, & segments) . or_else ( || {
291+ tracing:: debug!(
292+ "Unable to resolve `{first}` as an item in local {}" ,
293+ module_to_string( tcx, current_module)
294+ ) ;
295+ None
296+ } )
297+ }
298+
299+ /// Resolves a path relative to a local or foreign module.
300+ fn resolve_in_module ( tcx : TyCtxt , current_module : DefId , segments : Segments ) -> Option < DefId > {
301+ match current_module. as_local ( ) {
302+ None => resolve_in_foreign_module ( tcx, current_module, segments) ,
303+ Some ( local_id) => resolve_relative ( tcx, local_id, segments) ,
304+ }
305+ }
306+
307+ /// Resolves a path by exploring a non-glob use statement.
308+ fn resolve_in_use ( tcx : TyCtxt , use_path : & rustc_hir:: Path , segments : Segments ) -> Option < DefId > {
309+ if let Res :: Def ( def_kind, def_id) = use_path. res {
310+ tracing:: debug!(
311+ "Resolving `{}` via `use` import of `{}`" ,
312+ segments_to_string( & segments) ,
313+ tcx. def_path_str( def_id)
314+ ) ;
315+ match def_kind {
316+ DefKind :: Fn => {
317+ if segments. is_empty ( ) {
318+ tracing:: debug!(
319+ "Resolved `{}` to a function via `use` import of `{}`" ,
320+ segments_to_string( & segments) ,
321+ tcx. def_path_str( def_id)
322+ ) ;
323+ return Some ( def_id) ;
324+ }
325+ }
326+ DefKind :: Mod => return resolve_in_module ( tcx, def_id, segments) ,
327+ DefKind :: Struct | DefKind :: Enum | DefKind :: Union => {
328+ if segments. len ( ) == 1 {
329+ return resolve_in_type ( tcx, def_id, & segments[ 0 ] ) ;
330+ }
331+ }
332+ _ => ( ) ,
333+ }
334+ }
335+ tracing:: debug!( "Unable to resolve `{}` via `use` import" , segments_to_string( & segments) ) ;
274336 None
275337}
276338
277- /// Resolves a name in an `impl` block.
278- fn resolve_in_impl ( tcx : TyCtxt , impl_id : DefId , name : & str ) -> Option < DefId > {
279- tracing:: debug!( "Resolving `{name}` in impl block `{}`" , tcx. def_path_str( impl_id) ) ;
280- for assoc_item in tcx. associated_item_def_ids ( impl_id) {
281- let item_path = tcx. def_path_str ( * assoc_item) ;
282- let last = item_path. split ( "::" ) . last ( ) . unwrap ( ) ;
283- if last == name {
284- tracing:: debug!( "Resolved `{name}` in impl block `{}`" , tcx. def_path_str( impl_id) ) ;
285- return Some ( * assoc_item) ;
339+ /// Resolves a path by exploring glob use statements.
340+ fn resolve_in_glob_uses (
341+ tcx : TyCtxt ,
342+ current_module : LocalDefId ,
343+ glob_imports : Vec < & rustc_hir:: Path > ,
344+ segments : & Segments ,
345+ ) -> Option < DefId > {
346+ let glob_resolves = glob_imports
347+ . iter ( )
348+ . filter_map ( |use_path| {
349+ let span = tracing:: span!( tracing:: Level :: DEBUG , "glob_resolution" ) ;
350+ let _enter = span. enter ( ) ;
351+ resolve_in_glob_use ( tcx, use_path, segments. clone ( ) )
352+ } )
353+ . collect :: < Vec < _ > > ( ) ;
354+ if glob_resolves. len ( ) == 1 {
355+ return glob_resolves. first ( ) . copied ( ) ;
356+ }
357+ if glob_resolves. len ( ) > 1 {
358+ // Raise an error if it's ambiguous which glob import a function comes
359+ // from. rustc will also raise an error in this case if the ambiguous
360+ // function is present in code (and not just as an attribute argument).
361+ // TODO: We should make this consistent with error handling for other
362+ // cases (see <https://github.com/model-checking/kani/issues/2013>).
363+ let location = module_to_string ( tcx, current_module) ;
364+ let mut msg = format ! (
365+ "glob imports in local {location} make it impossible to \
366+ unambiguously resolve path; the possibilities are:"
367+ ) ;
368+ for def_id in glob_resolves {
369+ msg. push_str ( "\n \t " ) ;
370+ msg. push_str ( & tcx. def_path_str ( def_id) ) ;
286371 }
372+ tcx. sess . err ( msg) ;
287373 }
288- tracing:: debug!( "Unable to resolve `{name}` in impl block `{}`" , tcx. def_path_str( impl_id) ) ;
289374 None
290375}
291376
292- /// Resolves a name in the inherent `impl` blocks of a type (i.e., non-trait
293- /// `impl`s).
294- fn resolve_in_inherent_impls ( tcx : TyCtxt , type_id : DefId , name : & str ) -> Option < DefId > {
377+ /// Resolves a path by exploring a glob use statement.
378+ fn resolve_in_glob_use (
379+ tcx : TyCtxt ,
380+ use_path : & rustc_hir:: Path ,
381+ segments : Segments ,
382+ ) -> Option < DefId > {
383+ if let Res :: Def ( DefKind :: Mod , def_id) = use_path. res {
384+ resolve_in_module ( tcx, def_id, segments)
385+ } else {
386+ None
387+ }
388+ }
389+
390+ /// Resolves a method in a type. It currently does not resolve trait methods
391+ /// (see <https://github.com/model-checking/kani/issues/1997>).
392+ fn resolve_in_type ( tcx : TyCtxt , type_id : DefId , name : & str ) -> Option < DefId > {
295393 tracing:: debug!( "Resolving `{name}` in type `{}`" , tcx. def_path_str( type_id) ) ;
394+ // Try the inherent `impl` blocks (i.e., non-trait `impl`s).
296395 for impl_ in tcx. inherent_impls ( type_id) {
297396 let maybe_resolved = resolve_in_impl ( tcx, * impl_, name) ;
298397 if maybe_resolved. is_some ( ) {
@@ -303,6 +402,21 @@ fn resolve_in_inherent_impls(tcx: TyCtxt, type_id: DefId, name: &str) -> Option<
303402 None
304403}
305404
405+ /// Resolves a name in an `impl` block.
406+ fn resolve_in_impl ( tcx : TyCtxt , impl_id : DefId , name : & str ) -> Option < DefId > {
407+ tracing:: debug!( "Resolving `{name}` in impl block `{}`" , tcx. def_path_str( impl_id) ) ;
408+ for assoc_item in tcx. associated_item_def_ids ( impl_id) {
409+ let item_path = tcx. def_path_str ( * assoc_item) ;
410+ let last = item_path. split ( "::" ) . last ( ) . unwrap ( ) ;
411+ if last == name {
412+ tracing:: debug!( "Resolved `{name}` in impl block `{}`" , tcx. def_path_str( impl_id) ) ;
413+ return Some ( * assoc_item) ;
414+ }
415+ }
416+ tracing:: debug!( "Unable to resolve `{name}` in impl block `{}`" , tcx. def_path_str( impl_id) ) ;
417+ None
418+ }
419+
306420/// Does the current module have a (direct) submodule with the given name?
307421fn has_submodule_with_name ( tcx : TyCtxt , current_module : LocalDefId , name : & str ) -> bool {
308422 for item_id in tcx. hir ( ) . module_items ( current_module) {
0 commit comments