Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--crates/hir-def/src/body/lower.rs4
-rw-r--r--crates/ide-assists/src/handlers/unmerge_match_arm.rs3
-rw-r--r--crates/parser/src/grammar/patterns.rs13
-rw-r--r--crates/parser/src/tests/top_entries.rs32
-rw-r--r--crates/parser/test_data/parser/inline/ok/match_arm.rast15
-rw-r--r--crates/parser/test_data/parser/inline/ok/slice_pat.rast11
-rw-r--r--crates/parser/test_data/parser/inline/ok/tuple_pat.rast15
-rw-r--r--crates/parser/test_data/parser/inline/ok/tuple_pat_fields.rast11
-rw-r--r--crates/syntax/rust.ungram2
-rw-r--r--crates/syntax/src/ast/generated/nodes.rs2
-rw-r--r--crates/syntax/src/ast/node_ext.rs10
11 files changed, 87 insertions, 31 deletions
diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs
index b87e94f9c5..6117664c64 100644
--- a/crates/hir-def/src/body/lower.rs
+++ b/crates/hir-def/src/body/lower.rs
@@ -1598,6 +1598,10 @@ impl ExprCollector<'_> {
for (id, _) in current_is_used.into_iter() {
binding_list.check_is_used(self, id);
}
+ if let &[pat] = &*pats {
+ // Leading pipe without real OR pattern. Leaving an one-item OR pattern may confuse later stages.
+ return pat;
+ }
Pat::Or(pats.into())
}
ast::Pat::ParenPat(p) => return self.collect_pat_opt(p.pat(), binding_list),
diff --git a/crates/ide-assists/src/handlers/unmerge_match_arm.rs b/crates/ide-assists/src/handlers/unmerge_match_arm.rs
index db789cfa33..648bf358b4 100644
--- a/crates/ide-assists/src/handlers/unmerge_match_arm.rs
+++ b/crates/ide-assists/src/handlers/unmerge_match_arm.rs
@@ -34,6 +34,9 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
pub(crate) fn unmerge_match_arm(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
let pipe_token = ctx.find_token_syntax_at_offset(T![|])?;
let or_pat = ast::OrPat::cast(pipe_token.parent()?)?.clone_for_update();
+ if or_pat.leading_pipe().is_some_and(|it| it == pipe_token) {
+ return None;
+ }
let match_arm = ast::MatchArm::cast(or_pat.syntax().parent()?)?;
let match_arm_body = match_arm.expr()?;
diff --git a/crates/parser/src/grammar/patterns.rs b/crates/parser/src/grammar/patterns.rs
index 3db1a10ca5..ed01fca2ac 100644
--- a/crates/parser/src/grammar/patterns.rs
+++ b/crates/parser/src/grammar/patterns.rs
@@ -21,7 +21,8 @@ const RANGE_PAT_END_FIRST: TokenSet =
expressions::LITERAL_FIRST.union(paths::PATH_FIRST).union(TokenSet::new(&[T![-], T![const]]));
pub(crate) fn pattern(p: &mut Parser<'_>) {
- pattern_r(p, PAT_RECOVERY_SET);
+ let m = p.start();
+ pattern_r(p, m, false, PAT_RECOVERY_SET);
}
/// Parses a pattern list separated by pipes `|`.
@@ -36,8 +37,9 @@ pub(crate) fn pattern_single(p: &mut Parser<'_>) {
/// Parses a pattern list separated by pipes `|`
/// using the given `recovery_set`.
pub(super) fn pattern_top_r(p: &mut Parser<'_>, recovery_set: TokenSet) {
- p.eat(T![|]);
- pattern_r(p, recovery_set);
+ let m = p.start();
+ let has_leading_pipe = p.eat(T![|]);
+ pattern_r(p, m, has_leading_pipe, recovery_set);
}
// test or_pattern
@@ -51,11 +53,10 @@ pub(super) fn pattern_top_r(p: &mut Parser<'_>, recovery_set: TokenSet) {
// }
/// Parses a pattern list separated by pipes `|`, with no leading `|`,using the
/// given `recovery_set`.
-fn pattern_r(p: &mut Parser<'_>, recovery_set: TokenSet) {
- let m = p.start();
+fn pattern_r(p: &mut Parser<'_>, m: Marker, has_leading_pipe: bool, recovery_set: TokenSet) {
pattern_single_r(p, recovery_set);
- if !p.at(T![|]) {
+ if !p.at(T![|]) && !has_leading_pipe {
m.abandon(p);
return;
}
diff --git a/crates/parser/src/tests/top_entries.rs b/crates/parser/src/tests/top_entries.rs
index c56bf0b644..7076e03ba4 100644
--- a/crates/parser/src/tests/top_entries.rs
+++ b/crates/parser/src/tests/top_entries.rs
@@ -195,6 +195,38 @@ fn macro_pattern() {
error 0: expected pattern
"#]],
);
+
+ check(
+ TopEntryPoint::Pattern,
+ "| 42 | 43",
+ expect![[r#"
+ OR_PAT
+ PIPE "|"
+ WHITESPACE " "
+ LITERAL_PAT
+ LITERAL
+ INT_NUMBER "42"
+ WHITESPACE " "
+ PIPE "|"
+ WHITESPACE " "
+ LITERAL_PAT
+ LITERAL
+ INT_NUMBER "43"
+ "#]],
+ );
+
+ check(
+ TopEntryPoint::Pattern,
+ "| 42",
+ expect![[r#"
+ OR_PAT
+ PIPE "|"
+ WHITESPACE " "
+ LITERAL_PAT
+ LITERAL
+ INT_NUMBER "42"
+ "#]],
+ );
}
#[test]
diff --git a/crates/parser/test_data/parser/inline/ok/match_arm.rast b/crates/parser/test_data/parser/inline/ok/match_arm.rast
index 8189cf0a8e..9221028162 100644
--- a/crates/parser/test_data/parser/inline/ok/match_arm.rast
+++ b/crates/parser/test_data/parser/inline/ok/match_arm.rast
@@ -102,9 +102,9 @@ SOURCE_FILE
COMMA ","
WHITESPACE "\n "
MATCH_ARM
- PIPE "|"
- WHITESPACE " "
OR_PAT
+ PIPE "|"
+ WHITESPACE " "
IDENT_PAT
NAME
IDENT "X"
@@ -132,11 +132,12 @@ SOURCE_FILE
COMMA ","
WHITESPACE "\n "
MATCH_ARM
- PIPE "|"
- WHITESPACE " "
- IDENT_PAT
- NAME
- IDENT "X"
+ OR_PAT
+ PIPE "|"
+ WHITESPACE " "
+ IDENT_PAT
+ NAME
+ IDENT "X"
WHITESPACE " "
FAT_ARROW "=>"
WHITESPACE " "
diff --git a/crates/parser/test_data/parser/inline/ok/slice_pat.rast b/crates/parser/test_data/parser/inline/ok/slice_pat.rast
index dff72ba886..06c30bba59 100644
--- a/crates/parser/test_data/parser/inline/ok/slice_pat.rast
+++ b/crates/parser/test_data/parser/inline/ok/slice_pat.rast
@@ -43,11 +43,12 @@ SOURCE_FILE
WHITESPACE " "
SLICE_PAT
L_BRACK "["
- PIPE "|"
- WHITESPACE " "
- IDENT_PAT
- NAME
- IDENT "a"
+ OR_PAT
+ PIPE "|"
+ WHITESPACE " "
+ IDENT_PAT
+ NAME
+ IDENT "a"
COMMA ","
WHITESPACE " "
REST_PAT
diff --git a/crates/parser/test_data/parser/inline/ok/tuple_pat.rast b/crates/parser/test_data/parser/inline/ok/tuple_pat.rast
index 1a01e0f693..c7cd11f774 100644
--- a/crates/parser/test_data/parser/inline/ok/tuple_pat.rast
+++ b/crates/parser/test_data/parser/inline/ok/tuple_pat.rast
@@ -91,9 +91,9 @@ SOURCE_FILE
WHITESPACE " "
TUPLE_PAT
L_PAREN "("
- PIPE "|"
- WHITESPACE " "
OR_PAT
+ PIPE "|"
+ WHITESPACE " "
IDENT_PAT
NAME
IDENT "a"
@@ -105,11 +105,12 @@ SOURCE_FILE
IDENT "a"
COMMA ","
WHITESPACE " "
- PIPE "|"
- WHITESPACE " "
- IDENT_PAT
- NAME
- IDENT "b"
+ OR_PAT
+ PIPE "|"
+ WHITESPACE " "
+ IDENT_PAT
+ NAME
+ IDENT "b"
R_PAREN ")"
WHITESPACE " "
EQ "="
diff --git a/crates/parser/test_data/parser/inline/ok/tuple_pat_fields.rast b/crates/parser/test_data/parser/inline/ok/tuple_pat_fields.rast
index 55baf2fdcb..96353f4697 100644
--- a/crates/parser/test_data/parser/inline/ok/tuple_pat_fields.rast
+++ b/crates/parser/test_data/parser/inline/ok/tuple_pat_fields.rast
@@ -110,11 +110,12 @@ SOURCE_FILE
NAME_REF
IDENT "S"
L_PAREN "("
- PIPE "|"
- WHITESPACE " "
- IDENT_PAT
- NAME
- IDENT "a"
+ OR_PAT
+ PIPE "|"
+ WHITESPACE " "
+ IDENT_PAT
+ NAME
+ IDENT "a"
R_PAREN ")"
WHITESPACE " "
EQ "="
diff --git a/crates/syntax/rust.ungram b/crates/syntax/rust.ungram
index 3609da0bb2..02c59646a9 100644
--- a/crates/syntax/rust.ungram
+++ b/crates/syntax/rust.ungram
@@ -736,7 +736,7 @@ PathPat =
Path
OrPat =
- (Pat ('|' Pat)* '|'?)
+ '|'? (Pat ('|' Pat)*)
BoxPat =
'box' Pat
diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs
index b9eb1abb11..23d2b355a9 100644
--- a/crates/syntax/src/ast/generated/nodes.rs
+++ b/crates/syntax/src/ast/generated/nodes.rs
@@ -1283,6 +1283,8 @@ pub struct OrPat {
impl OrPat {
#[inline]
pub fn pats(&self) -> AstChildren<Pat> { support::children(&self.syntax) }
+ #[inline]
+ pub fn pipe_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![|]) }
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs
index 11f5e662e3..6ec73e76f7 100644
--- a/crates/syntax/src/ast/node_ext.rs
+++ b/crates/syntax/src/ast/node_ext.rs
@@ -1140,3 +1140,13 @@ impl From<ast::AssocItem> for ast::AnyHasAttrs {
Self::new(node)
}
}
+
+impl ast::OrPat {
+ pub fn leading_pipe(&self) -> Option<SyntaxToken> {
+ self.syntax
+ .children_with_tokens()
+ .find(|it| !it.kind().is_trivia())
+ .and_then(NodeOrToken::into_token)
+ .filter(|it| it.kind() == T![|])
+ }
+}