Unnamed repository; edit this file 'description' to name the repository.
Merge pull request #18952 from lh123/add-raw-keyword-complete
feat: complete raw, const keyword
Lukas Wirth 2025-01-16
parent 1f717b0 · parent a282733 · commit 903bc81
-rw-r--r--crates/hir-def/src/body/lower.rs3
-rw-r--r--crates/ide-completion/src/completions/expr.rs26
-rw-r--r--crates/ide-completion/src/context.rs1
-rw-r--r--crates/ide-completion/src/context/analysis.rs4
-rw-r--r--crates/ide-completion/src/tests/expression.rs109
-rw-r--r--crates/parser/src/grammar/expressions.rs13
-rw-r--r--crates/parser/test_data/parser/inline/ok/ref_expr.rast19
-rw-r--r--crates/parser/test_data/parser/inline/ok/ref_expr.rs1
8 files changed, 168 insertions, 8 deletions
diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs
index d7e28da644..1327bb3ab5 100644
--- a/crates/hir-def/src/body/lower.rs
+++ b/crates/hir-def/src/body/lower.rs
@@ -581,10 +581,7 @@ impl ExprCollector<'_> {
let mutability = if raw_tok {
if e.mut_token().is_some() {
Mutability::Mut
- } else if e.const_token().is_some() {
- Mutability::Shared
} else {
- never!("parser only remaps to raw_token() if matching mutability token follows");
Mutability::Shared
}
} else {
diff --git a/crates/ide-completion/src/completions/expr.rs b/crates/ide-completion/src/completions/expr.rs
index c2e5eefe10..db18b531d7 100644
--- a/crates/ide-completion/src/completions/expr.rs
+++ b/crates/ide-completion/src/completions/expr.rs
@@ -62,6 +62,7 @@ pub(crate) fn complete_expr_path(
in_condition,
incomplete_let,
ref ref_expr_parent,
+ after_amp,
ref is_func_update,
ref innermost_ret_ty,
ref impl_,
@@ -69,8 +70,23 @@ pub(crate) fn complete_expr_path(
..
} = expr_ctx;
- let wants_mut_token =
- ref_expr_parent.as_ref().map(|it| it.mut_token().is_none()).unwrap_or(false);
+ let (has_raw_token, has_const_token, has_mut_token) = ref_expr_parent
+ .as_ref()
+ .map(|it| (it.raw_token().is_some(), it.const_token().is_some(), it.mut_token().is_some()))
+ .unwrap_or((false, false, false));
+
+ let wants_raw_token = ref_expr_parent.is_some() && !has_raw_token && after_amp;
+ let wants_const_token =
+ ref_expr_parent.is_some() && has_raw_token && !has_const_token && !has_mut_token;
+ let wants_mut_token = if ref_expr_parent.is_some() {
+ if has_raw_token {
+ !has_const_token && !has_mut_token
+ } else {
+ !has_mut_token
+ }
+ } else {
+ false
+ };
let scope_def_applicable = |def| match def {
ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_)) | ScopeDef::Label(_) => false,
@@ -354,6 +370,12 @@ pub(crate) fn complete_expr_path(
add_keyword("else if", "else if $1 {\n $0\n}");
}
+ if wants_raw_token {
+ add_keyword("raw", "raw ");
+ }
+ if wants_const_token {
+ add_keyword("const", "const ");
+ }
if wants_mut_token {
add_keyword("mut", "mut ");
}
diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs
index 461b1dc036..3a2a4a23a1 100644
--- a/crates/ide-completion/src/context.rs
+++ b/crates/ide-completion/src/context.rs
@@ -146,6 +146,7 @@ pub(crate) struct PathExprCtx {
pub(crate) in_condition: bool,
pub(crate) incomplete_let: bool,
pub(crate) ref_expr_parent: Option<ast::RefExpr>,
+ 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>,
diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs
index 1a5609baf9..3c4d489c0f 100644
--- a/crates/ide-completion/src/context/analysis.rs
+++ b/crates/ide-completion/src/context/analysis.rs
@@ -1151,6 +1151,9 @@ fn classify_name_ref(
let after_if_expr = after_if_expr(it.clone());
let ref_expr_parent =
path.as_single_name_ref().and_then(|_| it.parent()).and_then(ast::RefExpr::cast);
+ let after_amp = non_trivia_sibling(it.clone().into(), Direction::Prev)
+ .map(|it| it.kind() == SyntaxKind::AMP)
+ .unwrap_or(false);
let (innermost_ret_ty, self_param) = {
let find_ret_ty = |it: SyntaxNode| {
if let Some(item) = ast::Item::cast(it.clone()) {
@@ -1220,6 +1223,7 @@ fn classify_name_ref(
after_if_expr,
in_condition,
ref_expr_parent,
+ after_amp,
is_func_update,
innermost_ret_ty,
self_param,
diff --git a/crates/ide-completion/src/tests/expression.rs b/crates/ide-completion/src/tests/expression.rs
index d0dc6206f4..e117dbf4bd 100644
--- a/crates/ide-completion/src/tests/expression.rs
+++ b/crates/ide-completion/src/tests/expression.rs
@@ -66,6 +66,7 @@ fn baz() {
kw loop
kw match
kw mut
+ kw raw
kw return
kw self::
kw true
@@ -437,6 +438,114 @@ fn completes_in_let_initializer() {
}
#[test]
+fn completes_after_ref_expr() {
+ check(
+ r#"fn main() { let _ = &$0 }"#,
+ expect![[r#"
+ fn main() fn()
+ bt u32 u32
+ kw crate::
+ kw false
+ kw for
+ kw if
+ kw if let
+ kw loop
+ kw match
+ kw mut
+ kw raw
+ kw return
+ kw self::
+ kw true
+ kw unsafe
+ kw while
+ kw while let
+ "#]],
+ );
+ check(
+ r#"fn main() { let _ = &raw $0 }"#,
+ expect![[r#"
+ fn main() fn()
+ bt u32 u32
+ kw const
+ kw crate::
+ kw false
+ kw for
+ kw if
+ kw if let
+ kw loop
+ kw match
+ kw mut
+ kw return
+ kw self::
+ kw true
+ kw unsafe
+ kw while
+ kw while let
+ "#]],
+ );
+ check(
+ r#"fn main() { let _ = &raw const $0 }"#,
+ expect![[r#"
+ fn main() fn()
+ bt u32 u32
+ kw crate::
+ kw false
+ kw for
+ kw if
+ kw if let
+ kw loop
+ kw match
+ kw return
+ kw self::
+ kw true
+ kw unsafe
+ kw while
+ kw while let
+ "#]],
+ );
+ check(
+ r#"fn main() { let _ = &raw mut $0 }"#,
+ expect![[r#"
+ fn main() fn()
+ bt u32 u32
+ kw crate::
+ kw false
+ kw for
+ kw if
+ kw if let
+ kw loop
+ kw match
+ kw return
+ kw self::
+ kw true
+ kw unsafe
+ kw while
+ kw while let
+ "#]],
+ );
+ check(
+ r#"fn main() { let _ = &mut $0 }"#,
+ expect![[r#"
+ fn main() fn()
+ bt u32 u32
+ kw crate::
+ kw false
+ kw for
+ kw if
+ kw if let
+ kw loop
+ kw match
+ kw return
+ kw self::
+ kw true
+ kw unsafe
+ kw while
+ kw while let
+ "#]],
+ )
+}
+
+#[test]
fn struct_initializer_field_expr() {
check(
r#"
diff --git a/crates/parser/src/grammar/expressions.rs b/crates/parser/src/grammar/expressions.rs
index 3b3f11be13..03439b784d 100644
--- a/crates/parser/src/grammar/expressions.rs
+++ b/crates/parser/src/grammar/expressions.rs
@@ -339,13 +339,20 @@ fn lhs(p: &mut Parser<'_>, r: Restrictions) -> Option<(CompletedMarker, BlockLik
// // raw reference operator
// let _ = &raw mut foo;
// let _ = &raw const foo;
+ // let _ = &raw foo;
// }
T![&] => {
m = p.start();
p.bump(T![&]);
- if p.at_contextual_kw(T![raw]) && [T![mut], T![const]].contains(&p.nth(1)) {
- p.bump_remap(T![raw]);
- p.bump_any();
+ if p.at_contextual_kw(T![raw]) {
+ if [T![mut], T![const]].contains(&p.nth(1)) {
+ p.bump_remap(T![raw]);
+ p.bump_any();
+ } else if p.nth_at(1, SyntaxKind::IDENT) {
+ // we treat raw as keyword in this case
+ // &raw foo;
+ p.bump_remap(T![raw]);
+ }
} else {
p.eat(T![mut]);
}
diff --git a/crates/parser/test_data/parser/inline/ok/ref_expr.rast b/crates/parser/test_data/parser/inline/ok/ref_expr.rast
index 108b0802c3..8dc916e5cc 100644
--- a/crates/parser/test_data/parser/inline/ok/ref_expr.rast
+++ b/crates/parser/test_data/parser/inline/ok/ref_expr.rast
@@ -134,6 +134,25 @@ SOURCE_FILE
NAME_REF
IDENT "foo"
SEMICOLON ";"
+ WHITESPACE "\n "
+ LET_STMT
+ LET_KW "let"
+ WHITESPACE " "
+ WILDCARD_PAT
+ UNDERSCORE "_"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ REF_EXPR
+ AMP "&"
+ RAW_KW "raw"
+ WHITESPACE " "
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "foo"
+ SEMICOLON ";"
WHITESPACE "\n"
R_CURLY "}"
WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/ref_expr.rs b/crates/parser/test_data/parser/inline/ok/ref_expr.rs
index c5262f4469..31a2485b43 100644
--- a/crates/parser/test_data/parser/inline/ok/ref_expr.rs
+++ b/crates/parser/test_data/parser/inline/ok/ref_expr.rs
@@ -7,4 +7,5 @@ fn foo() {
// raw reference operator
let _ = &raw mut foo;
let _ = &raw const foo;
+ let _ = &raw foo;
}