Disallow cast with trailing braced macro in let-else
This commit is contained in:
parent
75a34ca262
commit
a36b94d088
4 changed files with 124 additions and 17 deletions
|
@ -81,8 +81,17 @@ pub fn expr_requires_semi_to_be_stmt(e: &ast::Expr) -> bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum TrailingBrace<'a> {
|
||||||
|
/// Trailing brace in a macro call, like the one in `x as *const brace! {}`.
|
||||||
|
/// We will suggest changing the macro call to a different delimiter.
|
||||||
|
MacCall(&'a ast::MacCall),
|
||||||
|
/// Trailing brace in any other expression, such as `a + B {}`. We will
|
||||||
|
/// suggest wrapping the innermost expression in parentheses: `a + (B {})`.
|
||||||
|
Expr(&'a ast::Expr),
|
||||||
|
}
|
||||||
|
|
||||||
/// If an expression ends with `}`, returns the innermost expression ending in the `}`
|
/// If an expression ends with `}`, returns the innermost expression ending in the `}`
|
||||||
pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<&ast::Expr> {
|
pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<TrailingBrace<'_>> {
|
||||||
loop {
|
loop {
|
||||||
match &expr.kind {
|
match &expr.kind {
|
||||||
AddrOf(_, _, e)
|
AddrOf(_, _, e)
|
||||||
|
@ -111,10 +120,14 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<&ast::Expr> {
|
||||||
| Struct(..)
|
| Struct(..)
|
||||||
| TryBlock(..)
|
| TryBlock(..)
|
||||||
| While(..)
|
| While(..)
|
||||||
| ConstBlock(_) => break Some(expr),
|
| ConstBlock(_) => break Some(TrailingBrace::Expr(expr)),
|
||||||
|
|
||||||
|
Cast(_, ty) => {
|
||||||
|
break type_trailing_braced_mac_call(ty).map(TrailingBrace::MacCall);
|
||||||
|
}
|
||||||
|
|
||||||
MacCall(mac) => {
|
MacCall(mac) => {
|
||||||
break (mac.args.delim == Delimiter::Brace).then_some(expr);
|
break (mac.args.delim == Delimiter::Brace).then_some(TrailingBrace::MacCall(mac));
|
||||||
}
|
}
|
||||||
|
|
||||||
InlineAsm(_) | OffsetOf(_, _) | IncludedBytes(_) | FormatArgs(_) => {
|
InlineAsm(_) | OffsetOf(_, _) | IncludedBytes(_) | FormatArgs(_) => {
|
||||||
|
@ -131,7 +144,6 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<&ast::Expr> {
|
||||||
| MethodCall(_)
|
| MethodCall(_)
|
||||||
| Tup(_)
|
| Tup(_)
|
||||||
| Lit(_)
|
| Lit(_)
|
||||||
| Cast(_, _)
|
|
||||||
| Type(_, _)
|
| Type(_, _)
|
||||||
| Await(_, _)
|
| Await(_, _)
|
||||||
| Field(_, _)
|
| Field(_, _)
|
||||||
|
@ -148,3 +160,78 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<&ast::Expr> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// If the type's last token is `}`, it must be due to a braced macro call, such
|
||||||
|
/// as in `*const brace! { ... }`. Returns that trailing macro call.
|
||||||
|
fn type_trailing_braced_mac_call(mut ty: &ast::Ty) -> Option<&ast::MacCall> {
|
||||||
|
loop {
|
||||||
|
match &ty.kind {
|
||||||
|
ast::TyKind::MacCall(mac) => {
|
||||||
|
break (mac.args.delim == Delimiter::Brace).then_some(mac);
|
||||||
|
}
|
||||||
|
|
||||||
|
ast::TyKind::Ptr(mut_ty) | ast::TyKind::Ref(_, mut_ty) => {
|
||||||
|
ty = &mut_ty.ty;
|
||||||
|
}
|
||||||
|
|
||||||
|
ast::TyKind::BareFn(fn_ty) => match &fn_ty.decl.output {
|
||||||
|
ast::FnRetTy::Default(_) => break None,
|
||||||
|
ast::FnRetTy::Ty(ret) => ty = ret,
|
||||||
|
},
|
||||||
|
|
||||||
|
ast::TyKind::Path(_, path) => match path_return_type(path) {
|
||||||
|
Some(trailing_ty) => ty = trailing_ty,
|
||||||
|
None => break None,
|
||||||
|
},
|
||||||
|
|
||||||
|
ast::TyKind::TraitObject(bounds, _) | ast::TyKind::ImplTrait(_, bounds, _) => {
|
||||||
|
match bounds.last() {
|
||||||
|
Some(ast::GenericBound::Trait(bound, _)) => {
|
||||||
|
match path_return_type(&bound.trait_ref.path) {
|
||||||
|
Some(trailing_ty) => ty = trailing_ty,
|
||||||
|
None => break None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(ast::GenericBound::Outlives(_)) | None => break None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast::TyKind::Slice(..)
|
||||||
|
| ast::TyKind::Array(..)
|
||||||
|
| ast::TyKind::Never
|
||||||
|
| ast::TyKind::Tup(..)
|
||||||
|
| ast::TyKind::Paren(..)
|
||||||
|
| ast::TyKind::Typeof(..)
|
||||||
|
| ast::TyKind::Infer
|
||||||
|
| ast::TyKind::ImplicitSelf
|
||||||
|
| ast::TyKind::CVarArgs
|
||||||
|
| ast::TyKind::Pat(..)
|
||||||
|
| ast::TyKind::Dummy
|
||||||
|
| ast::TyKind::Err(..) => break None,
|
||||||
|
|
||||||
|
// These end in brace, but cannot occur in a let-else statement.
|
||||||
|
// They are only parsed as fields of a data structure. For the
|
||||||
|
// purpose of denying trailing braces in the expression of a
|
||||||
|
// let-else, we can disregard these.
|
||||||
|
ast::TyKind::AnonStruct(..) | ast::TyKind::AnonUnion(..) => break None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the trailing return type in the given path, if it has one.
|
||||||
|
///
|
||||||
|
/// ```ignore (illustrative)
|
||||||
|
/// ::std::ops::FnOnce(&str) -> fn() -> *const c_void
|
||||||
|
/// ^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
/// ```
|
||||||
|
fn path_return_type(path: &ast::Path) -> Option<&ast::Ty> {
|
||||||
|
let last_segment = path.segments.last()?;
|
||||||
|
let args = last_segment.args.as_ref()?;
|
||||||
|
match &**args {
|
||||||
|
ast::GenericArgs::Parenthesized(args) => match &args.output {
|
||||||
|
ast::FnRetTy::Default(_) => None,
|
||||||
|
ast::FnRetTy::Ty(ret) => Some(ret),
|
||||||
|
},
|
||||||
|
ast::GenericArgs::AngleBracketed(_) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ use ast::Label;
|
||||||
use rustc_ast as ast;
|
use rustc_ast as ast;
|
||||||
use rustc_ast::ptr::P;
|
use rustc_ast::ptr::P;
|
||||||
use rustc_ast::token::{self, Delimiter, TokenKind};
|
use rustc_ast::token::{self, Delimiter, TokenKind};
|
||||||
use rustc_ast::util::classify;
|
use rustc_ast::util::classify::{self, TrailingBrace};
|
||||||
use rustc_ast::{AttrStyle, AttrVec, LocalKind, MacCall, MacCallStmt, MacStmtStyle};
|
use rustc_ast::{AttrStyle, AttrVec, LocalKind, MacCall, MacCallStmt, MacStmtStyle};
|
||||||
use rustc_ast::{Block, BlockCheckMode, Expr, ExprKind, HasAttrs, Local, Recovered, Stmt};
|
use rustc_ast::{Block, BlockCheckMode, Expr, ExprKind, HasAttrs, Local, Recovered, Stmt};
|
||||||
use rustc_ast::{StmtKind, DUMMY_NODE_ID};
|
use rustc_ast::{StmtKind, DUMMY_NODE_ID};
|
||||||
|
@ -407,18 +407,24 @@ impl<'a> Parser<'a> {
|
||||||
|
|
||||||
fn check_let_else_init_trailing_brace(&self, init: &ast::Expr) {
|
fn check_let_else_init_trailing_brace(&self, init: &ast::Expr) {
|
||||||
if let Some(trailing) = classify::expr_trailing_brace(init) {
|
if let Some(trailing) = classify::expr_trailing_brace(init) {
|
||||||
let sugg = match &trailing.kind {
|
let (span, sugg) = match trailing {
|
||||||
ExprKind::MacCall(mac) => errors::WrapInParentheses::MacroArgs {
|
TrailingBrace::MacCall(mac) => (
|
||||||
left: mac.args.dspan.open,
|
mac.span(),
|
||||||
right: mac.args.dspan.close,
|
errors::WrapInParentheses::MacroArgs {
|
||||||
},
|
left: mac.args.dspan.open,
|
||||||
_ => errors::WrapInParentheses::Expression {
|
right: mac.args.dspan.close,
|
||||||
left: trailing.span.shrink_to_lo(),
|
},
|
||||||
right: trailing.span.shrink_to_hi(),
|
),
|
||||||
},
|
TrailingBrace::Expr(expr) => (
|
||||||
|
expr.span,
|
||||||
|
errors::WrapInParentheses::Expression {
|
||||||
|
left: expr.span.shrink_to_lo(),
|
||||||
|
right: expr.span.shrink_to_hi(),
|
||||||
|
},
|
||||||
|
),
|
||||||
};
|
};
|
||||||
self.dcx().emit_err(errors::InvalidCurlyInLetElse {
|
self.dcx().emit_err(errors::InvalidCurlyInLetElse {
|
||||||
span: trailing.span.with_lo(trailing.span.hi() - BytePos(1)),
|
span: span.with_lo(span.hi() - BytePos(1)),
|
||||||
sugg,
|
sugg,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -205,7 +205,7 @@ fn t() {
|
||||||
//~^ WARN irrefutable `let...else` pattern
|
//~^ WARN irrefutable `let...else` pattern
|
||||||
8
|
8
|
||||||
} else {
|
} else {
|
||||||
// FIXME: right curly brace `}` before `else` in a `let...else` statement not allowed
|
//~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -241,6 +241,20 @@ help: use parentheses instead of braces for this macro
|
||||||
LL | let bad = format_args! ("") else { return; };
|
LL | let bad = format_args! ("") else { return; };
|
||||||
| ~ ~
|
| ~ ~
|
||||||
|
|
||||||
|
error: right curly brace `}` before `else` in a `let...else` statement not allowed
|
||||||
|
--> $DIR/bad-let-else-statement.rs:207:5
|
||||||
|
|
|
||||||
|
LL | } else {
|
||||||
|
| ^
|
||||||
|
|
|
||||||
|
help: use parentheses instead of braces for this macro
|
||||||
|
|
|
||||||
|
LL ~ let foo = &std::ptr::null as &'static dyn std::ops::Fn() -> *const primitive! (
|
||||||
|
LL |
|
||||||
|
LL | 8
|
||||||
|
LL ~ ) else {
|
||||||
|
|
|
||||||
|
|
||||||
error: right curly brace `}` before `else` in a `let...else` statement not allowed
|
error: right curly brace `}` before `else` in a `let...else` statement not allowed
|
||||||
--> $DIR/bad-let-else-statement.rs:190:25
|
--> $DIR/bad-let-else-statement.rs:190:25
|
||||||
|
|
|
|
||||||
|
@ -311,5 +325,5 @@ LL | | } else {
|
||||||
= note: this pattern will always match, so the `else` clause is useless
|
= note: this pattern will always match, so the `else` clause is useless
|
||||||
= help: consider removing the `else` clause
|
= help: consider removing the `else` clause
|
||||||
|
|
||||||
error: aborting due to 19 previous errors; 5 warnings emitted
|
error: aborting due to 20 previous errors; 5 warnings emitted
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue