Skip to content

Commit b8d783a

Browse files
committed
Resynthesize foo<bar>( and foo<bar>:: in check_no_chained_comparison
Signed-off-by: xizheyin <[email protected]>
1 parent 32e7a4b commit b8d783a

File tree

3 files changed

+187
-21
lines changed

3 files changed

+187
-21
lines changed

compiler/rustc_parse/src/parser/diagnostics.rs

Lines changed: 154 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ use rustc_ast::token::{self, Lit, LitKind, Token, TokenKind};
88
use rustc_ast::util::parser::AssocOp;
99
use rustc_ast::{
1010
AngleBracketedArg, AngleBracketedArgs, AnonConst, AttrVec, BinOpKind, BindingMode, Block,
11-
BlockCheckMode, Expr, ExprKind, GenericArg, Generics, Item, ItemKind, Param, Pat, PatKind,
12-
Path, PathSegment, QSelf, Recovered, Ty, TyKind,
11+
BlockCheckMode, DUMMY_NODE_ID, Expr, ExprKind, GenericArg, GenericArgs, Generics, Item,
12+
ItemKind, Param, Pat, PatKind, Path, PathSegment, QSelf, Recovered, Ty, TyKind,
1313
};
1414
use rustc_ast_pretty::pprust;
1515
use rustc_data_structures::fx::FxHashSet;
@@ -1453,6 +1453,70 @@ impl<'a> Parser<'a> {
14531453
let mk_err_expr =
14541454
|this: &Self, span, guar| Ok(Some(this.mk_expr(span, ExprKind::Err(guar))));
14551455

1456+
// Helper function to check if we should attempt turbofish recovery and resynthesize
1457+
// FIXME: Check more cases
1458+
let mk_turbofish_expr = |this: &Self,
1459+
span: Span,
1460+
l1: &P<Expr>,
1461+
r1: &P<Expr>,
1462+
as_call: bool|
1463+
-> Option<P<Expr>> {
1464+
// First, actually check that the two expressions in the binop are paths.
1465+
// Only proceed if both sides are simple paths.
1466+
let (func_path, type_arg_path) = match (&l1.kind, &r1.kind) {
1467+
(ExprKind::Path(None, func_path), ExprKind::Path(None, type_arg_path)) => {
1468+
// Ensure both paths have no existing generic arguments to avoid confusion
1469+
let func_has_generics = func_path.segments.iter().any(|seg| seg.args.is_some());
1470+
let type_has_generics =
1471+
type_arg_path.segments.iter().any(|seg| seg.args.is_some());
1472+
if func_has_generics || type_has_generics {
1473+
return None;
1474+
}
1475+
(func_path, type_arg_path)
1476+
}
1477+
_ => return None,
1478+
};
1479+
1480+
// Only handle simple function calls (not complex paths)
1481+
if func_path.segments.len() != 1 || type_arg_path.segments.len() != 1 {
1482+
return None;
1483+
}
1484+
1485+
// Build the corrected turbofish expression
1486+
let type_arg = P(Ty {
1487+
id: DUMMY_NODE_ID,
1488+
kind: TyKind::Path(None, type_arg_path.clone()),
1489+
span: r1.span,
1490+
tokens: None,
1491+
});
1492+
1493+
let generic_arg = GenericArg::Type(type_arg);
1494+
let angle_bracketed_arg = AngleBracketedArg::Arg(generic_arg);
1495+
let angle_bracketed_args = AngleBracketedArgs {
1496+
span: l1.span.to(r1.span),
1497+
args: thin_vec![angle_bracketed_arg],
1498+
};
1499+
let generic_args = GenericArgs::AngleBracketed(angle_bracketed_args);
1500+
1501+
// Create the function path with generic arguments
1502+
let mut func_path_with_generics = func_path.clone();
1503+
if let Some(last_segment) = func_path_with_generics.segments.last_mut() {
1504+
last_segment.args = Some(P(generic_args));
1505+
}
1506+
1507+
// Create the appropriate expression based on context
1508+
if as_call {
1509+
// For `foo<bar>()` case - create a function call expression
1510+
let func_expr = this
1511+
.mk_expr(l1.span.to(r1.span), ExprKind::Path(None, func_path_with_generics));
1512+
let args = thin_vec![]; // Empty arguments since they were already consumed
1513+
Some(this.mk_expr(span, ExprKind::Call(func_expr, args)))
1514+
} else {
1515+
// For `foo<bar>::` case - create a path expression
1516+
Some(this.mk_expr(span, ExprKind::Path(None, func_path_with_generics)))
1517+
}
1518+
};
1519+
14561520
match &inner_op.kind {
14571521
ExprKind::Binary(op, l1, r1) if op.node.is_comparison() => {
14581522
let mut err = ComparisonOperatorsCannotBeChained {
@@ -1496,13 +1560,84 @@ impl<'a> Parser<'a> {
14961560

14971561
// Consume the rest of the likely `foo<bar>::new()` or return at `foo<bar>`.
14981562
match self.parse_expr() {
1499-
Ok(_) => {
1563+
Ok(parsed_expr) => {
15001564
// 99% certain that the suggestion is correct, continue parsing.
15011565
let guar = self.dcx().emit_err(err);
1502-
// FIXME: actually check that the two expressions in the binop are
1503-
// paths and resynthesize new fn call expression instead of using
1504-
// `ExprKind::Err` placeholder.
1505-
mk_err_expr(self, inner_op.span.to(self.prev_token.span), guar)
1566+
// Check if we can resynthesize the complete expression combining
1567+
// the corrected path with the parsed suffix
1568+
if let Some(corrected_path_expr) =
1569+
mk_turbofish_expr(self, l1.span.to(r1.span), l1, r1, false)
1570+
{
1571+
// We have a corrected path (e.g., Vec::<i32>), now combine it with
1572+
// the parsed expression (e.g., new()) to form the complete expression
1573+
// (e.g., Vec::<i32>::new())
1574+
1575+
// Try to construct the complete path expression
1576+
if let ExprKind::Path(qself, corrected_path) =
1577+
corrected_path_expr.kind
1578+
{
1579+
// Combine the corrected path with the parsed expression
1580+
// The parsed_expr should be handled as a continuation of the path
1581+
match &parsed_expr.kind {
1582+
ExprKind::Call(func, args) => {
1583+
// Handle case like Vec::<i32>::new()
1584+
if let ExprKind::Path(None, method_path) =
1585+
&func.kind
1586+
{
1587+
// Extend the corrected path with the method path
1588+
let mut extended_path = corrected_path;
1589+
extended_path
1590+
.segments
1591+
.extend(method_path.segments.clone());
1592+
let extended_func = self.mk_expr(
1593+
l1.span.to(func.span),
1594+
ExprKind::Path(qself, extended_path),
1595+
);
1596+
let complete_expr = self.mk_expr(
1597+
inner_op.span.to(self.prev_token.span),
1598+
ExprKind::Call(extended_func, args.clone()),
1599+
);
1600+
Ok(Some(complete_expr))
1601+
} else {
1602+
mk_err_expr(
1603+
self,
1604+
inner_op.span.to(self.prev_token.span),
1605+
guar,
1606+
)
1607+
}
1608+
}
1609+
ExprKind::Path(None, method_path) => {
1610+
// Handle case like Vec::<i32>::CONSTANT
1611+
let mut extended_path = corrected_path;
1612+
extended_path
1613+
.segments
1614+
.extend(method_path.segments.clone());
1615+
let complete_expr = self.mk_expr(
1616+
inner_op.span.to(self.prev_token.span),
1617+
ExprKind::Path(qself, extended_path),
1618+
);
1619+
Ok(Some(complete_expr))
1620+
}
1621+
_ => {
1622+
// For other cases, fall back to error expr
1623+
mk_err_expr(
1624+
self,
1625+
inner_op.span.to(self.prev_token.span),
1626+
guar,
1627+
)
1628+
}
1629+
}
1630+
} else {
1631+
mk_err_expr(
1632+
self,
1633+
inner_op.span.to(self.prev_token.span),
1634+
guar,
1635+
)
1636+
}
1637+
} else {
1638+
// Validation failed: not both paths, use error expr
1639+
mk_err_expr(self, inner_op.span.to(self.prev_token.span), guar)
1640+
}
15061641
}
15071642
Err(expr_err) => {
15081643
expr_err.cancel();
@@ -1527,10 +1662,18 @@ impl<'a> Parser<'a> {
15271662
Err(()) => Err(self.dcx().create_err(err)),
15281663
Ok(()) => {
15291664
let guar = self.dcx().emit_err(err);
1530-
// FIXME: actually check that the two expressions in the binop are
1531-
// paths and resynthesize new fn call expression instead of using
1532-
// `ExprKind::Err` placeholder.
1533-
mk_err_expr(self, inner_op.span.to(self.prev_token.span), guar)
1665+
// Try to resynthesize the function call expression (for `foo<bar>()` case)
1666+
if let Some(call_expr) = mk_turbofish_expr(
1667+
self,
1668+
inner_op.span.to(self.prev_token.span),
1669+
l1,
1670+
r1,
1671+
true,
1672+
) {
1673+
Ok(Some(call_expr))
1674+
} else {
1675+
mk_err_expr(self, inner_op.span.to(self.prev_token.span), guar)
1676+
}
15341677
}
15351678
}
15361679
} else {

tests/ui/parser/require-parens-for-chained-comparison.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,13 @@ fn main() {
1010
f<X>();
1111
//~^ ERROR comparison operators cannot be chained
1212
//~| HELP use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
13+
//~| ERROR cannot find type `X` in this scope [E0412]
14+
//~| ERROR cannot find function `f` in this scope [E0425]
1315

1416
f<Result<Option<X>, Option<Option<X>>>(1, 2);
1517
//~^ ERROR comparison operators cannot be chained
1618
//~| HELP use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
19+
//~| ERROR cannot find function `f` in this scope [E0425]
1720

1821
let _ = f<u8, i8>();
1922
//~^ ERROR expected one of

tests/ui/parser/require-parens-for-chained-comparison.stderr

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ LL | f::<X>();
3232
| ++
3333

3434
error: comparison operators cannot be chained
35-
--> $DIR/require-parens-for-chained-comparison.rs:14:6
35+
--> $DIR/require-parens-for-chained-comparison.rs:16:6
3636
|
3737
LL | f<Result<Option<X>, Option<Option<X>>>(1, 2);
3838
| ^ ^
@@ -43,7 +43,7 @@ LL | f::<Result<Option<X>, Option<Option<X>>>(1, 2);
4343
| ++
4444

4545
error: expected one of `!`, `.`, `::`, `;`, `?`, `else`, `{`, or an operator, found `,`
46-
--> $DIR/require-parens-for-chained-comparison.rs:18:17
46+
--> $DIR/require-parens-for-chained-comparison.rs:21:17
4747
|
4848
LL | let _ = f<u8, i8>();
4949
| ^ expected one of 8 possible tokens
@@ -54,13 +54,13 @@ LL | let _ = f::<u8, i8>();
5454
| ++
5555

5656
error: invalid label name `'_`
57-
--> $DIR/require-parens-for-chained-comparison.rs:22:15
57+
--> $DIR/require-parens-for-chained-comparison.rs:25:15
5858
|
5959
LL | let _ = f<'_, i8>();
6060
| ^^
6161

6262
error: expected `while`, `for`, `loop` or `{` after a label
63-
--> $DIR/require-parens-for-chained-comparison.rs:22:17
63+
--> $DIR/require-parens-for-chained-comparison.rs:25:17
6464
|
6565
LL | let _ = f<'_, i8>();
6666
| ^ expected `while`, `for`, `loop` or `{` after a label
@@ -71,7 +71,7 @@ LL | let _ = f<'_', i8>();
7171
| +
7272

7373
error: expected one of `.`, `:`, `;`, `?`, `else`, `for`, `loop`, `while`, or an operator, found `,`
74-
--> $DIR/require-parens-for-chained-comparison.rs:22:17
74+
--> $DIR/require-parens-for-chained-comparison.rs:25:17
7575
|
7676
LL | let _ = f<'_, i8>();
7777
| ^ expected one of 9 possible tokens
@@ -82,13 +82,13 @@ LL | let _ = f::<'_, i8>();
8282
| ++
8383

8484
error: invalid label name `'_`
85-
--> $DIR/require-parens-for-chained-comparison.rs:29:7
85+
--> $DIR/require-parens-for-chained-comparison.rs:32:7
8686
|
8787
LL | f<'_>();
8888
| ^^
8989

9090
error: expected `while`, `for`, `loop` or `{` after a label
91-
--> $DIR/require-parens-for-chained-comparison.rs:29:9
91+
--> $DIR/require-parens-for-chained-comparison.rs:32:9
9292
|
9393
LL | f<'_>();
9494
| ^ expected `while`, `for`, `loop` or `{` after a label
@@ -99,7 +99,7 @@ LL | f<'_'>();
9999
| +
100100

101101
error: comparison operators cannot be chained
102-
--> $DIR/require-parens-for-chained-comparison.rs:29:6
102+
--> $DIR/require-parens-for-chained-comparison.rs:32:6
103103
|
104104
LL | f<'_>();
105105
| ^ ^
@@ -110,13 +110,33 @@ LL | f::<'_>();
110110
| ++
111111

112112
error: comparison operators cannot be chained
113-
--> $DIR/require-parens-for-chained-comparison.rs:36:14
113+
--> $DIR/require-parens-for-chained-comparison.rs:39:14
114114
|
115115
LL | let _ = f<u8>;
116116
| ^ ^
117117
|
118118
= help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
119119
= help: or use `(...)` if you meant to specify fn arguments
120120

121-
error: aborting due to 12 previous errors
121+
error[E0412]: cannot find type `X` in this scope
122+
--> $DIR/require-parens-for-chained-comparison.rs:10:7
123+
|
124+
LL | f<X>();
125+
| ^ not found in this scope
126+
127+
error[E0425]: cannot find function `f` in this scope
128+
--> $DIR/require-parens-for-chained-comparison.rs:10:5
129+
|
130+
LL | f<X>();
131+
| ^ not found in this scope
132+
133+
error[E0425]: cannot find function `f` in this scope
134+
--> $DIR/require-parens-for-chained-comparison.rs:16:5
135+
|
136+
LL | f<Result<Option<X>, Option<Option<X>>>(1, 2);
137+
| ^ not found in this scope
138+
139+
error: aborting due to 15 previous errors
122140

141+
Some errors have detailed explanations: E0412, E0425.
142+
For more information about an error, try `rustc --explain E0412`.

0 commit comments

Comments
 (0)