Unnamed repository; edit this file 'description' to name the repository.
Merge the different identifier contexts into one enum
Lukas Wirth 2022-05-07
parent 99fa37d · commit 44c3cc1
-rw-r--r--crates/ide-completion/src/completions/attribute.rs7
-rw-r--r--crates/ide-completion/src/completions/dot.rs2
-rw-r--r--crates/ide-completion/src/completions/extern_abi.rs16
-rw-r--r--crates/ide-completion/src/completions/format_string.rs17
-rw-r--r--crates/ide-completion/src/completions/keyword.rs10
-rw-r--r--crates/ide-completion/src/completions/lifetime.rs4
-rw-r--r--crates/ide-completion/src/completions/mod_.rs2
-rw-r--r--crates/ide-completion/src/completions/postfix.rs2
-rw-r--r--crates/ide-completion/src/completions/use_.rs2
-rw-r--r--crates/ide-completion/src/context.rs145
-rw-r--r--crates/ide-completion/src/render/function.rs2
-rw-r--r--crates/ide-completion/src/tests.rs1
-rw-r--r--crates/ide-completion/src/tests/attribute.rs19
13 files changed, 138 insertions, 91 deletions
diff --git a/crates/ide-completion/src/completions/attribute.rs b/crates/ide-completion/src/completions/attribute.rs
index ec95021ec0..0b7479fd0e 100644
--- a/crates/ide-completion/src/completions/attribute.rs
+++ b/crates/ide-completion/src/completions/attribute.rs
@@ -18,7 +18,7 @@ use syntax::{
use crate::{
completions::module_or_attr,
- context::{CompletionContext, PathCompletionCtx, PathKind, PathQualifierCtx},
+ context::{CompletionContext, IdentContext, PathCompletionCtx, PathKind, PathQualifierCtx},
item::CompletionItem,
Completions,
};
@@ -35,7 +35,10 @@ pub(crate) fn complete_known_attribute_input(
acc: &mut Completions,
ctx: &CompletionContext,
) -> Option<()> {
- let attribute = ctx.fake_attribute_under_caret.as_ref()?;
+ let attribute = match &ctx.ident_ctx {
+ IdentContext::UnexpandedAttrTT { fake_attribute_under_caret: Some(it) } => it,
+ _ => return None,
+ };
let name_ref = match attribute.path() {
Some(p) => Some(p.as_single_name_ref()?),
None => None,
diff --git a/crates/ide-completion/src/completions/dot.rs b/crates/ide-completion/src/completions/dot.rs
index 439745ffba..6a553eadc1 100644
--- a/crates/ide-completion/src/completions/dot.rs
+++ b/crates/ide-completion/src/completions/dot.rs
@@ -9,7 +9,7 @@ use crate::{
/// Complete dot accesses, i.e. fields or methods.
pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
- let (dot_access, dot_receiver) = match &ctx.nameref_ctx {
+ let (dot_access, dot_receiver) = match ctx.nameref_ctx() {
Some(NameRefContext {
dot_access:
Some(
diff --git a/crates/ide-completion/src/completions/extern_abi.rs b/crates/ide-completion/src/completions/extern_abi.rs
index 87fccec008..ae8c199f0c 100644
--- a/crates/ide-completion/src/completions/extern_abi.rs
+++ b/crates/ide-completion/src/completions/extern_abi.rs
@@ -5,7 +5,9 @@ use syntax::{
};
use crate::{
- completions::Completions, context::CompletionContext, CompletionItem, CompletionItemKind,
+ completions::Completions,
+ context::{CompletionContext, IdentContext},
+ CompletionItem, CompletionItemKind,
};
// Most of these are feature gated, we should filter/add feature gate completions once we have them.
@@ -41,10 +43,14 @@ const SUPPORTED_CALLING_CONVENTIONS: &[&str] = &[
];
pub(crate) fn complete_extern_abi(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
- if ctx.token.parent().and_then(ast::Abi::cast).is_none() {
- return None;
- }
- let abi_str = ast::String::cast(ctx.token.clone())?;
+ let abi_str = match &ctx.ident_ctx {
+ IdentContext::String { expanded: Some(expanded), .. }
+ if expanded.syntax().parent().map_or(false, |it| ast::Abi::can_cast(it.kind())) =>
+ {
+ expanded
+ }
+ _ => return None,
+ };
let source_range = abi_str.text_range_between_quotes()?;
for &abi in SUPPORTED_CALLING_CONVENTIONS {
CompletionItem::new(CompletionItemKind::Keyword, source_range, abi).add_to(acc);
diff --git a/crates/ide-completion/src/completions/format_string.rs b/crates/ide-completion/src/completions/format_string.rs
index f0c994f6b6..132599906a 100644
--- a/crates/ide-completion/src/completions/format_string.rs
+++ b/crates/ide-completion/src/completions/format_string.rs
@@ -2,16 +2,21 @@
use ide_db::syntax_helpers::format_string::is_format_string;
use itertools::Itertools;
-use syntax::{ast, AstToken, TextRange, TextSize};
+use syntax::{AstToken, TextRange, TextSize};
-use crate::{context::CompletionContext, CompletionItem, CompletionItemKind, Completions};
+use crate::{
+ context::{CompletionContext, IdentContext},
+ CompletionItem, CompletionItemKind, Completions,
+};
/// Complete identifiers in format strings.
pub(crate) fn format_string(acc: &mut Completions, ctx: &CompletionContext) {
- let string = match ast::String::cast(ctx.token.clone())
- .zip(ast::String::cast(ctx.original_token.clone()))
- {
- Some((expanded, original)) if is_format_string(&expanded) => original,
+ let string = match &ctx.ident_ctx {
+ IdentContext::String { expanded: Some(expanded), original }
+ if is_format_string(&expanded) =>
+ {
+ original
+ }
_ => return,
};
let cursor = ctx.position.offset;
diff --git a/crates/ide-completion/src/completions/keyword.rs b/crates/ide-completion/src/completions/keyword.rs
index 766ab4fcd7..b34545414e 100644
--- a/crates/ide-completion/src/completions/keyword.rs
+++ b/crates/ide-completion/src/completions/keyword.rs
@@ -2,7 +2,7 @@
//! - `self`, `super` and `crate`, as these are considered part of path completions.
//! - `await`, as this is a postfix completion we handle this in the postfix completions.
-use syntax::{SyntaxKind, T};
+use syntax::T;
use crate::{
context::{PathCompletionCtx, PathKind},
@@ -11,18 +11,10 @@ use crate::{
};
pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
- if ctx.token.kind() == SyntaxKind::COMMENT {
- cov_mark::hit!(no_keyword_completion_in_comments);
- return;
- }
if matches!(ctx.completion_location, Some(ImmediateLocation::RecordExpr(_))) {
cov_mark::hit!(no_keyword_completion_in_record_lit);
return;
}
- if ctx.fake_attribute_under_caret.is_some() {
- cov_mark::hit!(no_keyword_completion_in_attr_of_expr);
- return;
- }
if ctx.is_non_trivial_path() {
cov_mark::hit!(no_keyword_completion_in_non_trivial_path);
return;
diff --git a/crates/ide-completion/src/completions/lifetime.rs b/crates/ide-completion/src/completions/lifetime.rs
index 7c1e77c66e..12fcc8920a 100644
--- a/crates/ide-completion/src/completions/lifetime.rs
+++ b/crates/ide-completion/src/completions/lifetime.rs
@@ -17,7 +17,7 @@ use crate::{
/// Completes lifetimes.
pub(crate) fn complete_lifetime(acc: &mut Completions, ctx: &CompletionContext) {
- let (lp, lifetime) = match &ctx.lifetime_ctx {
+ let (lp, lifetime) = match ctx.lifetime_ctx() {
Some(LifetimeContext { kind: LifetimeKind::Lifetime, lifetime }) => (None, lifetime),
Some(LifetimeContext {
kind: LifetimeKind::LifetimeParam { is_decl: false, param },
@@ -49,7 +49,7 @@ pub(crate) fn complete_lifetime(acc: &mut Completions, ctx: &CompletionContext)
/// Completes labels.
pub(crate) fn complete_label(acc: &mut Completions, ctx: &CompletionContext) {
- if !matches!(ctx.lifetime_ctx, Some(LifetimeContext { kind: LifetimeKind::LabelRef, .. })) {
+ if !matches!(ctx.lifetime_ctx(), Some(LifetimeContext { kind: LifetimeKind::LabelRef, .. })) {
return;
}
ctx.process_all_names_raw(&mut |name, res| {
diff --git a/crates/ide-completion/src/completions/mod_.rs b/crates/ide-completion/src/completions/mod_.rs
index 21b108ab1d..3ba663067a 100644
--- a/crates/ide-completion/src/completions/mod_.rs
+++ b/crates/ide-completion/src/completions/mod_.rs
@@ -16,7 +16,7 @@ use crate::{
/// Complete mod declaration, i.e. `mod ;`
pub(crate) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
- let mod_under_caret = match &ctx.name_ctx {
+ let mod_under_caret = match ctx.name_ctx() {
Some(NameContext { kind: NameKind::Module(mod_under_caret), .. })
if mod_under_caret.item_list().is_none() =>
{
diff --git a/crates/ide-completion/src/completions/postfix.rs b/crates/ide-completion/src/completions/postfix.rs
index ef765a345a..be0f674889 100644
--- a/crates/ide-completion/src/completions/postfix.rs
+++ b/crates/ide-completion/src/completions/postfix.rs
@@ -23,7 +23,7 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
return;
}
- let (dot_receiver, receiver_is_ambiguous_float_literal) = match &ctx.nameref_ctx {
+ let (dot_receiver, receiver_is_ambiguous_float_literal) = match ctx.nameref_ctx() {
Some(NameRefContext {
dot_access: Some(DotAccess::Method { receiver: Some(it), .. }),
..
diff --git a/crates/ide-completion/src/completions/use_.rs b/crates/ide-completion/src/completions/use_.rs
index eb9449e761..f1beeb454c 100644
--- a/crates/ide-completion/src/completions/use_.rs
+++ b/crates/ide-completion/src/completions/use_.rs
@@ -11,7 +11,7 @@ use crate::{
};
pub(crate) fn complete_use_tree(acc: &mut Completions, ctx: &CompletionContext) {
- let (&is_absolute_path, qualifier, name_ref) = match &ctx.nameref_ctx {
+ let (&is_absolute_path, qualifier, name_ref) = match ctx.nameref_ctx() {
Some(NameRefContext {
path_ctx:
Some(PathCompletionCtx { kind: PathKind::Use, is_absolute_path, qualifier, .. }),
diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs
index 94d920257f..1381e87031 100644
--- a/crates/ide-completion/src/context.rs
+++ b/crates/ide-completion/src/context.rs
@@ -15,7 +15,7 @@ use ide_db::{
use syntax::{
algo::{find_node_at_offset, non_trivia_sibling},
ast::{self, AttrKind, HasArgList, HasName, NameOrNameRef},
- match_ast, AstNode, NodeOrToken,
+ match_ast, AstNode, AstToken, NodeOrToken,
SyntaxKind::{self, *},
SyntaxNode, SyntaxToken, TextRange, TextSize, T,
};
@@ -170,6 +170,21 @@ pub(super) struct NameRefContext {
}
#[derive(Debug)]
+pub(super) enum IdentContext {
+ Name(NameContext),
+ NameRef(NameRefContext),
+ Lifetime(LifetimeContext),
+ /// Original token, fake token
+ String {
+ original: ast::String,
+ expanded: Option<ast::String>,
+ },
+ UnexpandedAttrTT {
+ fake_attribute_under_caret: Option<ast::Attr>,
+ },
+}
+
+#[derive(Debug)]
pub(super) enum DotAccess {
Field {
receiver: Option<ast::Expr>,
@@ -223,12 +238,9 @@ pub(crate) struct CompletionContext<'a> {
pub(super) completion_location: Option<ImmediateLocation>,
pub(super) prev_sibling: Option<ImmediatePrevSibling>,
- pub(super) fake_attribute_under_caret: Option<ast::Attr>,
pub(super) previous_token: Option<SyntaxToken>,
- pub(super) name_ctx: Option<NameContext>,
- pub(super) lifetime_ctx: Option<LifetimeContext>,
- pub(super) nameref_ctx: Option<NameRefContext>,
+ pub(super) ident_ctx: IdentContext,
pub(super) pattern_ctx: Option<PatternContext>,
@@ -262,8 +274,29 @@ impl<'a> CompletionContext<'a> {
FamousDefs(&self.sema, self.krate)
}
+ pub(super) fn nameref_ctx(&self) -> Option<&NameRefContext> {
+ match &self.ident_ctx {
+ IdentContext::NameRef(it) => Some(it),
+ _ => None,
+ }
+ }
+
+ pub(super) fn name_ctx(&self) -> Option<&NameContext> {
+ match &self.ident_ctx {
+ IdentContext::Name(it) => Some(it),
+ _ => None,
+ }
+ }
+
+ pub(super) fn lifetime_ctx(&self) -> Option<&LifetimeContext> {
+ match &self.ident_ctx {
+ IdentContext::Lifetime(it) => Some(it),
+ _ => None,
+ }
+ }
+
pub(crate) fn dot_receiver(&self) -> Option<&ast::Expr> {
- match &self.nameref_ctx {
+ match self.nameref_ctx() {
Some(NameRefContext {
dot_access:
Some(DotAccess::Method { receiver, .. } | DotAccess::Field { receiver, .. }),
@@ -282,7 +315,7 @@ impl<'a> CompletionContext<'a> {
}
pub(crate) fn expects_variant(&self) -> bool {
- matches!(self.name_ctx, Some(NameContext { kind: NameKind::Variant, .. }))
+ matches!(self.name_ctx(), Some(NameContext { kind: NameKind::Variant, .. }))
}
pub(crate) fn expects_non_trait_assoc_item(&self) -> bool {
@@ -307,7 +340,7 @@ impl<'a> CompletionContext<'a> {
pub(crate) fn expect_field(&self) -> bool {
matches!(self.completion_location, Some(ImmediateLocation::TupleField))
- || matches!(self.name_ctx, Some(NameContext { kind: NameKind::RecordField, .. }))
+ || matches!(self.name_ctx(), Some(NameContext { kind: NameKind::RecordField, .. }))
}
/// Whether the cursor is right after a trait or impl header.
@@ -345,13 +378,13 @@ impl<'a> CompletionContext<'a> {
Some(ImmediateLocation::RecordPat(_) | ImmediateLocation::RecordExpr(_))
)
|| matches!(
- self.name_ctx,
+ self.name_ctx(),
Some(NameContext { kind: NameKind::Module(_) | NameKind::Rename, .. })
)
}
pub(crate) fn path_context(&self) -> Option<&PathCompletionCtx> {
- self.nameref_ctx.as_ref().and_then(|ctx| ctx.path_ctx.as_ref())
+ self.nameref_ctx().and_then(|ctx| ctx.path_ctx.as_ref())
}
pub(crate) fn expects_expression(&self) -> bool {
@@ -501,7 +534,9 @@ impl<'a> CompletionContext<'a> {
file_with_fake_ident.syntax().token_at_offset(offset).right_biased()?;
let original_token = original_file.syntax().token_at_offset(offset).left_biased()?;
+ dbg!(&original_token);
let token = sema.descend_into_macros_single(original_token.clone());
+ dbg!(&token);
let scope = sema.scope_at_offset(&token.parent()?, offset)?;
let krate = scope.krate();
let module = scope.module();
@@ -530,11 +565,9 @@ impl<'a> CompletionContext<'a> {
incomplete_let: false,
completion_location: None,
prev_sibling: None,
- fake_attribute_under_caret: None,
previous_token: None,
- name_ctx: None,
- lifetime_ctx: None,
- nameref_ctx: None,
+ // dummy value, will be overwritten
+ ident_ctx: IdentContext::UnexpandedAttrTT { fake_attribute_under_caret: None },
pattern_ctx: None,
existing_derives: Default::default(),
locals,
@@ -544,7 +577,7 @@ impl<'a> CompletionContext<'a> {
file_with_fake_ident.syntax().clone(),
offset,
fake_ident_token,
- );
+ )?;
Some(ctx)
}
@@ -557,7 +590,7 @@ impl<'a> CompletionContext<'a> {
mut speculative_file: SyntaxNode,
mut offset: TextSize,
mut fake_ident_token: SyntaxToken,
- ) {
+ ) -> Option<()> {
let _p = profile::span("CompletionContext::expand_and_fill");
let mut derive_ctx = None;
@@ -687,7 +720,7 @@ impl<'a> CompletionContext<'a> {
break 'expansion;
}
- self.fill(&original_file, speculative_file, offset, derive_ctx);
+ self.fill(&original_file, speculative_file, offset, derive_ctx)
}
/// Calculate the expected type and name of the cursor position.
@@ -835,7 +868,7 @@ impl<'a> CompletionContext<'a> {
file_with_fake_ident: SyntaxNode,
offset: TextSize,
derive_ctx: Option<(SyntaxNode, SyntaxNode, TextSize, ast::Attr)>,
- ) {
+ ) -> Option<()> {
let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap();
let syntax_element = NodeOrToken::Token(fake_ident_token);
if is_in_token_of_for_loop(syntax_element.clone()) {
@@ -844,11 +877,10 @@ impl<'a> CompletionContext<'a> {
// don't bother populating the context
// FIXME: the completion calculations should end up good enough
// such that this special case becomes unnecessary
- return;
+ return None;
}
self.previous_token = previous_token(syntax_element.clone());
- self.fake_attribute_under_caret = syntax_element.ancestors().find_map(ast::Attr::cast);
self.incomplete_let =
syntax_element.ancestors().take(6).find_map(ast::LetStmt::cast).map_or(false, |it| {
@@ -870,21 +902,49 @@ impl<'a> CompletionContext<'a> {
if let Some(ast::NameLike::NameRef(name_ref)) =
find_node_at_offset(&file_with_fake_ident, offset)
{
- if let Some(parent) = name_ref.syntax().parent() {
- let (mut nameref_ctx, _) =
- Self::classify_name_ref(&self.sema, &original_file, name_ref, parent);
- if let Some(path_ctx) = &mut nameref_ctx.path_ctx {
- path_ctx.kind = PathKind::Derive;
- }
- self.nameref_ctx = Some(nameref_ctx);
+ let parent = name_ref.syntax().parent()?;
+ let (mut nameref_ctx, _) =
+ Self::classify_name_ref(&self.sema, &original_file, name_ref, parent);
+ if let Some(path_ctx) = &mut nameref_ctx.path_ctx {
+ path_ctx.kind = PathKind::Derive;
}
+ self.ident_ctx = IdentContext::NameRef(nameref_ctx);
+ return Some(());
}
- return;
+ return None;
}
let name_like = match find_node_at_offset(&file_with_fake_ident, offset) {
Some(it) => it,
- None => return,
+ None => {
+ if let Some(original) = ast::String::cast(self.original_token.clone()) {
+ self.ident_ctx = IdentContext::String {
+ original,
+ expanded: ast::String::cast(self.token.clone()),
+ };
+ } else {
+ // Fix up trailing whitespace problem
+ // #[attr(foo = $0
+ let token = if self.token.kind() == SyntaxKind::WHITESPACE {
+ self.previous_token.as_ref()?
+ } else {
+ &self.token
+ };
+ let p = token.parent()?;
+ if p.kind() == SyntaxKind::TOKEN_TREE
+ && p.ancestors().any(|it| it.kind() == SyntaxKind::META)
+ {
+ self.ident_ctx = IdentContext::UnexpandedAttrTT {
+ fake_attribute_under_caret: syntax_element
+ .ancestors()
+ .find_map(ast::Attr::cast),
+ };
+ } else {
+ return None;
+ }
+ }
+ return Some(());
+ }
};
self.completion_location =
determine_location(&self.sema, original_file, offset, &name_like);
@@ -902,25 +962,26 @@ impl<'a> CompletionContext<'a> {
match name_like {
ast::NameLike::Lifetime(lifetime) => {
- self.lifetime_ctx = Self::classify_lifetime(&self.sema, original_file, lifetime);
+ self.ident_ctx = IdentContext::Lifetime(Self::classify_lifetime(
+ &self.sema,
+ original_file,
+ lifetime,
+ )?);
}
ast::NameLike::NameRef(name_ref) => {
- if let Some(parent) = name_ref.syntax().parent() {
- let (nameref_ctx, pat_ctx) =
- Self::classify_name_ref(&self.sema, &original_file, name_ref, parent);
- self.nameref_ctx = Some(nameref_ctx);
- self.pattern_ctx = pat_ctx;
- }
+ let parent = name_ref.syntax().parent()?;
+ let (nameref_ctx, pat_ctx) =
+ Self::classify_name_ref(&self.sema, &original_file, name_ref, parent);
+ self.ident_ctx = IdentContext::NameRef(nameref_ctx);
+ self.pattern_ctx = pat_ctx;
}
ast::NameLike::Name(name) => {
- if let Some((name_ctx, pat_ctx)) =
- Self::classify_name(&self.sema, original_file, name)
- {
- self.pattern_ctx = pat_ctx;
- self.name_ctx = Some(name_ctx);
- }
+ let (name_ctx, pat_ctx) = Self::classify_name(&self.sema, original_file, name)?;
+ self.pattern_ctx = pat_ctx;
+ self.ident_ctx = IdentContext::Name(name_ctx);
}
}
+ Some(())
}
fn classify_lifetime(
diff --git a/crates/ide-completion/src/render/function.rs b/crates/ide-completion/src/render/function.rs
index 93c64eec6f..1ede314e87 100644
--- a/crates/ide-completion/src/render/function.rs
+++ b/crates/ide-completion/src/render/function.rs
@@ -207,7 +207,7 @@ fn should_add_parens(ctx: &CompletionContext) -> bool {
};
if matches!(
- ctx.nameref_ctx,
+ ctx.nameref_ctx(),
Some(NameRefContext { dot_access: Some(DotAccess::Method { has_parens: true, .. }), .. })
) {
return false;
diff --git a/crates/ide-completion/src/tests.rs b/crates/ide-completion/src/tests.rs
index 72b7a5584a..0430aeea59 100644
--- a/crates/ide-completion/src/tests.rs
+++ b/crates/ide-completion/src/tests.rs
@@ -257,7 +257,6 @@ fn foo() {
#[test]
fn no_completions_in_comments() {
- cov_mark::check!(no_keyword_completion_in_comments);
assert_eq!(
completion_list(
r#"
diff --git a/crates/ide-completion/src/tests/attribute.rs b/crates/ide-completion/src/tests/attribute.rs
index 664bd05e2d..52c69f84b6 100644
--- a/crates/ide-completion/src/tests/attribute.rs
+++ b/crates/ide-completion/src/tests/attribute.rs
@@ -580,25 +580,6 @@ fn attr_on_fn() {
}
#[test]
-fn attr_on_expr() {
- cov_mark::check!(no_keyword_completion_in_attr_of_expr);
- check(
- r#"fn main() { #[$0] foo() }"#,
- expect![[r#"
- at allow(…)
- at cfg(…)
- at cfg_attr(…)
- at deny(…)
- at forbid(…)
- at warn(…)
- kw crate::
- kw self::
- kw super::
- "#]],
- );
-}
-
-#[test]
fn attr_in_source_file_end() {
check(
r#"#[$0]"#,