about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_ast/src/util/classify.rs95
-rw-r--r--compiler/rustc_parse/src/parser/stmt.rs28
2 files changed, 108 insertions, 15 deletions
diff --git a/compiler/rustc_ast/src/util/classify.rs b/compiler/rustc_ast/src/util/classify.rs
index f6e9e1a87c4..382c903625f 100644
--- a/compiler/rustc_ast/src/util/classify.rs
+++ b/compiler/rustc_ast/src/util/classify.rs
@@ -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 `}`
-pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<&ast::Expr> {
+pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<TrailingBrace<'_>> {
     loop {
         match &expr.kind {
             AddrOf(_, _, e)
@@ -111,10 +120,14 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<&ast::Expr> {
             | Struct(..)
             | TryBlock(..)
             | 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) => {
-                break (mac.args.delim == Delimiter::Brace).then_some(expr);
+                break (mac.args.delim == Delimiter::Brace).then_some(TrailingBrace::MacCall(mac));
             }
 
             InlineAsm(_) | OffsetOf(_, _) | IncludedBytes(_) | FormatArgs(_) => {
@@ -131,7 +144,6 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<&ast::Expr> {
             | MethodCall(_)
             | Tup(_)
             | Lit(_)
-            | Cast(_, _)
             | Type(_, _)
             | Await(_, _)
             | 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,
+    }
+}
diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs
index d70afebf1b2..941b145e2db 100644
--- a/compiler/rustc_parse/src/parser/stmt.rs
+++ b/compiler/rustc_parse/src/parser/stmt.rs
@@ -15,7 +15,7 @@ use ast::Label;
 use rustc_ast as ast;
 use rustc_ast::ptr::P;
 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::{Block, BlockCheckMode, Expr, ExprKind, HasAttrs, Local, Recovered, Stmt};
 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) {
         if let Some(trailing) = classify::expr_trailing_brace(init) {
-            let sugg = match &trailing.kind {
-                ExprKind::MacCall(mac) => errors::WrapInParentheses::MacroArgs {
-                    left: mac.args.dspan.open,
-                    right: mac.args.dspan.close,
-                },
-                _ => errors::WrapInParentheses::Expression {
-                    left: trailing.span.shrink_to_lo(),
-                    right: trailing.span.shrink_to_hi(),
-                },
+            let (span, sugg) = match trailing {
+                TrailingBrace::MacCall(mac) => (
+                    mac.span(),
+                    errors::WrapInParentheses::MacroArgs {
+                        left: mac.args.dspan.open,
+                        right: mac.args.dspan.close,
+                    },
+                ),
+                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 {
-                span: trailing.span.with_lo(trailing.span.hi() - BytePos(1)),
+                span: span.with_lo(span.hi() - BytePos(1)),
                 sugg,
             });
         }