Skip to content
/ rust Public
forked from rust-lang/rust

Commit a04e649

Browse files
committed
Auto merge of rust-lang#114028 - Centri3:ternary-operator, r=compiler-errors
Gracefully handle ternary operator Fixes rust-lang#112578 ~~May not be the best way to do this as it doesn't check for a single `:`, so it could perhaps appear even when the actual issue is just a missing semicolon. May not be the biggest deal, though?~~ Nevermind, got it working properly now ^^
2 parents 4c96822 + 79b6ed0 commit a04e649

File tree

5 files changed

+241
-2
lines changed

5 files changed

+241
-2
lines changed

compiler/rustc_parse/messages.ftl

+4
Original file line numberDiff line numberDiff line change
@@ -724,6 +724,10 @@ parse_sugg_wrap_pattern_in_parens = wrap the pattern in parentheses
724724
725725
parse_switch_mut_let_order =
726726
switch the order of `mut` and `let`
727+
728+
parse_ternary_operator = Rust has no ternary operator
729+
.help = use an `if-else` expression instead
730+
727731
parse_tilde_const_lifetime = `~const` may only modify trait bounds, not lifetime bounds
728732
729733
parse_tilde_is_not_unary_operator = `~` cannot be used as a unary operator

compiler/rustc_parse/src/errors.rs

