Skip to content

Commit b2ecedf

Browse files
authored
Unrolled build for rust-lang#125049
Rollup merge of rust-lang#125049 - dtolnay:castbrace, r=compiler-errors Disallow cast with trailing braced macro in let-else This fixes an edge case I noticed while porting rust-lang#118880 and rust-lang#119062 to syn. Previously, rustc incorrectly accepted code such as: ```rust let foo = &std::ptr::null as &'static dyn std::ops::Fn() -> *const primitive! { 8 } else { return; }; ``` even though a right curl brace `}` directly before `else` in a `let...else` statement is not supposed to be valid syntax.
2 parents 22f5bdc + a36b94d commit b2ecedf

File tree

4 files changed

+209
-297
lines changed

4 files changed

+209
-297
lines changed

compiler/rustc_ast/src/util/classify.rs

+91-4
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,17 @@ pub fn expr_requires_semi_to_be_stmt(e: &ast::Expr) -> bool {
8181
}
8282
}
8383

84+
pub enum TrailingBrace<'a> {
85+
/// Trailing brace in a macro call, like the one in `x as *const brace! {}`.
86+
/// We will suggest changing the macro call to a different delimiter.
87+
MacCall(&'a ast::MacCall),
88+
/// Trailing brace in any other expression, such as `a + B {}`. We will
89+
/// suggest wrapping the innermost expression in parentheses: `a + (B {})`.
90+
Expr(&'a ast::Expr),
91+
}
92+
8493
/// If an expression ends with `}`, returns the innermost expression ending in the `}`
85-
pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<&ast::Expr> {
94+
pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<TrailingBrace<'_>> {
8695
loop {
8796
match &expr.kind {
8897
AddrOf(_, _, e)
@@ -111,10 +120,14 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<&ast::Expr> {
111120
| Struct(..)
112121
| TryBlock(..)
113122
| While(..)
114-
| ConstBlock(_) => break Some(expr),
123+
| ConstBlock(_) => break Some(TrailingBrace::Expr(expr)),
124+
125+
Cast(_, ty) => {
126+
break type_trailing_braced_mac_call(ty).map(TrailingBrace::MacCall);
127+
}
115128

116129
MacCall(mac) => {
117-
break (mac.args.delim == Delimiter::Brace).then_some(expr);
130+
break (mac.args.delim == Delimiter::Brace).then_some(TrailingBrace::MacCall(mac));
118131
}
119132

120133
InlineAsm(_) | OffsetOf(_, _) | IncludedBytes(_) | FormatArgs(_) => {
@@ -131,7 +144,6 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<&ast::Expr> {
131144
| MethodCall(_)
132145
| Tup(_)
133146
| Lit(_)
134-
| Cast(_, _)
135147
| Type(_, _)
136148
| Await(_, _)
137149
| Field(_, _)
@@ -148,3 +160,78 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<&ast::Expr> {
148160
}
149161
}
150162
}
163+
164+
/// If the type's last token is `}`, it must be due to a braced macro call, such
165+
/// as in `*const brace! { ... }`. Returns that trailing macro call.
166+
fn type_trailing_braced_mac_call(mut ty: &ast::Ty) -> Option<&ast::MacCall> {
167+
loop {
168+
match &ty.kind {
169+
ast::TyKind::MacCall(mac) => {
170+
break (mac.args.delim == Delimiter::Brace).then_some(mac);
171+
}
172+
173+
ast::TyKind::Ptr(mut_ty) | ast::TyKind::Ref(_, mut_ty) => {
174+
ty = &mut_ty.ty;
175+
}
176+
177+
ast::TyKind::BareFn(fn_ty) => match &fn_ty.decl.output {
178+
ast::FnRetTy::Default(_) => break None,
179+
ast::FnRetTy::Ty(ret) => ty = ret,
180+
},
181+
182+
ast::TyKind::Path(_, path) => match path_return_type(path) {
183+
Some(trailing_ty) => ty = trailing_ty,
184+
None => break None,
185+
},
186+
187+
ast::TyKind::TraitObject(bounds, _) | ast::TyKind::ImplTrait(_, bounds, _) => {
188+
match bounds.last() {
189+
Some(ast::GenericBound::Trait(bound, _)) => {
190+
match path_return_type(&bound.trait_ref.path) {
191+
Some(trailing_ty) => ty = trailing_ty,
192+
None => break None,
193+
}
194+
}
195+
Some(ast::GenericBound::Outlives(_)) | None => break None,
196+
}
197+
}
198+
199+
ast::TyKind::Slice(..)
200+
| ast::TyKind::Array(..)
201+
| ast::TyKind::Never
202+
| ast::TyKind::Tup(..)
203+
| ast::TyKind::Paren(..)
204+
| ast::TyKind::Typeof(..)
205+
| ast::TyKind::Infer
206+
| ast::TyKind::ImplicitSelf
207+
| ast::TyKind::CVarArgs
208+
| ast::TyKind::Pat(..)
209+
| ast::TyKind::Dummy
210+
| ast::TyKind::Err(..) => break None,
211+
212+
// These end in brace, but cannot occur in a let-else statement.
213+
// They are only parsed as fields of a data structure. For the
214+
// purpose of denying trailing braces in the expression of a
215+
// let-else, we can disregard these.
216+
ast::TyKind::AnonStruct(..) | ast::TyKind::AnonUnion(..) => break None,
217+
}
218+
}
219+
}
220+
221+
/// Returns the trailing return type in the given path, if it has one.
222+
///
223+
/// ```ignore (illustrative)
224+
/// ::std::ops::FnOnce(&str) -> fn() -> *const c_void
225+
/// ^^^^^^^^^^^^^^^^^^^^^
226+
/// ```
227+
fn path_return_type(path: &ast::Path) -> Option<&ast::Ty> {
228+
let last_segment = path.segments.last()?;
229+
let args = last_segment.args.as_ref()?;
230+
match &**args {
231+
ast::GenericArgs::Parenthesized(args) => match &args.output {
232+
ast::FnRetTy::Default(_) => None,
233+
ast::FnRetTy::Ty(ret) => Some(ret),
234+
},
235+
ast::GenericArgs::AngleBracketed(_) => None,
236+
}
237+
}

compiler/rustc_parse/src/parser/stmt.rs

+17-11
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use ast::Label;
1515
use rustc_ast as ast;
1616
use rustc_ast::ptr::P;
1717
use rustc_ast::token::{self, Delimiter, TokenKind};
18-
use rustc_ast::util::classify;
18+
use rustc_ast::util::classify::{self, TrailingBrace};
1919
use rustc_ast::{AttrStyle, AttrVec, LocalKind, MacCall, MacCallStmt, MacStmtStyle};
2020
use rustc_ast::{Block, BlockCheckMode, Expr, ExprKind, HasAttrs, Local, Recovered, Stmt};
2121
use rustc_ast::{StmtKind, DUMMY_NODE_ID};
@@ -407,18 +407,24 @@ impl<'a> Parser<'a> {
407407

408408
fn check_let_else_init_trailing_brace(&self, init: &ast::Expr) {
409409
if let Some(trailing) = classify::expr_trailing_brace(init) {
410-
let sugg = match &trailing.kind {
411-
ExprKind::MacCall(mac) => errors::WrapInParentheses::MacroArgs {
412-
left: mac.args.dspan.open,
413-
right: mac.args.dspan.close,
414-
},
415-
_ => errors::WrapInParentheses::Expression {
416-
left: trailing.span.shrink_to_lo(),
417-
right: trailing.span.shrink_to_hi(),
418-
},
410+
let (span, sugg) = match trailing {
411+
TrailingBrace::MacCall(mac) => (
412+
mac.span(),
413+
errors::WrapInParentheses::MacroArgs {
414+
left: mac.args.dspan.open,
415+
right: mac.args.dspan.close,
416+
},
417+
),
418+
TrailingBrace::Expr(expr) => (
419+
expr.span,
420+
errors::WrapInParentheses::Expression {
421+
left: expr.span.shrink_to_lo(),
422+
right: expr.span.shrink_to_hi(),
423+
},
424+
),
419425
};
420426
self.dcx().emit_err(errors::InvalidCurlyInLetElse {
421-
span: trailing.span.with_lo(trailing.span.hi() - BytePos(1)),
427+
span: span.with_lo(span.hi() - BytePos(1)),
422428
sugg,
423429
});
424430
}

tests/ui/parser/bad-let-else-statement.rs

+40-33
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@
33
#![feature(explicit_tail_calls)]
44

55
fn a() {
6-
let foo = {
7-
//~^ WARN irrefutable `let...else` pattern
6+
let 0 = {
87
1
98
} else {
109
//~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
@@ -22,8 +21,7 @@ fn b() {
2221
}
2322

2423
fn c() {
25-
let foo = if true {
26-
//~^ WARN irrefutable `let...else` pattern
24+
let 0 = if true {
2725
1
2826
} else {
2927
0
@@ -43,8 +41,7 @@ fn d() {
4341
}
4442

4543
fn e() {
46-
let foo = match true {
47-
//~^ WARN irrefutable `let...else` pattern
44+
let 0 = match true {
4845
true => 1,
4946
false => 0
5047
} else {
@@ -53,10 +50,12 @@ fn e() {
5350
};
5451
}
5552

56-
struct X {a: i32}
5753
fn f() {
58-
let foo = X {
59-
//~^ WARN irrefutable `let...else` pattern
54+
struct X {
55+
a: i32,
56+
}
57+
58+
let X { a: 0 } = X {
6059
a: 1
6160
} else {
6261
//~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
@@ -74,8 +73,7 @@ fn g() {
7473
}
7574

7675
fn h() {
77-
let foo = const {
78-
//~^ WARN irrefutable `let...else` pattern
76+
let 0 = const {
7977
1
8078
} else {
8179
//~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
@@ -84,8 +82,7 @@ fn h() {
8482
}
8583

8684
fn i() {
87-
let foo = &{
88-
//~^ WARN irrefutable `let...else` pattern
85+
let 0 = &{
8986
1
9087
} else {
9188
//~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
@@ -94,8 +91,8 @@ fn i() {
9491
}
9592

9693
fn j() {
97-
let bar = 0;
98-
let foo = bar = { //~ ERROR: cannot assign twice
94+
let mut bar = 0;
95+
let foo = bar = {
9996
//~^ WARN irrefutable `let...else` pattern
10097
1
10198
} else {
@@ -105,8 +102,7 @@ fn j() {
105102
}
106103

107104
fn k() {
108-
let foo = 1 + {
109-
//~^ WARN irrefutable `let...else` pattern
105+
let 0 = 1 + {
110106
1
111107
} else {
112108
//~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
@@ -115,8 +111,8 @@ fn k() {
115111
}
116112

117113
fn l() {
118-
let foo = 1..{
119-
//~^ WARN irrefutable `let...else` pattern
114+
const RANGE: std::ops::Range<u8> = 0..0;
115+
let RANGE = 1..{
120116
1
121117
} else {
122118
//~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
@@ -125,8 +121,7 @@ fn l() {
125121
}
126122

127123
fn m() {
128-
let foo = return {
129-
//~^ WARN irrefutable `let...else` pattern
124+
let 0 = return {
130125
()
131126
} else {
132127
//~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
@@ -135,8 +130,7 @@ fn m() {
135130
}
136131

137132
fn n() {
138-
let foo = -{
139-
//~^ WARN irrefutable `let...else` pattern
133+
let 0 = -{
140134
1
141135
} else {
142136
//~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
@@ -145,8 +139,7 @@ fn n() {
145139
}
146140

147141
fn o() -> Result<(), ()> {
148-
let foo = do yeet {
149-
//~^ WARN irrefutable `let...else` pattern
142+
let 0 = do yeet {
150143
()
151144
} else {
152145
//~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
@@ -155,8 +148,7 @@ fn o() -> Result<(), ()> {
155148
}
156149

157150
fn p() {
158-
let foo = become {
159-
//~^ WARN irrefutable `let...else` pattern
151+
let 0 = become {
160152
()
161153
} else {
162154
//~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
@@ -185,22 +177,37 @@ fn r() {
185177

186178
fn s() {
187179
macro_rules! a {
188-
() => { {} }
189-
//~^ WARN irrefutable `let...else` pattern
190-
//~| WARN irrefutable `let...else` pattern
180+
() => {
181+
{ 1 }
182+
};
191183
}
192184

193185
macro_rules! b {
194186
(1) => {
195-
let x = a!() else { return; };
187+
let 0 = a!() else { return; };
196188
};
197189
(2) => {
198-
let x = a! {} else { return; };
190+
let 0 = a! {} else { return; };
199191
//~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
200192
};
201193
}
202194

203-
b!(1); b!(2);
195+
b!(1);
196+
b!(2);
197+
}
198+
199+
fn t() {
200+
macro_rules! primitive {
201+
(8) => { u8 };
202+
}
203+
204+
let foo = &std::ptr::null as &'static dyn std::ops::Fn() -> *const primitive! {
205+
//~^ WARN irrefutable `let...else` pattern
206+
8
207+
} else {
208+
//~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
209+
return;
210+
};
204211
}
205212

206213
fn main() {}

0 commit comments

Comments
 (0)