Skip to content

Commit 5901d40

Browse files
authoredMay 28, 2024··
[C] Disallow declarations where a statement is required (#92908)
This fixes a regression introduced in 8bd06d5 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
1 parent d33864d commit 5901d40

File tree

6 files changed

+63
-7
lines changed

6 files changed

+63
-7
lines changed
 

‎clang/docs/ReleaseNotes.rst

+3
Original file line numberDiff line numberDiff line change
@@ -632,6 +632,9 @@ Bug Fixes in This Version
632632
- ``__is_array`` and ``__is_bounded_array`` no longer return ``true`` for
633633
zero-sized arrays. Fixes (#GH54705).
634634

635+
- Correctly reject declarations where a statement is required in C.
636+
Fixes #GH92775
637+
635638
Bug Fixes to Compiler Builtins
636639
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
637640

‎clang/include/clang/Parse/Parser.h

+6-3
Original file line numberDiff line numberDiff line change
@@ -467,15 +467,18 @@ class Parser : public CodeCompletionHandler {
467467

468468
/// Flags describing a context in which we're parsing a statement.
469469
enum class ParsedStmtContext {
470+
/// This context permits declarations in language modes where declarations
471+
/// are not statements.
472+
AllowDeclarationsInC = 0x1,
470473
/// This context permits standalone OpenMP directives.
471-
AllowStandaloneOpenMPDirectives = 0x1,
474+
AllowStandaloneOpenMPDirectives = 0x2,
472475
/// This context is at the top level of a GNU statement expression.
473-
InStmtExpr = 0x2,
476+
InStmtExpr = 0x4,
474477

475478
/// The context of a regular substatement.
476479
SubStmt = 0,
477480
/// The context of a compound-statement.
478-
Compound = AllowStandaloneOpenMPDirectives,
481+
Compound = AllowDeclarationsInC | AllowStandaloneOpenMPDirectives,
479482

480483
LLVM_MARK_AS_BITMASK_ENUM(InStmtExpr)
481484
};

‎clang/lib/Parse/ParseStmt.cpp

+9-1
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,15 @@ StmtResult Parser::ParseStatementOrDeclarationAfterAttributes(
239239
auto IsStmtAttr = [](ParsedAttr &Attr) { return Attr.isStmtAttr(); };
240240
bool AllAttrsAreStmtAttrs = llvm::all_of(CXX11Attrs, IsStmtAttr) &&
241241
llvm::all_of(GNUAttrs, IsStmtAttr);
242-
if (((GNUAttributeLoc.isValid() && !(HaveAttrs && AllAttrsAreStmtAttrs)) ||
242+
// In C, the grammar production for statement (C23 6.8.1p1) does not allow
243+
// for declarations, which is different from C++ (C++23 [stmt.pre]p1). So
244+
// in C++, we always allow a declaration, but in C we need to check whether
245+
// we're in a statement context that allows declarations. e.g., in C, the
246+
// following is invalid: if (1) int x;
247+
if ((getLangOpts().CPlusPlus || getLangOpts().MicrosoftExt ||
248+
(StmtCtx & ParsedStmtContext::AllowDeclarationsInC) !=
249+
ParsedStmtContext()) &&
250+
((GNUAttributeLoc.isValid() && !(HaveAttrs && AllAttrsAreStmtAttrs)) ||
243251
isDeclarationStatement())) {
244252
SourceLocation DeclStart = Tok.getLocation(), DeclEnd;
245253
DeclGroupPtrTy Decl;

‎clang/test/C/C99/block-scopes.c

+2-1
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@
1818

1919
enum {a, b};
2020
void different(void) {
21-
if (sizeof(enum {b, a}) != sizeof(int))
21+
if (sizeof(enum {b, a}) != sizeof(int)) {
2222
_Static_assert(a == 1, "");
23+
}
2324
/* In C89, the 'b' found here would have been from the enum declaration in
2425
* the controlling expression of the selection statement, not from the global
2526
* declaration. In C99 and later, that enumeration is scoped to the 'if'

‎clang/test/Parser/decls.c

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// RUN: %clang_cc1 %s -fsyntax-only -verify -pedantic
2+
3+
// Test that we can parse declarations at global scope.
4+
int v;
5+
6+
void func(void) {
7+
// Test that we can parse declarations within a compound statement.
8+
int a;
9+
{
10+
int b;
11+
}
12+
13+
int z = ({ // expected-warning {{use of GNU statement expression extension}}
14+
// Test that we can parse declarations within a GNU statement expression.
15+
int w = 12;
16+
w;
17+
});
18+
19+
// Test that we diagnose declarations where a statement is required.
20+
// See GH92775.
21+
if (1)
22+
int x; // expected-error {{expected expression}}
23+
for (;;)
24+
int c; // expected-error {{expected expression}}
25+
26+
label:
27+
int y; // expected-warning {{label followed by a declaration is a C23 extension}}
28+
29+
// Test that lookup works as expected.
30+
(void)a;
31+
(void)v;
32+
(void)z;
33+
(void)b; // expected-error {{use of undeclared identifier 'b'}}
34+
(void)w; // expected-error {{use of undeclared identifier 'w'}}
35+
(void)x; // expected-error {{use of undeclared identifier 'x'}}
36+
(void)c; // expected-error {{use of undeclared identifier 'c'}}
37+
(void)y;
38+
}
39+

‎clang/test/SemaOpenACC/parallel-loc-and-stmt.c

+4-2
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,11 @@ int foo3;
3333

3434
void func() {
3535
// FIXME: Should we disallow this on declarations, or consider this to be on
36-
// the initialization?
36+
// the initialization? This is currently rejected in C because
37+
// Parser::ParseOpenACCDirectiveStmt() calls ParseStatement() and passes the
38+
// statement context as "SubStmt" which does not allow for a declaration in C.
3739
#pragma acc parallel
38-
int foo;
40+
int foo; // expected-error {{expected expression}}
3941

4042
#pragma acc parallel
4143
{

0 commit comments

Comments
 (0)
Please sign in to comment.