Skip to content

Commit

Permalink
[C11] Diagnose C11 keywords as being incompatible w/earlier standards (
Browse files Browse the repository at this point in the history
…#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.
  • Loading branch information
AaronBallman committed Feb 16, 2024
1 parent 4214f25 commit 97434cb
Show file tree
Hide file tree
Showing 9 changed files with 68 additions and 18 deletions.
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}}
*/
}

0 comments on commit 97434cb

Please sign in to comment.