1mod analysis;
4#[cfg(test)]
5mod tests;
6
7use std::{iter, sync::LazyLock};
8
9use base_db::toolchain_channel;
10use hir::{
11 DisplayTarget, HasAttrs, InFile, Local, ModuleDef, ModuleSource, Name, PathResolution,
12 ScopeDef, Semantics, SemanticsScope, Symbol, Type, TypeInfo, sym,
13};
14use ide_db::{
15 FilePosition, FxHashMap, FxHashSet, RootDatabase, famous_defs::FamousDefs,
16 helpers::is_editable_crate, syntax_helpers::node_ext::is_in_macro_matcher,
17};
18use itertools::Either;
19use syntax::{
20 AstNode, Edition, SmolStr,
21 SyntaxKind::{self, *},
22 SyntaxToken, T, TextRange, TextSize,
23 ast::{self, AttrKind, NameOrNameRef},
24};
25
26use crate::{
27 CompletionConfig,
28 config::AutoImportExclusionType,
29 context::analysis::{AnalysisResult, expand_and_analyze},
30};
31
32const COMPLETION_MARKER: &str = "raCompletionMarker";
33
34#[derive(Copy, Clone, Debug, PartialEq, Eq)]
35pub(crate) enum PatternRefutability {
36 Refutable,
37 Irrefutable,
38}
39
40#[derive(Debug)]
41pub(crate) enum Visible {
42 Yes,
43 Editable,
44 No,
45}
46
47#[derive(Debug, Default)]
49pub(crate) struct QualifierCtx {
50 pub(crate) async_tok: Option<SyntaxToken>,
52 pub(crate) unsafe_tok: Option<SyntaxToken>,
53 pub(crate) safe_tok: Option<SyntaxToken>,
54 pub(crate) vis_node: Option<ast::Visibility>,
55 pub(crate) abi_node: Option<ast::Abi>,
56}
57
58impl QualifierCtx {
59 pub(crate) fn none(&self) -> bool {
60 self.async_tok.is_none()
61 && self.unsafe_tok.is_none()
62 && self.safe_tok.is_none()
63 && self.vis_node.is_none()
64 && self.abi_node.is_none()
65 }
66}
67
68#[derive(Debug)]
70pub(crate) struct PathCompletionCtx<'db> {
71 pub(crate) has_call_parens: bool,
73 pub(crate) has_macro_bang: bool,
75 pub(crate) qualified: Qualified<'db>,
77 pub(crate) parent: Option<ast::Path>,
79 #[allow(dead_code)]
80 pub(crate) path: ast::Path,
82 pub(crate) original_path: Option<ast::Path>,
84 pub(crate) kind: PathKind<'db>,
85 pub(crate) has_type_args: bool,
87 pub(crate) use_tree_parent: bool,
89}
90
91impl PathCompletionCtx<'_> {
92 pub(crate) fn is_trivial_path(&self) -> bool {
93 matches!(
94 self,
95 PathCompletionCtx {
96 has_call_parens: false,
97 has_macro_bang: false,
98 qualified: Qualified::No,
99 parent: None,
100 has_type_args: false,
101 ..
102 }
103 )
104 }
105
106 pub(crate) fn required_thin_arrow(
107 &self,
108 sema: &Semantics<'_, RootDatabase>,
109 ) -> Option<(&'static str, TextSize)> {
110 let PathKind::Type {
111 location:
112 TypeLocation::TypeAscription(TypeAscriptionTarget::RetType {
113 item: Some(ref fn_item),
114 ..
115 }),
116 } = self.kind
117 else {
118 return None;
119 };
120 if fn_item.ret_type().is_some_and(|it| it.thin_arrow_token().is_some()) {
121 return None;
122 }
123 let unmap = |node: &_| sema.original_range_opt(node).map(|it| it.range);
124 let ret_type = fn_item.ret_type().and_then(|it| it.ty());
125 match (ret_type, fn_item.param_list()) {
126 (Some(ty), _) => Some(("-> ", unmap(ty.syntax())?.start())),
127 (None, Some(param)) => Some((" ->", unmap(param.syntax())?.end())),
128 (None, None) => None,
129 }
130 }
131}
132
133#[derive(Debug, PartialEq, Eq)]
135pub(crate) enum PathKind<'db> {
136 Expr {
137 expr_ctx: PathExprCtx<'db>,
138 },
139 Type {
140 location: TypeLocation,
141 },
142 Attr {
143 attr_ctx: AttrCtx,
144 },
145 Derive {
146 existing_derives: ExistingDerives,
147 },
148 Item {
150 kind: ItemListKind,
151 },
152 Pat {
153 pat_ctx: PatternContext,
154 },
155 Vis {
156 has_in_token: bool,
157 },
158 Use,
159}
160
161pub(crate) type ExistingDerives = FxHashSet<hir::Macro>;
162
163#[derive(Debug, PartialEq, Eq)]
164pub(crate) struct AttrCtx {
165 pub(crate) kind: AttrKind,
166 pub(crate) annotated_item_kind: Option<SyntaxKind>,
167 pub(crate) derive_helpers: Vec<(Symbol, Symbol)>,
168}
169
170#[derive(Debug, PartialEq, Eq)]
171pub(crate) struct PathExprCtx<'db> {
172 pub(crate) in_block_expr: bool,
173 pub(crate) in_breakable: Option<BreakableKind>,
174 pub(crate) after_if_expr: bool,
175 pub(crate) before_else_kw: bool,
176 pub(crate) in_condition: bool,
178 pub(crate) incomplete_let: bool,
179 pub(crate) after_incomplete_let: bool,
180 pub(crate) in_value: bool,
181 pub(crate) ref_expr_parent: Option<ast::RefExpr>,
182 pub(crate) after_amp: bool,
183 pub(crate) is_func_update: Option<ast::RecordExpr>,
185 pub(crate) self_param: Option<Either<hir::SelfParam, hir::Param<'db>>>,
186 pub(crate) innermost_ret_ty: Option<hir::Type<'db>>,
187 pub(crate) innermost_breakable_ty: Option<hir::Type<'db>>,
188 pub(crate) impl_: Option<ast::Impl>,
189 pub(crate) in_match_guard: bool,
192}
193
194#[derive(Clone, Debug, PartialEq, Eq)]
196pub(crate) enum TypeLocation {
197 TupleField,
198 TypeAscription(TypeAscriptionTarget),
199 GenericArg {
201 args: Option<ast::GenericArgList>,
203 of_trait: Option<hir::Trait>,
205 corresponding_param: Option<ast::GenericParam>,
207 },
208 AssocTypeEq,
210 AssocConstEq,
212 TypeBound,
213 ImplTarget,
214 ImplTrait,
215 Other,
216}
217
218impl TypeLocation {
219 pub(crate) fn complete_lifetimes(&self) -> bool {
220 matches!(
221 self,
222 TypeLocation::GenericArg {
223 corresponding_param: Some(ast::GenericParam::LifetimeParam(_)),
224 ..
225 }
226 )
227 }
228
229 pub(crate) fn complete_consts(&self) -> bool {
230 matches!(
231 self,
232 TypeLocation::GenericArg {
233 corresponding_param: Some(ast::GenericParam::ConstParam(_)),
234 ..
235 } | TypeLocation::AssocConstEq
236 )
237 }
238
239 pub(crate) fn complete_types(&self) -> bool {
240 match self {
241 TypeLocation::GenericArg { corresponding_param: Some(param), .. } => {
242 matches!(param, ast::GenericParam::TypeParam(_))
243 }
244 TypeLocation::AssocConstEq => false,
245 TypeLocation::AssocTypeEq => true,
246 TypeLocation::ImplTrait => false,
247 _ => true,
248 }
249 }
250
251 pub(crate) fn complete_self_type(&self) -> bool {
252 self.complete_types() && !matches!(self, TypeLocation::ImplTarget | TypeLocation::ImplTrait)
253 }
254}
255
256#[derive(Clone, Debug, PartialEq, Eq)]
257pub(crate) enum TypeAscriptionTarget {
258 Let(Option<ast::Pat>),
259 FnParam(Option<ast::Pat>),
260 RetType { body: Option<ast::Expr>, item: Option<ast::Fn> },
261 Const(Option<ast::Expr>),
262}
263
264#[derive(Debug, PartialEq, Eq)]
266pub(crate) enum ItemListKind {
267 SourceFile,
268 Module,
269 Impl,
270 TraitImpl(Option<ast::Impl>),
271 Trait,
272 ExternBlock { is_unsafe: bool },
273}
274
275#[derive(Debug)]
276pub(crate) enum Qualified<'db> {
277 No,
278 With {
279 path: ast::Path,
280 resolution: Option<PathResolution<'db>>,
281 super_chain_len: Option<usize>,
292 },
293 TypeAnchor {
295 ty: Option<hir::Type<'db>>,
296 trait_: Option<hir::Trait>,
297 },
298 Absolute,
300}
301
302#[derive(Debug, Clone, PartialEq, Eq)]
304pub(crate) struct PatternContext {
305 pub(crate) refutability: PatternRefutability,
306 pub(crate) param_ctx: Option<ParamContext>,
307 pub(crate) has_type_ascription: bool,
308 pub(crate) should_suggest_name: bool,
309 pub(crate) after_if_expr: bool,
310 pub(crate) parent_pat: Option<ast::Pat>,
311 pub(crate) ref_token: Option<SyntaxToken>,
312 pub(crate) mut_token: Option<SyntaxToken>,
313 pub(crate) record_pat: Option<ast::RecordPat>,
315 pub(crate) impl_or_trait: Option<Either<ast::Impl, ast::Trait>>,
316 pub(crate) missing_variants: Vec<hir::EnumVariant>,
318}
319
320#[derive(Debug, Clone, PartialEq, Eq)]
321pub(crate) struct ParamContext {
322 pub(crate) param_list: ast::ParamList,
323 pub(crate) param: ast::Param,
324 pub(crate) kind: ParamKind,
325}
326
327#[derive(Debug)]
329pub(crate) struct LifetimeContext {
330 pub(crate) kind: LifetimeKind,
331}
332
333#[derive(Debug)]
335pub(crate) enum LifetimeKind {
336 LifetimeParam,
337 Lifetime { in_lifetime_param_bound: bool, def: Option<hir::GenericDef> },
338 LabelRef,
339 LabelDef,
340}
341
342#[derive(Debug)]
344pub(crate) struct NameContext {
345 #[allow(dead_code)]
346 pub(crate) name: Option<ast::Name>,
347 pub(crate) kind: NameKind,
348}
349
350#[derive(Debug)]
352#[allow(dead_code)]
353pub(crate) enum NameKind {
354 Const,
355 ConstParam,
356 Enum,
357 Function,
358 IdentPat(PatternContext),
359 MacroDef,
360 MacroRules,
361 Module(ast::Module),
363 RecordField,
364 Rename,
365 SelfParam,
366 Static,
367 Struct,
368 Trait,
369 TypeAlias,
370 TypeParam,
371 Union,
372 Variant,
373}
374
375#[derive(Debug)]
377pub(crate) struct NameRefContext<'db> {
378 pub(crate) nameref: Option<ast::NameRef>,
380 pub(crate) kind: NameRefKind<'db>,
381}
382
383#[derive(Debug)]
385pub(crate) enum NameRefKind<'db> {
386 Path(PathCompletionCtx<'db>),
387 DotAccess(DotAccess<'db>),
388 Keyword(ast::Item),
390 RecordExpr {
392 dot_prefix: bool,
393 expr: ast::RecordExpr,
394 },
395 Pattern(PatternContext),
396 ExternCrate,
397}
398
399#[derive(Debug)]
401pub(crate) enum CompletionAnalysis<'db> {
402 Name(NameContext),
403 NameRef(NameRefContext<'db>),
404 Lifetime(LifetimeContext),
405 String {
407 original: ast::String,
409 expanded: Option<ast::String>,
411 },
412 UnexpandedAttrTT {
414 colon_prefix: bool,
415 fake_attribute_under_caret: Option<ast::TokenTreeMeta>,
416 extern_crate: Option<ast::ExternCrate>,
417 },
418 CfgPredicate,
420 MacroSegment,
421}
422
423#[derive(Debug)]
425pub(crate) struct DotAccess<'db> {
426 pub(crate) receiver: Option<ast::Expr>,
427 pub(crate) receiver_ty: Option<TypeInfo<'db>>,
428 pub(crate) kind: DotAccessKind,
429 pub(crate) ctx: DotAccessExprCtx,
430}
431
432#[derive(Debug, Clone, Copy)]
433pub(crate) enum DotAccessKind {
434 Field {
435 receiver_is_ambiguous_float_literal: bool,
438 },
439 Method,
440}
441
442#[derive(Debug, Clone, Copy, PartialEq, Eq)]
443pub(crate) struct DotAccessExprCtx {
444 pub(crate) in_block_expr: bool,
445 pub(crate) in_breakable: Option<BreakableKind>,
446}
447
448#[derive(Copy, Clone, Debug, PartialEq, Eq)]
449pub(crate) enum BreakableKind {
450 Loop,
451 For,
452 While,
453 Block,
454}
455
456#[derive(Clone, Debug, PartialEq, Eq)]
457pub(crate) enum ParamKind {
458 Function(ast::Fn),
459 Closure(ast::ClosureExpr),
460}
461
462#[derive(Debug)]
465pub(crate) struct CompletionContext<'a, 'db> {
466 pub(crate) sema: Semantics<'db, RootDatabase>,
467 pub(crate) scope: SemanticsScope<'db>,
468 pub(crate) db: &'db RootDatabase,
469 pub(crate) config: &'a CompletionConfig<'a>,
470 pub(crate) position: FilePosition,
471
472 pub(crate) trigger_character: Option<char>,
473 pub(crate) original_token: SyntaxToken,
475 pub(crate) token: SyntaxToken,
477 pub(crate) krate: hir::Crate,
479 pub(crate) display_target: DisplayTarget,
480 pub(crate) module: hir::Module,
482 pub(crate) containing_function: Option<hir::Function>,
484 pub(crate) is_nightly: bool,
486 pub(crate) edition: Edition,
489
490 pub(crate) expected_name: Option<NameOrNameRef>,
493 pub(crate) expected_type: Option<Type<'db>>,
495
496 pub(crate) qualifier_ctx: QualifierCtx,
497
498 pub(crate) locals: FxHashMap<Name, Local<'db>>,
499
500 pub(crate) depth_from_crate_root: usize,
507
508 pub(crate) exclude_flyimport: FxHashMap<ModuleDef, AutoImportExclusionType>,
513 pub(crate) exclude_traits: FxHashSet<hir::Trait>,
518
519 pub(crate) complete_semicolon: CompleteSemicolon,
521}
522
523#[derive(Debug)]
524pub(crate) enum CompleteSemicolon {
525 DoNotComplete,
526 CompleteSemi,
527 CompleteComma,
528}
529
530impl<'db> CompletionContext<'_, 'db> {
531 pub(crate) fn source_range(&self) -> TextRange {
533 let kind = self.original_token.kind();
534 match kind {
535 CHAR => {
536 cov_mark::hit!(completes_if_lifetime_without_idents);
538 TextRange::at(self.original_token.text_range().start(), TextSize::from(1))
539 }
540 LIFETIME_IDENT | UNDERSCORE | INT_NUMBER => self.original_token.text_range(),
541 _ if kind.is_any_identifier() => self.original_token.text_range(),
543 _ => TextRange::empty(self.position.offset),
544 }
545 }
546
547 pub(crate) fn famous_defs(&self) -> FamousDefs<'_, 'db> {
548 FamousDefs(&self.sema, self.krate)
549 }
550
551 pub(crate) fn def_is_visible(&self, item: &ScopeDef<'db>) -> Visible {
553 match item {
554 ScopeDef::ModuleDef(def) => match def {
555 hir::ModuleDef::Module(it) => self.is_visible(it),
556 hir::ModuleDef::Function(it) => self.is_visible(it),
557 hir::ModuleDef::Adt(it) => self.is_visible(it),
558 hir::ModuleDef::EnumVariant(it) => self.is_visible(it),
559 hir::ModuleDef::Const(it) => self.is_visible(it),
560 hir::ModuleDef::Static(it) => self.is_visible(it),
561 hir::ModuleDef::Trait(it) => self.is_visible(it),
562 hir::ModuleDef::TypeAlias(it) => self.is_visible(it),
563 hir::ModuleDef::Macro(it) => self.is_visible(it),
564 hir::ModuleDef::BuiltinType(_) => Visible::Yes,
565 },
566 ScopeDef::GenericParam(_)
567 | ScopeDef::ImplSelfType(_)
568 | ScopeDef::AdtSelfType(_)
569 | ScopeDef::Local(_)
570 | ScopeDef::Label(_)
571 | ScopeDef::Unknown => Visible::Yes,
572 }
573 }
574
575 pub(crate) fn is_visible<I>(&self, item: &I) -> Visible
577 where
578 I: hir::HasVisibility + hir::HasAttrs + hir::HasCrate + Copy,
579 {
580 let vis = item.visibility(self.db);
581 let attrs = item.attrs(self.db);
582 self.is_visible_impl(&vis, &attrs, item.krate(self.db))
583 }
584
585 pub(crate) fn doc_aliases<I>(&self, item: &I) -> Vec<SmolStr>
586 where
587 I: hir::HasAttrs + Copy,
588 {
589 let attrs = item.attrs(self.db);
590 attrs.doc_aliases(self.db).iter().map(|it| it.as_str().into()).collect()
591 }
592
593 pub(crate) fn is_item_hidden(&self, item: &hir::ItemInNs) -> bool {
595 let attrs = item.attrs(self.db);
596 let krate = item.krate(self.db);
597 match (attrs, krate) {
598 (Some(attrs), Some(krate)) => self.is_doc_hidden(&attrs, krate),
599 _ => false,
600 }
601 }
602
603 pub(crate) fn check_stability(&self, attrs: Option<&hir::AttrsWithOwner>) -> bool {
605 let Some(attrs) = attrs else {
606 return true;
607 };
608 if !attrs.is_unstable() {
609 return true;
610 }
611 if !self.is_nightly {
612 return false;
613 }
614 let Some(unstable_feature) = attrs.unstable_feature(self.db) else {
616 return true;
617 };
618 !is_internal_feature(&unstable_feature)
619 || self.krate.is_unstable_feature_enabled(self.db, &unstable_feature)
620 }
621
622 pub(crate) fn check_stability_and_hidden<I>(&self, item: I) -> bool
623 where
624 I: hir::HasAttrs + hir::HasCrate,
625 {
626 let defining_crate = item.krate(self.db);
627 let attrs = item.attrs(self.db);
628 self.check_stability(Some(&attrs)) && !self.is_doc_hidden(&attrs, defining_crate)
629 }
630
631 pub(crate) fn is_ops_trait(&self, trait_: hir::Trait) -> bool {
633 match trait_.attrs(self.db).lang(self.db) {
634 Some(lang) => OP_TRAIT_LANG.contains(&lang),
635 None => false,
636 }
637 }
638
639 pub(crate) fn is_doc_notable_trait(&self, trait_: hir::Trait) -> bool {
641 trait_.attrs(self.db).is_doc_notable_trait()
642 }
643
644 pub(crate) fn traits_in_scope(&self) -> hir::VisibleTraits {
646 let mut traits_in_scope = self.scope.visible_traits();
647 if let Some(drop) = self.famous_defs().core_ops_Drop() {
648 traits_in_scope.0.remove(&drop.into());
649 }
650 traits_in_scope
651 }
652
653 pub(crate) fn iterate_path_candidates(
654 &self,
655 ty: &hir::Type<'_>,
656 mut cb: impl FnMut(hir::AssocItem),
657 ) {
658 let mut seen = FxHashSet::default();
659 ty.iterate_path_candidates(self.db, &self.scope, &self.traits_in_scope(), None, |item| {
660 if seen.insert(item) {
663 cb(item)
664 }
665 None::<()>
666 });
667 }
668
669 pub(crate) fn process_all_names(&self, f: &mut dyn FnMut(Name, ScopeDef<'db>, Vec<SmolStr>)) {
672 let _p = tracing::info_span!("CompletionContext::process_all_names").entered();
673 self.scope.process_all_names(&mut |name, def| {
674 if self.is_scope_def_hidden(def) {
675 return;
676 }
677 let doc_aliases = self.doc_aliases_in_scope(def);
678 f(name, def, doc_aliases);
679 });
680 }
681
682 pub(crate) fn process_all_names_raw(&self, f: &mut dyn FnMut(Name, ScopeDef<'db>)) {
683 let _p = tracing::info_span!("CompletionContext::process_all_names_raw").entered();
684 self.scope.process_all_names(f);
685 }
686
687 fn is_scope_def_hidden(&self, scope_def: ScopeDef<'db>) -> bool {
688 if let (Some(attrs), Some(krate)) = (scope_def.attrs(self.db), scope_def.krate(self.db)) {
689 return self.is_doc_hidden(&attrs, krate);
690 }
691
692 false
693 }
694
695 fn is_visible_impl(
696 &self,
697 vis: &hir::Visibility,
698 attrs: &hir::AttrsWithOwner,
699 defining_crate: hir::Crate,
700 ) -> Visible {
701 if !self.check_stability(Some(attrs)) {
702 return Visible::No;
703 }
704
705 if !vis.is_visible_from(self.db, self.module.into()) {
706 if !self.config.enable_private_editable {
707 return Visible::No;
708 }
709 return if is_editable_crate(defining_crate, self.db) {
711 Visible::Editable
712 } else {
713 Visible::No
714 };
715 }
716
717 if self.is_doc_hidden(attrs, defining_crate) { Visible::No } else { Visible::Yes }
718 }
719
720 pub(crate) fn is_doc_hidden(
721 &self,
722 attrs: &hir::AttrsWithOwner,
723 defining_crate: hir::Crate,
724 ) -> bool {
725 self.krate != defining_crate && attrs.is_doc_hidden()
727 }
728
729 pub(crate) fn doc_aliases_in_scope(&self, scope_def: ScopeDef<'db>) -> Vec<SmolStr> {
730 if let Some(attrs) = scope_def.attrs(self.db) {
731 attrs.doc_aliases(self.db).iter().map(|it| it.as_str().into()).collect()
732 } else {
733 vec![]
734 }
735 }
736
737 pub(crate) fn rebase_ty(&self, ty: &hir::Type<'db>) -> hir::Type<'db> {
738 self.scope
739 .generic_def()
740 .and_then(|def| ty.try_rebase_into_owner(self.db, def))
741 .unwrap_or_else(|| ty.instantiate_with_errors())
742 }
743}
744
745impl<'a, 'db> CompletionContext<'a, 'db> {
747 pub(crate) fn new(
748 db: &'db RootDatabase,
749 position @ FilePosition { file_id, offset }: FilePosition,
750 config: &'a CompletionConfig<'a>,
751 trigger_character: Option<char>,
752 ) -> Option<(CompletionContext<'a, 'db>, CompletionAnalysis<'db>)> {
753 let _p = tracing::info_span!("CompletionContext::new").entered();
754 let sema = Semantics::new(db);
755
756 let editioned_file_id = sema.attach_first_edition(file_id);
757 let original_file = sema.parse(editioned_file_id);
758
759 let file_with_fake_ident = {
763 let (_, edition) = editioned_file_id.unpack(db);
764 let parse = editioned_file_id.parse(db);
765 parse.reparse(TextRange::empty(offset), COMPLETION_MARKER, edition).tree()
766 };
767
768 let original_token = original_file.syntax().token_at_offset(offset).left_biased()?;
771
772 if original_token.kind() == T![:] {
775 let prev_token = original_token.prev_token()?;
777
778 if prev_token.kind() != T![:] && !is_in_macro_matcher(&original_token) {
780 return None;
781 }
782
783 if prev_token
787 .prev_token()
788 .map(|t| t.kind() == T![:] || t.kind() == T![::])
789 .unwrap_or(false)
790 {
791 return None;
792 }
793 }
794
795 let AnalysisResult {
796 analysis,
797 expected: (expected_type, expected_name),
798 qualifier_ctx,
799 token,
800 original_offset,
801 } = expand_and_analyze(
802 &sema,
803 InFile::new(editioned_file_id.into(), original_file.syntax().clone()),
804 file_with_fake_ident.syntax().clone(),
805 offset,
806 &original_token,
807 )?;
808
809 let scope = sema.scope_at_offset(&token.parent()?, original_offset)?;
811
812 let krate = scope.krate();
813 let module = scope.module();
814 let containing_function = scope.containing_function();
815 let edition = krate.edition(db);
816
817 let toolchain = toolchain_channel(db, krate.into());
818 let is_nightly = matches!(toolchain, Some(base_db::ReleaseChannel::Nightly) | None);
821
822 let mut locals = FxHashMap::default();
823 scope.process_all_names(&mut |name, scope| {
824 if let ScopeDef::Local(local) = scope {
825 if name.as_str().starts_with('<') {
828 return;
829 }
830 locals.insert(name, local);
831 }
832 });
833
834 let depth_from_crate_root = iter::successors(Some(module), |m| m.parent(db))
835 .filter(|m| !matches!(m.definition_source(db).value, ModuleSource::BlockExpr(_)))
837 .count()
838 .saturating_sub(1);
840
841 let exclude_traits: FxHashSet<_> = config
842 .exclude_traits
843 .iter()
844 .filter_map(|path| {
845 hir::resolve_absolute_path(db, path.split("::").map(Symbol::intern)).find_map(
846 |it| match it {
847 hir::ItemInNs::Types(ModuleDef::Trait(t)) => Some(t),
848 _ => None,
849 },
850 )
851 })
852 .collect();
853
854 let mut exclude_flyimport: FxHashMap<_, _> = config
855 .exclude_flyimport
856 .iter()
857 .flat_map(|(path, kind)| {
858 hir::resolve_absolute_path(db, path.split("::").map(Symbol::intern))
859 .map(|it| (it.into_module_def(), *kind))
860 })
861 .collect();
862 let exclude_subitems = exclude_flyimport
863 .iter()
864 .flat_map(|it| match it {
865 (ModuleDef::Module(module), AutoImportExclusionType::SubItems) => {
866 module.scope(db, None)
867 }
868 _ => vec![],
869 })
870 .filter_map(|(_, def)| match def {
871 ScopeDef::ModuleDef(module_def) => Some(module_def),
872 _ => None,
873 })
874 .collect::<Vec<_>>();
875 let exclude_variants = exclude_flyimport
876 .iter()
877 .flat_map(|it| match it {
878 (ModuleDef::Adt(hir::Adt::Enum(enum_)), AutoImportExclusionType::Variants) => {
879 enum_.variants(db)
880 }
881 _ => vec![],
882 })
883 .collect::<Vec<_>>();
884 exclude_flyimport
885 .extend(exclude_traits.iter().map(|&t| (t.into(), AutoImportExclusionType::Always)));
886 exclude_flyimport
887 .extend(exclude_subitems.into_iter().map(|it| (it, AutoImportExclusionType::Always)));
888 exclude_flyimport.extend(
889 exclude_variants.into_iter().map(|it| (it.into(), AutoImportExclusionType::Always)),
890 );
891
892 let complete_semicolon = if !config.add_semicolon_to_unit {
894 CompleteSemicolon::DoNotComplete
895 } else if let Some(term_node) =
896 sema.token_ancestors_with_macros(token.clone()).find(|node| {
897 matches!(
898 node.kind(),
899 BLOCK_EXPR
900 | MATCH_ARM
901 | CLOSURE_EXPR
902 | ARG_LIST
903 | PAREN_EXPR
904 | ARRAY_EXPR
905 | MATCH_EXPR
906 )
907 })
908 {
909 let next_token = iter::successors(token.next_token(), |it| it.next_token())
910 .map(|it| it.kind())
911 .find(|kind| !kind.is_trivia());
912 match term_node.kind() {
913 MATCH_ARM if next_token != Some(T![,]) => CompleteSemicolon::CompleteComma,
914 BLOCK_EXPR if next_token != Some(T![;]) => CompleteSemicolon::CompleteSemi,
915 _ => CompleteSemicolon::DoNotComplete,
916 }
917 } else {
918 CompleteSemicolon::DoNotComplete
919 };
920
921 let display_target = krate.to_display_target(db);
922 let ctx = CompletionContext {
923 sema,
924 scope,
925 db,
926 config,
927 position,
928 trigger_character,
929 original_token,
930 token,
931 krate,
932 module,
933 containing_function,
934 is_nightly,
935 edition,
936 expected_name,
937 expected_type,
938 qualifier_ctx,
939 locals,
940 depth_from_crate_root,
941 exclude_flyimport,
942 exclude_traits,
943 complete_semicolon,
944 display_target,
945 };
946 Some((ctx, analysis))
947 }
948}
949
950const OP_TRAIT_LANG: &[hir::LangItem] = &[
951 hir::LangItem::AddAssign,
952 hir::LangItem::Add,
953 hir::LangItem::BitAndAssign,
954 hir::LangItem::BitAnd,
955 hir::LangItem::BitOrAssign,
956 hir::LangItem::BitOr,
957 hir::LangItem::BitXorAssign,
958 hir::LangItem::BitXor,
959 hir::LangItem::DerefMut,
960 hir::LangItem::Deref,
961 hir::LangItem::DivAssign,
962 hir::LangItem::Div,
963 hir::LangItem::PartialEq,
964 hir::LangItem::FnMut,
965 hir::LangItem::FnOnce,
966 hir::LangItem::Fn,
967 hir::LangItem::IndexMut,
968 hir::LangItem::Index,
969 hir::LangItem::MulAssign,
970 hir::LangItem::Mul,
971 hir::LangItem::Neg,
972 hir::LangItem::Not,
973 hir::LangItem::PartialOrd,
974 hir::LangItem::RemAssign,
975 hir::LangItem::Rem,
976 hir::LangItem::ShlAssign,
977 hir::LangItem::Shl,
978 hir::LangItem::ShrAssign,
979 hir::LangItem::Shr,
980 hir::LangItem::Sub,
981];
982
983const INTERNAL_FEATURES_LIST: &[Symbol] = &[
985 sym::abi_unadjusted,
986 sym::allocator_internals,
987 sym::allow_internal_unsafe,
988 sym::allow_internal_unstable,
989 sym::cfg_emscripten_wasm_eh,
990 sym::cfg_target_has_reliable_f16_f128,
991 sym::compiler_builtins,
992 sym::custom_mir,
993 sym::eii_internals,
994 sym::field_representing_type_raw,
995 sym::intrinsics,
996 sym::core_intrinsics,
997 sym::lang_items,
998 sym::link_cfg,
999 sym::more_maybe_bounds,
1000 sym::negative_bounds,
1001 sym::pattern_complexity_limit,
1002 sym::prelude_import,
1003 sym::profiler_runtime,
1004 sym::rustc_attrs,
1005 sym::staged_api,
1006 sym::test_unstable_lint,
1007 sym::builtin_syntax,
1008 sym::link_llvm_intrinsics,
1009 sym::needs_panic_runtime,
1010 sym::panic_runtime,
1011 sym::pattern_types,
1012 sym::rustdoc_internals,
1013 sym::contracts_internals,
1014 sym::freeze_impls,
1015 sym::unsized_fn_params,
1016];
1017
1018static INTERNAL_FEATURES: LazyLock<FxHashSet<Symbol>> =
1019 LazyLock::new(|| INTERNAL_FEATURES_LIST.iter().cloned().collect());
1020
1021fn is_internal_feature(feature: &Symbol) -> bool {
1022 if INTERNAL_FEATURES.contains(feature) {
1023 return true;
1024 }
1025 let feature = feature.as_str();
1027 feature.ends_with("_internal") || feature.ends_with("_internals")
1028}