Skip to content

Commit abae8bb

Browse files
committed
Disambiguate newline-qualified paths from lt continuations
1 parent 58a29b0 commit abae8bb

11 files changed

Lines changed: 115 additions & 95 deletions

File tree

crates/parser/src/parser/expr.rs

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::convert::{Infallible, identity};
22
use unwrap_infallible::UnwrapInfallible;
33

44
use super::{
5-
Checkpoint, ErrProof, Parser, Recovery, define_scope,
5+
Checkpoint, ErrProof, Parser, Recovery, TextSize, define_scope,
66
expr_atom::{self, is_expr_atom_head},
77
param::{CallArgListScope, GenericArgListScope},
88
path::is_qualified_type,
@@ -61,13 +61,21 @@ fn parse_expr_with_min_bp<S: TokenStream>(
6161
&& has_line_break_before(parser)
6262
&& !is_aug_assign(parser)
6363
{
64+
let range = line_start_op_range(parser);
6465
parser.add_error(ParseError::Msg(
6566
"line-start `-` after an expression is ambiguous; move `-` to the previous line for subtraction or parenthesize explicitly"
6667
.to_string(),
67-
TextRange::empty(parser.end_of_prev_token),
68+
range,
6869
));
6970
break;
7071
}
72+
if min_bp == 0
73+
&& kind == SyntaxKind::Lt
74+
&& has_line_break_before(parser)
75+
&& is_line_start_qualified_type(parser)
76+
{
77+
break;
78+
}
7179

7280
// Parse postfix operators.
7381
match postfix_binding_power(parser) {
@@ -201,10 +209,6 @@ fn infix_binding_power<S: TokenStream>(parser: &mut Parser<S>) -> Option<(u8, u8
201209
Amp2 => (60, 61),
202210
NotEq | Eq2 => (70, 71),
203211
Lt => {
204-
if has_line_break_before(parser) && is_qualified_type(parser) {
205-
parser.set_newline_as_trivia(is_trivia);
206-
return None;
207-
}
208212
if is_lshift(parser) {
209213
(110, 111)
210214
} else {
@@ -495,6 +499,26 @@ fn has_line_break_before<S: TokenStream>(parser: &mut Parser<S>) -> bool {
495499
has_line_break
496500
}
497501

502+
fn is_line_start_qualified_type<S: TokenStream>(parser: &mut Parser<S>) -> bool {
503+
let nt = parser.set_newline_as_trivia(true);
504+
let is_qualified = parser.current_kind() == Some(SyntaxKind::Lt) && is_qualified_type(parser);
505+
parser.set_newline_as_trivia(nt);
506+
is_qualified
507+
}
508+
509+
fn line_start_op_range<S: TokenStream>(parser: &mut Parser<S>) -> TextRange {
510+
parser.dry_run(|parser| {
511+
let nt = parser.set_newline_as_trivia(true);
512+
parser.bump_trivias();
513+
let start = parser.current_pos;
514+
let end = parser
515+
.current_token()
516+
.map_or(start, |current_token| start + current_token.text_size());
517+
parser.set_newline_as_trivia(nt);
518+
TextRange::new(start, end)
519+
})
520+
}
521+
498522
fn bump_bin_op<S: TokenStream>(parser: &mut Parser<S>) {
499523
match parser.current_kind() {
500524
Some(SyntaxKind::Lt) => {

crates/parser/src/parser/path.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,9 @@ impl super::Parse for QualifiedTypeScope {
9494
pub(super) fn is_qualified_type<S: TokenStream>(parser: &mut Parser<S>) -> bool {
9595
parser
9696
.dry_run(|parser| {
97-
parser.bump_expected(SyntaxKind::Lt);
97+
if !parser.bump_if(SyntaxKind::Lt) {
98+
return None;
99+
}
98100
parse_type(parser, None).ok()?;
99101
(parser.current_kind() == Some(SyntaxKind::AsKw)).then_some(())
100102
})

crates/parser/test_files/syntax_node/exprs/binop.fe

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@ a
1414
+ b
1515
a
1616
-= b
17-
a
18-
< b
1917
true
2018
|| false
2119
a.b.c

crates/parser/test_files/syntax_node/exprs/binop.snap

Lines changed: 35 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ source: crates/parser/tests/syntax_node.rs
33
expression: node
44
input_file: test_files/syntax_node/exprs/binop.fe
55
---
6-
Root@0..214
6+
Root@0..204
77
BinExpr@0..9
88
LitExpr@0..1
99
Lit@0..1
@@ -274,51 +274,37 @@ Root@0..214
274274
PathSegment@172..173
275275
Ident@172..173 "b"
276276
Newline@173..174 "\n"
277-
BinExpr@174..183
278-
PathExpr@174..175
279-
Path@174..175
280-
PathSegment@174..175
281-
Ident@174..175 "a"
282-
Newline@175..176 "\n"
283-
WhiteSpace@176..180 " "
284-
Lt@180..181 "<"
285-
WhiteSpace@181..182 " "
286-
PathExpr@182..183
287-
Path@182..183
288-
PathSegment@182..183
289-
Ident@182..183 "b"
290-
Newline@183..184 "\n"
291-
BinExpr@184..201
292-
LitExpr@184..188
293-
Lit@184..188
294-
TrueKw@184..188 "true"
295-
Newline@188..189 "\n"
296-
WhiteSpace@189..193 " "
297-
Pipe2@193..195 "||"
298-
WhiteSpace@195..196 " "
299-
LitExpr@196..201
300-
Lit@196..201
301-
FalseKw@196..201 "false"
302-
Newline@201..202 "\n"
303-
FieldExpr@202..207
304-
FieldExpr@202..205
305-
PathExpr@202..203
306-
Path@202..203
307-
PathSegment@202..203
308-
Ident@202..203 "a"
309-
Dot@203..204 "."
310-
Ident@204..205 "b"
311-
Dot@205..206 "."
312-
Ident@206..207 "c"
313-
Newline@207..208 "\n"
314-
FieldExpr@208..213
315-
FieldExpr@208..211
316-
PathExpr@208..209
317-
Path@208..209
318-
PathSegment@208..209
319-
Ident@208..209 "a"
320-
Dot@209..210 "."
321-
Int@210..211 "0"
322-
Dot@211..212 "."
323-
Ident@212..213 "c"
324-
Newline@213..214 "\n"
277+
BinExpr@174..191
278+
LitExpr@174..178
279+
Lit@174..178
280+
TrueKw@174..178 "true"
281+
Newline@178..179 "\n"
282+
WhiteSpace@179..183 " "
283+
Pipe2@183..185 "||"
284+
WhiteSpace@185..186 " "
285+
LitExpr@186..191
286+
Lit@186..191
287+
FalseKw@186..191 "false"
288+
Newline@191..192 "\n"
289+
FieldExpr@192..197
290+
FieldExpr@192..195
291+
PathExpr@192..193
292+
Path@192..193
293+
PathSegment@192..193
294+
Ident@192..193 "a"
295+
Dot@193..194 "."
296+
Ident@194..195 "b"
297+
Dot@195..196 "."
298+
Ident@196..197 "c"
299+
Newline@197..198 "\n"
300+
FieldExpr@198..203
301+
FieldExpr@198..201
302+
PathExpr@198..199
303+
Path@198..199
304+
PathSegment@198..199
305+
Ident@198..199 "a"
306+
Dot@199..200 "."
307+
Int@200..201 "0"
308+
Dot@201..202 "."
309+
Ident@202..203 "c"
310+
Newline@203..204 "\n"

crates/tree-sitter-fe/src/scanner.c

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -143,37 +143,6 @@ static bool scan_automatic_semicolon(TSLexer *lexer) {
143143
return lexer->lookahead != '=';
144144
}
145145

146-
if (c == '<') {
147-
// Disambiguate newline-start `<` without consuming too much input:
148-
// - `< x` continues previous expression (emit COMPARISON_LT).
149-
// - `<=` / `<<` continue previous expression (handled by internal lexer).
150-
// - `<T...` / `<<T...` start a new qualified-path expression statement.
151-
advance(lexer);
152-
int32_t next = lexer->lookahead;
153-
154-
if (next == '=') return false;
155-
156-
if (next == '<') {
157-
advance(lexer);
158-
int32_t after_shift = lexer->lookahead;
159-
if (after_shift == '=' ||
160-
after_shift == ' ' || after_shift == '\t' ||
161-
after_shift == '\r' || after_shift == '\n' ||
162-
after_shift == '/') {
163-
return false;
164-
}
165-
return true;
166-
}
167-
168-
if (next == ' ' || next == '\t' || next == '\r' || next == '\n' || next == '/') {
169-
lexer->result_symbol = COMPARISON_LT;
170-
lexer->mark_end(lexer);
171-
return true;
172-
}
173-
174-
return true;
175-
}
176-
177146
// Any other token after a newline -- insert semicolon
178147
return true;
179148
}
Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
---
22
source: crates/uitest/tests/parser.rs
3+
assertion_line: 26
34
expression: diags
45
input_file: fixtures/parser/newline_ambiguous_minus.fe
56
---
67
error[1-0001]: line-start `-` after an expression is ambiguous; move `-` to the previous line for subtraction or parenthesize explicitly
7-
┌─ newline_ambiguous_minus.fe:2:14
8+
┌─ newline_ambiguous_minus.fe:3:9
89
9-
2let a = x
10-
^ line-start `-` after an expression is ambiguous; move `-` to the previous line for subtraction or parenthesize explicitly
10+
3 - y
11+
^ line-start `-` after an expression is ambiguous; move `-` to the previous line for subtraction or parenthesize explicitly
1112

1213
error[1-0001]: line-start `-` after an expression is ambiguous; move `-` to the previous line for subtraction or parenthesize explicitly
13-
┌─ newline_ambiguous_minus.fe:6:12
14+
┌─ newline_ambiguous_minus.fe:7:9
1415
15-
6+ y
16-
^ line-start `-` after an expression is ambiguous; move `-` to the previous line for subtraction or parenthesize explicitly
16+
7- z
17+
^ line-start `-` after an expression is ambiguous; move `-` to the previous line for subtraction or parenthesize explicitly

crates/uitest/fixtures/parser/newline_lt_continuation.fe

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,8 @@ fn f(x: i32, y: i32) {
77

88
let c = x
99
<< y
10+
11+
let mut d = x
12+
d
13+
<<= y
1014
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
fn f() {
2+
x
3+
<T as Foo>::bar()
4+
5+
x
6+
< T as Foo >::bar()
7+
8+
x
9+
<<T as Foo>::Assoc as Bar>::baz()
10+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
source: crates/uitest/tests/parser.rs
3+
assertion_line: 26
4+
expression: diags
5+
input_file: fixtures/parser/newline_qualified_path_after_expr.fe
6+
---

crates/uitest/fixtures/ty_check/unary.fe

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,9 @@ fn foo() {
55
let _x = +f
66
let _y = -f
77
let _z = !f
8+
9+
let a: usize = 1
10+
+2
11+
-3
12+
*4
813
}

0 commit comments

Comments
 (0)