[C] Disallow declarations where a statement is required (#92908)
This fixes a regression introduced in 8bd06d5b65845e5e01dd899a2deb773580460b89 where Clang began to accept a declaration where a statement is required. e.g., ``` if (1) int x; // Previously accepted, now properly rejected ``` Fixes #92775
This commit is contained in:
parent
d33864d5d8
commit
5901d4005f
@ -632,6 +632,9 @@ Bug Fixes in This Version
|
||||
- ``__is_array`` and ``__is_bounded_array`` no longer return ``true`` for
|
||||
zero-sized arrays. Fixes (#GH54705).
|
||||
|
||||
- Correctly reject declarations where a statement is required in C.
|
||||
Fixes #GH92775
|
||||
|
||||
Bug Fixes to Compiler Builtins
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
@ -467,15 +467,18 @@ class Parser : public CodeCompletionHandler {
|
||||
|
||||
/// Flags describing a context in which we're parsing a statement.
|
||||
enum class ParsedStmtContext {
|
||||
/// This context permits declarations in language modes where declarations
|
||||
/// are not statements.
|
||||
AllowDeclarationsInC = 0x1,
|
||||
/// This context permits standalone OpenMP directives.
|
||||
AllowStandaloneOpenMPDirectives = 0x1,
|
||||
AllowStandaloneOpenMPDirectives = 0x2,
|
||||
/// This context is at the top level of a GNU statement expression.
|
||||
InStmtExpr = 0x2,
|
||||
InStmtExpr = 0x4,
|
||||
|
||||
/// The context of a regular substatement.
|
||||
SubStmt = 0,
|
||||
/// The context of a compound-statement.
|
||||
Compound = AllowStandaloneOpenMPDirectives,
|
||||
Compound = AllowDeclarationsInC | AllowStandaloneOpenMPDirectives,
|
||||
|
||||
LLVM_MARK_AS_BITMASK_ENUM(InStmtExpr)
|
||||
};
|
||||
|
@ -239,7 +239,15 @@ Retry:
|
||||
auto IsStmtAttr = [](ParsedAttr &Attr) { return Attr.isStmtAttr(); };
|
||||
bool AllAttrsAreStmtAttrs = llvm::all_of(CXX11Attrs, IsStmtAttr) &&
|
||||
llvm::all_of(GNUAttrs, IsStmtAttr);
|
||||
if (((GNUAttributeLoc.isValid() && !(HaveAttrs && AllAttrsAreStmtAttrs)) ||
|
||||
// In C, the grammar production for statement (C23 6.8.1p1) does not allow
|
||||
// for declarations, which is different from C++ (C++23 [stmt.pre]p1). So
|
||||
// in C++, we always allow a declaration, but in C we need to check whether
|
||||
// we're in a statement context that allows declarations. e.g., in C, the
|
||||
// following is invalid: if (1) int x;
|
||||
if ((getLangOpts().CPlusPlus || getLangOpts().MicrosoftExt ||
|
||||
(StmtCtx & ParsedStmtContext::AllowDeclarationsInC) !=
|
||||
ParsedStmtContext()) &&
|
||||
((GNUAttributeLoc.isValid() && !(HaveAttrs && AllAttrsAreStmtAttrs)) ||
|
||||
isDeclarationStatement())) {
|
||||
SourceLocation DeclStart = Tok.getLocation(), DeclEnd;
|
||||
DeclGroupPtrTy Decl;
|
||||
|
@ -18,8 +18,9 @@
|
||||
|
||||
enum {a, b};
|
||||
void different(void) {
|
||||
if (sizeof(enum {b, a}) != sizeof(int))
|
||||
if (sizeof(enum {b, a}) != sizeof(int)) {
|
||||
_Static_assert(a == 1, "");
|
||||
}
|
||||
/* In C89, the 'b' found here would have been from the enum declaration in
|
||||
* the controlling expression of the selection statement, not from the global
|
||||
* declaration. In C99 and later, that enumeration is scoped to the 'if'
|
||||
|
39
clang/test/Parser/decls.c
Normal file
39
clang/test/Parser/decls.c
Normal file
@ -0,0 +1,39 @@
|
||||
// RUN: %clang_cc1 %s -fsyntax-only -verify -pedantic
|
||||
|
||||
// Test that we can parse declarations at global scope.
|
||||
int v;
|
||||
|
||||
void func(void) {
|
||||
// Test that we can parse declarations within a compound statement.
|
||||
int a;
|
||||
{
|
||||
int b;
|
||||
}
|
||||
|
||||
int z = ({ // expected-warning {{use of GNU statement expression extension}}
|
||||
// Test that we can parse declarations within a GNU statement expression.
|
||||
int w = 12;
|
||||
w;
|
||||
});
|
||||
|
||||
// Test that we diagnose declarations where a statement is required.
|
||||
// See GH92775.
|
||||
if (1)
|
||||
int x; // expected-error {{expected expression}}
|
||||
for (;;)
|
||||
int c; // expected-error {{expected expression}}
|
||||
|
||||
label:
|
||||
int y; // expected-warning {{label followed by a declaration is a C23 extension}}
|
||||
|
||||
// Test that lookup works as expected.
|
||||
(void)a;
|
||||
(void)v;
|
||||
(void)z;
|
||||
(void)b; // expected-error {{use of undeclared identifier 'b'}}
|
||||
(void)w; // expected-error {{use of undeclared identifier 'w'}}
|
||||
(void)x; // expected-error {{use of undeclared identifier 'x'}}
|
||||
(void)c; // expected-error {{use of undeclared identifier 'c'}}
|
||||
(void)y;
|
||||
}
|
||||
|
@ -33,9 +33,11 @@ int foo3;
|
||||
|
||||
void func() {
|
||||
// FIXME: Should we disallow this on declarations, or consider this to be on
|
||||
// the initialization?
|
||||
// the initialization? This is currently rejected in C because
|
||||
// Parser::ParseOpenACCDirectiveStmt() calls ParseStatement() and passes the
|
||||
// statement context as "SubStmt" which does not allow for a declaration in C.
|
||||
#pragma acc parallel
|
||||
int foo;
|
||||
int foo; // expected-error {{expected expression}}
|
||||
|
||||
#pragma acc parallel
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user