• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! See [`CompletionContext`] structure.
2 
3 mod analysis;
4 #[cfg(test)]
5 mod tests;
6 
7 use std::iter;
8 
9 use hir::{
10     HasAttrs, Local, Name, PathResolution, ScopeDef, Semantics, SemanticsScope, Type, TypeInfo,
11 };
12 use ide_db::{
13     base_db::{FilePosition, SourceDatabase},
14     famous_defs::FamousDefs,
15     helpers::is_editable_crate,
16     FxHashMap, FxHashSet, RootDatabase,
17 };
18 use syntax::{
19     ast::{self, AttrKind, NameOrNameRef},
20     AstNode, SmolStr,
21     SyntaxKind::{self, *},
22     SyntaxToken, TextRange, TextSize, T,
23 };
24 use text_edit::Indel;
25 
26 use crate::{
27     context::analysis::{expand_and_analyze, AnalysisResult},
28     CompletionConfig,
29 };
30 
31 const COMPLETION_MARKER: &str = "intellijRulezz";
32 
33 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
34 pub(crate) enum PatternRefutability {
35     Refutable,
36     Irrefutable,
37 }
38 
39 #[derive(Debug)]
40 pub(crate) enum Visible {
41     Yes,
42     Editable,
43     No,
44 }
45 
46 /// Existing qualifiers for the thing we are currently completing.
47 #[derive(Debug, Default)]
48 pub(super) struct QualifierCtx {
49     pub(super) unsafe_tok: Option<SyntaxToken>,
50     pub(super) vis_node: Option<ast::Visibility>,
51 }
52 
53 impl QualifierCtx {
none(&self) -> bool54     pub(super) fn none(&self) -> bool {
55         self.unsafe_tok.is_none() && self.vis_node.is_none()
56     }
57 }
58 
59 /// The state of the path we are currently completing.
60 #[derive(Debug)]
61 pub(crate) struct PathCompletionCtx {
62     /// If this is a call with () already there (or {} in case of record patterns)
63     pub(super) has_call_parens: bool,
64     /// If this has a macro call bang !
65     pub(super) has_macro_bang: bool,
66     /// The qualifier of the current path.
67     pub(super) qualified: Qualified,
68     /// The parent of the path we are completing.
69     pub(super) parent: Option<ast::Path>,
70     #[allow(dead_code)]
71     /// The path of which we are completing the segment
72     pub(super) path: ast::Path,
73     /// The path of which we are completing the segment in the original file
74     pub(crate) original_path: Option<ast::Path>,
75     pub(super) kind: PathKind,
76     /// Whether the path segment has type args or not.
77     pub(super) has_type_args: bool,
78     /// Whether the qualifier comes from a use tree parent or not
79     pub(crate) use_tree_parent: bool,
80 }
81 
82 impl PathCompletionCtx {
is_trivial_path(&self) -> bool83     pub(super) fn is_trivial_path(&self) -> bool {
84         matches!(
85             self,
86             PathCompletionCtx {
87                 has_call_parens: false,
88                 has_macro_bang: false,
89                 qualified: Qualified::No,
90                 parent: None,
91                 has_type_args: false,
92                 ..
93             }
94         )
95     }
96 }
97 
98 /// The kind of path we are completing right now.
99 #[derive(Debug, PartialEq, Eq)]
100 pub(super) enum PathKind {
101     Expr {
102         expr_ctx: ExprCtx,
103     },
104     Type {
105         location: TypeLocation,
106     },
107     Attr {
108         attr_ctx: AttrCtx,
109     },
110     Derive {
111         existing_derives: ExistingDerives,
112     },
113     /// Path in item position, that is inside an (Assoc)ItemList
114     Item {
115         kind: ItemListKind,
116     },
117     Pat {
118         pat_ctx: PatternContext,
119     },
120     Vis {
121         has_in_token: bool,
122     },
123     Use,
124 }
125 
126 pub(crate) type ExistingDerives = FxHashSet<hir::Macro>;
127 
128 #[derive(Debug, PartialEq, Eq)]
129 pub(crate) struct AttrCtx {
130     pub(crate) kind: AttrKind,
131     pub(crate) annotated_item_kind: Option<SyntaxKind>,
132 }
133 
134 #[derive(Debug, PartialEq, Eq)]
135 pub(crate) struct ExprCtx {
136     pub(crate) in_block_expr: bool,
137     pub(crate) in_loop_body: bool,
138     pub(crate) after_if_expr: bool,
139     /// Whether this expression is the direct condition of an if or while expression
140     pub(crate) in_condition: bool,
141     pub(crate) incomplete_let: bool,
142     pub(crate) ref_expr_parent: Option<ast::RefExpr>,
143     /// The surrounding RecordExpression we are completing a functional update
144     pub(crate) is_func_update: Option<ast::RecordExpr>,
145     pub(crate) self_param: Option<hir::SelfParam>,
146     pub(crate) innermost_ret_ty: Option<hir::Type>,
147     pub(crate) impl_: Option<ast::Impl>,
148     /// Whether this expression occurs in match arm guard position: before the
149     /// fat arrow token
150     pub(crate) in_match_guard: bool,
151 }
152 
153 /// Original file ast nodes
154 #[derive(Clone, Debug, PartialEq, Eq)]
155 pub(crate) enum TypeLocation {
156     TupleField,
157     TypeAscription(TypeAscriptionTarget),
158     GenericArgList(Option<ast::GenericArgList>),
159     TypeBound,
160     ImplTarget,
161     ImplTrait,
162     Other,
163 }
164 
165 #[derive(Clone, Debug, PartialEq, Eq)]
166 pub(crate) enum TypeAscriptionTarget {
167     Let(Option<ast::Pat>),
168     FnParam(Option<ast::Pat>),
169     RetType(Option<ast::Expr>),
170     Const(Option<ast::Expr>),
171 }
172 
173 /// The kind of item list a [`PathKind::Item`] belongs to.
174 #[derive(Debug, PartialEq, Eq)]
175 pub(super) enum ItemListKind {
176     SourceFile,
177     Module,
178     Impl,
179     TraitImpl(Option<ast::Impl>),
180     Trait,
181     ExternBlock,
182 }
183 
184 #[derive(Debug)]
185 pub(super) enum Qualified {
186     No,
187     With {
188         path: ast::Path,
189         resolution: Option<PathResolution>,
190         /// How many `super` segments are present in the path
191         ///
192         /// This would be None, if path is not solely made of
193         /// `super` segments, e.g.
194         ///
195         /// ```rust
196         ///   use super::foo;
197         /// ```
198         ///
199         /// Otherwise it should be Some(count of `super`)
200         super_chain_len: Option<usize>,
201     },
202     /// <_>::
203     TypeAnchor {
204         ty: Option<hir::Type>,
205         trait_: Option<hir::Trait>,
206     },
207     /// Whether the path is an absolute path
208     Absolute,
209 }
210 
211 /// The state of the pattern we are completing.
212 #[derive(Debug, Clone, PartialEq, Eq)]
213 pub(super) struct PatternContext {
214     pub(super) refutability: PatternRefutability,
215     pub(super) param_ctx: Option<ParamContext>,
216     pub(super) has_type_ascription: bool,
217     pub(super) parent_pat: Option<ast::Pat>,
218     pub(super) ref_token: Option<SyntaxToken>,
219     pub(super) mut_token: Option<SyntaxToken>,
220     /// The record pattern this name or ref is a field of
221     pub(super) record_pat: Option<ast::RecordPat>,
222     pub(super) impl_: Option<ast::Impl>,
223     /// List of missing variants in a match expr
224     pub(super) missing_variants: Vec<hir::Variant>,
225 }
226 
227 #[derive(Debug, Clone, PartialEq, Eq)]
228 pub(super) struct ParamContext {
229     pub(super) param_list: ast::ParamList,
230     pub(super) param: ast::Param,
231     pub(super) kind: ParamKind,
232 }
233 
234 /// The state of the lifetime we are completing.
235 #[derive(Debug)]
236 pub(super) struct LifetimeContext {
237     pub(super) lifetime: Option<ast::Lifetime>,
238     pub(super) kind: LifetimeKind,
239 }
240 
241 /// The kind of lifetime we are completing.
242 #[derive(Debug)]
243 pub(super) enum LifetimeKind {
244     LifetimeParam { is_decl: bool, param: ast::LifetimeParam },
245     Lifetime,
246     LabelRef,
247     LabelDef,
248 }
249 
250 /// The state of the name we are completing.
251 #[derive(Debug)]
252 pub(super) struct NameContext {
253     #[allow(dead_code)]
254     pub(super) name: Option<ast::Name>,
255     pub(super) kind: NameKind,
256 }
257 
258 /// The kind of the name we are completing.
259 #[derive(Debug)]
260 #[allow(dead_code)]
261 pub(super) enum NameKind {
262     Const,
263     ConstParam,
264     Enum,
265     Function,
266     IdentPat(PatternContext),
267     MacroDef,
268     MacroRules,
269     /// Fake node
270     Module(ast::Module),
271     RecordField,
272     Rename,
273     SelfParam,
274     Static,
275     Struct,
276     Trait,
277     TypeAlias,
278     TypeParam,
279     Union,
280     Variant,
281 }
282 
283 /// The state of the NameRef we are completing.
284 #[derive(Debug)]
285 pub(super) struct NameRefContext {
286     /// NameRef syntax in the original file
287     pub(super) nameref: Option<ast::NameRef>,
288     pub(super) kind: NameRefKind,
289 }
290 
291 /// The kind of the NameRef we are completing.
292 #[derive(Debug)]
293 pub(super) enum NameRefKind {
294     Path(PathCompletionCtx),
295     DotAccess(DotAccess),
296     /// Position where we are only interested in keyword completions
297     Keyword(ast::Item),
298     /// The record expression this nameref is a field of and whether a dot precedes the completion identifier.
299     RecordExpr {
300         dot_prefix: bool,
301         expr: ast::RecordExpr,
302     },
303     Pattern(PatternContext),
304 }
305 
306 /// The identifier we are currently completing.
307 #[derive(Debug)]
308 pub(super) enum CompletionAnalysis {
309     Name(NameContext),
310     NameRef(NameRefContext),
311     Lifetime(LifetimeContext),
312     /// The string the cursor is currently inside
313     String {
314         /// original token
315         original: ast::String,
316         /// fake token
317         expanded: Option<ast::String>,
318     },
319     /// Set if we are currently completing in an unexpanded attribute, this usually implies a builtin attribute like `allow($0)`
320     UnexpandedAttrTT {
321         colon_prefix: bool,
322         fake_attribute_under_caret: Option<ast::Attr>,
323     },
324 }
325 
326 /// Information about the field or method access we are completing.
327 #[derive(Debug)]
328 pub(super) struct DotAccess {
329     pub(super) receiver: Option<ast::Expr>,
330     pub(super) receiver_ty: Option<TypeInfo>,
331     pub(super) kind: DotAccessKind,
332 }
333 
334 #[derive(Debug)]
335 pub(super) enum DotAccessKind {
336     Field {
337         /// True if the receiver is an integer and there is no ident in the original file after it yet
338         /// like `0.$0`
339         receiver_is_ambiguous_float_literal: bool,
340     },
341     Method {
342         has_parens: bool,
343     },
344 }
345 
346 #[derive(Clone, Debug, PartialEq, Eq)]
347 pub(crate) enum ParamKind {
348     Function(ast::Fn),
349     Closure(ast::ClosureExpr),
350 }
351 
352 /// `CompletionContext` is created early during completion to figure out, where
353 /// exactly is the cursor, syntax-wise.
354 #[derive(Debug)]
355 pub(crate) struct CompletionContext<'a> {
356     pub(super) sema: Semantics<'a, RootDatabase>,
357     pub(super) scope: SemanticsScope<'a>,
358     pub(super) db: &'a RootDatabase,
359     pub(super) config: &'a CompletionConfig,
360     pub(super) position: FilePosition,
361 
362     /// The token before the cursor, in the original file.
363     pub(super) original_token: SyntaxToken,
364     /// The token before the cursor, in the macro-expanded file.
365     pub(super) token: SyntaxToken,
366     /// The crate of the current file.
367     pub(super) krate: hir::Crate,
368     /// The module of the `scope`.
369     pub(super) module: hir::Module,
370     /// Whether nightly toolchain is used. Cached since this is looked up a lot.
371     is_nightly: bool,
372 
373     /// The expected name of what we are completing.
374     /// This is usually the parameter name of the function argument we are completing.
375     pub(super) expected_name: Option<NameOrNameRef>,
376     /// The expected type of what we are completing.
377     pub(super) expected_type: Option<Type>,
378 
379     pub(super) qualifier_ctx: QualifierCtx,
380 
381     pub(super) locals: FxHashMap<Name, Local>,
382 
383     /// The module depth of the current module of the cursor position.
384     /// - crate-root
385     ///  - mod foo
386     ///   - mod bar
387     /// Here depth will be 2
388     pub(super) depth_from_crate_root: usize,
389 }
390 
391 impl CompletionContext<'_> {
392     /// The range of the identifier that is being completed.
source_range(&self) -> TextRange393     pub(crate) fn source_range(&self) -> TextRange {
394         let kind = self.original_token.kind();
395         match kind {
396             CHAR => {
397                 // assume we are completing a lifetime but the user has only typed the '
398                 cov_mark::hit!(completes_if_lifetime_without_idents);
399                 TextRange::at(self.original_token.text_range().start(), TextSize::from(1))
400             }
401             IDENT | LIFETIME_IDENT | UNDERSCORE => self.original_token.text_range(),
402             _ if kind.is_keyword() => self.original_token.text_range(),
403             _ => TextRange::empty(self.position.offset),
404         }
405     }
406 
famous_defs(&self) -> FamousDefs<'_, '_>407     pub(crate) fn famous_defs(&self) -> FamousDefs<'_, '_> {
408         FamousDefs(&self.sema, self.krate)
409     }
410 
411     /// Checks if an item is visible and not `doc(hidden)` at the completion site.
def_is_visible(&self, item: &ScopeDef) -> Visible412     pub(crate) fn def_is_visible(&self, item: &ScopeDef) -> Visible {
413         match item {
414             ScopeDef::ModuleDef(def) => match def {
415                 hir::ModuleDef::Module(it) => self.is_visible(it),
416                 hir::ModuleDef::Function(it) => self.is_visible(it),
417                 hir::ModuleDef::Adt(it) => self.is_visible(it),
418                 hir::ModuleDef::Variant(it) => self.is_visible(it),
419                 hir::ModuleDef::Const(it) => self.is_visible(it),
420                 hir::ModuleDef::Static(it) => self.is_visible(it),
421                 hir::ModuleDef::Trait(it) => self.is_visible(it),
422                 hir::ModuleDef::TraitAlias(it) => self.is_visible(it),
423                 hir::ModuleDef::TypeAlias(it) => self.is_visible(it),
424                 hir::ModuleDef::Macro(it) => self.is_visible(it),
425                 hir::ModuleDef::BuiltinType(_) => Visible::Yes,
426             },
427             ScopeDef::GenericParam(_)
428             | ScopeDef::ImplSelfType(_)
429             | ScopeDef::AdtSelfType(_)
430             | ScopeDef::Local(_)
431             | ScopeDef::Label(_)
432             | ScopeDef::Unknown => Visible::Yes,
433         }
434     }
435 
436     /// Checks if an item is visible and not `doc(hidden)` at the completion site.
is_visible<I>(&self, item: &I) -> Visible where I: hir::HasVisibility + hir::HasAttrs + hir::HasCrate + Copy,437     pub(crate) fn is_visible<I>(&self, item: &I) -> Visible
438     where
439         I: hir::HasVisibility + hir::HasAttrs + hir::HasCrate + Copy,
440     {
441         let vis = item.visibility(self.db);
442         let attrs = item.attrs(self.db);
443         self.is_visible_impl(&vis, &attrs, item.krate(self.db))
444     }
445 
doc_aliases<I>(&self, item: &I) -> Vec<SmolStr> where I: hir::HasAttrs + Copy,446     pub(crate) fn doc_aliases<I>(&self, item: &I) -> Vec<SmolStr>
447     where
448         I: hir::HasAttrs + Copy,
449     {
450         let attrs = item.attrs(self.db);
451         attrs.doc_aliases().collect()
452     }
453 
454     /// Check if an item is `#[doc(hidden)]`.
is_item_hidden(&self, item: &hir::ItemInNs) -> bool455     pub(crate) fn is_item_hidden(&self, item: &hir::ItemInNs) -> bool {
456         let attrs = item.attrs(self.db);
457         let krate = item.krate(self.db);
458         match (attrs, krate) {
459             (Some(attrs), Some(krate)) => self.is_doc_hidden(&attrs, krate),
460             _ => false,
461         }
462     }
463 
464     /// Checks whether this item should be listed in regards to stability. Returns `true` if we should.
check_stability(&self, attrs: Option<&hir::Attrs>) -> bool465     pub(crate) fn check_stability(&self, attrs: Option<&hir::Attrs>) -> bool {
466         let Some(attrs) = attrs else { return true; };
467         !attrs.is_unstable() || self.is_nightly
468     }
469 
470     /// Whether the given trait is an operator trait or not.
is_ops_trait(&self, trait_: hir::Trait) -> bool471     pub(crate) fn is_ops_trait(&self, trait_: hir::Trait) -> bool {
472         match trait_.attrs(self.db).lang() {
473             Some(lang) => OP_TRAIT_LANG_NAMES.contains(&lang.as_str()),
474             None => false,
475         }
476     }
477 
478     /// Returns the traits in scope, with the [`Drop`] trait removed.
traits_in_scope(&self) -> hir::VisibleTraits479     pub(crate) fn traits_in_scope(&self) -> hir::VisibleTraits {
480         let mut traits_in_scope = self.scope.visible_traits();
481         if let Some(drop) = self.famous_defs().core_ops_Drop() {
482             traits_in_scope.0.remove(&drop.into());
483         }
484         traits_in_scope
485     }
486 
iterate_path_candidates( &self, ty: &hir::Type, mut cb: impl FnMut(hir::AssocItem), )487     pub(crate) fn iterate_path_candidates(
488         &self,
489         ty: &hir::Type,
490         mut cb: impl FnMut(hir::AssocItem),
491     ) {
492         let mut seen = FxHashSet::default();
493         ty.iterate_path_candidates(
494             self.db,
495             &self.scope,
496             &self.traits_in_scope(),
497             Some(self.module),
498             None,
499             |item| {
500                 // We might iterate candidates of a trait multiple times here, so deduplicate
501                 // them.
502                 if seen.insert(item) {
503                     cb(item)
504                 }
505                 None::<()>
506             },
507         );
508     }
509 
510     /// A version of [`SemanticsScope::process_all_names`] that filters out `#[doc(hidden)]` items and
511     /// passes all doc-aliases along, to funnel it into [`Completions::add_path_resolution`].
process_all_names(&self, f: &mut dyn FnMut(Name, ScopeDef, Vec<SmolStr>))512     pub(crate) fn process_all_names(&self, f: &mut dyn FnMut(Name, ScopeDef, Vec<SmolStr>)) {
513         let _p = profile::span("CompletionContext::process_all_names");
514         self.scope.process_all_names(&mut |name, def| {
515             if self.is_scope_def_hidden(def) {
516                 return;
517             }
518             let doc_aliases = self.doc_aliases_in_scope(def);
519             f(name, def, doc_aliases);
520         });
521     }
522 
process_all_names_raw(&self, f: &mut dyn FnMut(Name, ScopeDef))523     pub(crate) fn process_all_names_raw(&self, f: &mut dyn FnMut(Name, ScopeDef)) {
524         let _p = profile::span("CompletionContext::process_all_names_raw");
525         self.scope.process_all_names(f);
526     }
527 
is_scope_def_hidden(&self, scope_def: ScopeDef) -> bool528     fn is_scope_def_hidden(&self, scope_def: ScopeDef) -> bool {
529         if let (Some(attrs), Some(krate)) = (scope_def.attrs(self.db), scope_def.krate(self.db)) {
530             return self.is_doc_hidden(&attrs, krate);
531         }
532 
533         false
534     }
535 
is_visible_impl( &self, vis: &hir::Visibility, attrs: &hir::Attrs, defining_crate: hir::Crate, ) -> Visible536     fn is_visible_impl(
537         &self,
538         vis: &hir::Visibility,
539         attrs: &hir::Attrs,
540         defining_crate: hir::Crate,
541     ) -> Visible {
542         if !vis.is_visible_from(self.db, self.module.into()) {
543             if !self.config.enable_private_editable {
544                 return Visible::No;
545             }
546             // If the definition location is editable, also show private items
547             return if is_editable_crate(defining_crate, self.db) {
548                 Visible::Editable
549             } else {
550                 Visible::No
551             };
552         }
553 
554         if self.is_doc_hidden(attrs, defining_crate) {
555             Visible::No
556         } else {
557             Visible::Yes
558         }
559     }
560 
is_doc_hidden(&self, attrs: &hir::Attrs, defining_crate: hir::Crate) -> bool561     fn is_doc_hidden(&self, attrs: &hir::Attrs, defining_crate: hir::Crate) -> bool {
562         // `doc(hidden)` items are only completed within the defining crate.
563         self.krate != defining_crate && attrs.has_doc_hidden()
564     }
565 
doc_aliases_in_scope(&self, scope_def: ScopeDef) -> Vec<SmolStr>566     pub(crate) fn doc_aliases_in_scope(&self, scope_def: ScopeDef) -> Vec<SmolStr> {
567         if let Some(attrs) = scope_def.attrs(self.db) {
568             attrs.doc_aliases().collect()
569         } else {
570             vec![]
571         }
572     }
573 }
574 
575 // CompletionContext construction
576 impl<'a> CompletionContext<'a> {
577     pub(super) fn new(
578         db: &'a RootDatabase,
579         position @ FilePosition { file_id, offset }: FilePosition,
580         config: &'a CompletionConfig,
581     ) -> Option<(CompletionContext<'a>, CompletionAnalysis)> {
582         let _p = profile::span("CompletionContext::new");
583         let sema = Semantics::new(db);
584 
585         let original_file = sema.parse(file_id);
586 
587         // Insert a fake ident to get a valid parse tree. We will use this file
588         // to determine context, though the original_file will be used for
589         // actual completion.
590         let file_with_fake_ident = {
591             let parse = db.parse(file_id);
592             let edit = Indel::insert(offset, COMPLETION_MARKER.to_string());
593             parse.reparse(&edit).tree()
594         };
595 
596         // always pick the token to the immediate left of the cursor, as that is what we are actually
597         // completing on
598         let original_token = original_file.syntax().token_at_offset(offset).left_biased()?;
599 
600         // try to skip completions on path with invalid colons
601         // this approach works in normal path and inside token tree
602         if original_token.kind() == T![:] {
603             // return if no prev token before colon
604             let prev_token = original_token.prev_token()?;
605 
606             // only has a single colon
607             if prev_token.kind() != T![:] {
608                 return None;
609             }
610 
611             // has 3 colon or 2 coloncolon in a row
612             // special casing this as per discussion in https://github.com/rust-lang/rust-analyzer/pull/13611#discussion_r1031845205
613             // and https://github.com/rust-lang/rust-analyzer/pull/13611#discussion_r1032812751
614             if prev_token
615                 .prev_token()
616                 .map(|t| t.kind() == T![:] || t.kind() == T![::])
617                 .unwrap_or(false)
618             {
619                 return None;
620             }
621         }
622 
623         let AnalysisResult {
624             analysis,
625             expected: (expected_type, expected_name),
626             qualifier_ctx,
627             token,
628             offset,
629         } = expand_and_analyze(
630             &sema,
631             original_file.syntax().clone(),
632             file_with_fake_ident.syntax().clone(),
633             offset,
634             &original_token,
635         )?;
636 
637         // adjust for macro input, this still fails if there is no token written yet
638         let scope = sema.scope_at_offset(&token.parent()?, offset)?;
639 
640         let krate = scope.krate();
641         let module = scope.module();
642 
643         let toolchain = db.crate_graph()[krate.into()].channel;
644         // `toolchain == None` means we're in some detached files. Since we have no information on
645         // the toolchain being used, let's just allow unstable items to be listed.
646         let is_nightly = matches!(toolchain, Some(base_db::ReleaseChannel::Nightly) | None);
647 
648         let mut locals = FxHashMap::default();
649         scope.process_all_names(&mut |name, scope| {
650             if let ScopeDef::Local(local) = scope {
651                 locals.insert(name, local);
652             }
653         });
654 
655         let depth_from_crate_root = iter::successors(module.parent(db), |m| m.parent(db)).count();
656 
657         let ctx = CompletionContext {
658             sema,
659             scope,
660             db,
661             config,
662             position,
663             original_token,
664             token,
665             krate,
666             module,
667             is_nightly,
668             expected_name,
669             expected_type,
670             qualifier_ctx,
671             locals,
672             depth_from_crate_root,
673         };
674         Some((ctx, analysis))
675     }
676 }
677 
678 const OP_TRAIT_LANG_NAMES: &[&str] = &[
679     "add_assign",
680     "add",
681     "bitand_assign",
682     "bitand",
683     "bitor_assign",
684     "bitor",
685     "bitxor_assign",
686     "bitxor",
687     "deref_mut",
688     "deref",
689     "div_assign",
690     "div",
691     "eq",
692     "fn_mut",
693     "fn_once",
694     "fn",
695     "index_mut",
696     "index",
697     "mul_assign",
698     "mul",
699     "neg",
700     "not",
701     "partial_ord",
702     "rem_assign",
703     "rem",
704     "shl_assign",
705     "shl",
706     "shr_assign",
707     "shr",
708     "sub",
709 ];
710