+8
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,14 @@ pub(crate) enum IfExpressionMissingThenBlockSub {
365365
AddThenBlock(#[primary_span] Span),
366366
}
367367

368+
#[derive(Diagnostic)]
369+
#[diag(parse_ternary_operator)]
370+
#[help]
371+
pub struct TernaryOperator {
372+
#[primary_span]
373+
pub span: Span,
374+
}
375+
368376
#[derive(Subdiagnostic)]
369377
#[suggestion(parse_extra_if_in_let_else, applicability = "maybe-incorrect", code = "")]
370378
pub(crate) struct IfExpressionLetSomeSub {

compiler/rustc_parse/src/parser/diagnostics.rs

+45-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use crate::errors::{
1414
PatternMethodParamWithoutBody, QuestionMarkInType, QuestionMarkInTypeSugg, SelfParamNotFirst,
1515
StructLiteralBodyWithoutPath, StructLiteralBodyWithoutPathSugg, StructLiteralNeedingParens,
1616
StructLiteralNeedingParensSugg, SuggAddMissingLetStmt, SuggEscapeIdentifier, SuggRemoveComma,
17-
UnexpectedConstInGenericParam, UnexpectedConstParamDeclaration,
17+
TernaryOperator, UnexpectedConstInGenericParam, UnexpectedConstParamDeclaration,
1818
UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets, UseEqInstead,
1919
};
2020

@@ -500,6 +500,10 @@ impl<'a> Parser<'a> {
500500

501501
// Special-case "expected `;`" errors
502502
if expected.contains(&TokenType::Token(token::Semi)) {
503+
if self.prev_token == token::Question && self.maybe_recover_from_ternary_operator() {
504+
return Ok(true);
505+
}
506+
503507
if self.token.span == DUMMY_SP || self.prev_token.span == DUMMY_SP {
504508
// Likely inside a macro, can't provide meaningful suggestions.
505509
} else if !sm.is_multiline(self.prev_token.span.until(self.token.span)) {
@@ -1330,6 +1334,45 @@ impl<'a> Parser<'a> {
13301334
}
13311335
}
13321336

1337+
/// Rust has no ternary operator (`cond ? then : else`). Parse it and try
1338+
/// to recover from it if `then` and `else` are valid expressions. Returns
1339+
/// whether it was a ternary operator.
1340+
pub(super) fn maybe_recover_from_ternary_operator(&mut self) -> bool {
1341+
if self.prev_token != token::Question {
1342+
return false;
1343+
}
1344+
1345+
let lo = self.prev_token.span.lo();
1346+
let snapshot = self.create_snapshot_for_diagnostic();
1347+
1348+
if match self.parse_expr() {
1349+
Ok(_) => true,
1350+
Err(err) => {
1351+
err.cancel();
1352+
// The colon can sometimes be mistaken for type
1353+
// ascription. Catch when this happens and continue.
1354+
self.token == token::Colon
1355+
}
1356+
} {
1357+
if self.eat_noexpect(&token::Colon) {
1358+
match self.parse_expr() {
1359+
Ok(_) => {
1360+
self.sess.emit_err(TernaryOperator { span: self.token.span.with_lo(lo) });
1361+
return true;
1362+
}
1363+
Err(err) => {
1364+
err.cancel();
1365+
self.restore_snapshot(snapshot);
1366+
}
1367+
};
1368+
}
1369+
} else {
1370+
self.restore_snapshot(snapshot);
1371+
};
1372+
1373+
false
1374+
}
1375+
13331376
pub(super) fn maybe_recover_from_bad_type_plus(&mut self, ty: &Ty) -> PResult<'a, ()> {
13341377
// Do not add `+` to expected tokens.
13351378
if !self.token.is_like_plus() {
@@ -2111,7 +2154,7 @@ impl<'a> Parser<'a> {
21112154
}
21122155
_ => (
21132156
self.token.span,
2114-
format!("expected expression, found {}", super::token_descr(&self.token),),
2157+
format!("expected expression, found {}", super::token_descr(&self.token)),
21152158
),
21162159
};
21172160
let mut err = self.struct_span_err(span, msg);

tests/ui/parser/ternary_operator.rs

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// A good chunk of these errors aren't shown to the user, but are still
2+
// required in the test for it to pass.
3+
4+
fn a() { //~ NOTE this function should return `Result` or `Option` to accept `?`
5+
let x = 5 > 2 ? true : false;
6+
//~^ ERROR Rust has no ternary operator
7+
//~| HELP use an `if-else` expression instead
8+
//~| ERROR the `?` operator can only be applied to values that implement `Try` [E0277]
9+
//~| HELP the trait `Try` is not implemented for `{integer}`
10+
//~| ERROR the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`) [E0277]
11+
//~| HELP the trait `FromResidual<_>` is not implemented for `()`
12+
//~| NOTE in this expansion of desugaring of operator `?`
13+
//~| NOTE the `?` operator cannot be applied to type `{integer}`
14+
//~| NOTE in this expansion of desugaring of operator `?`
15+
//~| NOTE in this expansion of desugaring of operator `?`
16+
//~| NOTE cannot use the `?` operator in a function that returns `()`
17+
//~| NOTE in this expansion of desugaring of operator `?`
18+
}
19+
20+
fn b() { //~ NOTE this function should return `Result` or `Option` to accept `?`
21+
let x = 5 > 2 ? { true } : { false };
22+
//~^ ERROR Rust has no ternary operator
23+
//~| HELP use an `if-else` expression instead
24+
//~| ERROR the `?` operator can only be applied to values that implement `Try` [E0277]
25+
//~| HELP the trait `Try` is not implemented for `{integer}`
26+
//~| ERROR the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`) [E0277]
27+
//~| HELP the trait `FromResidual<_>` is not implemented for `()`
28+
//~| NOTE in this expansion of desugaring of operator `?`
29+
//~| NOTE the `?` operator cannot be applied to type `{integer}`
30+
//~| NOTE in this expansion of desugaring of operator `?`
31+
//~| NOTE in this expansion of desugaring of operator `?`
32+
//~| NOTE cannot use the `?` operator in a function that returns `()`
33+
//~| NOTE in this expansion of desugaring of operator `?`
34+
}
35+
36+
fn c() { //~ NOTE this function should return `Result` or `Option` to accept `?`
37+
let x = 5 > 2 ? f32::MAX : f32::MIN;
38+
//~^ ERROR Rust has no ternary operator
39+
//~| HELP use an `if-else` expression instead
40+
//~| ERROR the `?` operator can only be applied to values that implement `Try` [E0277]
41+
//~| HELP the trait `Try` is not implemented for `{integer}`
42+
//~| ERROR the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`) [E0277]
43+
//~| HELP the trait `FromResidual<_>` is not implemented for `()`
44+
//~| NOTE in this expansion of desugaring of operator `?`
45+
//~| NOTE the `?` operator cannot be applied to type `{integer}`
46+
//~| NOTE in this expansion of desugaring of operator `?`
47+
//~| NOTE in this expansion of desugaring of operator `?`
48+
//~| NOTE cannot use the `?` operator in a function that returns `()`
49+
//~| NOTE in this expansion of desugaring of operator `?`
50+
}
51+
52+
fn main() { //~ NOTE this function should return `Result` or `Option` to accept `?`
53+
let x = 5 > 2 ? { let x = vec![]: Vec<u16>; x } : { false };
54+
//~^ ERROR Rust has no ternary operator
55+
//~| HELP use an `if-else` expression instead
56+
//~| ERROR expected one of `.`, `;`, `?`, `else`, or an operator, found `:`
57+
//~| NOTE expected one of `.`, `;`, `?`, `else`, or an operator
58+
//~| ERROR the `?` operator can only be applied to values that implement `Try` [E0277]
59+
//~| HELP the trait `Try` is not implemented for `{integer}`
60+
//~| ERROR the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`) [E0277]
61+
//~| HELP the trait `FromResidual<_>` is not implemented for `()`
62+
//~| NOTE type ascription syntax has been removed, see issue #101728 <https://github.com/rust-lang/rust/issues/101728>
63+
//~| NOTE in this expansion of desugaring of operator `?`
64+
//~| NOTE the `?` operator cannot be applied to type `{integer}`
65+
//~| NOTE in this expansion of desugaring of operator `?`
66+
//~| NOTE in this expansion of desugaring of operator `?`
67+
//~| NOTE cannot use the `?` operator in a function that returns `()`
68+
//~| NOTE in this expansion of desugaring of operator `?`
69+
}
+115
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
error: Rust has no ternary operator
2+
--> $DIR/ternary_operator.rs:5:19
3+
|
4+
LL | let x = 5 > 2 ? true : false;
5+
| ^^^^^^^^^^^^^^^
6+
|
7+
= help: use an `if-else` expression instead
8+
9+
error: Rust has no ternary operator
10+
--> $DIR/ternary_operator.rs:21:19
11+
|
12+
LL | let x = 5 > 2 ? { true } : { false };
13+
| ^^^^^^^^^^^^^^^^^^^^^^^
14+
|
15+
= help: use an `if-else` expression instead
16+
17+
error: Rust has no ternary operator
18+
--> $DIR/ternary_operator.rs:37:19
19+
|
20+
LL | let x = 5 > 2 ? f32::MAX : f32::MIN;
21+
| ^^^^^^^^^^^^^^^^^^^^^^
22+
|
23+
= help: use an `if-else` expression instead
24+
25+
error: expected one of `.`, `;`, `?`, `else`, or an operator, found `:`
26+
--> $DIR/ternary_operator.rs:53:37
27+
|
28+
LL | let x = 5 > 2 ? { let x = vec![]: Vec<u16>; x } : { false };
29+
| ^ expected one of `.`, `;`, `?`, `else`, or an operator
30+
|
31+
= note: type ascription syntax has been removed, see issue #101728 <https://github.com/rust-lang/rust/issues/101728>
32+
33+
error: Rust has no ternary operator
34+
--> $DIR/ternary_operator.rs:53:19
35+
|
36+
LL | let x = 5 > 2 ? { let x = vec![]: Vec<u16>; x } : { false };
37+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
38+
|
39+
= help: use an `if-else` expression instead
40+
41+
error[E0277]: the `?` operator can only be applied to values that implement `Try`
42+
--> $DIR/ternary_operator.rs:5:17
43+
|
44+
LL | let x = 5 > 2 ? true : false;
45+
| ^^^ the `?` operator cannot be applied to type `{integer}`
46+
|
47+
= help: the trait `Try` is not implemented for `{integer}`
48+
49+
error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`)
50+
--> $DIR/ternary_operator.rs:5:19
51+
|
52+
LL | fn a() {
53+
| ------ this function should return `Result` or `Option` to accept `?`
54+
LL | let x = 5 > 2 ? true : false;
55+
| ^ cannot use the `?` operator in a function that returns `()`
56+
|
57+
= help: the trait `FromResidual<_>` is not implemented for `()`
58+
59+
error[E0277]: the `?` operator can only be applied to values that implement `Try`
60+
--> $DIR/ternary_operator.rs:21:17
61+
|
62+
LL | let x = 5 > 2 ? { true } : { false };
63+
| ^^^ the `?` operator cannot be applied to type `{integer}`
64+
|
65+
= help: the trait `Try` is not implemented for `{integer}`
66+
67+
error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`)
68+
--> $DIR/ternary_operator.rs:21:19
69+
|
70+
LL | fn b() {
71+
| ------ this function should return `Result` or `Option` to accept `?`
72+
LL | let x = 5 > 2 ? { true } : { false };
73+
| ^ cannot use the `?` operator in a function that returns `()`
74+
|
75+
= help: the trait `FromResidual<_>` is not implemented for `()`
76+
77+
error[E0277]: the `?` operator can only be applied to values that implement `Try`
78+
--> $DIR/ternary_operator.rs:37:17
79+
|
80+
LL | let x = 5 > 2 ? f32::MAX : f32::MIN;
81+
| ^^^ the `?` operator cannot be applied to type `{integer}`
82+
|
83+
= help: the trait `Try` is not implemented for `{integer}`
84+
85+
error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`)
86+
--> $DIR/ternary_operator.rs:37:19
87+
|
88+
LL | fn c() {
89+
| ------ this function should return `Result` or `Option` to accept `?`
90+
LL | let x = 5 > 2 ? f32::MAX : f32::MIN;
91+
| ^ cannot use the `?` operator in a function that returns `()`
92+
|
93+
= help: the trait `FromResidual<_>` is not implemented for `()`
94+
95+
error[E0277]: the `?` operator can only be applied to values that implement `Try`
96+
--> $DIR/ternary_operator.rs:53:17
97+
|
98+
LL | let x = 5 > 2 ? { let x = vec![]: Vec<u16>; x } : { false };
99+
| ^^^ the `?` operator cannot be applied to type `{integer}`
100+
|
101+
= help: the trait `Try` is not implemented for `{integer}`
102+
103+
error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`)
104+
--> $DIR/ternary_operator.rs:53:19
105+
|
106+
LL | fn main() {
107+
| --------- this function should return `Result` or `Option` to accept `?`
108+
LL | let x = 5 > 2 ? { let x = vec![]: Vec<u16>; x } : { false };
109+
| ^ cannot use the `?` operator in a function that returns `()`
110+
|
111+
= help: the trait `FromResidual<_>` is not implemented for `()`
112+
113+
error: aborting due to 13 previous errors
114+
115+
For more information about this error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)