From 9a514b92e61926a331fc189d8f987fb9a00994e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 22 Sep 2019 21:58:40 -0700 Subject: [PATCH 1/3] Suggest enclosing const expression in block When encountering `foo::<1 + 1>()`, suggest `foo::<{ 1 + 1 }>()`. Fix #61175. --- src/libsyntax/ast.rs | 17 ++++++ src/libsyntax/parse/parser.rs | 5 +- src/libsyntax/parse/parser/expr.rs | 17 +++++- src/libsyntax/parse/parser/path.rs | 44 +++++++++++++- .../const-expression-parameter.rs | 19 +++++- .../const-expression-parameter.stderr | 59 +++++++++++++++++-- .../disallowed-positions.rs | 5 +- .../disallowed-positions.stderr | 28 +++++++-- 8 files changed, 177 insertions(+), 17 deletions(-) diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 7a5c92167bc45..4a9032d75c917 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -1253,6 +1253,23 @@ pub enum ExprKind { Err, } +impl ExprKind { + /// Whether this expression can appear in a contst argument without being surrounded by braces. + /// + /// Only used in error recovery. + pub(crate) fn is_valid_const_on_its_own(&self) -> bool { + match self { + ExprKind::Tup(_) | + ExprKind::Lit(_) | + ExprKind::Type(..) | + ExprKind::Path(..) | + ExprKind::Unary(..) | + ExprKind::Err => true, + _ => false, + } + } +} + /// The explicit `Self` type in a "qualified path". The actual /// path, including the trait and the associated item, is stored /// separately. `position` represents the index of the associated diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 2fb6f197dad7c..a9bb532032e33 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -38,8 +38,9 @@ use std::path::PathBuf; bitflags::bitflags! { struct Restrictions: u8 { - const STMT_EXPR = 1 << 0; - const NO_STRUCT_LITERAL = 1 << 1; + const STMT_EXPR = 1 << 0; + const NO_STRUCT_LITERAL = 1 << 1; + const CONST_EXPR_RECOVERY = 1 << 2; } } diff --git a/src/libsyntax/parse/parser/expr.rs b/src/libsyntax/parse/parser/expr.rs index 23674ad589dc5..9d78c771be232 100644 --- a/src/libsyntax/parse/parser/expr.rs +++ b/src/libsyntax/parse/parser/expr.rs @@ -186,6 +186,12 @@ impl<'a> Parser<'a> { self.last_type_ascription = None; return Ok(lhs); } + (true, Some(AssocOp::Greater)) if self.restrictions.contains( + Restrictions::CONST_EXPR_RECOVERY, + ) => { // Recovering likely const argument without braces. + self.last_type_ascription = None; + return Ok(lhs); + } (true, Some(_)) => { // We've found an expression that would be parsed as a statement, but the next // token implies this should be parsed as an expression. @@ -205,6 +211,11 @@ impl<'a> Parser<'a> { } self.expected_tokens.push(TokenType::Operator); while let Some(op) = AssocOp::from_token(&self.token) { + if let (AssocOp::Greater, true) = (&op, self.restrictions.contains( + Restrictions::CONST_EXPR_RECOVERY, + )) { // Recovering likely const argument without braces. + return Ok(lhs); + } // Adjust the span for interpolated LHS to point to the `$lhs` token and not to what // it refers to. Interpolated identifiers are unwrapped early and never show up here @@ -341,8 +352,10 @@ impl<'a> Parser<'a> { /// Checks if this expression is a successfully parsed statement. fn expr_is_complete(&self, e: &Expr) -> bool { - self.restrictions.contains(Restrictions::STMT_EXPR) && - !classify::expr_requires_semi_to_be_stmt(e) + (self.restrictions.contains(Restrictions::STMT_EXPR) && + !classify::expr_requires_semi_to_be_stmt(e)) || + (self.restrictions.contains(Restrictions::CONST_EXPR_RECOVERY) && + (self.token == token::Lt || self.token == token::Gt || self.token == token::Comma)) } fn is_at_start_of_range_notation_rhs(&self) -> bool { diff --git a/src/libsyntax/parse/parser/path.rs b/src/libsyntax/parse/parser/path.rs index 463ae9124ca23..fcb53afc0f43e 100644 --- a/src/libsyntax/parse/parser/path.rs +++ b/src/libsyntax/parse/parser/path.rs @@ -1,4 +1,4 @@ -use super::{Parser, PResult, TokenType}; +use super::{Parser, PResult, Restrictions, TokenType}; use crate::{maybe_whole, ThinVec}; use crate::ast::{self, QSelf, Path, PathSegment, Ident, ParenthesizedArgs, AngleBracketedArgs}; @@ -415,10 +415,52 @@ impl<'a> Parser<'a> { assoc_ty_constraints.push(span); } else if self.check_const_arg() { // Parse const argument. + let invalid = self.look_ahead(1, |t| t != &token::Lt && t != &token::Comma); let expr = if let token::OpenDelim(token::Brace) = self.token.kind { self.parse_block_expr( None, self.token.span, BlockCheckMode::Default, ThinVec::new() )? + } else if invalid { + // This can't possibly be a valid const arg, it is likely missing braces. + let snapshot = self.clone(); + match self.parse_expr_res(Restrictions::CONST_EXPR_RECOVERY, None) { + Ok(expr) => { + if self.token == token::Comma || self.token == token::Gt { + // We parsed the whole const argument successfully without braces. + if !expr.node.is_valid_const_on_its_own() { + // But it wasn't a literal, so we emit a custom error and + // suggest the appropriate code. + let msg = + "complex const arguments must be surrounded by braces"; + let appl = Applicability::MachineApplicable; + self.span_fatal(expr.span, msg) + .multipart_suggestion( + "surround this const argument in braces", + vec![ + (expr.span.shrink_to_lo(), "{ ".to_string()), + (expr.span.shrink_to_hi(), " }".to_string()), + ], + appl, + ) + .emit(); + } + expr + } else { + // We parsed *some* expression, but it isn't the whole argument + // so we can't ensure it was a const argument with missing braces. + // Roll-back and emit a regular parser error. + mem::replace(self, snapshot); + self.parse_literal_maybe_minus()? + } + } + Err(mut err) => { + // We couldn't parse an expression successfully. + // Roll-back, hide the error and emit a regular parser error. + err.cancel(); + mem::replace(self, snapshot); + self.parse_literal_maybe_minus()? + } + } } else if self.token.is_ident() { // FIXME(const_generics): to distinguish between idents for types and consts, // we should introduce a GenericArg::Ident in the AST and distinguish when diff --git a/src/test/ui/const-generics/const-expression-parameter.rs b/src/test/ui/const-generics/const-expression-parameter.rs index 22c6c35162281..80ee4a0808543 100644 --- a/src/test/ui/const-generics/const-expression-parameter.rs +++ b/src/test/ui/const-generics/const-expression-parameter.rs @@ -10,13 +10,30 @@ fn foo_a() { } fn foo_b() { - i32_identity::<1 + 2>(); //~ ERROR expected one of `,` or `>`, found `+` + i32_identity::<1 + 2>(); //~ ERROR complex const arguments must be surrounded by braces } fn foo_c() { i32_identity::< -1 >(); // ok } +fn foo_d() { + i32_identity::<1 + 2, 3 + 4>(); + //~^ ERROR complex const arguments must be surrounded by braces + //~| ERROR complex const arguments must be surrounded by braces + //~| ERROR wrong number of const arguments: expected 1, found 2 +} + +fn baz() -> i32 { + 42 +} + +fn foo_e() { + baz::<1 + 2, 3 + 4>(); + //~^ ERROR complex const arguments must be surrounded by braces + //~| ERROR complex const arguments must be surrounded by braces +} + fn main() { i32_identity::<5>(); // ok } diff --git a/src/test/ui/const-generics/const-expression-parameter.stderr b/src/test/ui/const-generics/const-expression-parameter.stderr index 7311e27c289f7..856176aa5d894 100644 --- a/src/test/ui/const-generics/const-expression-parameter.stderr +++ b/src/test/ui/const-generics/const-expression-parameter.stderr @@ -1,8 +1,52 @@ -error: expected one of `,` or `>`, found `+` - --> $DIR/const-expression-parameter.rs:13:22 +error: complex const arguments must be surrounded by braces + --> $DIR/const-expression-parameter.rs:13:20 | LL | i32_identity::<1 + 2>(); - | ^ expected one of `,` or `>` here + | ^^^^^ +help: surround this const argument in braces + | +LL | i32_identity::<{ 1 + 2 }>(); + | ^ ^ + +error: complex const arguments must be surrounded by braces + --> $DIR/const-expression-parameter.rs:21:20 + | +LL | i32_identity::<1 + 2, 3 + 4>(); + | ^^^^^ +help: surround this const argument in braces + | +LL | i32_identity::<{ 1 + 2 }, 3 + 4>(); + | ^ ^ + +error: complex const arguments must be surrounded by braces + --> $DIR/const-expression-parameter.rs:21:27 + | +LL | i32_identity::<1 + 2, 3 + 4>(); + | ^^^^^ +help: surround this const argument in braces + | +LL | i32_identity::<1 + 2, { 3 + 4 }>(); + | ^ ^ + +error: complex const arguments must be surrounded by braces + --> $DIR/const-expression-parameter.rs:32:11 + | +LL | baz::<1 + 2, 3 + 4>(); + | ^^^^^ +help: surround this const argument in braces + | +LL | baz::<{ 1 + 2 }, 3 + 4>(); + | ^ ^ + +error: complex const arguments must be surrounded by braces + --> $DIR/const-expression-parameter.rs:32:18 + | +LL | baz::<1 + 2, 3 + 4>(); + | ^^^^^ +help: surround this const argument in braces + | +LL | baz::<1 + 2, { 3 + 4 }>(); + | ^ ^ warning: the feature `const_generics` is incomplete and may cause the compiler to crash --> $DIR/const-expression-parameter.rs:1:12 @@ -12,5 +56,12 @@ LL | #![feature(const_generics)] | = note: `#[warn(incomplete_features)]` on by default -error: aborting due to previous error +error[E0107]: wrong number of const arguments: expected 1, found 2 + --> $DIR/const-expression-parameter.rs:21:27 + | +LL | i32_identity::<1 + 2, 3 + 4>(); + | ^^^^^ unexpected const argument + +error: aborting due to 6 previous errors +For more information about this error, try `rustc --explain E0107`. diff --git a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.rs b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.rs index 7d1e5c3d64df3..b074ea22e8c86 100644 --- a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.rs +++ b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.rs @@ -239,6 +239,7 @@ fn inside_const_generic_arguments() { // admit non-IDENT expressions in const generic arguments. if A::< - true && let 1 = 1 //~ ERROR expected one of `,` or `>`, found `&&` - >::O == 5 {} + true && let 1 = 1 //~ ERROR complex const arguments must be surrounded by braces + >::O == 5 {} //~ ERROR chained comparison operators require parentheses + //~^ ERROR expected one of `,`, `.`, `>`, `?`, or an operator, found `{` } diff --git a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr index 4edc00efc7e72..31709838c5e03 100644 --- a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr +++ b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr @@ -1,8 +1,26 @@ -error: expected one of `,` or `>`, found `&&` - --> $DIR/disallowed-positions.rs:242:14 +error: chained comparison operators require parentheses + --> $DIR/disallowed-positions.rs:243:5 | -LL | true && let 1 = 1 - | ^^ expected one of `,` or `>` here +LL | >::O == 5 {} + | ^^^^^^^^^ + +error: complex const arguments must be surrounded by braces + --> $DIR/disallowed-positions.rs:242:9 + | +LL | / true && let 1 = 1 +LL | | >::O == 5 {} + | |_____________^ +help: surround this const argument in braces + | +LL | { true && let 1 = 1 +LL | >::O == 5 } {} + | + +error: expected one of `,`, `.`, `>`, `?`, or an operator, found `{` + --> $DIR/disallowed-positions.rs:243:15 + | +LL | >::O == 5 {} + | ^ expected one of `,`, `.`, `>`, `?`, or an operator here error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:32:9 @@ -983,7 +1001,7 @@ error[E0019]: constant contains unimplemented expression type LL | true && let 1 = 1 | ^ -error: aborting due to 109 previous errors +error: aborting due to 111 previous errors Some errors have detailed explanations: E0019, E0277, E0308, E0600, E0614. For more information about an error, try `rustc --explain E0019`. From 279f0a7d1bca4b1fc2143481a040cefd30c59d23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 23 Sep 2019 12:46:52 -0700 Subject: [PATCH 2/3] review comments --- src/libsyntax/ast.rs | 23 ++-- src/libsyntax/parse/parser/expr.rs | 4 +- src/libsyntax/parse/parser/path.rs | 117 +++++++++++------- .../const-expression-parameter.rs | 45 +++---- .../const-expression-parameter.stderr | 99 +++++++++++---- .../disallowed-positions.rs | 5 +- .../disallowed-positions.stderr | 22 +--- 7 files changed, 200 insertions(+), 115 deletions(-) diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 4a9032d75c917..7ab0f59912ff9 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -1254,17 +1254,26 @@ pub enum ExprKind { } impl ExprKind { - /// Whether this expression can appear in a contst argument without being surrounded by braces. + /// Whether this expression can appear applied to a `const` parameter without being surrounded + /// by braces. /// /// Only used in error recovery. - pub(crate) fn is_valid_const_on_its_own(&self) -> bool { + crate fn is_valid_const_on_its_own(&self) -> bool { + fn is_const_lit(kind: &ExprKind) -> bool { + // These are the only literals that can be negated as a bare `const` argument. + match kind { + ExprKind::Lit(Lit { node: LitKind::Int(..), ..}) | + ExprKind::Lit(Lit { node: LitKind::Float(..), ..}) | + ExprKind::Lit(Lit { node: LitKind::FloatUnsuffixed(..), ..}) => true, + _ => false, + } + } + match self { - ExprKind::Tup(_) | - ExprKind::Lit(_) | - ExprKind::Type(..) | - ExprKind::Path(..) | - ExprKind::Unary(..) | + ExprKind::Lit(_) | // `foo::<42>()` ExprKind::Err => true, + // `foo::<-42>()` + ExprKind::Unary(UnOp::Neg, expr) if is_const_lit(&expr.node) => true, _ => false, } } diff --git a/src/libsyntax/parse/parser/expr.rs b/src/libsyntax/parse/parser/expr.rs index 9d78c771be232..ad1c46b636d3d 100644 --- a/src/libsyntax/parse/parser/expr.rs +++ b/src/libsyntax/parse/parser/expr.rs @@ -355,7 +355,9 @@ impl<'a> Parser<'a> { (self.restrictions.contains(Restrictions::STMT_EXPR) && !classify::expr_requires_semi_to_be_stmt(e)) || (self.restrictions.contains(Restrictions::CONST_EXPR_RECOVERY) && - (self.token == token::Lt || self.token == token::Gt || self.token == token::Comma)) + // `<` is necessary here to avoid cases like `foo::< 1 < 3 >()` where we'll fallback + // to a regular parse error without recovery or suggestions. + [token::Lt, token::Gt, token::Comma].contains(&self.token.kind)) } fn is_at_start_of_range_notation_rhs(&self) -> bool { diff --git a/src/libsyntax/parse/parser/path.rs b/src/libsyntax/parse/parser/path.rs index fcb53afc0f43e..7a165e9752124 100644 --- a/src/libsyntax/parse/parser/path.rs +++ b/src/libsyntax/parse/parser/path.rs @@ -1,8 +1,10 @@ -use super::{Parser, PResult, Restrictions, TokenType}; +use super::{P, Parser, PResult, Restrictions, TokenType}; use crate::{maybe_whole, ThinVec}; -use crate::ast::{self, QSelf, Path, PathSegment, Ident, ParenthesizedArgs, AngleBracketedArgs}; -use crate::ast::{AnonConst, GenericArg, AssocTyConstraint, AssocTyConstraintKind, BlockCheckMode}; +use crate::ast::{ + self, AngleBracketedArgs, AnonConst, AssocTyConstraint, AssocTyConstraintKind, BlockCheckMode, + Expr, GenericArg, Ident, ParenthesizedArgs, Path, PathSegment, QSelf, +}; use crate::parse::token::{self, Token}; use crate::source_map::{Span, BytePos}; use crate::symbol::kw; @@ -413,54 +415,37 @@ impl<'a> Parser<'a> { span, }); assoc_ty_constraints.push(span); + } else if [ + token::Not, + token::OpenDelim(token::Paren), + ].contains(&self.token.kind) && self.look_ahead(1, |t| t.is_lit() || t.is_bool_lit()) { + // Parse bad `const` argument. `!` is only allowed here to go through + // `recover_bare_const_expr` for better diagnostics when encountering + // `foo::()`. `(` is allowed for the case `foo::<(1, 2, 3)>()`. + + // This can't possibly be a valid const arg, it is likely missing braces. + let value = AnonConst { + id: ast::DUMMY_NODE_ID, + value: self.recover_bare_const_expr()?, + }; + args.push(GenericArg::Const(value)); + misplaced_assoc_ty_constraints.append(&mut assoc_ty_constraints); } else if self.check_const_arg() { - // Parse const argument. + // Parse `const` argument. + + // `const` arguments that don't require surrunding braces would have a length of + // one token, so anything that *isn't* surrounded by braces and is not + // immediately followed by `,` or `>` is not a valid `const` argument. let invalid = self.look_ahead(1, |t| t != &token::Lt && t != &token::Comma); + let expr = if let token::OpenDelim(token::Brace) = self.token.kind { + // Parse `const` argument surrounded by braces. self.parse_block_expr( None, self.token.span, BlockCheckMode::Default, ThinVec::new() )? } else if invalid { // This can't possibly be a valid const arg, it is likely missing braces. - let snapshot = self.clone(); - match self.parse_expr_res(Restrictions::CONST_EXPR_RECOVERY, None) { - Ok(expr) => { - if self.token == token::Comma || self.token == token::Gt { - // We parsed the whole const argument successfully without braces. - if !expr.node.is_valid_const_on_its_own() { - // But it wasn't a literal, so we emit a custom error and - // suggest the appropriate code. - let msg = - "complex const arguments must be surrounded by braces"; - let appl = Applicability::MachineApplicable; - self.span_fatal(expr.span, msg) - .multipart_suggestion( - "surround this const argument in braces", - vec![ - (expr.span.shrink_to_lo(), "{ ".to_string()), - (expr.span.shrink_to_hi(), " }".to_string()), - ], - appl, - ) - .emit(); - } - expr - } else { - // We parsed *some* expression, but it isn't the whole argument - // so we can't ensure it was a const argument with missing braces. - // Roll-back and emit a regular parser error. - mem::replace(self, snapshot); - self.parse_literal_maybe_minus()? - } - } - Err(mut err) => { - // We couldn't parse an expression successfully. - // Roll-back, hide the error and emit a regular parser error. - err.cancel(); - mem::replace(self, snapshot); - self.parse_literal_maybe_minus()? - } - } + self.recover_bare_const_expr()? } else if self.token.is_ident() { // FIXME(const_generics): to distinguish between idents for types and consts, // we should introduce a GenericArg::Ident in the AST and distinguish when @@ -513,4 +498,50 @@ impl<'a> Parser<'a> { Ok((args, constraints)) } + + fn recover_bare_const_expr(&mut self) -> PResult<'a, P> { + let snapshot = self.clone(); + debug!("recover_bare_const_expr {:?}", self.token); + match self.parse_expr_res(Restrictions::CONST_EXPR_RECOVERY, None) { + Ok(expr) => { + debug!("recover_bare_const_expr expr {:?} {:?}", expr, expr.node); + if let token::Comma | token::Gt = self.token.kind { + // We parsed the whole const argument successfully without braces. + debug!("recover_bare_const_expr ok"); + if !expr.node.is_valid_const_on_its_own() { + // But it wasn't a literal, so we emit a custom error and + // suggest the appropriate code. `foo::<-1>()` is valid but gets parsed + // here, so we need to gate the error only for invalid cases. + self.span_fatal( + expr.span, + "complex const arguments must be surrounded by braces", + ).multipart_suggestion( + "surround this const argument in braces", + vec![ + (expr.span.shrink_to_lo(), "{ ".to_string()), + (expr.span.shrink_to_hi(), " }".to_string()), + ], + Applicability::MachineApplicable, + ).emit(); + } + Ok(expr) + } else { + debug!("recover_bare_const_expr not"); + // We parsed *some* expression, but it isn't the whole argument + // so we can't ensure it was a const argument with missing braces. + // Roll-back and emit a regular parser error. + mem::replace(self, snapshot); + self.parse_literal_maybe_minus() + } + } + Err(mut err) => { + debug!("recover_bare_const_expr err"); + // We couldn't parse an expression successfully. + // Roll-back, hide the error and emit a regular parser error. + err.cancel(); + mem::replace(self, snapshot); + self.parse_literal_maybe_minus() + } + } + } } diff --git a/src/test/ui/const-generics/const-expression-parameter.rs b/src/test/ui/const-generics/const-expression-parameter.rs index 80ee4a0808543..105bcdc29d135 100644 --- a/src/test/ui/const-generics/const-expression-parameter.rs +++ b/src/test/ui/const-generics/const-expression-parameter.rs @@ -1,39 +1,40 @@ #![feature(const_generics)] //~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash -fn i32_identity() -> i32 { - 5 -} +fn foo() -> i32 { 5 } -fn foo_a() { - i32_identity::<-1>(); // ok -} +fn baz() { } -fn foo_b() { - i32_identity::<1 + 2>(); //~ ERROR complex const arguments must be surrounded by braces -} +fn bar() {} -fn foo_c() { - i32_identity::< -1 >(); // ok -} +fn bat() {} -fn foo_d() { - i32_identity::<1 + 2, 3 + 4>(); +fn main() { + foo::<-1>(); // ok + foo::<1 + 2>(); //~ ERROR complex const arguments must be surrounded by braces + foo::< -1 >(); // ok + foo::<1 + 2, 3 + 4>(); //~^ ERROR complex const arguments must be surrounded by braces //~| ERROR complex const arguments must be surrounded by braces //~| ERROR wrong number of const arguments: expected 1, found 2 -} + foo::<5>(); // ok -fn baz() -> i32 { - 42 -} - -fn foo_e() { + baz::<-1, -2>(); // ok baz::<1 + 2, 3 + 4>(); //~^ ERROR complex const arguments must be surrounded by braces //~| ERROR complex const arguments must be surrounded by braces + baz::< -1 , 2 >(); // ok + baz::< -1 , "2" >(); //~ ERROR mismatched types + + bat::<(1, 2, 3)>(); //~ ERROR complex const arguments must be surrounded by braces + bat::<(1, 2)>(); + //~^ ERROR complex const arguments must be surrounded by braces + //~| ERROR mismatched types + + bar::(); // ok + bar::(); //~ ERROR complex const arguments must be surrounded by braces } -fn main() { - i32_identity::<5>(); // ok +fn parse_err_1() { + bar::< 3 < 4 >(); //~ ERROR expected one of `,` or `>`, found `<` } diff --git a/src/test/ui/const-generics/const-expression-parameter.stderr b/src/test/ui/const-generics/const-expression-parameter.stderr index 856176aa5d894..e968f195212f0 100644 --- a/src/test/ui/const-generics/const-expression-parameter.stderr +++ b/src/test/ui/const-generics/const-expression-parameter.stderr @@ -1,35 +1,35 @@ error: complex const arguments must be surrounded by braces - --> $DIR/const-expression-parameter.rs:13:20 + --> $DIR/const-expression-parameter.rs:14:11 | -LL | i32_identity::<1 + 2>(); - | ^^^^^ +LL | foo::<1 + 2>(); + | ^^^^^ help: surround this const argument in braces | -LL | i32_identity::<{ 1 + 2 }>(); - | ^ ^ +LL | foo::<{ 1 + 2 }>(); + | ^ ^ error: complex const arguments must be surrounded by braces - --> $DIR/const-expression-parameter.rs:21:20 + --> $DIR/const-expression-parameter.rs:16:11 | -LL | i32_identity::<1 + 2, 3 + 4>(); - | ^^^^^ +LL | foo::<1 + 2, 3 + 4>(); + | ^^^^^ help: surround this const argument in braces | -LL | i32_identity::<{ 1 + 2 }, 3 + 4>(); - | ^ ^ +LL | foo::<{ 1 + 2 }, 3 + 4>(); + | ^ ^ error: complex const arguments must be surrounded by braces - --> $DIR/const-expression-parameter.rs:21:27 + --> $DIR/const-expression-parameter.rs:16:18 | -LL | i32_identity::<1 + 2, 3 + 4>(); - | ^^^^^ +LL | foo::<1 + 2, 3 + 4>(); + | ^^^^^ help: surround this const argument in braces | -LL | i32_identity::<1 + 2, { 3 + 4 }>(); - | ^ ^ +LL | foo::<1 + 2, { 3 + 4 }>(); + | ^ ^ error: complex const arguments must be surrounded by braces - --> $DIR/const-expression-parameter.rs:32:11 + --> $DIR/const-expression-parameter.rs:23:11 | LL | baz::<1 + 2, 3 + 4>(); | ^^^^^ @@ -39,7 +39,7 @@ LL | baz::<{ 1 + 2 }, 3 + 4>(); | ^ ^ error: complex const arguments must be surrounded by braces - --> $DIR/const-expression-parameter.rs:32:18 + --> $DIR/const-expression-parameter.rs:23:18 | LL | baz::<1 + 2, 3 + 4>(); | ^^^^^ @@ -48,6 +48,42 @@ help: surround this const argument in braces LL | baz::<1 + 2, { 3 + 4 }>(); | ^ ^ +error: complex const arguments must be surrounded by braces + --> $DIR/const-expression-parameter.rs:29:11 + | +LL | bat::<(1, 2, 3)>(); + | ^^^^^^^^^ +help: surround this const argument in braces + | +LL | bat::<{ (1, 2, 3) }>(); + | ^ ^ + +error: complex const arguments must be surrounded by braces + --> $DIR/const-expression-parameter.rs:30:11 + | +LL | bat::<(1, 2)>(); + | ^^^^^^ +help: surround this const argument in braces + | +LL | bat::<{ (1, 2) }>(); + | ^ ^ + +error: complex const arguments must be surrounded by braces + --> $DIR/const-expression-parameter.rs:35:11 + | +LL | bar::(); + | ^^^^^^ +help: surround this const argument in braces + | +LL | bar::<{ !false }>(); + | ^ ^ + +error: expected one of `,` or `>`, found `<` + --> $DIR/const-expression-parameter.rs:39:14 + | +LL | bar::< 3 < 4 >(); + | ^ expected one of `,` or `>` here + warning: the feature `const_generics` is incomplete and may cause the compiler to crash --> $DIR/const-expression-parameter.rs:1:12 | @@ -57,11 +93,30 @@ LL | #![feature(const_generics)] = note: `#[warn(incomplete_features)]` on by default error[E0107]: wrong number of const arguments: expected 1, found 2 - --> $DIR/const-expression-parameter.rs:21:27 + --> $DIR/const-expression-parameter.rs:16:18 + | +LL | foo::<1 + 2, 3 + 4>(); + | ^^^^^ unexpected const argument + +error[E0308]: mismatched types + --> $DIR/const-expression-parameter.rs:27:17 + | +LL | baz::< -1 , "2" >(); + | ^^^ expected i32, found reference + | + = note: expected type `i32` + found type `&'static str` + +error[E0308]: mismatched types + --> $DIR/const-expression-parameter.rs:30:11 + | +LL | bat::<(1, 2)>(); + | ^^^^^^ expected a tuple with 3 elements, found one with 2 elements | -LL | i32_identity::<1 + 2, 3 + 4>(); - | ^^^^^ unexpected const argument + = note: expected type `(i32, i32, i32)` + found type `(i32, i32)` -error: aborting due to 6 previous errors +error: aborting due to 12 previous errors -For more information about this error, try `rustc --explain E0107`. +Some errors have detailed explanations: E0107, E0308. +For more information about an error, try `rustc --explain E0107`. diff --git a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.rs b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.rs index b074ea22e8c86..0c5d4d4820ca3 100644 --- a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.rs +++ b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.rs @@ -239,7 +239,6 @@ fn inside_const_generic_arguments() { // admit non-IDENT expressions in const generic arguments. if A::< - true && let 1 = 1 //~ ERROR complex const arguments must be surrounded by braces - >::O == 5 {} //~ ERROR chained comparison operators require parentheses - //~^ ERROR expected one of `,`, `.`, `>`, `?`, or an operator, found `{` + true && let 1 = 1 //~ ERROR expected one of `,` or `>`, found `&&` + >::O == 5 {} //~ ERROR chained comparison operators require parentheses } diff --git a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr index 31709838c5e03..329a9a5487fc0 100644 --- a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr +++ b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr @@ -4,23 +4,11 @@ error: chained comparison operators require parentheses LL | >::O == 5 {} | ^^^^^^^^^ -error: complex const arguments must be surrounded by braces - --> $DIR/disallowed-positions.rs:242:9 +error: expected one of `,` or `>`, found `&&` + --> $DIR/disallowed-positions.rs:242:14 | -LL | / true && let 1 = 1 -LL | | >::O == 5 {} - | |_____________^ -help: surround this const argument in braces - | -LL | { true && let 1 = 1 -LL | >::O == 5 } {} - | - -error: expected one of `,`, `.`, `>`, `?`, or an operator, found `{` - --> $DIR/disallowed-positions.rs:243:15 - | -LL | >::O == 5 {} - | ^ expected one of `,`, `.`, `>`, `?`, or an operator here +LL | true && let 1 = 1 + | ^^ expected one of `,` or `>` here error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:32:9 @@ -1001,7 +989,7 @@ error[E0019]: constant contains unimplemented expression type LL | true && let 1 = 1 | ^ -error: aborting due to 111 previous errors +error: aborting due to 110 previous errors Some errors have detailed explanations: E0019, E0277, E0308, E0600, E0614. For more information about an error, try `rustc --explain E0019`. From 370f4f55259003639b378d52516807a998bfacc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 29 Sep 2019 16:25:11 -0700 Subject: [PATCH 3/3] Rework generic args parsing --- src/librustc_resolve/late.rs | 24 +-- src/libsyntax/ast.rs | 26 --- src/libsyntax/parse/parser.rs | 2 +- src/libsyntax/parse/parser/expr.rs | 39 ++-- src/libsyntax/parse/parser/path.rs | 180 +++++++++--------- src/libsyntax/parse/parser/ty.rs | 8 +- .../const-expression-parameter.rs | 52 +++-- .../const-expression-parameter.stderr | 152 +++++++++------ .../disallowed-positions.rs | 3 +- .../disallowed-positions.stderr | 8 +- 10 files changed, 273 insertions(+), 221 deletions(-) diff --git a/src/librustc_resolve/late.rs b/src/librustc_resolve/late.rs index 33a85c6c77026..217cd22d20c4c 100644 --- a/src/librustc_resolve/late.rs +++ b/src/librustc_resolve/late.rs @@ -1494,14 +1494,15 @@ impl<'a, 'b> LateResolutionVisitor<'a, '_> { ); } - fn smart_resolve_path_fragment(&mut self, - id: NodeId, - qself: Option<&QSelf>, - path: &[Segment], - span: Span, - source: PathSource<'_>, - crate_lint: CrateLint) - -> PartialRes { + fn smart_resolve_path_fragment( + &mut self, + id: NodeId, + qself: Option<&QSelf>, + path: &[Segment], + span: Span, + source: PathSource<'_>, + crate_lint: CrateLint, + ) -> PartialRes { let ns = source.namespace(); let is_expected = &|res| source.is_expected(res); @@ -1620,9 +1621,10 @@ impl<'a, 'b> LateResolutionVisitor<'a, '_> { match self.resolve_qpath(id, qself, path, ns, span, crate_lint) { // If defer_to_typeck, then resolution > no resolution, // otherwise full resolution > partial resolution > no resolution. - Some(partial_res) if partial_res.unresolved_segments() == 0 || - defer_to_typeck => - return Some(partial_res), + Some(partial_res) + if partial_res.unresolved_segments() == 0 || defer_to_typeck => { + return Some(partial_res) + } partial_res => if fin_res.is_none() { fin_res = partial_res }, } } diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 7ab0f59912ff9..7a5c92167bc45 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -1253,32 +1253,6 @@ pub enum ExprKind { Err, } -impl ExprKind { - /// Whether this expression can appear applied to a `const` parameter without being surrounded - /// by braces. - /// - /// Only used in error recovery. - crate fn is_valid_const_on_its_own(&self) -> bool { - fn is_const_lit(kind: &ExprKind) -> bool { - // These are the only literals that can be negated as a bare `const` argument. - match kind { - ExprKind::Lit(Lit { node: LitKind::Int(..), ..}) | - ExprKind::Lit(Lit { node: LitKind::Float(..), ..}) | - ExprKind::Lit(Lit { node: LitKind::FloatUnsuffixed(..), ..}) => true, - _ => false, - } - } - - match self { - ExprKind::Lit(_) | // `foo::<42>()` - ExprKind::Err => true, - // `foo::<-42>()` - ExprKind::Unary(UnOp::Neg, expr) if is_const_lit(&expr.node) => true, - _ => false, - } - } -} - /// The explicit `Self` type in a "qualified path". The actual /// path, including the trait and the associated item, is stored /// separately. `position` represents the index of the associated diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index a9bb532032e33..e831333cfae1b 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -40,7 +40,7 @@ bitflags::bitflags! { struct Restrictions: u8 { const STMT_EXPR = 1 << 0; const NO_STRUCT_LITERAL = 1 << 1; - const CONST_EXPR_RECOVERY = 1 << 2; + const CLOSING_ANGLE_BRACKET = 1 << 2; } } diff --git a/src/libsyntax/parse/parser/expr.rs b/src/libsyntax/parse/parser/expr.rs index ad1c46b636d3d..44503dda7b96a 100644 --- a/src/libsyntax/parse/parser/expr.rs +++ b/src/libsyntax/parse/parser/expr.rs @@ -186,9 +186,12 @@ impl<'a> Parser<'a> { self.last_type_ascription = None; return Ok(lhs); } - (true, Some(AssocOp::Greater)) if self.restrictions.contains( - Restrictions::CONST_EXPR_RECOVERY, - ) => { // Recovering likely const argument without braces. + (true, Some(AssocOp::Greater)) | + (true, Some(AssocOp::Less)) | + (true, Some(AssocOp::ShiftLeft)) | + (true, Some(AssocOp::ShiftRight)) + if self.restrictions.contains(Restrictions::CLOSING_ANGLE_BRACKET) => { + // Bare `const` argument expression. self.last_type_ascription = None; return Ok(lhs); } @@ -211,9 +214,13 @@ impl<'a> Parser<'a> { } self.expected_tokens.push(TokenType::Operator); while let Some(op) = AssocOp::from_token(&self.token) { - if let (AssocOp::Greater, true) = (&op, self.restrictions.contains( - Restrictions::CONST_EXPR_RECOVERY, - )) { // Recovering likely const argument without braces. + if let (true, AssocOp::Greater) | + (true, AssocOp::Less) | + (true, AssocOp::ShiftLeft) | + (true, AssocOp::ShiftRight) = ( + self.restrictions.contains(Restrictions::CLOSING_ANGLE_BRACKET), &op + ) { + // Bare `const` argument expression fully parsed. return Ok(lhs); } @@ -352,12 +359,20 @@ impl<'a> Parser<'a> { /// Checks if this expression is a successfully parsed statement. fn expr_is_complete(&self, e: &Expr) -> bool { - (self.restrictions.contains(Restrictions::STMT_EXPR) && - !classify::expr_requires_semi_to_be_stmt(e)) || - (self.restrictions.contains(Restrictions::CONST_EXPR_RECOVERY) && - // `<` is necessary here to avoid cases like `foo::< 1 < 3 >()` where we'll fallback - // to a regular parse error without recovery or suggestions. - [token::Lt, token::Gt, token::Comma].contains(&self.token.kind)) + ( + self.restrictions.contains(Restrictions::STMT_EXPR) && + !classify::expr_requires_semi_to_be_stmt(e) + ) || ( + self.restrictions.contains(Restrictions::CLOSING_ANGLE_BRACKET) && + // Comparison and bitshift operators are not allowed in bare const expressions. + [ + token::Lt, + token::Gt, + token::Comma, + token::BinOp(token::Shl), + token::BinOp(token::Shr), + ].contains(&self.token.kind) + ) } fn is_at_start_of_range_notation_rhs(&self) -> bool { diff --git a/src/libsyntax/parse/parser/path.rs b/src/libsyntax/parse/parser/path.rs index 7a165e9752124..018e4d11db373 100644 --- a/src/libsyntax/parse/parser/path.rs +++ b/src/libsyntax/parse/parser/path.rs @@ -1,9 +1,9 @@ -use super::{P, Parser, PResult, Restrictions, TokenType}; +use super::{Parser, PResult, Restrictions, TokenType}; use crate::{maybe_whole, ThinVec}; use crate::ast::{ self, AngleBracketedArgs, AnonConst, AssocTyConstraint, AssocTyConstraintKind, BlockCheckMode, - Expr, GenericArg, Ident, ParenthesizedArgs, Path, PathSegment, QSelf, + GenericArg, Ident, ParenthesizedArgs, Path, PathSegment, QSelf, }; use crate::parse::token::{self, Token}; use crate::source_map::{Span, BytePos}; @@ -323,7 +323,7 @@ impl<'a> Parser<'a> { }; debug!("parse_generic_args_with_leading_angle_bracket_recovery: (snapshotting)"); - match self.parse_generic_args() { + match self.parse_generic_args(style) { Ok(value) => Ok(value), Err(ref mut e) if is_first_invocation && self.unmatched_angle_bracket_count > 0 => { // Cancel error from being unable to find `>`. We know the error @@ -370,7 +370,7 @@ impl<'a> Parser<'a> { .emit(); // Try again without unmatched angle bracket characters. - self.parse_generic_args() + self.parse_generic_args(style) }, Err(e) => Err(e), } @@ -378,7 +378,10 @@ impl<'a> Parser<'a> { /// Parses (possibly empty) list of lifetime and type arguments and associated type bindings, /// possibly including trailing comma. - fn parse_generic_args(&mut self) -> PResult<'a, (Vec, Vec)> { + fn parse_generic_args( + &mut self, + style: PathStyle, + ) -> PResult<'a, (Vec, Vec)> { let mut args = Vec::new(); let mut constraints = Vec::new(); let mut misplaced_assoc_ty_constraints: Vec = Vec::new(); @@ -415,59 +418,13 @@ impl<'a> Parser<'a> { span, }); assoc_ty_constraints.push(span); - } else if [ - token::Not, - token::OpenDelim(token::Paren), - ].contains(&self.token.kind) && self.look_ahead(1, |t| t.is_lit() || t.is_bool_lit()) { - // Parse bad `const` argument. `!` is only allowed here to go through - // `recover_bare_const_expr` for better diagnostics when encountering - // `foo::()`. `(` is allowed for the case `foo::<(1, 2, 3)>()`. - - // This can't possibly be a valid const arg, it is likely missing braces. - let value = AnonConst { - id: ast::DUMMY_NODE_ID, - value: self.recover_bare_const_expr()?, - }; - args.push(GenericArg::Const(value)); + } else if self.check_possible_const_needing_braces(style) { + args.push(self.parse_const_or_type_arg()?); misplaced_assoc_ty_constraints.append(&mut assoc_ty_constraints); } else if self.check_const_arg() { - // Parse `const` argument. - - // `const` arguments that don't require surrunding braces would have a length of - // one token, so anything that *isn't* surrounded by braces and is not - // immediately followed by `,` or `>` is not a valid `const` argument. - let invalid = self.look_ahead(1, |t| t != &token::Lt && t != &token::Comma); - - let expr = if let token::OpenDelim(token::Brace) = self.token.kind { - // Parse `const` argument surrounded by braces. - self.parse_block_expr( - None, self.token.span, BlockCheckMode::Default, ThinVec::new() - )? - } else if invalid { - // This can't possibly be a valid const arg, it is likely missing braces. - self.recover_bare_const_expr()? - } else if self.token.is_ident() { - // FIXME(const_generics): to distinguish between idents for types and consts, - // we should introduce a GenericArg::Ident in the AST and distinguish when - // lowering to the HIR. For now, idents for const args are not permitted. - if self.token.is_bool_lit() { - self.parse_literal_maybe_minus()? - } else { - return Err( - self.fatal("identifiers may currently not be used for const generics") - ); - } - } else { - self.parse_literal_maybe_minus()? - }; - let value = AnonConst { - id: ast::DUMMY_NODE_ID, - value: expr, - }; - args.push(GenericArg::Const(value)); + args.push(self.parse_const_arg()?); misplaced_assoc_ty_constraints.append(&mut assoc_ty_constraints); } else if self.check_type() { - // Parse type argument. args.push(GenericArg::Type(self.parse_ty()?)); misplaced_assoc_ty_constraints.append(&mut assoc_ty_constraints); } else { @@ -499,49 +456,86 @@ impl<'a> Parser<'a> { Ok((args, constraints)) } - fn recover_bare_const_expr(&mut self) -> PResult<'a, P> { - let snapshot = self.clone(); - debug!("recover_bare_const_expr {:?}", self.token); - match self.parse_expr_res(Restrictions::CONST_EXPR_RECOVERY, None) { - Ok(expr) => { - debug!("recover_bare_const_expr expr {:?} {:?}", expr, expr.node); - if let token::Comma | token::Gt = self.token.kind { - // We parsed the whole const argument successfully without braces. - debug!("recover_bare_const_expr ok"); - if !expr.node.is_valid_const_on_its_own() { - // But it wasn't a literal, so we emit a custom error and - // suggest the appropriate code. `foo::<-1>()` is valid but gets parsed - // here, so we need to gate the error only for invalid cases. - self.span_fatal( - expr.span, - "complex const arguments must be surrounded by braces", - ).multipart_suggestion( - "surround this const argument in braces", - vec![ - (expr.span.shrink_to_lo(), "{ ".to_string()), - (expr.span.shrink_to_hi(), " }".to_string()), - ], - Applicability::MachineApplicable, - ).emit(); + fn parse_const_arg(&mut self) -> PResult<'a, GenericArg> { + let value = if let token::OpenDelim(token::Brace) = self.token.kind { + // Parse `const` argument surrounded by braces. + self.parse_block_expr(None, self.token.span, BlockCheckMode::Default, ThinVec::new())? + } else { + self.parse_expr_res(Restrictions::CLOSING_ANGLE_BRACKET, None)? + }; + let value = AnonConst { id: ast::DUMMY_NODE_ID, value }; + Ok(GenericArg::Const(value)) + } + + /// Check some ambiguous cases between type and non-block const arguments. + fn check_possible_const_needing_braces(&mut self, style: PathStyle) -> bool { + style == PathStyle::Expr && ( + self.token.kind == token::Not || // `foo::()` + self.token.kind == token::OpenDelim(token::Paren) // `foo::<(1, 2, 3)>()` + ) + } + + /// There's intent ambiguity for this argument, it could be a const expression missing braces, + /// or it could be a type argument. We try the later as the grammar expects, and if it fails we + /// attempt the former and emit a targetted suggestion if valid. + fn parse_const_or_type_arg(&mut self) -> PResult<'a, GenericArg> { + let mut snapshot = self.clone(); + match self.parse_ty() { + // Nothing to do, this was indeed a type argument. + Ok(ty) if [ + token::Comma, + token::Gt, + token::BinOp(token::Shr), + ].contains(&self.token.kind) => Ok(GenericArg::Type(ty)), + Ok(ty) => { // Could have found `foo::()`, needs `foo::<{ !false }>()`. + mem::swap(self, &mut snapshot); + match self.parse_bad_const_arg() { + Ok(arg) => Ok(arg), + Err(mut err) => { + mem::swap(self, &mut snapshot); + err.cancel(); + Ok(GenericArg::Type(ty)) + } + } + } + Err(mut ty_err) => { + mem::swap(self, &mut snapshot); + match self.parse_bad_const_arg() { + Ok(arg) => { + ty_err.cancel(); + Ok(arg) + } + Err(mut err) => { + mem::swap(self, &mut snapshot); + err.cancel(); + Err(ty_err) } - Ok(expr) - } else { - debug!("recover_bare_const_expr not"); - // We parsed *some* expression, but it isn't the whole argument - // so we can't ensure it was a const argument with missing braces. - // Roll-back and emit a regular parser error. - mem::replace(self, snapshot); - self.parse_literal_maybe_minus() } } - Err(mut err) => { - debug!("recover_bare_const_expr err"); - // We couldn't parse an expression successfully. - // Roll-back, hide the error and emit a regular parser error. - err.cancel(); - mem::replace(self, snapshot); - self.parse_literal_maybe_minus() + } + } + + fn parse_bad_const_arg(&mut self) -> PResult<'a, GenericArg> { + let msg = if self.token.kind == token::OpenDelim(token::Paren) { + // `foo::<(1, 2, 3)>()` + "tuples in const arguments must be surrounded by braces" + } else { + "complex const arguments must be surrounded by braces" + }; + match self.parse_const_arg() { + Ok(arg) => { + self.span_fatal(arg.span(), msg) + .multipart_suggestion( + "surround this const argument in braces", + vec![ + (arg.span().shrink_to_lo(), "{ ".to_string()), + (arg.span().shrink_to_hi(), " }".to_string()), + ], + Applicability::MachineApplicable, + ).emit(); + Ok(arg) } + Err(err) => Err(err), } } } diff --git a/src/libsyntax/parse/parser/ty.rs b/src/libsyntax/parse/parser/ty.rs index c52d3733b5e0a..137763091891d 100644 --- a/src/libsyntax/parse/parser/ty.rs +++ b/src/libsyntax/parse/parser/ty.rs @@ -48,8 +48,12 @@ impl<'a> Parser<'a> { } } - pub(super) fn parse_ty_common(&mut self, allow_plus: bool, allow_qpath_recovery: bool, - allow_c_variadic: bool) -> PResult<'a, P> { + pub(super) fn parse_ty_common( + &mut self, + allow_plus: bool, + allow_qpath_recovery: bool, + allow_c_variadic: bool, + ) -> PResult<'a, P> { maybe_recover_from_interpolated_ty_qpath!(self, allow_qpath_recovery); maybe_whole!(self, NtTy, |x| x); diff --git a/src/test/ui/const-generics/const-expression-parameter.rs b/src/test/ui/const-generics/const-expression-parameter.rs index 105bcdc29d135..843db56c076e1 100644 --- a/src/test/ui/const-generics/const-expression-parameter.rs +++ b/src/test/ui/const-generics/const-expression-parameter.rs @@ -1,6 +1,15 @@ #![feature(const_generics)] //~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +const fn const_i32() -> i32 { + 42 +} + +const fn const_bool() -> bool { + true +} +const X: i32 = 1; + fn foo() -> i32 { 5 } fn baz() { } @@ -11,30 +20,51 @@ fn bat() {} fn main() { foo::<-1>(); // ok - foo::<1 + 2>(); //~ ERROR complex const arguments must be surrounded by braces + foo::<1 + 2>(); // ok foo::< -1 >(); // ok - foo::<1 + 2, 3 + 4>(); - //~^ ERROR complex const arguments must be surrounded by braces - //~| ERROR complex const arguments must be surrounded by braces - //~| ERROR wrong number of const arguments: expected 1, found 2 + foo::<1 + 2, 3 + 4>(); //~ ERROR wrong number of const arguments: expected 1, found 2 foo::<5>(); // ok + foo::< const_i32() >(); //~ ERROR expected type, found function `const_i32` + //~^ ERROR wrong number of const arguments: expected 1, found 0 + //~| ERROR wrong number of type arguments: expected 0, found 1 + foo::< X >(); //~ ERROR expected type, found constant `X` + //~^ ERROR wrong number of const arguments: expected 1, found 0 + //~| ERROR wrong number of type arguments: expected 0, found 1 + foo::<{ X }>(); // ok + foo::< 42 + X >(); // ok + foo::<{ const_i32() }>(); // ok baz::<-1, -2>(); // ok - baz::<1 + 2, 3 + 4>(); - //~^ ERROR complex const arguments must be surrounded by braces - //~| ERROR complex const arguments must be surrounded by braces + baz::<1 + 2, 3 + 4>(); // ok baz::< -1 , 2 >(); // ok baz::< -1 , "2" >(); //~ ERROR mismatched types - bat::<(1, 2, 3)>(); //~ ERROR complex const arguments must be surrounded by braces + bat::<(1, 2, 3)>(); //~ ERROR tuples in const arguments must be surrounded by braces bat::<(1, 2)>(); - //~^ ERROR complex const arguments must be surrounded by braces + //~^ ERROR tuples in const arguments must be surrounded by braces //~| ERROR mismatched types bar::(); // ok bar::(); //~ ERROR complex const arguments must be surrounded by braces + bar::<{ const_bool() }>(); // ok + bar::< const_bool() >(); //~ ERROR expected type, found function `const_bool` + //~^ ERROR wrong number of const arguments: expected 1, found 0 + //~| ERROR wrong number of type arguments: expected 0, found 1 + bar::<{ !const_bool() }>(); // ok + bar::< !const_bool() >(); //~ ERROR complex const arguments must be surrounded by braces + + foo::()>(); //~ ERROR expected one of `!`, `+`, `,`, `::`, or `>`, found `(` } fn parse_err_1() { - bar::< 3 < 4 >(); //~ ERROR expected one of `,` or `>`, found `<` + bar::< 3 < 4 >(); //~ ERROR expected one of `,`, `.`, `>`, or `?`, found `<` +} + +fn parse_err_2() { + foo::< const_i32() + 42 >(); + //~^ ERROR expected one of `!`, `(`, `,`, `>`, `?`, `for`, lifetime, or path, found `42` +} +fn parse_err_3() { + foo::< X + 42 >(); + //~^ ERROR expected one of `!`, `(`, `,`, `>`, `?`, `for`, lifetime, or path, found `42` } diff --git a/src/test/ui/const-generics/const-expression-parameter.stderr b/src/test/ui/const-generics/const-expression-parameter.stderr index e968f195212f0..abc52fcdf1ea6 100644 --- a/src/test/ui/const-generics/const-expression-parameter.stderr +++ b/src/test/ui/const-generics/const-expression-parameter.stderr @@ -1,88 +1,84 @@ -error: complex const arguments must be surrounded by braces - --> $DIR/const-expression-parameter.rs:14:11 +error: tuples in const arguments must be surrounded by braces + --> $DIR/const-expression-parameter.rs:42:11 | -LL | foo::<1 + 2>(); - | ^^^^^ +LL | bat::<(1, 2, 3)>(); + | ^^^^^^^^^ help: surround this const argument in braces | -LL | foo::<{ 1 + 2 }>(); - | ^ ^ +LL | bat::<{ (1, 2, 3) }>(); + | ^ ^ -error: complex const arguments must be surrounded by braces - --> $DIR/const-expression-parameter.rs:16:11 +error: tuples in const arguments must be surrounded by braces + --> $DIR/const-expression-parameter.rs:43:11 | -LL | foo::<1 + 2, 3 + 4>(); - | ^^^^^ +LL | bat::<(1, 2)>(); + | ^^^^^^ help: surround this const argument in braces | -LL | foo::<{ 1 + 2 }, 3 + 4>(); - | ^ ^ +LL | bat::<{ (1, 2) }>(); + | ^ ^ error: complex const arguments must be surrounded by braces - --> $DIR/const-expression-parameter.rs:16:18 + --> $DIR/const-expression-parameter.rs:48:11 | -LL | foo::<1 + 2, 3 + 4>(); - | ^^^^^ +LL | bar::(); + | ^^^^^^ help: surround this const argument in braces | -LL | foo::<1 + 2, { 3 + 4 }>(); - | ^ ^ +LL | bar::<{ !false }>(); + | ^ ^ error: complex const arguments must be surrounded by braces - --> $DIR/const-expression-parameter.rs:23:11 + --> $DIR/const-expression-parameter.rs:54:12 | -LL | baz::<1 + 2, 3 + 4>(); - | ^^^^^ +LL | bar::< !const_bool() >(); + | ^^^^^^^^^^^^^ help: surround this const argument in braces | -LL | baz::<{ 1 + 2 }, 3 + 4>(); - | ^ ^ +LL | bar::< { !const_bool() } >(); + | ^ ^ -error: complex const arguments must be surrounded by braces - --> $DIR/const-expression-parameter.rs:23:18 +error: expected one of `!`, `+`, `,`, `::`, or `>`, found `(` + --> $DIR/const-expression-parameter.rs:56:20 | -LL | baz::<1 + 2, 3 + 4>(); - | ^^^^^ -help: surround this const argument in braces - | -LL | baz::<1 + 2, { 3 + 4 }>(); - | ^ ^ +LL | foo::()>(); + | ^ expected one of `!`, `+`, `,`, `::`, or `>` here -error: complex const arguments must be surrounded by braces - --> $DIR/const-expression-parameter.rs:29:11 +error: expected one of `,`, `.`, `>`, or `?`, found `<` + --> $DIR/const-expression-parameter.rs:60:14 | -LL | bat::<(1, 2, 3)>(); - | ^^^^^^^^^ -help: surround this const argument in braces - | -LL | bat::<{ (1, 2, 3) }>(); - | ^ ^ +LL | bar::< 3 < 4 >(); + | ^ expected one of `,`, `.`, `>`, or `?` here -error: complex const arguments must be surrounded by braces - --> $DIR/const-expression-parameter.rs:30:11 +error: expected one of `!`, `(`, `,`, `>`, `?`, `for`, lifetime, or path, found `42` + --> $DIR/const-expression-parameter.rs:64:26 | -LL | bat::<(1, 2)>(); - | ^^^^^^ -help: surround this const argument in braces +LL | foo::< const_i32() + 42 >(); + | ^^ expected one of 8 possible tokens here + +error: expected one of `!`, `(`, `,`, `>`, `?`, `for`, lifetime, or path, found `42` + --> $DIR/const-expression-parameter.rs:68:16 | -LL | bat::<{ (1, 2) }>(); - | ^ ^ +LL | foo::< X + 42 >(); + | ^^ expected one of 8 possible tokens here -error: complex const arguments must be surrounded by braces - --> $DIR/const-expression-parameter.rs:35:11 +error[E0573]: expected type, found function `const_i32` + --> $DIR/const-expression-parameter.rs:27:12 | -LL | bar::(); - | ^^^^^^ -help: surround this const argument in braces +LL | foo::< const_i32() >(); + | ^^^^^^^^^^^ not a type + +error[E0573]: expected type, found constant `X` + --> $DIR/const-expression-parameter.rs:30:12 | -LL | bar::<{ !false }>(); - | ^ ^ +LL | foo::< X >(); + | ^ not a type -error: expected one of `,` or `>`, found `<` - --> $DIR/const-expression-parameter.rs:39:14 +error[E0573]: expected type, found function `const_bool` + --> $DIR/const-expression-parameter.rs:50:12 | -LL | bar::< 3 < 4 >(); - | ^ expected one of `,` or `>` here +LL | bar::< const_bool() >(); + | ^^^^^^^^^^^^ not a type warning: the feature `const_generics` is incomplete and may cause the compiler to crash --> $DIR/const-expression-parameter.rs:1:12 @@ -93,13 +89,37 @@ LL | #![feature(const_generics)] = note: `#[warn(incomplete_features)]` on by default error[E0107]: wrong number of const arguments: expected 1, found 2 - --> $DIR/const-expression-parameter.rs:16:18 + --> $DIR/const-expression-parameter.rs:25:18 | LL | foo::<1 + 2, 3 + 4>(); | ^^^^^ unexpected const argument +error[E0107]: wrong number of const arguments: expected 1, found 0 + --> $DIR/const-expression-parameter.rs:27:5 + | +LL | foo::< const_i32() >(); + | ^^^^^^^^^^^^^^^^^^^^ expected 1 const argument + +error[E0107]: wrong number of type arguments: expected 0, found 1 + --> $DIR/const-expression-parameter.rs:27:12 + | +LL | foo::< const_i32() >(); + | ^^^^^^^^^^^ unexpected type argument + +error[E0107]: wrong number of const arguments: expected 1, found 0 + --> $DIR/const-expression-parameter.rs:30:5 + | +LL | foo::< X >(); + | ^^^^^^^^^^ expected 1 const argument + +error[E0107]: wrong number of type arguments: expected 0, found 1 + --> $DIR/const-expression-parameter.rs:30:12 + | +LL | foo::< X >(); + | ^ unexpected type argument + error[E0308]: mismatched types - --> $DIR/const-expression-parameter.rs:27:17 + --> $DIR/const-expression-parameter.rs:40:17 | LL | baz::< -1 , "2" >(); | ^^^ expected i32, found reference @@ -108,7 +128,7 @@ LL | baz::< -1 , "2" >(); found type `&'static str` error[E0308]: mismatched types - --> $DIR/const-expression-parameter.rs:30:11 + --> $DIR/const-expression-parameter.rs:43:11 | LL | bat::<(1, 2)>(); | ^^^^^^ expected a tuple with 3 elements, found one with 2 elements @@ -116,7 +136,19 @@ LL | bat::<(1, 2)>(); = note: expected type `(i32, i32, i32)` found type `(i32, i32)` -error: aborting due to 12 previous errors +error[E0107]: wrong number of const arguments: expected 1, found 0 + --> $DIR/const-expression-parameter.rs:50:5 + | +LL | bar::< const_bool() >(); + | ^^^^^^^^^^^^^^^^^^^^^ expected 1 const argument + +error[E0107]: wrong number of type arguments: expected 0, found 1 + --> $DIR/const-expression-parameter.rs:50:12 + | +LL | bar::< const_bool() >(); + | ^^^^^^^^^^^^ unexpected type argument + +error: aborting due to 20 previous errors Some errors have detailed explanations: E0107, E0308. For more information about an error, try `rustc --explain E0107`. diff --git a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.rs b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.rs index 0c5d4d4820ca3..091becfb63e51 100644 --- a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.rs +++ b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.rs @@ -239,6 +239,7 @@ fn inside_const_generic_arguments() { // admit non-IDENT expressions in const generic arguments. if A::< - true && let 1 = 1 //~ ERROR expected one of `,` or `>`, found `&&` + true && let 1 = 1 >::O == 5 {} //~ ERROR chained comparison operators require parentheses + //~^ ERROR expected one of `,`, `.`, `>`, `?`, or an operator, found `{` } diff --git a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr index 329a9a5487fc0..c05cf1aa84be2 100644 --- a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr +++ b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr @@ -4,11 +4,11 @@ error: chained comparison operators require parentheses LL | >::O == 5 {} | ^^^^^^^^^ -error: expected one of `,` or `>`, found `&&` - --> $DIR/disallowed-positions.rs:242:14 +error: expected one of `,`, `.`, `>`, `?`, or an operator, found `{` + --> $DIR/disallowed-positions.rs:243:15 | -LL | true && let 1 = 1 - | ^^ expected one of `,` or `>` here +LL | >::O == 5 {} + | ^ expected one of `,`, `.`, `>`, `?`, or an operator here error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:32:9