Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide-completion/src/context.rs')
-rw-r--r--crates/ide-completion/src/context.rs177
1 files changed, 74 insertions, 103 deletions
diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs
index 2f166b7184..d116f665ad 100644
--- a/crates/ide-completion/src/context.rs
+++ b/crates/ide-completion/src/context.rs
@@ -4,7 +4,7 @@ mod analysis;
#[cfg(test)]
mod tests;
-use std::{iter, ops::ControlFlow};
+use std::iter;
use base_db::RootQueryDb as _;
use hir::{
@@ -13,7 +13,7 @@ use hir::{
};
use ide_db::{
FilePosition, FxHashMap, FxHashSet, RootDatabase, famous_defs::FamousDefs,
- helpers::is_editable_crate,
+ helpers::is_editable_crate, syntax_helpers::node_ext::is_in_macro_matcher,
};
use itertools::Either;
use syntax::{
@@ -21,7 +21,6 @@ use syntax::{
SyntaxKind::{self, *},
SyntaxToken, T, TextRange, TextSize,
ast::{self, AttrKind, NameOrNameRef},
- match_ast,
};
use crate::{
@@ -53,6 +52,7 @@ pub(crate) struct QualifierCtx {
pub(crate) unsafe_tok: Option<SyntaxToken>,
pub(crate) safe_tok: Option<SyntaxToken>,
pub(crate) vis_node: Option<ast::Visibility>,
+ pub(crate) abi_node: Option<ast::Abi>,
}
impl QualifierCtx {
@@ -61,6 +61,7 @@ impl QualifierCtx {
&& self.unsafe_tok.is_none()
&& self.safe_tok.is_none()
&& self.vis_node.is_none()
+ && self.abi_node.is_none()
}
}
@@ -155,7 +156,7 @@ pub(crate) struct PathExprCtx<'db> {
pub(crate) after_amp: bool,
/// The surrounding RecordExpression we are completing a functional update
pub(crate) is_func_update: Option<ast::RecordExpr>,
- pub(crate) self_param: Option<hir::SelfParam>,
+ pub(crate) self_param: Option<Either<hir::SelfParam, hir::Param<'db>>>,
pub(crate) innermost_ret_ty: Option<hir::Type<'db>>,
pub(crate) innermost_breakable_ty: Option<hir::Type<'db>>,
pub(crate) impl_: Option<ast::Impl>,
@@ -388,6 +389,7 @@ pub(crate) enum CompletionAnalysis<'db> {
fake_attribute_under_caret: Option<ast::Attr>,
extern_crate: Option<ast::ExternCrate>,
},
+ MacroSegment,
}
/// Information about the field or method access we are completing.
@@ -557,7 +559,7 @@ impl CompletionContext<'_> {
I: hir::HasAttrs + Copy,
{
let attrs = item.attrs(self.db);
- attrs.doc_aliases().map(|it| it.as_str().into()).collect()
+ attrs.doc_aliases(self.db).iter().map(|it| it.as_str().into()).collect()
}
/// Check if an item is `#[doc(hidden)]`.
@@ -571,7 +573,7 @@ impl CompletionContext<'_> {
}
/// Checks whether this item should be listed in regards to stability. Returns `true` if we should.
- pub(crate) fn check_stability(&self, attrs: Option<&hir::Attrs>) -> bool {
+ pub(crate) fn check_stability(&self, attrs: Option<&hir::AttrsWithOwner>) -> bool {
let Some(attrs) = attrs else {
return true;
};
@@ -589,15 +591,15 @@ impl CompletionContext<'_> {
/// Whether the given trait is an operator trait or not.
pub(crate) fn is_ops_trait(&self, trait_: hir::Trait) -> bool {
- match trait_.attrs(self.db).lang() {
- Some(lang) => OP_TRAIT_LANG_NAMES.contains(&lang.as_str()),
+ match trait_.attrs(self.db).lang(self.db) {
+ Some(lang) => OP_TRAIT_LANG.contains(&lang),
None => false,
}
}
/// Whether the given trait has `#[doc(notable_trait)]`
pub(crate) fn is_doc_notable_trait(&self, trait_: hir::Trait) -> bool {
- trait_.attrs(self.db).has_doc_notable_trait()
+ trait_.attrs(self.db).is_doc_notable_trait()
}
/// Returns the traits in scope, with the [`Drop`] trait removed.
@@ -615,21 +617,14 @@ impl CompletionContext<'_> {
mut cb: impl FnMut(hir::AssocItem),
) {
let mut seen = FxHashSet::default();
- ty.iterate_path_candidates(
- self.db,
- &self.scope,
- &self.traits_in_scope(),
- Some(self.module),
- None,
- |item| {
- // We might iterate candidates of a trait multiple times here, so deduplicate
- // them.
- if seen.insert(item) {
- cb(item)
- }
- None::<()>
- },
- );
+ ty.iterate_path_candidates(self.db, &self.scope, &self.traits_in_scope(), None, |item| {
+ // We might iterate candidates of a trait multiple times here, so deduplicate
+ // them.
+ if seen.insert(item) {
+ cb(item)
+ }
+ None::<()>
+ });
}
/// A version of [`SemanticsScope::process_all_names`] that filters out `#[doc(hidden)]` items and
@@ -661,7 +656,7 @@ impl CompletionContext<'_> {
fn is_visible_impl(
&self,
vis: &hir::Visibility,
- attrs: &hir::Attrs,
+ attrs: &hir::AttrsWithOwner,
defining_crate: hir::Crate,
) -> Visible {
if !self.check_stability(Some(attrs)) {
@@ -683,14 +678,18 @@ impl CompletionContext<'_> {
if self.is_doc_hidden(attrs, defining_crate) { Visible::No } else { Visible::Yes }
}
- pub(crate) fn is_doc_hidden(&self, attrs: &hir::Attrs, defining_crate: hir::Crate) -> bool {
+ pub(crate) fn is_doc_hidden(
+ &self,
+ attrs: &hir::AttrsWithOwner,
+ defining_crate: hir::Crate,
+ ) -> bool {
// `doc(hidden)` items are only completed within the defining crate.
- self.krate != defining_crate && attrs.has_doc_hidden()
+ self.krate != defining_crate && attrs.is_doc_hidden()
}
pub(crate) fn doc_aliases_in_scope(&self, scope_def: ScopeDef) -> Vec<SmolStr> {
if let Some(attrs) = scope_def.attrs(self.db) {
- attrs.doc_aliases().map(|it| it.as_str().into()).collect()
+ attrs.doc_aliases(self.db).iter().map(|it| it.as_str().into()).collect()
} else {
vec![]
}
@@ -708,7 +707,7 @@ impl<'db> CompletionContext<'db> {
let _p = tracing::info_span!("CompletionContext::new").entered();
let sema = Semantics::new(db);
- let editioned_file_id = sema.attach_first_edition(file_id)?;
+ let editioned_file_id = sema.attach_first_edition(file_id);
let original_file = sema.parse(editioned_file_id);
// Insert a fake ident to get a valid parse tree. We will use this file
@@ -731,7 +730,7 @@ impl<'db> CompletionContext<'db> {
let prev_token = original_token.prev_token()?;
// only has a single colon
- if prev_token.kind() != T![:] {
+ if prev_token.kind() != T![:] && !is_in_macro_matcher(&original_token) {
return None;
}
@@ -818,48 +817,20 @@ impl<'db> CompletionContext<'db> {
.extend(exclude_traits.iter().map(|&t| (t.into(), AutoImportExclusionType::Always)));
// FIXME: This should be part of `CompletionAnalysis` / `expand_and_analyze`
- let complete_semicolon = if config.add_semicolon_to_unit {
- let inside_closure_ret = token.parent_ancestors().try_for_each(|ancestor| {
- match_ast! {
- match ancestor {
- ast::BlockExpr(_) => ControlFlow::Break(false),
- ast::ClosureExpr(_) => ControlFlow::Break(true),
- _ => ControlFlow::Continue(())
- }
- }
- });
-
- if inside_closure_ret == ControlFlow::Break(true) {
- CompleteSemicolon::DoNotComplete
- } else {
- let next_non_trivia_token =
- std::iter::successors(token.next_token(), |it| it.next_token())
- .find(|it| !it.kind().is_trivia());
- let in_match_arm = token.parent_ancestors().try_for_each(|ancestor| {
- if ast::MatchArm::can_cast(ancestor.kind()) {
- ControlFlow::Break(true)
- } else if matches!(
- ancestor.kind(),
- SyntaxKind::EXPR_STMT | SyntaxKind::BLOCK_EXPR
- ) {
- ControlFlow::Break(false)
- } else {
- ControlFlow::Continue(())
- }
- });
- // FIXME: This will assume expr macros are not inside match, we need to somehow go to the "parent" of the root node.
- let in_match_arm = match in_match_arm {
- ControlFlow::Continue(()) => false,
- ControlFlow::Break(it) => it,
- };
- let complete_token = if in_match_arm { T![,] } else { T![;] };
- if next_non_trivia_token.map(|it| it.kind()) == Some(complete_token) {
- CompleteSemicolon::DoNotComplete
- } else if in_match_arm {
- CompleteSemicolon::CompleteComma
- } else {
- CompleteSemicolon::CompleteSemi
- }
+ let complete_semicolon = if !config.add_semicolon_to_unit {
+ CompleteSemicolon::DoNotComplete
+ } else if let Some(term_node) =
+ sema.token_ancestors_with_macros(token.clone()).find(|node| {
+ matches!(node.kind(), BLOCK_EXPR | MATCH_ARM | CLOSURE_EXPR | ARG_LIST | PAREN_EXPR)
+ })
+ {
+ let next_token = iter::successors(token.next_token(), |it| it.next_token())
+ .map(|it| it.kind())
+ .find(|kind| !kind.is_trivia());
+ match term_node.kind() {
+ MATCH_ARM if next_token != Some(T![,]) => CompleteSemicolon::CompleteComma,
+ BLOCK_EXPR if next_token != Some(T![;]) => CompleteSemicolon::CompleteSemi,
+ _ => CompleteSemicolon::DoNotComplete,
}
} else {
CompleteSemicolon::DoNotComplete
@@ -894,35 +865,35 @@ impl<'db> CompletionContext<'db> {
}
}
-const OP_TRAIT_LANG_NAMES: &[&str] = &[
- "add_assign",
- "add",
- "bitand_assign",
- "bitand",
- "bitor_assign",
- "bitor",
- "bitxor_assign",
- "bitxor",
- "deref_mut",
- "deref",
- "div_assign",
- "div",
- "eq",
- "fn_mut",
- "fn_once",
- "fn",
- "index_mut",
- "index",
- "mul_assign",
- "mul",
- "neg",
- "not",
- "partial_ord",
- "rem_assign",
- "rem",
- "shl_assign",
- "shl",
- "shr_assign",
- "shr",
- "sub",
+const OP_TRAIT_LANG: &[hir::LangItem] = &[
+ hir::LangItem::AddAssign,
+ hir::LangItem::Add,
+ hir::LangItem::BitAndAssign,
+ hir::LangItem::BitAnd,
+ hir::LangItem::BitOrAssign,
+ hir::LangItem::BitOr,
+ hir::LangItem::BitXorAssign,
+ hir::LangItem::BitXor,
+ hir::LangItem::DerefMut,
+ hir::LangItem::Deref,
+ hir::LangItem::DivAssign,
+ hir::LangItem::Div,
+ hir::LangItem::PartialEq,
+ hir::LangItem::FnMut,
+ hir::LangItem::FnOnce,
+ hir::LangItem::Fn,
+ hir::LangItem::IndexMut,
+ hir::LangItem::Index,
+ hir::LangItem::MulAssign,
+ hir::LangItem::Mul,
+ hir::LangItem::Neg,
+ hir::LangItem::Not,
+ hir::LangItem::PartialOrd,
+ hir::LangItem::RemAssign,
+ hir::LangItem::Rem,
+ hir::LangItem::ShlAssign,
+ hir::LangItem::Shl,
+ hir::LangItem::ShrAssign,
+ hir::LangItem::Shr,
+ hir::LangItem::Sub,
];