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 pub(crate) at_optional_block: bool,
193}
194
195#[derive(Clone, Debug, PartialEq, Eq)]
197pub(crate) enum TypeLocation {
198 TupleField,
199 TypeAscription(TypeAscriptionTarget),
200 GenericArg {
202 args: Option<ast::GenericArgList>,
204 of_trait: Option<hir::Trait>,
206 corresponding_param: Option<ast::GenericParam>,
208 },
209 AssocTypeEq,
211 AssocConstEq,
213 TypeBound,
214 ImplTarget,
215 ImplTrait,
216 Other,
217}
218
219impl TypeLocation {
220 pub(crate) fn complete_lifetimes(&self) -> bool {
221 matches!(
222 self,
223 TypeLocation::GenericArg {
224 corresponding_param: Some(ast::GenericParam::LifetimeParam(_)),
225 ..
226 }
227 )
228 }
229
230 pub(crate) fn complete_consts(&self) -> bool {
231 matches!(
232 self,
233 TypeLocation::GenericArg {
234 corresponding_param: Some(ast::GenericParam::ConstParam(_)),
235 ..
236 } | TypeLocation::AssocConstEq
237 )
238 }
239
240 pub(crate) fn complete_types(&self) -> bool {
241 match self {
242 TypeLocation::GenericArg { corresponding_param: Some(param), .. } => {
243 matches!(param, ast::GenericParam::TypeParam(_))
244 }
245 TypeLocation::AssocConstEq => false,
246 TypeLocation::AssocTypeEq => true,
247 TypeLocation::ImplTrait => false,
248 _ => true,
249 }
250 }
251
252 pub(crate) fn complete_self_type(&self) -> bool {
253 self.complete_types() && !matches!(self, TypeLocation::ImplTarget | TypeLocation::ImplTrait)
254 }
255}
256
257#[derive(Clone, Debug, PartialEq, Eq)]
258pub(crate) enum TypeAscriptionTarget {
259 Let(Option<ast::Pat>),
260 FnParam(Option<ast::Pat>),
261 RetType { body: Option<ast::Expr>, item: Option<ast::Fn> },
262 Const(Option<ast::Expr>),
263}
264
265#[derive(Debug, PartialEq, Eq)]
267pub(crate) enum ItemListKind {
268 SourceFile,
269 Module,
270 Impl,
271 TraitImpl(Option<ast::Impl>),
272 Trait,
273 ExternBlock { is_unsafe: bool },
274}
275
276#[derive(Debug)]
277pub(crate) enum Qualified<'db> {
278 No,
279 With {
280 path: ast::Path,
281 resolution: Option<PathResolution<'db>>,
282 super_chain_len: Option<usize>,
293 },
294 TypeAnchor {
296 ty: Option<hir::Type<'db>>,
297 trait_: Option<hir::Trait>,
298 },
299 Absolute,
301}
302
303#[derive(Debug, Clone, PartialEq, Eq)]
305pub(crate) struct PatternContext {
306 pub(crate) refutability: PatternRefutability,
307 pub(crate) param_ctx: Option<ParamContext>,
308 pub(crate) has_type_ascription: bool,
309 pub(crate) should_suggest_name: bool,
310 pub(crate) after_if_expr: bool,
311 pub(crate) parent_pat: Option<ast::Pat>,
312 pub(crate) ref_token: Option<SyntaxToken>,
313 pub(crate) mut_token: Option<SyntaxToken>,
314 pub(crate) record_pat: Option<ast::RecordPat>,
316 pub(crate) impl_or_trait: Option<Either<ast::Impl, ast::Trait>>,
317 pub(crate) missing_variants: Vec<hir::EnumVariant>,
319}
320
321#[derive(Debug, Clone, PartialEq, Eq)]
322pub(crate) struct ParamContext {
323 pub(crate) param_list: ast::ParamList,
324 pub(crate) param: ast::Param,
325 pub(crate) kind: ParamKind,
326}
327
328#[derive(Debug)]
330pub(crate) struct LifetimeContext {
331 pub(crate) kind: LifetimeKind,
332}
333
334#[derive(Debug)]
336pub(crate) enum LifetimeKind {
337 LifetimeParam,
338 Lifetime { in_lifetime_param_bound: bool, def: Option<hir::GenericDef> },
339 LabelRef,
340 LabelDef,
341}
342
343#[derive(Debug)]
345pub(crate) struct NameContext {
346 #[allow(dead_code)]
347 pub(crate) name: Option<ast::Name>,
348 pub(crate) kind: NameKind,
349}
350
351#[derive(Debug)]
353#[allow(dead_code)]
354pub(crate) enum NameKind {
355 Const,
356 ConstParam,
357 Enum,
358 Function,
359 IdentPat(PatternContext),
360 MacroDef,
361 MacroRules,
362 Module(ast::Module),
364 RecordField,
365 Rename,
366 SelfParam,
367 Static,
368 Struct,
369 Trait,
370 TypeAlias,
371 TypeParam,
372 Union,
373 Variant,
374}
375
376#[derive(Debug)]
378pub(crate) struct NameRefContext<'db> {
379 pub(crate) nameref: Option<ast::NameRef>,
381 pub(crate) kind: NameRefKind<'db>,
382}
383
384#[derive(Debug)]
386pub(crate) enum NameRefKind<'db> {
387 Path(PathCompletionCtx<'db>),
388 DotAccess(DotAccess<'db>),
389 Keyword(ast::Item),
391 RecordExpr {
393 dot_prefix: bool,
394 expr: ast::RecordExpr,
395 },
396 Pattern(PatternContext),
397 ExternCrate,
398}
399
400#[derive(Debug)]
402pub(crate) enum CompletionAnalysis<'db> {
403 Name(NameContext),
404 NameRef(NameRefContext<'db>),
405 Lifetime(LifetimeContext),
406 String {
408 original: ast::String,
410 expanded: Option<ast::String>,
412 },
413 UnexpandedAttrTT {
415 colon_prefix: bool,
416 fake_attribute_under_caret: Option<ast::TokenTreeMeta>,
417 extern_crate: Option<ast::ExternCrate>,
418 },
419 CfgPredicate,
421 MacroSegment,
422}
423
424#[derive(Debug)]
426pub(crate) struct DotAccess<'db> {
427 pub(crate) receiver: Option<ast::Expr>,
428 pub(crate) receiver_ty: Option<TypeInfo<'db>>,
429 pub(crate) kind: DotAccessKind,
430 pub(crate) ctx: DotAccessExprCtx,
431}
432
433#[derive(Debug, Clone, Copy)]
434pub(crate) enum DotAccessKind {
435 Field {
436 receiver_is_ambiguous_float_literal: bool,
439 },
440 Method,
441}
442
443#[derive(Debug, Clone, Copy, PartialEq, Eq)]
444pub(crate) struct DotAccessExprCtx {
445 pub(crate) in_block_expr: bool,
446 pub(crate) in_breakable: Option<BreakableKind>,
447}
448
449#[derive(Copy, Clone, Debug, PartialEq, Eq)]
450pub(crate) enum BreakableKind {
451 Loop,
452 For,
453 While,
454 Block,
455}
456
457#[derive(Clone, Debug, PartialEq, Eq)]
458pub(crate) enum ParamKind {
459 Function(ast::Fn),
460 Closure(ast::ClosureExpr),
461}
462
463#[derive(Debug)]
466pub(crate) struct CompletionContext<'a, 'db> {
467 pub(crate) sema: Semantics<'db, RootDatabase>,
468 pub(crate) scope: SemanticsScope<'db>,
469 pub(crate) db: &'db RootDatabase,
470 pub(crate) config: &'a CompletionConfig<'a>,
471 pub(crate) position: FilePosition,
472
473 pub(crate) trigger_character: Option<char>,
474 pub(crate) original_token: SyntaxToken,
476 pub(crate) token: SyntaxToken,
478 pub(crate) krate: hir::Crate,
480 pub(crate) display_target: DisplayTarget,
481 pub(crate) module: hir::Module,
483 pub(crate) containing_function: Option<hir::Function>,
485 pub(crate) is_nightly: bool,
487 pub(crate) edition: Edition,
490
491 pub(crate) expected_name: Option<NameOrNameRef>,
494 pub(crate) expected_type: Option<Type<'db>>,
496 pub(crate) expected_func: Option<hir::Callable<'db>>,
497
498 pub(crate) qualifier_ctx: QualifierCtx,
499
500 pub(crate) locals: FxHashMap<Name, Local<'db>>,
501
502 pub(crate) depth_from_crate_root: usize,
509
510 pub(crate) exclude_flyimport: FxHashMap<ModuleDef, AutoImportExclusionType>,
515 pub(crate) exclude_traits: FxHashSet<hir::Trait>,
520
521 pub(crate) complete_semicolon: CompleteSemicolon,
523}
524
525#[derive(Debug, Default)]
526pub(crate) struct ExpectedInfo<'db> {
527 expected_type: Option<Type<'db>>,
528 expected_name: Option<NameOrNameRef>,
529 expected_func: Option<hir::Callable<'db>>,
530}
531
532#[derive(Debug)]
533pub(crate) enum CompleteSemicolon {
534 DoNotComplete,
535 CompleteSemi,
536 CompleteComma,
537}
538
539impl<'db> CompletionContext<'_, 'db> {
540 pub(crate) fn source_range(&self) -> TextRange {
542 let kind = self.original_token.kind();
543 match kind {
544 CHAR => {
545 cov_mark::hit!(completes_if_lifetime_without_idents);
547 TextRange::at(self.original_token.text_range().start(), TextSize::from(1))
548 }
549 LIFETIME_IDENT | UNDERSCORE | INT_NUMBER => self.original_token.text_range(),
550 _ if kind.is_any_identifier() => self.original_token.text_range(),
552 _ => TextRange::empty(self.position.offset),
553 }
554 }
555
556 pub(crate) fn famous_defs(&self) -> FamousDefs<'_, 'db> {
557 FamousDefs(&self.sema, self.krate)
558 }
559
560 pub(crate) fn def_is_visible(&self, item: &ScopeDef<'db>) -> Visible {
562 match item {
563 ScopeDef::ModuleDef(def) => match def {
564 hir::ModuleDef::Module(it) => self.is_visible(it),
565 hir::ModuleDef::Function(it) => self.is_visible(it),
566 hir::ModuleDef::Adt(it) => self.is_visible(it),
567 hir::ModuleDef::EnumVariant(it) => self.is_visible(it),
568 hir::ModuleDef::Const(it) => self.is_visible(it),
569 hir::ModuleDef::Static(it) => self.is_visible(it),
570 hir::ModuleDef::Trait(it) => self.is_visible(it),
571 hir::ModuleDef::TypeAlias(it) => self.is_visible(it),
572 hir::ModuleDef::Macro(it) => self.is_visible(it),
573 hir::ModuleDef::BuiltinType(_) => Visible::Yes,
574 },
575 ScopeDef::GenericParam(_)
576 | ScopeDef::ImplSelfType(_)
577 | ScopeDef::AdtSelfType(_)
578 | ScopeDef::Local(_)
579 | ScopeDef::Label(_)
580 | ScopeDef::Unknown => Visible::Yes,
581 }
582 }
583
584 pub(crate) fn is_visible<I>(&self, item: &I) -> Visible
586 where
587 I: hir::HasVisibility + hir::HasAttrs + hir::HasCrate + Copy,
588 {
589 let vis = item.visibility(self.db);
590 let attrs = item.attrs(self.db);
591 self.is_visible_impl(&vis, &attrs, item.krate(self.db))
592 }
593
594 pub(crate) fn doc_aliases<I>(&self, item: &I) -> Vec<SmolStr>
595 where
596 I: hir::HasAttrs + Copy,
597 {
598 let attrs = item.attrs(self.db);
599 attrs.doc_aliases(self.db).iter().map(|it| it.as_str().into()).collect()
600 }
601
602 pub(crate) fn is_item_hidden(&self, item: &hir::ItemInNs) -> bool {
604 let attrs = item.attrs(self.db);
605 let krate = item.krate(self.db);
606 match (attrs, krate) {
607 (Some(attrs), Some(krate)) => self.is_doc_hidden(&attrs, krate),
608 _ => false,
609 }
610 }
611
612 pub(crate) fn check_stability(&self, attrs: Option<&hir::AttrsWithOwner>) -> bool {
614 let Some(attrs) = attrs else {
615 return true;
616 };
617 if !attrs.is_unstable() {
618 return true;
619 }
620 if !self.is_nightly {
621 return false;
622 }
623 let Some(unstable_feature) = attrs.unstable_feature(self.db) else {
625 return true;
626 };
627 !is_internal_feature(&unstable_feature)
628 || self.krate.is_unstable_feature_enabled(self.db, &unstable_feature)
629 }
630
631 pub(crate) fn check_stability_and_hidden<I>(&self, item: I) -> bool
632 where
633 I: hir::HasAttrs + hir::HasCrate,
634 {
635 let defining_crate = item.krate(self.db);
636 let attrs = item.attrs(self.db);
637 self.check_stability(Some(&attrs)) && !self.is_doc_hidden(&attrs, defining_crate)
638 }
639
640 pub(crate) fn is_ops_trait(&self, trait_: hir::Trait) -> bool {
642 match trait_.attrs(self.db).lang(self.db) {
643 Some(lang) => OP_TRAIT_LANG.contains(&lang),
644 None => false,
645 }
646 }
647
648 pub(crate) fn is_doc_notable_trait(&self, trait_: hir::Trait) -> bool {
650 trait_.attrs(self.db).is_doc_notable_trait()
651 }
652
653 pub(crate) fn traits_in_scope(&self) -> hir::VisibleTraits {
655 let mut traits_in_scope = self.scope.visible_traits();
656 if let Some(drop) = self.famous_defs().core_ops_Drop() {
657 traits_in_scope.0.remove(&drop.into());
658 }
659 traits_in_scope
660 }
661
662 pub(crate) fn iterate_path_candidates(
663 &self,
664 ty: &hir::Type<'_>,
665 mut cb: impl FnMut(hir::AssocItem),
666 ) {
667 let mut seen = FxHashSet::default();
668 ty.iterate_path_candidates(self.db, &self.scope, &self.traits_in_scope(), None, |item| {
669 if seen.insert(item) {
672 cb(item)
673 }
674 None::<()>
675 });
676 }
677
678 pub(crate) fn process_all_names(&self, f: &mut dyn FnMut(Name, ScopeDef<'db>, Vec<SmolStr>)) {
681 let _p = tracing::info_span!("CompletionContext::process_all_names").entered();
682 self.scope.process_all_names(&mut |name, def| {
683 if self.is_scope_def_hidden(def) {
684 return;
685 }
686 let doc_aliases = self.doc_aliases_in_scope(def);
687 f(name, def, doc_aliases);
688 });
689 }
690
691 pub(crate) fn process_all_names_raw(&self, f: &mut dyn FnMut(Name, ScopeDef<'db>)) {
692 let _p = tracing::info_span!("CompletionContext::process_all_names_raw").entered();
693 self.scope.process_all_names(f);
694 }
695
696 fn is_scope_def_hidden(&self, scope_def: ScopeDef<'db>) -> bool {
697 if let (Some(attrs), Some(krate)) = (scope_def.attrs(self.db), scope_def.krate(self.db)) {
698 return self.is_doc_hidden(&attrs, krate);
699 }
700
701 false
702 }
703
704 fn is_visible_impl(
705 &self,
706 vis: &hir::Visibility,
707 attrs: &hir::AttrsWithOwner,
708 defining_crate: hir::Crate,
709 ) -> Visible {
710 if !self.check_stability(Some(attrs)) {
711 return Visible::No;
712 }
713
714 if !vis.is_visible_from(self.db, self.module.into()) {
715 if !self.config.enable_private_editable {
716 return Visible::No;
717 }
718 return if is_editable_crate(defining_crate, self.db) {
720 Visible::Editable
721 } else {
722 Visible::No
723 };
724 }
725
726 if self.is_doc_hidden(attrs, defining_crate) { Visible::No } else { Visible::Yes }
727 }
728
729 pub(crate) fn is_doc_hidden(
730 &self,
731 attrs: &hir::AttrsWithOwner,
732 defining_crate: hir::Crate,
733 ) -> bool {
734 self.krate != defining_crate && attrs.is_doc_hidden()
736 }
737
738 pub(crate) fn doc_aliases_in_scope(&self, scope_def: ScopeDef<'db>) -> Vec<SmolStr> {
739 if let Some(attrs) = scope_def.attrs(self.db) {
740 attrs.doc_aliases(self.db).iter().map(|it| it.as_str().into()).collect()
741 } else {
742 vec![]
743 }
744 }
745
746 pub(crate) fn rebase_ty(&self, ty: &hir::Type<'db>) -> hir::Type<'db> {
747 self.scope
748 .generic_def()
749 .and_then(|def| ty.try_rebase_into_owner(self.db, def))
750 .unwrap_or_else(|| ty.instantiate_with_errors())
751 }
752}
753
754impl<'a, 'db> CompletionContext<'a, 'db> {
756 pub(crate) fn new(
757 db: &'db RootDatabase,
758 position @ FilePosition { file_id, offset }: FilePosition,
759 config: &'a CompletionConfig<'a>,
760 trigger_character: Option<char>,
761 ) -> Option<(CompletionContext<'a, 'db>, CompletionAnalysis<'db>)> {
762 let _p = tracing::info_span!("CompletionContext::new").entered();
763 let sema = Semantics::new(db);
764
765 let editioned_file_id = sema.attach_first_edition(file_id);
766 let original_file = sema.parse(editioned_file_id);
767
768 let file_with_fake_ident = {
772 let (_, edition) = editioned_file_id.unpack(db);
773 let parse = editioned_file_id.parse(db);
774 parse.reparse(TextRange::empty(offset), COMPLETION_MARKER, edition).tree()
775 };
776
777 let original_token = original_file.syntax().token_at_offset(offset).left_biased()?;
780
781 if original_token.kind() == T![:] {
784 let prev_token = original_token.prev_token()?;
786
787 if prev_token.kind() != T![:] && !is_in_macro_matcher(&original_token) {
789 return None;
790 }
791
792 if prev_token
796 .prev_token()
797 .map(|t| t.kind() == T![:] || t.kind() == T![::])
798 .unwrap_or(false)
799 {
800 return None;
801 }
802 }
803
804 let AnalysisResult {
805 analysis,
806 expected: ExpectedInfo { expected_type, expected_name, expected_func },
807 qualifier_ctx,
808 token,
809 original_offset,
810 } = expand_and_analyze(
811 &sema,
812 InFile::new(editioned_file_id.into(), original_file.syntax().clone()),
813 file_with_fake_ident.syntax().clone(),
814 offset,
815 &original_token,
816 )?;
817
818 let scope = sema.scope_at_offset(&token.parent()?, original_offset)?;
820
821 let krate = scope.krate();
822 let module = scope.module();
823 let containing_function = scope.containing_function();
824 let edition = krate.edition(db);
825
826 let toolchain = toolchain_channel(db, krate.into());
827 let is_nightly = matches!(toolchain, Some(base_db::ReleaseChannel::Nightly) | None);
830
831 let mut locals = FxHashMap::default();
832 scope.process_all_names(&mut |name, scope| {
833 if let ScopeDef::Local(local) = scope {
834 if name.as_str().starts_with('<') {
837 return;
838 }
839 locals.insert(name, local);
840 }
841 });
842
843 let depth_from_crate_root = iter::successors(Some(module), |m| m.parent(db))
844 .filter(|m| !matches!(m.definition_source(db).value, ModuleSource::BlockExpr(_)))
846 .count()
847 .saturating_sub(1);
849
850 let exclude_traits: FxHashSet<_> = config
851 .exclude_traits
852 .iter()
853 .filter_map(|path| {
854 hir::resolve_absolute_path(db, path.split("::").map(Symbol::intern)).find_map(
855 |it| match it {
856 hir::ItemInNs::Types(ModuleDef::Trait(t)) => Some(t),
857 _ => None,
858 },
859 )
860 })
861 .collect();
862
863 let mut exclude_flyimport: FxHashMap<_, _> = config
864 .exclude_flyimport
865 .iter()
866 .flat_map(|(path, kind)| {
867 hir::resolve_absolute_path(db, path.split("::").map(Symbol::intern))
868 .map(|it| (it.into_module_def(), *kind))
869 })
870 .collect();
871 let exclude_subitems = exclude_flyimport
872 .iter()
873 .flat_map(|it| match it {
874 (ModuleDef::Module(module), AutoImportExclusionType::SubItems) => {
875 module.scope(db, None)
876 }
877 _ => vec![],
878 })
879 .filter_map(|(_, def)| match def {
880 ScopeDef::ModuleDef(module_def) => Some(module_def),
881 _ => None,
882 })
883 .collect::<Vec<_>>();
884 let exclude_variants = exclude_flyimport
885 .iter()
886 .flat_map(|it| match it {
887 (ModuleDef::Adt(hir::Adt::Enum(enum_)), AutoImportExclusionType::Variants) => {
888 enum_.variants(db)
889 }
890 _ => vec![],
891 })
892 .collect::<Vec<_>>();
893 exclude_flyimport
894 .extend(exclude_traits.iter().map(|&t| (t.into(), AutoImportExclusionType::Always)));
895 exclude_flyimport
896 .extend(exclude_subitems.into_iter().map(|it| (it, AutoImportExclusionType::Always)));
897 exclude_flyimport.extend(
898 exclude_variants.into_iter().map(|it| (it.into(), AutoImportExclusionType::Always)),
899 );
900
901 let complete_semicolon = if !config.add_semicolon_to_unit {
903 CompleteSemicolon::DoNotComplete
904 } else if let Some(term_node) =
905 sema.token_ancestors_with_macros(token.clone()).find(|node| {
906 matches!(
907 node.kind(),
908 BLOCK_EXPR
909 | MATCH_ARM
910 | CLOSURE_EXPR
911 | ARG_LIST
912 | PAREN_EXPR
913 | ARRAY_EXPR
914 | MATCH_EXPR
915 )
916 })
917 {
918 let next_token = iter::successors(token.next_token(), |it| it.next_token())
919 .map(|it| it.kind())
920 .find(|kind| !kind.is_trivia());
921 match term_node.kind() {
922 MATCH_ARM if next_token != Some(T![,]) => CompleteSemicolon::CompleteComma,
923 BLOCK_EXPR if next_token != Some(T![;]) => CompleteSemicolon::CompleteSemi,
924 _ => CompleteSemicolon::DoNotComplete,
925 }
926 } else {
927 CompleteSemicolon::DoNotComplete
928 };
929
930 let display_target = krate.to_display_target(db);
931 let ctx = CompletionContext {
932 sema,
933 scope,
934 db,
935 config,
936 position,
937 trigger_character,
938 original_token,
939 token,
940 krate,
941 module,
942 containing_function,
943 is_nightly,
944 edition,
945 expected_name,
946 expected_type,
947 expected_func,
948 qualifier_ctx,
949 locals,
950 depth_from_crate_root,
951 exclude_flyimport,
952 exclude_traits,
953 complete_semicolon,
954 display_target,
955 };
956 Some((ctx, analysis))
957 }
958}
959
960const OP_TRAIT_LANG: &[hir::LangItem] = &[
961 hir::LangItem::AddAssign,
962 hir::LangItem::Add,
963 hir::LangItem::BitAndAssign,
964 hir::LangItem::BitAnd,
965 hir::LangItem::BitOrAssign,
966 hir::LangItem::BitOr,
967 hir::LangItem::BitXorAssign,
968 hir::LangItem::BitXor,
969 hir::LangItem::DerefMut,
970 hir::LangItem::Deref,
971 hir::LangItem::DivAssign,
972 hir::LangItem::Div,
973 hir::LangItem::PartialEq,
974 hir::LangItem::FnMut,
975 hir::LangItem::FnOnce,
976 hir::LangItem::Fn,
977 hir::LangItem::IndexMut,
978 hir::LangItem::Index,
979 hir::LangItem::MulAssign,
980 hir::LangItem::Mul,
981 hir::LangItem::Neg,
982 hir::LangItem::Not,
983 hir::LangItem::PartialOrd,
984 hir::LangItem::RemAssign,
985 hir::LangItem::Rem,
986 hir::LangItem::ShlAssign,
987 hir::LangItem::Shl,
988 hir::LangItem::ShrAssign,
989 hir::LangItem::Shr,
990 hir::LangItem::Sub,
991];
992
993const INTERNAL_FEATURES_LIST: &[Symbol] = &[
995 sym::abi_unadjusted,
996 sym::allocator_internals,
997 sym::allow_internal_unsafe,
998 sym::allow_internal_unstable,
999 sym::cfg_emscripten_wasm_eh,
1000 sym::cfg_target_has_reliable_f16_f128,
1001 sym::compiler_builtins,
1002 sym::custom_mir,
1003 sym::eii_internals,
1004 sym::field_representing_type_raw,
1005 sym::intrinsics,
1006 sym::core_intrinsics,
1007 sym::lang_items,
1008 sym::link_cfg,
1009 sym::more_maybe_bounds,
1010 sym::negative_bounds,
1011 sym::pattern_complexity_limit,
1012 sym::prelude_import,
1013 sym::profiler_runtime,
1014 sym::rustc_attrs,
1015 sym::staged_api,
1016 sym::test_unstable_lint,
1017 sym::builtin_syntax,
1018 sym::link_llvm_intrinsics,
1019 sym::needs_panic_runtime,
1020 sym::panic_runtime,
1021 sym::pattern_types,
1022 sym::rustdoc_internals,
1023 sym::contracts_internals,
1024 sym::freeze_impls,
1025 sym::unsized_fn_params,
1026];
1027
1028static INTERNAL_FEATURES: LazyLock<FxHashSet<Symbol>> =
1029 LazyLock::new(|| INTERNAL_FEATURES_LIST.iter().cloned().collect());
1030
1031fn is_internal_feature(feature: &Symbol) -> bool {
1032 if INTERNAL_FEATURES.contains(feature) {
1033 return true;
1034 }
1035 let feature = feature.as_str();
1037 feature.ends_with("_internal") || feature.ends_with("_internals")
1038}