Skip to content

Commit c22deda

Browse files
author
Gilad Chase
committed
fix(parser): better handling for multi-reference objects (&&T, &&&T, etc)
- Formatter no longer inserts breakline before & in an unary & (or &&), meaning &&T isn't coerced into & &T. This also had the effect of removing excess whitespace in some other cases (see tests). - Implementation: parser consumes && and pushes two & into the token queue, which is then parsed similarly to &(&T). - consolidated tests and added triple &&&T test
1 parent 87152ac commit c22deda

File tree

6 files changed

+381
-216
lines changed

6 files changed

+381
-216
lines changed

crates/cairo-lang-formatter/src/node_properties.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -758,7 +758,9 @@ impl<'a> SyntaxNodeFormat for SyntaxNode<'a> {
758758
true,
759759
))
760760
}
761-
SyntaxKind::TerminalAnd => {
761+
SyntaxKind::TerminalAnd
762+
if matches!(self.parent_kind(db), Some(SyntaxKind::ExprBinary)) =>
763+
{
762764
BreakLinePointsPositions::Leading(BreakLinePointProperties::new(
763765
13,
764766
BreakLinePointIndentation::Indented,

crates/cairo-lang-parser/src/operators.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ pub fn get_unary_operator_precedence(kind: SyntaxKind) -> Option<usize> {
44
match kind {
55
SyntaxKind::TerminalAt
66
| SyntaxKind::TerminalAnd
7+
| SyntaxKind::TerminalAndAnd
78
| SyntaxKind::TerminalNot
89
| SyntaxKind::TerminalBitNot
910
| SyntaxKind::TerminalMul

crates/cairo-lang-parser/src/parser.rs

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1503,7 +1503,10 @@ impl<'a, 'mt> Parser<'a, 'mt> {
15031503
fn expect_unary_operator(&mut self) -> UnaryOperatorGreen<'a> {
15041504
match self.peek().kind {
15051505
SyntaxKind::TerminalAt => self.take::<TerminalAt<'_>>().into(),
1506-
SyntaxKind::TerminalAnd => self.take::<TerminalAnd<'_>>().into(),
1506+
SyntaxKind::TerminalAnd | SyntaxKind::TerminalAndAnd => {
1507+
self.unglue_andand_for_unary();
1508+
self.take::<TerminalAnd<'_>>().into()
1509+
}
15071510
SyntaxKind::TerminalNot => self.take::<TerminalNot<'_>>().into(),
15081511
SyntaxKind::TerminalBitNot => self.take::<TerminalBitNot<'_>>().into(),
15091512
SyntaxKind::TerminalMinus => self.take::<TerminalMinus<'_>>().into(),
@@ -1707,7 +1710,8 @@ impl<'a, 'mt> Parser<'a, 'mt> {
17071710
let expr = self.parse_type_expr();
17081711
Ok(ExprUnary::new_green(self.db, op, expr).into())
17091712
}
1710-
SyntaxKind::TerminalAnd => {
1713+
SyntaxKind::TerminalAnd | SyntaxKind::TerminalAndAnd => {
1714+
self.unglue_andand_for_unary();
17111715
let op = self.take::<TerminalAnd<'_>>().into();
17121716
let expr = self.parse_type_expr();
17131717
Ok(ExprUnary::new_green(self.db, op, expr).into())
@@ -3547,6 +3551,26 @@ impl<'a, 'mt> Parser<'a, 'mt> {
35473551
self.next_next_terminal().kind
35483552
}
35493553

3554+
/// Consumes a '&&' token and pushes two '&' tokens into the token stream.
3555+
/// If the current token is not '&&', does nothing.
3556+
fn unglue_andand_for_unary(&mut self) {
3557+
if self.peek().kind != SyntaxKind::TerminalAndAnd {
3558+
return;
3559+
}
3560+
3561+
// Consume the && token and create base '&' token from it.
3562+
let and_terminal = LexerTerminal {
3563+
text: SmolStrId::from(self.db, "&"),
3564+
kind: SyntaxKind::TerminalAnd,
3565+
..self.advance() // Consume the && token and grab its trivia.
3566+
};
3567+
3568+
// The second '&' (hence pushing it first) was glued to the first, so no leading trivia.
3569+
self.terminals.push_front(LexerTerminal { leading_trivia: vec![], ..and_terminal });
3570+
// The first '&' was glued to the second, hence no trailing trivia.
3571+
self.terminals.push_front(LexerTerminal { trailing_trivia: vec![], ..and_terminal });
3572+
}
3573+
35503574
/// Move forward one terminal.
35513575
fn take_raw(&mut self) -> LexerTerminal<'a> {
35523576
self.offset = self.offset.add_width(self.current_width);

crates/cairo-lang-parser/src/parser_test_data/partial_trees/reference

Lines changed: 38 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
test_partial_parser_tree(expect_diagnostics: false)
55

66
//! > cairo_code
7-
fn f(x: &u32) {}
7+
fn f(x: &u32, y: &&u32, z: &&&u32) {}
88

99
//! > top_level_kind
1010
ExprUnary
@@ -21,6 +21,26 @@ ExprUnary
2121
└── segments (kind: ExprPathInner)
2222
└── item #0 (kind: PathSegmentSimple)
2323
└── ident (kind: TokenIdentifier): 'u32'
24+
└── Top level kind: ExprUnary
25+
├── op (kind: TokenAnd): '&'
26+
└── expr (kind: ExprUnary)
27+
├── op (kind: TokenAnd): '&'
28+
└── expr (kind: ExprPath)
29+
├── dollar (kind: OptionTerminalDollarEmpty) []
30+
└── segments (kind: ExprPathInner)
31+
└── item #0 (kind: PathSegmentSimple)
32+
└── ident (kind: TokenIdentifier): 'u32'
33+
└── Top level kind: ExprUnary
34+
├── op (kind: TokenAnd): '&'
35+
└── expr (kind: ExprUnary)
36+
├── op (kind: TokenAnd): '&'
37+
└── expr (kind: ExprUnary)
38+
├── op (kind: TokenAnd): '&'
39+
└── expr (kind: ExprPath)
40+
├── dollar (kind: OptionTerminalDollarEmpty) []
41+
└── segments (kind: ExprPathInner)
42+
└── item #0 (kind: PathSegmentSimple)
43+
└── ident (kind: TokenIdentifier): 'u32'
2444

2545
//! > ==========================================================================
2646

@@ -30,7 +50,7 @@ ExprUnary
3050
test_partial_parser_tree(expect_diagnostics: false)
3151

3252
//! > cairo_code
33-
fn f(x: &@u32, y: @ &u32) {}
53+
fn f(x: &@u32, y: @&u32) {}
3454

3555
//! > top_level_kind
3656
ParamList
@@ -79,7 +99,7 @@ ParamList
7999
test_partial_parser_tree(expect_diagnostics: false)
80100

81101
//! > cairo_code
82-
fn f() -> Option< &u64> {}
102+
fn f() -> Option<&u64> {}
83103

84104
//! > top_level_kind
85105
ExprUnary
@@ -99,13 +119,17 @@ ExprUnary
99119

100120
//! > ==========================================================================
101121

102-
//! > Test double reference &&x
122+
//! > Test double reference with literal &&5_u32
103123

104124
//! > test_runner_name
105125
test_partial_parser_tree(expect_diagnostics: false)
106126

107127
//! > cairo_code
108-
fn f(x: & &u32) {}
128+
fn f() {
129+
let x = &1_u32;
130+
let y = &&2_u32;
131+
let z = &&&3_u32;
132+
}
109133

110134
//! > top_level_kind
111135
ExprUnary
@@ -115,36 +139,18 @@ ExprUnary
115139
//! > expected_diagnostics
116140

117141
//! > expected_tree
142+
└── Top level kind: ExprUnary
143+
├── op (kind: TokenAnd): '&'
144+
└── expr (kind: TokenLiteralNumber): '1_u32'
118145
└── Top level kind: ExprUnary
119146
├── op (kind: TokenAnd): '&'
120147
└── expr (kind: ExprUnary)
121148
├── op (kind: TokenAnd): '&'
122-
└── expr (kind: ExprPath)
123-
├── dollar (kind: OptionTerminalDollarEmpty) []
124-
└── segments (kind: ExprPathInner)
125-
└── item #0 (kind: PathSegmentSimple)
126-
└── ident (kind: TokenIdentifier): 'u32'
127-
128-
//! > ==========================================================================
129-
130-
//! > Test double &T as expr
131-
132-
//! > test_runner_name
133-
test_partial_parser_tree(expect_diagnostics: false)
134-
135-
//! > cairo_code
136-
fn f() {
137-
let x = &1_u32;
138-
}
139-
140-
//! > top_level_kind
141-
ExprUnary
142-
143-
//! > ignored_kinds
144-
145-
//! > expected_diagnostics
146-
147-
//! > expected_tree
149+
└── expr (kind: TokenLiteralNumber): '2_u32'
148150
└── Top level kind: ExprUnary
149151
├── op (kind: TokenAnd): '&'
150-
└── expr (kind: TokenLiteralNumber): '1_u32'
152+
└── expr (kind: ExprUnary)
153+
├── op (kind: TokenAnd): '&'
154+
└── expr (kind: ExprUnary)
155+
├── op (kind: TokenAnd): '&'
156+
└── expr (kind: TokenLiteralNumber): '3_u32'

0 commit comments

Comments
 (0)