[clang] [C11] Diagnose C11 keywords as being incompatible w/earlier standards (PR #82015)

Aaron Ballman via cfe-commits cfe-commits at lists.llvm.org
Fri Feb 16 09:52:20 PST 2024


https://github.com/AaronBallman created https://github.com/llvm/llvm-project/pull/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.

>From a32d6677cc5398750a21c1328f72ecbb6fa97517 Mon Sep 17 00:00:00 2001
From: Aaron Ballman <aaron at aaronballman.com>
Date: Fri, 16 Feb 2024 12:44:32 -0500
Subject: [PATCH 1/2] [C11] Diagnose C11 keywords as being incompatible
 w/earlier standards

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.
---
 clang/include/clang/Basic/DiagnosticGroups.td |  3 ++
 .../clang/Basic/DiagnosticParseKinds.td       |  3 ++
 clang/include/clang/Parse/Parser.h            |  2 +
 clang/lib/Parse/ParseDecl.cpp                 | 16 +++-----
 clang/lib/Parse/ParseDeclCXX.cpp              |  6 +--
 clang/lib/Parse/ParseExpr.cpp                 |  7 ++--
 clang/lib/Parse/Parser.cpp                    | 13 +++++++
 clang/test/Sema/c11-keywords.c                | 37 +++++++++++++++++++
 8 files changed, 69 insertions(+), 18 deletions(-)
 create mode 100644 clang/test/Sema/c11-keywords.c

diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 7679b8528a4197..e8b4139d7893ce 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -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]>;
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 11b490a0928e60..c0dbc25a0c3265 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -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;
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 69b9e837fe8bef..071520f535bc95 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -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
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 9640d7ee70d27f..0728113ba7c936 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -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++
@@ -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;
@@ -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;
 
@@ -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;
@@ -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;
diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index 7d0dbc4ac69490..62632b2d79792e 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -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();
diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp
index 52cebdb6f64bac..f1417456ffe510 100644
--- a/clang/lib/Parse/ParseExpr.cpp
+++ b/clang/lib/Parse/ParseExpr.cpp
@@ -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
@@ -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);
diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index 2dd4a73bfbc262..abc06468fc9d61 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -2742,6 +2742,19 @@ 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.
+  unsigned DiagId;
+  if (!getLangOpts().C11) {
+    DiagId = diag::ext_c11_feature;
+  } else {
+    DiagId = diag::warn_c11_compat_keyword;
+  }
+  Diag(Tok, DiagId) << Tok.getName();
+}
+
 bool BalancedDelimiterTracker::diagnoseOverflow() {
   P.Diag(P.Tok, diag::err_bracket_depth_exceeded)
     << P.getLangOpts().BracketDepth;
diff --git a/clang/test/Sema/c11-keywords.c b/clang/test/Sema/c11-keywords.c
new file mode 100644
index 00000000000000..2621e1a1a8595d
--- /dev/null
+++ b/clang/test/Sema/c11-keywords.c
@@ -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}}
+                                  */
+}
+

>From 6bce3bc879072a915c6c77b2a3d9d96d60d568ee Mon Sep 17 00:00:00 2001
From: Aaron Ballman <aaron at aaronballman.com>
Date: Fri, 16 Feb 2024 12:49:58 -0500
Subject: [PATCH 2/2] Add release note

---
 clang/docs/ReleaseNotes.rst | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 16f79a349c20c8..06c7d57d73ca70 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -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
 ----------------------------------
 



More information about the cfe-commits mailing list