Skip to content

Commit 97434cb

Browse files
authored
[C11] Diagnose C11 keywords as being incompatible w/earlier standards (#82015)
Our usual pattern when issuing an extension warning is to also issue a default-off diagnostic about the keywords not being compatible with standards before a certain point. This adds those diagnostics for C11 keywords.
1 parent 4214f25 commit 97434cb

File tree

9 files changed

+68
-18
lines changed

9 files changed

+68
-18
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,9 @@ Improvements to Clang's diagnostics
185185

186186
- Clang now diagnoses friend declarations with an ``enum`` elaborated-type-specifier in language modes after C++98.
187187

188+
- Added diagnostics for C11 keywords being incompatible with language standards
189+
before C11, under a new warning group: ``-Wpre-c11-compat``.
190+
188191
Improvements to Clang's time-trace
189192
----------------------------------
190193

clang/include/clang/Basic/DiagnosticGroups.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,9 @@ def : DiagGroup<"c++1z-compat-mangling", [CXX17CompatMangling]>;
290290
def NoexceptType : DiagGroup<"noexcept-type", [CXX17CompatMangling]>;
291291

292292
// Warnings for C code which is not compatible with previous C standards.
293+
def CPre11Compat : DiagGroup<"pre-c11-compat">;
294+
def CPre11CompatPedantic : DiagGroup<"pre-c11-compat-pedantic",
295+
[CPre11Compat]>;
293296
def CPre23Compat : DiagGroup<"pre-c23-compat">;
294297
def CPre23CompatPedantic : DiagGroup<"pre-c23-compat-pedantic",
295298
[CPre23Compat]>;

clang/include/clang/Basic/DiagnosticParseKinds.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,9 @@ def ext_c99_feature : Extension<
165165
"'%0' is a C99 extension">, InGroup<C99>;
166166
def ext_c11_feature : Extension<
167167
"'%0' is a C11 extension">, InGroup<C11>;
168+
def warn_c11_compat_keyword : Warning<
169+
"'%0' is incompatible with C standards before C11">,
170+
InGroup<CPre11Compat>, DefaultIgnore;
168171
def warn_c23_compat_keyword : Warning<
169172
"'%0' is incompatible with C standards before C23">,
170173
InGroup<CPre23Compat>, DefaultIgnore;

clang/include/clang/Parse/Parser.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1122,6 +1122,8 @@ class Parser : public CodeCompletionHandler {
11221122
void checkCompoundToken(SourceLocation FirstTokLoc,
11231123
tok::TokenKind FirstTokKind, CompoundToken Op);
11241124

1125+
void diagnoseUseOfC11Keyword(const Token &Tok);
1126+
11251127
public:
11261128
//===--------------------------------------------------------------------===//
11271129
// Scope manipulation

clang/lib/Parse/ParseDecl.cpp

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3520,8 +3520,7 @@ void Parser::ParseDeclarationSpecifiers(
35203520

35213521
// alignment-specifier
35223522
case tok::kw__Alignas:
3523-
if (!getLangOpts().C11)
3524-
Diag(Tok, diag::ext_c11_feature) << Tok.getName();
3523+
diagnoseUseOfC11Keyword(Tok);
35253524
[[fallthrough]];
35263525
case tok::kw_alignas:
35273526
// _Alignas and alignas (C23, not C++) should parse the same way. The C++
@@ -4184,8 +4183,7 @@ void Parser::ParseDeclarationSpecifiers(
41844183
isStorageClass = true;
41854184
break;
41864185
case tok::kw__Thread_local:
4187-
if (!getLangOpts().C11)
4188-
Diag(Tok, diag::ext_c11_feature) << Tok.getName();
4186+
diagnoseUseOfC11Keyword(Tok);
41894187
isInvalid = DS.SetStorageClassSpecThread(DeclSpec::TSCS__Thread_local,
41904188
Loc, PrevSpec, DiagID);
41914189
isStorageClass = true;
@@ -4245,8 +4243,7 @@ void Parser::ParseDeclarationSpecifiers(
42454243
break;
42464244
}
42474245
case tok::kw__Noreturn:
4248-
if (!getLangOpts().C11)
4249-
Diag(Tok, diag::ext_c11_feature) << Tok.getName();
4246+
diagnoseUseOfC11Keyword(Tok);
42504247
isInvalid = DS.setFunctionSpecNoreturn(Loc, PrevSpec, DiagID);
42514248
break;
42524249

@@ -4576,9 +4573,7 @@ void Parser::ParseDeclarationSpecifiers(
45764573
// If the _Atomic keyword is immediately followed by a left parenthesis,
45774574
// it is interpreted as a type specifier (with a type name), not as a
45784575
// type qualifier.
4579-
if (!getLangOpts().C11)
4580-
Diag(Tok, diag::ext_c11_feature) << Tok.getName();
4581-
4576+
diagnoseUseOfC11Keyword(Tok);
45824577
if (NextToken().is(tok::l_paren)) {
45834578
ParseAtomicSpecifier(DS);
45844579
continue;
@@ -6154,8 +6149,7 @@ void Parser::ParseTypeQualifierListOpt(
61546149
case tok::kw__Atomic:
61556150
if (!AtomicAllowed)
61566151
goto DoneWithTypeQuals;
6157-
if (!getLangOpts().C11)
6158-
Diag(Tok, diag::ext_c11_feature) << Tok.getName();
6152+
diagnoseUseOfC11Keyword(Tok);
61596153
isInvalid = DS.SetTypeQual(DeclSpec::TQ_atomic, Loc, PrevSpec, DiagID,
61606154
getLangOpts());
61616155
break;

clang/lib/Parse/ParseDeclCXX.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -968,9 +968,9 @@ Decl *Parser::ParseStaticAssertDeclaration(SourceLocation &DeclEnd) {
968968
// Save the token name used for static assertion.
969969
const char *TokName = Tok.getName();
970970

971-
if (Tok.is(tok::kw__Static_assert) && !getLangOpts().C11)
972-
Diag(Tok, diag::ext_c11_feature) << Tok.getName();
973-
if (Tok.is(tok::kw_static_assert)) {
971+
if (Tok.is(tok::kw__Static_assert))
972+
diagnoseUseOfC11Keyword(Tok);
973+
else if (Tok.is(tok::kw_static_assert)) {
974974
if (!getLangOpts().CPlusPlus) {
975975
if (getLangOpts().C23)
976976
Diag(Tok, diag::warn_c23_compat_keyword) << Tok.getName();

clang/lib/Parse/ParseExpr.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1462,8 +1462,7 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind,
14621462
return Res;
14631463
}
14641464
case tok::kw__Alignof: // unary-expression: '_Alignof' '(' type-name ')'
1465-
if (!getLangOpts().C11)
1466-
Diag(Tok, diag::ext_c11_feature) << Tok.getName();
1465+
diagnoseUseOfC11Keyword(Tok);
14671466
[[fallthrough]];
14681467
case tok::kw_alignof: // unary-expression: 'alignof' '(' type-id ')'
14691468
case tok::kw___alignof: // unary-expression: '__alignof' unary-expression
@@ -3389,8 +3388,8 @@ ExprResult Parser::ParseStringLiteralExpression(bool AllowUserDefinedLiteral,
33893388
/// \endverbatim
33903389
ExprResult Parser::ParseGenericSelectionExpression() {
33913390
assert(Tok.is(tok::kw__Generic) && "_Generic keyword expected");
3392-
if (!getLangOpts().C11)
3393-
Diag(Tok, diag::ext_c11_feature) << Tok.getName();
3391+
3392+
diagnoseUseOfC11Keyword(Tok);
33943393

33953394
SourceLocation KeyLoc = ConsumeToken();
33963395
BalancedDelimiterTracker T(*this, tok::l_paren);

clang/lib/Parse/Parser.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2742,6 +2742,15 @@ bool Parser::parseMisplacedModuleImport() {
27422742
return false;
27432743
}
27442744

2745+
void Parser::diagnoseUseOfC11Keyword(const Token &Tok) {
2746+
// Warn that this is a C11 extension if in an older mode or if in C++.
2747+
// Otherwise, warn that it is incompatible with standards before C11 if in
2748+
// C11 or later.
2749+
Diag(Tok, getLangOpts().C11 ? diag::warn_c11_compat_keyword
2750+
: diag::ext_c11_feature)
2751+
<< Tok.getName();
2752+
}
2753+
27452754
bool BalancedDelimiterTracker::diagnoseOverflow() {
27462755
P.Diag(P.Tok, diag::err_bracket_depth_exceeded)
27472756
<< P.getLangOpts().BracketDepth;

clang/test/Parser/c11-keywords.c

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// RUN: %clang_cc1 %s -std=c11 -fsyntax-only -verify=compat -Wpre-c11-compat
2+
// RUN: %clang_cc1 %s -std=c99 -fsyntax-only -verify=ext -pedantic
3+
// RUN: %clang_cc1 %s -std=c11 -fsyntax-only -verify=good
4+
// RUN: %clang_cc1 -x c++ %s -fsyntax-only -verify=ext -pedantic
5+
6+
// good-no-diagnostics
7+
8+
extern _Noreturn void exit(int); /* compat-warning {{'_Noreturn' is incompatible with C standards before C11}}
9+
ext-warning {{'_Noreturn' is a C11 extension}}
10+
*/
11+
12+
void func(void) {
13+
static _Thread_local int tl; /* compat-warning {{'_Thread_local' is incompatible with C standards before C11}}
14+
ext-warning {{'_Thread_local' is a C11 extension}}
15+
*/
16+
_Alignas(8) char c; /* compat-warning {{'_Alignas' is incompatible with C standards before C11}}
17+
ext-warning {{'_Alignas' is a C11 extension}}
18+
*/
19+
_Atomic int i1; /* compat-warning {{'_Atomic' is incompatible with C standards before C11}}
20+
ext-warning {{'_Atomic' is a C11 extension}}
21+
*/
22+
_Atomic(int) i2; /* compat-warning {{'_Atomic' is incompatible with C standards before C11}}
23+
ext-warning {{'_Atomic' is a C11 extension}}
24+
*/
25+
26+
_Static_assert(1, ""); /* compat-warning {{'_Static_assert' is incompatible with C standards before C11}}
27+
ext-warning {{'_Static_assert' is a C11 extension}}
28+
*/
29+
30+
(void)_Generic(1, int : 1); /* compat-warning {{'_Generic' is incompatible with C standards before C11}}
31+
ext-warning {{'_Generic' is a C11 extension}}
32+
*/
33+
(void)_Alignof(int); /* compat-warning {{'_Alignof' is incompatible with C standards before C11}}
34+
ext-warning {{'_Alignof' is a C11 extension}}
35+
*/
36+
}
37+

0 commit comments

Comments
 (0)