Index: lib/Parse/ParseExprCXX.cpp =================================================================== --- lib/Parse/ParseExprCXX.cpp +++ lib/Parse/ParseExprCXX.cpp @@ -1744,17 +1744,34 @@ ParsedAttributesWithRange attrs(AttrFactory); MaybeParseCXX11Attributes(attrs); + const auto WarnOnInit = [this, &CK] { + Diag(Tok.getLocation(), getLangOpts().CPlusPlus17 + ? diag::warn_cxx14_compat_init_statement + : diag::ext_init_statement) + << (CK == Sema::ConditionKind::Switch); + }; + // Determine what kind of thing we have. switch (isCXXConditionDeclarationOrInitStatement(InitStmt)) { case ConditionOrInitStatement::Expression: { ProhibitAttributes(attrs); + // We can have an empty expression here. + // if (; true); + if (InitStmt && Tok.is(tok::semi)) { + WarnOnInit(); + SourceLocation SemiLoc = ConsumeToken(); + *InitStmt = Actions.ActOnNullStmt(SemiLoc); + return ParseCXXCondition(nullptr, Loc, CK); + } + // Parse the expression. ExprResult Expr = ParseExpression(); // expression if (Expr.isInvalid()) return Sema::ConditionError(); if (InitStmt && Tok.is(tok::semi)) { + WarnOnInit(); *InitStmt = Actions.ActOnExprStmt(Expr.get()); ConsumeToken(); return ParseCXXCondition(nullptr, Loc, CK); @@ -1764,10 +1781,7 @@ } case ConditionOrInitStatement::InitStmtDecl: { - Diag(Tok.getLocation(), getLangOpts().CPlusPlus17 - ? diag::warn_cxx14_compat_init_statement - : diag::ext_init_statement) - << (CK == Sema::ConditionKind::Switch); + WarnOnInit(); SourceLocation DeclStart = Tok.getLocation(), DeclEnd; DeclGroupPtrTy DG = ParseSimpleDeclaration(DeclaratorContext::InitStmtContext, DeclEnd, Index: test/CXX/stmt.stmt/stmt.select/p3.cpp =================================================================== --- test/CXX/stmt.stmt/stmt.select/p3.cpp +++ test/CXX/stmt.stmt/stmt.select/p3.cpp @@ -1,4 +1,5 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s +// RUN: %clang_cc1 -fsyntax-only -std=c++1z -Wc++14-compat -verify %s -DCPP17 int f(); @@ -10,10 +11,67 @@ } } - void h() { if (int x = f()) // expected-note 2{{previous definition}} int x; // expected-error{{redefinition of 'x'}} else int x; // expected-error{{redefinition of 'x'}} } + +void ifInitStatement() { + int Var = 0; + + if (int I = 0; true) {} + if (Var + Var; true) {} + if (; true) {} +#ifdef CPP17 + // expected-warning@-4 {{if initialization statements are incompatible with C++ standards before C++17}} + // expected-warning@-4 {{if initialization statements are incompatible with C++ standards before C++17}} + // expected-warning@-4 {{if initialization statements are incompatible with C++ standards before C++17}} +#else + // expected-warning@-8 {{'if' initialization statements are a C++17 extension}} + // expected-warning@-8 {{'if' initialization statements are a C++17 extension}} + // expected-warning@-8 {{'if' initialization statements are a C++17 extension}} +#endif +} + +void switchInitStatement() { + int Var = 0; + + switch (int I = 0; Var) {} + switch (Var + Var; Var) {} + switch (; Var) {} +#ifdef CPP17 + // expected-warning@-4 {{switch initialization statements are incompatible with C++ standards before C++17}} + // expected-warning@-4 {{switch initialization statements are incompatible with C++ standards before C++17}} + // expected-warning@-4 {{switch initialization statements are incompatible with C++ standards before C++17}} +#else + // expected-warning@-8 {{'switch' initialization statements are a C++17 extension}} + // expected-warning@-8 {{'switch' initialization statements are a C++17 extension}} + // expected-warning@-8 {{'switch' initialization statements are a C++17 extension}} +#endif +} + +// TODO: Better diagnostics for while init statements. +void whileInitStatement() { + while (int I = 10; I--); // expected-error {{expected ')'}} + // expected-note@-1 {{to match this '('}} + // expected-error@-2 {{use of undeclared identifier 'I'}} + + int Var = 10; + while (Var + Var; Var--) {} // expected-error {{expected ')'}} + // expected-note@-1 {{to match this '('}} + // expected-error@-2 {{expected ';' after expression}} + // expected-error@-3 {{expected expression}} + // expected-warning@-4 {{while loop has empty body}} + // expected-note@-5 {{put the semicolon on a separate line to silence this warning}} +} + +// TODO: This is needed because clang can't seem to diagnose invalid syntax after the +// last loop above. It would be nice to remove this. +void whileInitStatement2() { + while (; false) {} // expected-error {{expected expression}} + // expected-warning@-1 {{expression result unused}} + // expected-error@-2 {{expected ';' after expression}} + // expected-error@-3 {{expected expression}} +}