1 use crate::{CrateLint, Module, ModuleKind, ModuleOrUniformRoot}
;
2 use crate::{PathResult, PathSource, Segment}
;
3 use crate::path_names_to_string
;
4 use crate::diagnostics
::{ImportSuggestion, TypoSuggestion}
;
5 use crate::late
::{LateResolutionVisitor, RibKind}
;
7 use errors
::{Applicability, DiagnosticBuilder, DiagnosticId}
;
9 use rustc
::hir
::def
::{self, DefKind, CtorKind}
;
10 use rustc
::hir
::def
::Namespace
::{self, *}
;
11 use rustc
::hir
::def_id
::{CRATE_DEF_INDEX, DefId}
;
12 use rustc
::hir
::PrimTy
;
13 use rustc
::session
::config
::nightly_options
;
14 use rustc
::util
::nodemap
::FxHashSet
;
15 use syntax
::ast
::{self, Expr, ExprKind, Ident, NodeId, Path, Ty, TyKind}
;
16 use syntax
::symbol
::kw
;
17 use syntax
::util
::lev_distance
::find_best_match_for_name
;
18 use syntax_pos
::hygiene
::MacroKind
;
21 use rustc_error_codes
::*;
23 type Res
= def
::Res
<ast
::NodeId
>;
25 /// A field or associated item from self type suggested in case of resolution failure.
26 enum AssocSuggestion
{
32 fn is_self_type(path
: &[Segment
], namespace
: Namespace
) -> bool
{
33 namespace
== TypeNS
&& path
.len() == 1 && path
[0].ident
.name
== kw
::SelfUpper
36 fn is_self_value(path
: &[Segment
], namespace
: Namespace
) -> bool
{
37 namespace
== ValueNS
&& path
.len() == 1 && path
[0].ident
.name
== kw
::SelfLower
40 /// Gets the stringified path for an enum from an `ImportSuggestion` for an enum variant.
41 fn import_candidate_to_enum_paths(suggestion
: &ImportSuggestion
) -> (String
, String
) {
42 let variant_path
= &suggestion
.path
;
43 let variant_path_string
= path_names_to_string(variant_path
);
45 let path_len
= suggestion
.path
.segments
.len();
46 let enum_path
= ast
::Path
{
47 span
: suggestion
.path
.span
,
48 segments
: suggestion
.path
.segments
[0..path_len
- 1].to_vec(),
50 let enum_path_string
= path_names_to_string(&enum_path
);
52 (variant_path_string
, enum_path_string
)
55 impl<'a
> LateResolutionVisitor
<'a
, '_
> {
56 /// Handles error reporting for `smart_resolve_path_fragment` function.
57 /// Creates base error and amends it with one short label and possibly some longer helps/notes.
58 pub(crate) fn smart_resolve_report_errors(
62 source
: PathSource
<'_
>,
64 ) -> (DiagnosticBuilder
<'a
>, Vec
<ImportSuggestion
>) {
65 let ident_span
= path
.last().map_or(span
, |ident
| ident
.ident
.span
);
66 let ns
= source
.namespace();
67 let is_expected
= &|res
| source
.is_expected(res
);
68 let is_enum_variant
= &|res
| {
69 if let Res
::Def(DefKind
::Variant
, _
) = res { true }
else { false }
72 // Make the base error.
73 let expected
= source
.descr_expected();
74 let path_str
= Segment
::names_to_string(path
);
75 let item_str
= path
.last().unwrap().ident
;
76 let code
= source
.error_code(res
.is_some());
77 let (base_msg
, fallback_label
, base_span
, could_be_expr
) = if let Some(res
) = res
{
78 (format
!("expected {}, found {} `{}`", expected
, res
.descr(), path_str
),
79 format
!("not a {}", expected
),
82 Res
::Def(DefKind
::Fn
, _
) => {
83 // Verify whether this is a fn call or an Fn used as a type.
84 self.r
.session
.source_map().span_to_snippet(span
).map(|snippet
| {
85 snippet
.ends_with('
)'
)
88 Res
::Def(DefKind
::Ctor(..), _
) |
89 Res
::Def(DefKind
::Method
, _
) |
90 Res
::Def(DefKind
::Const
, _
) |
91 Res
::Def(DefKind
::AssocConst
, _
) |
94 Res
::Local(_
) => true,
98 let item_span
= path
.last().unwrap().ident
.span
;
99 let (mod_prefix
, mod_str
) = if path
.len() == 1 {
100 (String
::new(), "this scope".to_string())
101 } else if path
.len() == 2 && path
[0].ident
.name
== kw
::PathRoot
{
102 (String
::new(), "the crate root".to_string())
104 let mod_path
= &path
[..path
.len() - 1];
105 let mod_prefix
= match self.resolve_path(
106 mod_path
, Some(TypeNS
), false, span
, CrateLint
::No
108 PathResult
::Module(ModuleOrUniformRoot
::Module(module
)) => module
.res(),
110 }.map_or(String
::new(), |res
| format
!("{} ", res
.descr()));
111 (mod_prefix
, format
!("`{}`", Segment
::names_to_string(mod_path
)))
113 (format
!("cannot find {} `{}` in {}{}", expected
, item_str
, mod_prefix
, mod_str
),
114 format
!("not found in {}", mod_str
),
119 let code
= DiagnosticId
::Error(code
.into());
120 let mut err
= self.r
.session
.struct_span_err_with_code(base_span
, &base_msg
, code
);
122 // Emit help message for fake-self from other languages (e.g., `this` in Javascript).
123 if ["this", "my"].contains(&&*item_str
.as_str())
124 && self.self_value_is_available(path
[0].ident
.span
, span
) {
129 Applicability
::MaybeIncorrect
,
133 // Emit special messages for unresolved `Self` and `self`.
134 if is_self_type(path
, ns
) {
135 syntax
::diagnostic_used
!(E0411
);
136 err
.code(DiagnosticId
::Error("E0411".into()));
139 format
!("`Self` is only available in impls, traits, and type definitions"),
141 return (err
, Vec
::new());
143 if is_self_value(path
, ns
) {
144 debug
!("smart_resolve_path_fragment: E0424, source={:?}", source
);
146 syntax
::diagnostic_used
!(E0424
);
147 err
.code(DiagnosticId
::Error("E0424".into()));
148 err
.span_label(span
, match source
{
149 PathSource
::Pat
=> format
!(
150 "`self` value is a keyword and may not be bound to variables or shadowed",
153 "`self` value is a keyword only available in methods with a `self` parameter",
156 if let Some(span
) = &self.diagnostic_metadata
.current_function
{
157 err
.span_label(*span
, "this function doesn't have a `self` parameter");
159 return (err
, Vec
::new());
162 // Try to lookup name in more relaxed fashion for better error reporting.
163 let ident
= path
.last().unwrap().ident
;
164 let candidates
= self.r
.lookup_import_candidates(ident
, ns
, is_expected
)
166 .filter(|ImportSuggestion { did, .. }
| {
167 match (did
, res
.and_then(|res
| res
.opt_def_id())) {
168 (Some(suggestion_did
), Some(actual_did
)) => *suggestion_did
!= actual_did
,
172 .collect
::<Vec
<_
>>();
173 let crate_def_id
= DefId
::local(CRATE_DEF_INDEX
);
174 if candidates
.is_empty() && is_expected(Res
::Def(DefKind
::Enum
, crate_def_id
)) {
175 let enum_candidates
=
176 self.r
.lookup_import_candidates(ident
, ns
, is_enum_variant
);
177 let mut enum_candidates
= enum_candidates
.iter()
179 import_candidate_to_enum_paths(&suggestion
)
180 }).collect
::<Vec
<_
>>();
181 enum_candidates
.sort();
183 if !enum_candidates
.is_empty() {
184 // Contextualize for E0412 "cannot find type", but don't belabor the point
185 // (that it's a variant) for E0573 "expected type, found variant".
186 let preamble
= if res
.is_none() {
187 let others
= match enum_candidates
.len() {
189 2 => " and 1 other".to_owned(),
190 n
=> format
!(" and {} others", n
)
192 format
!("there is an enum variant `{}`{}; ",
193 enum_candidates
[0].0, others
)
197 let msg
= format
!("{}try using the variant's enum", preamble
);
199 err
.span_suggestions(
202 enum_candidates
.into_iter()
203 .map(|(_variant_path
, enum_ty_path
)| enum_ty_path
)
204 // Variants re-exported in prelude doesn't mean `prelude::v1` is the
206 // FIXME: is there a more principled way to do this that
207 // would work for other re-exports?
208 .filter(|enum_ty_path
| enum_ty_path
!= "std::prelude::v1")
209 // Also write `Option` rather than `std::prelude::v1::Option`.
210 .map(|enum_ty_path
| {
211 // FIXME #56861: DRY-er prelude filtering.
212 enum_ty_path
.trim_start_matches("std::prelude::v1::").to_owned()
214 Applicability
::MachineApplicable
,
218 if path
.len() == 1 && self.self_type_is_available(span
) {
219 if let Some(candidate
) = self.lookup_assoc_candidate(ident
, ns
, is_expected
) {
220 let self_is_available
= self.self_value_is_available(path
[0].ident
.span
, span
);
222 AssocSuggestion
::Field
=> {
223 if self_is_available
{
226 "you might have meant to use the available field",
227 format
!("self.{}", path_str
),
228 Applicability
::MachineApplicable
,
233 "a field by this name exists in `Self`",
237 AssocSuggestion
::MethodWithSelf
if self_is_available
=> {
241 format
!("self.{}", path_str
),
242 Applicability
::MachineApplicable
,
245 AssocSuggestion
::MethodWithSelf
| AssocSuggestion
::AssocItem
=> {
249 format
!("Self::{}", path_str
),
250 Applicability
::MachineApplicable
,
254 return (err
, candidates
);
258 // Try Levenshtein algorithm.
259 let typo_sugg
= self.lookup_typo_candidate(path
, ns
, is_expected
, span
);
260 let levenshtein_worked
= self.r
.add_typo_suggestion(&mut err
, typo_sugg
, ident_span
);
262 // Try context-dependent help if relaxed lookup didn't work.
263 if let Some(res
) = res
{
264 if self.smart_resolve_context_dependent_help(
272 return (err
, candidates
);
277 if !levenshtein_worked
{
278 err
.span_label(base_span
, fallback_label
);
279 self.type_ascription_suggestion(&mut err
, base_span
);
280 match self.diagnostic_metadata
.current_let_binding
{
281 Some((pat_sp
, Some(ty_sp
), None
))
282 if ty_sp
.contains(base_span
) && could_be_expr
=> {
283 err
.span_suggestion_short(
284 pat_sp
.between(ty_sp
),
285 "use `=` if you meant to assign",
287 Applicability
::MaybeIncorrect
,
296 fn followed_by_brace(&self, span
: Span
) -> (bool
, Option
<(Span
, String
)>) {
297 // HACK(estebank): find a better way to figure out that this was a
298 // parser issue where a struct literal is being used on an expression
299 // where a brace being opened means a block is being started. Look
300 // ahead for the next text to see if `span` is followed by a `{`.
301 let sm
= self.r
.session
.source_map();
304 sp
= sm
.next_point(sp
);
305 match sm
.span_to_snippet(sp
) {
307 if snippet
.chars().any(|c
| { !c.is_whitespace() }
) {
314 let followed_by_brace
= match sm
.span_to_snippet(sp
) {
315 Ok(ref snippet
) if snippet
== "{" => true,
318 // In case this could be a struct literal that needs to be surrounded
319 // by parenthesis, find the appropriate span.
321 let mut closing_brace
= None
;
323 sp
= sm
.next_point(sp
);
324 match sm
.span_to_snippet(sp
) {
327 let sp
= span
.to(sp
);
328 if let Ok(snippet
) = sm
.span_to_snippet(sp
) {
329 closing_brace
= Some((sp
, snippet
));
337 // The bigger the span, the more likely we're incorrect --
338 // bound it to 100 chars long.
343 return (followed_by_brace
, closing_brace
)
346 /// Provides context-dependent help for errors reported by the `smart_resolve_path_fragment`
348 /// Returns `true` if able to provide context-dependent help.
349 fn smart_resolve_context_dependent_help(
351 err
: &mut DiagnosticBuilder
<'a
>,
353 source
: PathSource
<'_
>,
356 fallback_label
: &str,
358 let ns
= source
.namespace();
359 let is_expected
= &|res
| source
.is_expected(res
);
361 let path_sep
= |err
: &mut DiagnosticBuilder
<'_
>, expr
: &Expr
| match expr
.kind
{
362 ExprKind
::Field(_
, ident
) => {
365 "use the path separator to refer to an item",
366 format
!("{}::{}", path_str
, ident
),
367 Applicability
::MaybeIncorrect
,
371 ExprKind
::MethodCall(ref segment
, ..) => {
372 let span
= expr
.span
.with_hi(segment
.ident
.span
.hi());
375 "use the path separator to refer to an item",
376 format
!("{}::{}", path_str
, segment
.ident
),
377 Applicability
::MaybeIncorrect
,
384 let mut bad_struct_syntax_suggestion
= |def_id
: DefId
| {
385 let (followed_by_brace
, closing_brace
) = self.followed_by_brace(span
);
386 let mut suggested
= false;
388 PathSource
::Expr(Some(parent
)) => {
389 suggested
= path_sep(err
, &parent
);
391 PathSource
::Expr(None
) if followed_by_brace
== true => {
392 if let Some((sp
, snippet
)) = closing_brace
{
395 "surround the struct literal with parenthesis",
396 format
!("({})", snippet
),
397 Applicability
::MaybeIncorrect
,
401 span
, // Note the parenthesis surrounding the suggestion below
402 format
!("did you mean `({} {{ /* fields */ }})`?", path_str
),
410 if let Some(span
) = self.r
.definitions
.opt_span(def_id
) {
411 err
.span_label(span
, &format
!("`{}` defined here", path_str
));
415 format
!("did you mean `{} {{ /* fields */ }}`?", path_str
),
420 match (res
, source
) {
421 (Res
::Def(DefKind
::Macro(MacroKind
::Bang
), _
), _
) => {
424 "use `!` to invoke the macro",
425 format
!("{}!", path_str
),
426 Applicability
::MaybeIncorrect
,
428 if path_str
== "try" && span
.rust_2015() {
429 err
.note("if you want the `try` keyword, you need to be in the 2018 edition");
432 (Res
::Def(DefKind
::TyAlias
, _
), PathSource
::Trait(_
)) => {
433 err
.span_label(span
, "type aliases cannot be used as traits");
434 if nightly_options
::is_nightly_build() {
435 err
.note("did you mean to use a trait alias?");
438 (Res
::Def(DefKind
::Mod
, _
), PathSource
::Expr(Some(parent
))) => {
439 if !path_sep(err
, &parent
) {
443 (Res
::Def(DefKind
::Enum
, def_id
), PathSource
::TupleStruct
)
444 | (Res
::Def(DefKind
::Enum
, def_id
), PathSource
::Expr(..)) => {
445 if let Some(variants
) = self.collect_enum_variants(def_id
) {
446 if !variants
.is_empty() {
447 let msg
= if variants
.len() == 1 {
448 "try using the enum's variant"
450 "try using one of the enum's variants"
453 err
.span_suggestions(
456 variants
.iter().map(path_names_to_string
),
457 Applicability
::MaybeIncorrect
,
461 err
.note("did you mean to use one of the enum's variants?");
464 (Res
::Def(DefKind
::Struct
, def_id
), _
) if ns
== ValueNS
=> {
465 if let Some((ctor_def
, ctor_vis
))
466 = self.r
.struct_constructors
.get(&def_id
).cloned() {
467 let accessible_ctor
=
468 self.r
.is_accessible_from(ctor_vis
, self.parent_scope
.module
);
469 if is_expected(ctor_def
) && !accessible_ctor
{
472 format
!("constructor is not visible here due to private fields"),
476 bad_struct_syntax_suggestion(def_id
);
479 (Res
::Def(DefKind
::Union
, def_id
), _
) |
480 (Res
::Def(DefKind
::Variant
, def_id
), _
) |
481 (Res
::Def(DefKind
::Ctor(_
, CtorKind
::Fictive
), def_id
), _
) if ns
== ValueNS
=> {
482 bad_struct_syntax_suggestion(def_id
);
484 (Res
::Def(DefKind
::Ctor(_
, CtorKind
::Fn
), def_id
), _
) if ns
== ValueNS
=> {
485 if let Some(span
) = self.r
.definitions
.opt_span(def_id
) {
486 err
.span_label(span
, &format
!("`{}` defined here", path_str
));
490 format
!("did you mean `{}( /* fields */ )`?", path_str
),
493 (Res
::SelfTy(..), _
) if ns
== ValueNS
=> {
494 err
.span_label(span
, fallback_label
);
495 err
.note("can't use `Self` as a constructor, you must use the implemented struct");
497 (Res
::Def(DefKind
::TyAlias
, _
), _
)
498 | (Res
::Def(DefKind
::AssocTy
, _
), _
) if ns
== ValueNS
=> {
499 err
.note("can't use a type alias as a constructor");
506 fn lookup_assoc_candidate
<FilterFn
>(&mut self,
510 -> Option
<AssocSuggestion
>
511 where FilterFn
: Fn(Res
) -> bool
513 fn extract_node_id(t
: &Ty
) -> Option
<NodeId
> {
515 TyKind
::Path(None
, _
) => Some(t
.id
),
516 TyKind
::Rptr(_
, ref mut_ty
) => extract_node_id(&mut_ty
.ty
),
517 // This doesn't handle the remaining `Ty` variants as they are not
518 // that commonly the self_type, it might be interesting to provide
519 // support for those in future.
524 // Fields are generally expected in the same contexts as locals.
525 if filter_fn(Res
::Local(ast
::DUMMY_NODE_ID
)) {
526 if let Some(node_id
) = self.diagnostic_metadata
.current_self_type
.as_ref()
527 .and_then(extract_node_id
)
529 // Look for a field with the same name in the current self_type.
530 if let Some(resolution
) = self.r
.partial_res_map
.get(&node_id
) {
531 match resolution
.base_res() {
532 Res
::Def(DefKind
::Struct
, did
) | Res
::Def(DefKind
::Union
, did
)
533 if resolution
.unresolved_segments() == 0 => {
534 if let Some(field_names
) = self.r
.field_names
.get(&did
) {
535 if field_names
.iter()
536 .any(|&field_name
| ident
.name
== field_name
.node
) {
537 return Some(AssocSuggestion
::Field
);
547 for assoc_type_ident
in &self.diagnostic_metadata
.current_trait_assoc_types
{
548 if *assoc_type_ident
== ident
{
549 return Some(AssocSuggestion
::AssocItem
);
553 // Look for associated items in the current trait.
554 if let Some((module
, _
)) = self.current_trait_ref
{
555 if let Ok(binding
) = self.r
.resolve_ident_in_module(
556 ModuleOrUniformRoot
::Module(module
),
563 let res
= binding
.res();
565 return Some(if self.r
.has_self
.contains(&res
.def_id()) {
566 AssocSuggestion
::MethodWithSelf
568 AssocSuggestion
::AssocItem
577 fn lookup_typo_candidate(
581 filter_fn
: &impl Fn(Res
) -> bool
,
583 ) -> Option
<TypoSuggestion
> {
584 let mut names
= Vec
::new();
586 // Search in lexical scope.
587 // Walk backwards up the ribs in scope and collect candidates.
588 for rib
in self.ribs
[ns
].iter().rev() {
589 // Locals and type parameters
590 for (ident
, &res
) in &rib
.bindings
{
592 names
.push(TypoSuggestion
::from_res(ident
.name
, res
));
596 if let RibKind
::ModuleRibKind(module
) = rib
.kind
{
597 // Items from this module
598 self.r
.add_module_candidates(module
, &mut names
, &filter_fn
);
600 if let ModuleKind
::Block(..) = module
.kind
{
601 // We can see through blocks
603 // Items from the prelude
604 if !module
.no_implicit_prelude
{
605 let extern_prelude
= self.r
.extern_prelude
.clone();
606 names
.extend(extern_prelude
.iter().flat_map(|(ident
, _
)| {
608 .maybe_process_path_extern(ident
.name
, ident
.span
)
609 .and_then(|crate_id
| {
610 let crate_mod
= Res
::Def(
614 index
: CRATE_DEF_INDEX
,
618 if filter_fn(crate_mod
) {
619 Some(TypoSuggestion
::from_res(ident
.name
, crate_mod
))
626 if let Some(prelude
) = self.r
.prelude
{
627 self.r
.add_module_candidates(prelude
, &mut names
, &filter_fn
);
634 // Add primitive types to the mix
635 if filter_fn(Res
::PrimTy(PrimTy
::Bool
)) {
637 self.r
.primitive_type_table
.primitive_types
.iter().map(|(name
, prim_ty
)| {
638 TypoSuggestion
::from_res(*name
, Res
::PrimTy(*prim_ty
))
644 let mod_path
= &path
[..path
.len() - 1];
645 if let PathResult
::Module(module
) = self.resolve_path(
646 mod_path
, Some(TypeNS
), false, span
, CrateLint
::No
648 if let ModuleOrUniformRoot
::Module(module
) = module
{
649 self.r
.add_module_candidates(module
, &mut names
, &filter_fn
);
654 let name
= path
[path
.len() - 1].ident
.name
;
655 // Make sure error reporting is deterministic.
656 names
.sort_by_cached_key(|suggestion
| suggestion
.candidate
.as_str());
658 match find_best_match_for_name(
659 names
.iter().map(|suggestion
| &suggestion
.candidate
),
663 Some(found
) if found
!= name
=> names
665 .find(|suggestion
| suggestion
.candidate
== found
),
670 /// Only used in a specific case of type ascription suggestions
671 fn get_colon_suggestion_span(&self, start
: Span
) -> Span
{
672 let cm
= self.r
.session
.source_map();
673 start
.to(cm
.next_point(start
))
676 fn type_ascription_suggestion(
678 err
: &mut DiagnosticBuilder
<'_
>,
681 let cm
= self.r
.session
.source_map();
682 let base_snippet
= cm
.span_to_snippet(base_span
);
683 if let Some(sp
) = self.diagnostic_metadata
.current_type_ascription
.last() {
686 // Try to find the `:`; bail on first non-':' / non-whitespace.
687 sp
= cm
.next_point(sp
);
688 if let Ok(snippet
) = cm
.span_to_snippet(sp
.to(cm
.next_point(sp
))) {
689 let line_sp
= cm
.lookup_char_pos(sp
.hi()).line
;
690 let line_base_sp
= cm
.lookup_char_pos(base_span
.lo()).line
;
692 let mut show_label
= true;
693 if line_sp
!= line_base_sp
{
694 err
.span_suggestion_short(
696 "did you mean to use `;` here instead?",
698 Applicability
::MaybeIncorrect
,
701 let colon_sp
= self.get_colon_suggestion_span(sp
);
702 let after_colon_sp
= self.get_colon_suggestion_span(
703 colon_sp
.shrink_to_hi(),
705 if !cm
.span_to_snippet(after_colon_sp
).map(|s
| s
== " ")
710 "maybe you meant to write a path separator here",
712 Applicability
::MaybeIncorrect
,
716 if let Ok(base_snippet
) = base_snippet
{
717 let mut sp
= after_colon_sp
;
719 // Try to find an assignment
720 sp
= cm
.next_point(sp
);
721 let snippet
= cm
.span_to_snippet(sp
.to(cm
.next_point(sp
)));
723 Ok(ref x
) if x
.as_str() == "=" => {
726 "maybe you meant to write an assignment here",
727 format
!("let {}", base_snippet
),
728 Applicability
::MaybeIncorrect
,
733 Ok(ref x
) if x
.as_str() == "\n" => break,
741 err
.span_label(base_span
,
742 "expecting a type here because of type ascription");
745 } else if !snippet
.trim().is_empty() {
746 debug
!("tried to find type ascription `:` token, couldn't find it");
756 fn find_module(&mut self, def_id
: DefId
) -> Option
<(Module
<'a
>, ImportSuggestion
)> {
757 let mut result
= None
;
758 let mut seen_modules
= FxHashSet
::default();
759 let mut worklist
= vec
![(self.r
.graph_root
, Vec
::new())];
761 while let Some((in_module
, path_segments
)) = worklist
.pop() {
762 // abort if the module is already found
763 if result
.is_some() { break; }
765 in_module
.for_each_child(self.r
, |_
, ident
, _
, name_binding
| {
766 // abort if the module is already found or if name_binding is private external
767 if result
.is_some() || !name_binding
.vis
.is_visible_locally() {
770 if let Some(module
) = name_binding
.module() {
772 let mut path_segments
= path_segments
.clone();
773 path_segments
.push(ast
::PathSegment
::from_ident(ident
));
774 let module_def_id
= module
.def_id().unwrap();
775 if module_def_id
== def_id
{
777 span
: name_binding
.span
,
778 segments
: path_segments
,
780 result
= Some((module
, ImportSuggestion { did: Some(def_id), path }
));
782 // add the module to the lookup
783 if seen_modules
.insert(module_def_id
) {
784 worklist
.push((module
, path_segments
));
794 fn collect_enum_variants(&mut self, def_id
: DefId
) -> Option
<Vec
<Path
>> {
795 self.find_module(def_id
).map(|(enum_module
, enum_import_suggestion
)| {
796 let mut variants
= Vec
::new();
797 enum_module
.for_each_child(self.r
, |_
, ident
, _
, name_binding
| {
798 if let Res
::Def(DefKind
::Variant
, _
) = name_binding
.res() {
799 let mut segms
= enum_import_suggestion
.path
.segments
.clone();
800 segms
.push(ast
::PathSegment
::from_ident(ident
));
802 span
: name_binding
.span
,