Skip to content

[C11] Diagnose C11 keywords as being incompatible w/earlier standards #82015

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Feb 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,9 @@ Improvements to Clang's diagnostics

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

- Added diagnostics for C11 keywords being incompatible with language standards
before C11, under a new warning group: ``-Wpre-c11-compat``.

Improvements to Clang's time-trace
----------------------------------

Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/DiagnosticGroups.td
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,9 @@ def : DiagGroup<"c++1z-compat-mangling", [CXX17CompatMangling]>;
def NoexceptType : DiagGroup<"noexcept-type", [CXX17CompatMangling]>;

// Warnings for C code which is not compatible with previous C standards.
def CPre11Compat : DiagGroup<"pre-c11-compat">;
def CPre11CompatPedantic : DiagGroup<"pre-c11-compat-pedantic",
[CPre11Compat]>;
def CPre23Compat : DiagGroup<"pre-c23-compat">;
def CPre23CompatPedantic : DiagGroup<"pre-c23-compat-pedantic",
[CPre23Compat]>;
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/DiagnosticParseKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,9 @@ def ext_c99_feature : Extension<
"'%0' is a C99 extension">, InGroup<C99>;
def ext_c11_feature : Extension<
"'%0' is a C11 extension">, InGroup<C11>;
def warn_c11_compat_keyword : Warning<
"'%0' is incompatible with C standards before C11">,
InGroup<CPre11Compat>, DefaultIgnore;
def warn_c23_compat_keyword : Warning<
"'%0' is incompatible with C standards before C23">,
InGroup<CPre23Compat>, DefaultIgnore;
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -1122,6 +1122,8 @@ class Parser : public CodeCompletionHandler {
void checkCompoundToken(SourceLocation FirstTokLoc,
tok::TokenKind FirstTokKind, CompoundToken Op);

void diagnoseUseOfC11Keyword(const Token &Tok);

public:
//===--------------------------------------------------------------------===//
// Scope manipulation
Expand Down
16 changes: 5 additions & 11 deletions clang/lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3520,8 +3520,7 @@ void Parser::ParseDeclarationSpecifiers(

// alignment-specifier
case tok::kw__Alignas:
if (!getLangOpts().C11)
Diag(Tok, diag::ext_c11_feature) << Tok.getName();
diagnoseUseOfC11Keyword(Tok);
[[fallthrough]];
case tok::kw_alignas:
// _Alignas and alignas (C23, not C++) should parse the same way. The C++
Expand Down Expand Up @@ -4184,8 +4183,7 @@ void Parser::ParseDeclarationSpecifiers(
isStorageClass = true;
break;
case tok::kw__Thread_local:
if (!getLangOpts().C11)
Diag(Tok, diag::ext_c11_feature) << Tok.getName();
diagnoseUseOfC11Keyword(Tok);
isInvalid = DS.SetStorageClassSpecThread(DeclSpec::TSCS__Thread_local,
Loc, PrevSpec, DiagID);
isStorageClass = true;
Expand Down Expand Up @@ -4245,8 +4243,7 @@ void Parser::ParseDeclarationSpecifiers(
break;
}
case tok::kw__Noreturn:
if (!getLangOpts().C11)
Diag(Tok, diag::ext_c11_feature) << Tok.getName();
diagnoseUseOfC11Keyword(Tok);
isInvalid = DS.setFunctionSpecNoreturn(Loc, PrevSpec, DiagID);
break;

Expand Down Expand Up @@ -4576,9 +4573,7 @@ void Parser::ParseDeclarationSpecifiers(
// If the _Atomic keyword is immediately followed by a left parenthesis,
// it is interpreted as a type specifier (with a type name), not as a
// type qualifier.
if (!getLangOpts().C11)
Diag(Tok, diag::ext_c11_feature) << Tok.getName();

diagnoseUseOfC11Keyword(Tok);
if (NextToken().is(tok::l_paren)) {
ParseAtomicSpecifier(DS);
continue;
Expand Down Expand Up @@ -6154,8 +6149,7 @@ void Parser::ParseTypeQualifierListOpt(
case tok::kw__Atomic:
if (!AtomicAllowed)
goto DoneWithTypeQuals;
if (!getLangOpts().C11)
Diag(Tok, diag::ext_c11_feature) << Tok.getName();
diagnoseUseOfC11Keyword(Tok);
isInvalid = DS.SetTypeQual(DeclSpec::TQ_atomic, Loc, PrevSpec, DiagID,
getLangOpts());
break;
Expand Down
6 changes: 3 additions & 3 deletions clang/lib/Parse/ParseDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -968,9 +968,9 @@ Decl *Parser::ParseStaticAssertDeclaration(SourceLocation &DeclEnd) {
// Save the token name used for static assertion.
const char *TokName = Tok.getName();

if (Tok.is(tok::kw__Static_assert) && !getLangOpts().C11)
Diag(Tok, diag::ext_c11_feature) << Tok.getName();
if (Tok.is(tok::kw_static_assert)) {
if (Tok.is(tok::kw__Static_assert))
diagnoseUseOfC11Keyword(Tok);
else if (Tok.is(tok::kw_static_assert)) {
if (!getLangOpts().CPlusPlus) {
if (getLangOpts().C23)
Diag(Tok, diag::warn_c23_compat_keyword) << Tok.getName();
Expand Down
7 changes: 3 additions & 4 deletions clang/lib/Parse/ParseExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1462,8 +1462,7 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind,
return Res;
}
case tok::kw__Alignof: // unary-expression: '_Alignof' '(' type-name ')'
if (!getLangOpts().C11)
Diag(Tok, diag::ext_c11_feature) << Tok.getName();
diagnoseUseOfC11Keyword(Tok);
[[fallthrough]];
case tok::kw_alignof: // unary-expression: 'alignof' '(' type-id ')'
case tok::kw___alignof: // unary-expression: '__alignof' unary-expression
Expand Down Expand Up @@ -3389,8 +3388,8 @@ ExprResult Parser::ParseStringLiteralExpression(bool AllowUserDefinedLiteral,
/// \endverbatim
ExprResult Parser::ParseGenericSelectionExpression() {
assert(Tok.is(tok::kw__Generic) && "_Generic keyword expected");
if (!getLangOpts().C11)
Diag(Tok, diag::ext_c11_feature) << Tok.getName();

diagnoseUseOfC11Keyword(Tok);

SourceLocation KeyLoc = ConsumeToken();
BalancedDelimiterTracker T(*this, tok::l_paren);
Expand Down
9 changes: 9 additions & 0 deletions clang/lib/Parse/Parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2742,6 +2742,15 @@ bool Parser::parseMisplacedModuleImport() {
return false;
}

void Parser::diagnoseUseOfC11Keyword(const Token &Tok) {
// Warn that this is a C11 extension if in an older mode or if in C++.
// Otherwise, warn that it is incompatible with standards before C11 if in
// C11 or later.
Diag(Tok, getLangOpts().C11 ? diag::warn_c11_compat_keyword
: diag::ext_c11_feature)
<< Tok.getName();
}

bool BalancedDelimiterTracker::diagnoseOverflow() {
P.Diag(P.Tok, diag::err_bracket_depth_exceeded)
<< P.getLangOpts().BracketDepth;
Expand Down
37 changes: 37 additions & 0 deletions clang/test/Parser/c11-keywords.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// RUN: %clang_cc1 %s -std=c11 -fsyntax-only -verify=compat -Wpre-c11-compat
// RUN: %clang_cc1 %s -std=c99 -fsyntax-only -verify=ext -pedantic
// RUN: %clang_cc1 %s -std=c11 -fsyntax-only -verify=good
// RUN: %clang_cc1 -x c++ %s -fsyntax-only -verify=ext -pedantic

// good-no-diagnostics

extern _Noreturn void exit(int); /* compat-warning {{'_Noreturn' is incompatible with C standards before C11}}
ext-warning {{'_Noreturn' is a C11 extension}}
*/

void func(void) {
static _Thread_local int tl; /* compat-warning {{'_Thread_local' is incompatible with C standards before C11}}
ext-warning {{'_Thread_local' is a C11 extension}}
*/
_Alignas(8) char c; /* compat-warning {{'_Alignas' is incompatible with C standards before C11}}
ext-warning {{'_Alignas' is a C11 extension}}
*/
_Atomic int i1; /* compat-warning {{'_Atomic' is incompatible with C standards before C11}}
ext-warning {{'_Atomic' is a C11 extension}}
*/
_Atomic(int) i2; /* compat-warning {{'_Atomic' is incompatible with C standards before C11}}
ext-warning {{'_Atomic' is a C11 extension}}
*/

_Static_assert(1, ""); /* compat-warning {{'_Static_assert' is incompatible with C standards before C11}}
ext-warning {{'_Static_assert' is a C11 extension}}
*/

(void)_Generic(1, int : 1); /* compat-warning {{'_Generic' is incompatible with C standards before C11}}
ext-warning {{'_Generic' is a C11 extension}}
*/
(void)_Alignof(int); /* compat-warning {{'_Alignof' is incompatible with C standards before C11}}
ext-warning {{'_Alignof' is a C11 extension}}
*/
}