Skip to content

[Clang] [Diagnostic] Extend DiagCompat() to C language modes#209241

Open
Sirraide wants to merge 1 commit into
llvm:mainfrom
Sirraide:ccompat
Open

[Clang] [Diagnostic] Extend DiagCompat() to C language modes#209241
Sirraide wants to merge 1 commit into
llvm:mainfrom
Sirraide:ccompat

Conversation

@Sirraide

Copy link
Copy Markdown
Member

This expands the compatibility warnings infrastructure added in #132348 to support C mode as well. Implementing this was actually fairly straight-forward because we are only ever in C or C++ mode, i.e. during a single compilation we either emit only C compatibility warnings or only C++ compatibility warnings. This means we can simply reuse the existing code and just check for different LangOpts depending on whether we're in C or C++ mode.

Concretely, this means that instead of e.g.

def ext_c2y_alignof_incomplete_array : Extension<
  "'alignof' on an incomplete array type is a C2y extension">,
  InGroup<C2y>;
def warn_c2y_compat_alignof_incomplete_array : Warning<
  "'alignof' on an incomplete array type is incompatible with C standards "
  "before C2y">, InGroup<CPre2yCompat>, DefaultIgnore;

you can now simply write

defm alignof_incomplete_array : C2yCompat<"'alignof' on an incomplete array type is">;

And when emitting the warning(s), code such as

Diag(OpLoc, getLangOpts().C2y
                ? diag::warn_c2y_compat_alignof_incomplete_array
                : diag::ext_c2y_alignof_incomplete_array);

can now be replaced with

DiagCompat(OpLoc, diag_compat::alignof_incomplete_array);

I've also migrated the C compatibility warnings in DiagnosticSemaKinds.td to use the new system to make sure that everything is working properly.

This expands the compatibility warnings infrastructure added
in llvm#132348 to support C mode as well. Implementing this was
actually fairly straight-forward because we are only ever in
C _or_ C++ mode, i.e. during a single compilation we either
emit only C compatibility warnings or only C++ compatibility
warnings. This means we can simply reuse the existing code
and just check for different LangOpts depending on whether
we're in C or C++ mode.

Concretely, this means that instead of e.g.

```
def ext_c2y_alignof_incomplete_array : Extension<
  "'alignof' on an incomplete array type is a C2y extension">,
  InGroup<C2y>;
def warn_c2y_compat_alignof_incomplete_array : Warning<
  "'alignof' on an incomplete array type is incompatible with C standards "
  "before C2y">, InGroup<CPre2yCompat>, DefaultIgnore;
```

you can now simply write

```
defm alignof_incomplete_array : C2yCompat<"'alignof' on an incomplete array type is">;
```

And when emitting the warning(s), code such as

```
Diag(OpLoc, getLangOpts().C2y
                ? diag::warn_c2y_compat_alignof_incomplete_array
                : diag::ext_c2y_alignof_incomplete_array);
```

can now be replaced with

```
DiagCompat(OpLoc, diag_compat::alignof_incomplete_array);
```
@Sirraide Sirraide added the clang:diagnostics New/improved warning or error message in Clang, but not in clang-tidy or static analyzer label Jul 13, 2026
@llvmorg-github-actions llvmorg-github-actions Bot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Jul 13, 2026
@llvmorg-github-actions

Copy link
Copy Markdown

@llvm/pr-subscribers-clang

Author: Sirraide

Changes

This expands the compatibility warnings infrastructure added in #132348 to support C mode as well. Implementing this was actually fairly straight-forward because we are only ever in C or C++ mode, i.e. during a single compilation we either emit only C compatibility warnings or only C++ compatibility warnings. This means we can simply reuse the existing code and just check for different LangOpts depending on whether we're in C or C++ mode.

Concretely, this means that instead of e.g.

def ext_c2y_alignof_incomplete_array : Extension&lt;
  "'alignof' on an incomplete array type is a C2y extension"&gt;,
  InGroup&lt;C2y&gt;;
def warn_c2y_compat_alignof_incomplete_array : Warning&lt;
  "'alignof' on an incomplete array type is incompatible with C standards "
  "before C2y"&gt;, InGroup&lt;CPre2yCompat&gt;, DefaultIgnore;

you can now simply write

defm alignof_incomplete_array : C2yCompat&lt;"'alignof' on an incomplete array type is"&gt;;

And when emitting the warning(s), code such as

Diag(OpLoc, getLangOpts().C2y
                ? diag::warn_c2y_compat_alignof_incomplete_array
                : diag::ext_c2y_alignof_incomplete_array);

can now be replaced with

DiagCompat(OpLoc, diag_compat::alignof_incomplete_array);

I've also migrated the C compatibility warnings in DiagnosticSemaKinds.td to use the new system to make sure that everything is working properly.


Patch is 21.38 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/209241.diff

9 Files Affected:

  • (modified) clang/include/clang/Basic/Diagnostic.td (+52-32)
  • (modified) clang/include/clang/Basic/DiagnosticIDs.h (+2-2)
  • (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+14-47)
  • (modified) clang/lib/Basic/DiagnosticIDs.cpp (+24-3)
  • (modified) clang/lib/Parse/Parser.cpp (+1-2)
  • (modified) clang/lib/Sema/SemaBase.cpp (+1-2)
  • (modified) clang/lib/Sema/SemaExpr.cpp (+6-13)
  • (modified) clang/lib/Sema/SemaStmt.cpp (+4-7)
  • (modified) clang/lib/Sema/SemaType.cpp (+1-3)
diff --git a/clang/include/clang/Basic/Diagnostic.td b/clang/include/clang/Basic/Diagnostic.td
index 693984d8241f0..4477828c05eda 100644
--- a/clang/include/clang/Basic/Diagnostic.td
+++ b/clang/include/clang/Basic/Diagnostic.td
@@ -191,60 +191,80 @@ class CompatWarningId<string name, int std, string diag, string diag_pre> {
   string CategoryName = ?;
 }
 
-// C++ compatibility warnings.
-multiclass CXXCompat<
+// Compatibility warnings.
+multiclass CompatWarning<
+    // Diagnostic message.
     string message,
+    // Version number.
     int std_ver,
+    // Is this C++?
+    bit cxx,
+    // ExtWarn if true, Extension if false.
     bit ext_warn = true,
-    string std_ver_override = ""#std_ver> {
-    // 'X is a C++YZ extension'.
-    def compat_pre_cxx#std_ver#_#NAME :
-        Diagnostic<!strconcat(message, " a C++", std_ver_override,  " extension"),
+    // Language mode name to use in the diagnostic text.
+    string std_ver_name = ""#std_ver,
+    // Version string used in diagnostic group names.
+    string diag_group_ver_str = ""#std_ver> {
+    defvar lang = !if(cxx, "C++", "C");
+    defvar prefix = !if(cxx, "CXX", "C");
+    defvar prefix_lower = !tolower(prefix#diag_group_ver_str);
+    defvar is_cxx_11 = !and(cxx, !eq(std_ver, 11));
+
+    // 'X is a C(++)YZ extension'.
+    def compat_pre_#prefix_lower#_#NAME :
+        Diagnostic<!strconcat(message, " a ", lang, std_ver_name,  " extension"),
                    CLASS_EXTENSION,
                    !if(ext_warn, SEV_Warning, SEV_Ignored)>,
-        InGroup<!cast<DiagGroup>("CXX"#std_ver)>;
+        InGroup<!cast<DiagGroup>(prefix#diag_group_ver_str)>;
 
-    // 'X is incompatible with C++98' (if std_ver == 11).
-    // 'X is incompatible with C++ standards before C++YZ' (otherwise).
-    def compat_cxx#std_ver#_#NAME :
-        Warning<!if(!eq(std_ver, 11),
+    // 'X is incompatible with C++98' (if is_cxx_11 is true).
+    // 'X is incompatible with C(++) standards before C(++)YZ' (otherwise).
+    def compat_#prefix_lower#_#NAME :
+        Warning<!if(is_cxx_11,
                     !strconcat(message, " incompatible with C++98"),
-                    !strconcat(message, " incompatible with C++ standards before C++", std_ver_override))>,
-        InGroup<!cast<DiagGroup>(!if(!eq(std_ver, 11),
-                                     "CXX98Compat",
-                                     "CXXPre"#std_ver#"Compat"))>,
+                    !strconcat(message, " incompatible with ", lang, " standards before ", lang, std_ver_name))>,
+        InGroup<!cast<DiagGroup>(!if(is_cxx_11,
+                                     prefix#"98Compat",
+                                     prefix#"Pre"#diag_group_ver_str#"Compat"))>,
         DefaultIgnore;
 
     def : CompatWarningId<
         NAME, std_ver,
-        "compat_cxx"#std_ver#"_"#NAME,
-        "compat_pre_cxx"#std_ver#"_"#NAME>;
+        "compat_"#prefix_lower#"_"#NAME,
+        "compat_pre_"#prefix_lower#"_"#NAME>;
 }
 
-// These generate pairs of C++ compatibility warnings of the form:
+// These generate pairs of C(++) compatibility warnings of the form:
 //
-//    - compat_cxx<std>_<name>
-//    - compat_pre_cxx<std>_<name>
+//    - compat_c(xx)<std>_<name>
+//    - compat_pre_c(xx)<std>_<name>
 //
-// The 'compat_cxx...' warning is intended to be issued in C++<std> mode,
-// and the 'compat_pre_cxx...' warning in C++ modes before C++<std>.
+// The 'compat_c(xx)...' warning is intended to be issued in C(++)<std> mode,
+// and the 'compat_pre_c(xx)...' warning in C(++) modes before C(++)<std>.
 //
 // Example:
 //
-//   defm inline_variable : CXX17Compat<"inline variables are">;
+//   defm inline_variable : C(XX)11Compat<"inline variables are">;
 //
 // This generates two warnings:
 //
-//   - compat_cxx17_inline_variable:     'inline variables are incompatible with C++ standards before C++17'
-//   - compat_pre_cxx17_inline_variable: 'inline variables are a C++17 extension'
+//   - compat_c(xx)11_inline_variable:     'inline variables are incompatible with C(++) standards before C(++)11'
+//   - compat_pre_c(xx)11_inline_variable: 'inline variables are a C(++)11 extension'
 //
-multiclass CXX11Compat<string message, bit ext_warn = true> : CXXCompat<message, 11, ext_warn>;
-multiclass CXX14Compat<string message, bit ext_warn = true> : CXXCompat<message, 14, ext_warn>;
-multiclass CXX17Compat<string message, bit ext_warn = true> : CXXCompat<message, 17, ext_warn>;
-multiclass CXX20Compat<string message, bit ext_warn = true> : CXXCompat<message, 20, ext_warn>;
-multiclass CXX23Compat<string message, bit ext_warn = true> : CXXCompat<message, 23, ext_warn>;
-multiclass CXX26Compat<string message, bit ext_warn = true> : CXXCompat<message, 26, ext_warn, "2c">;
-multiclass CXX29Compat<string message, bit ext_warn = true> : CXXCompat<message, 29, ext_warn, "2d">;
+multiclass CXX11Compat<string message, bit ext_warn = true> : CompatWarning<message, 11, /*cxx=*/true, ext_warn>;
+multiclass CXX14Compat<string message, bit ext_warn = true> : CompatWarning<message, 14, /*cxx=*/true, ext_warn>;
+multiclass CXX17Compat<string message, bit ext_warn = true> : CompatWarning<message, 17, /*cxx=*/true, ext_warn>;
+multiclass CXX20Compat<string message, bit ext_warn = true> : CompatWarning<message, 20, /*cxx=*/true, ext_warn>;
+multiclass CXX23Compat<string message, bit ext_warn = true> : CompatWarning<message, 23, /*cxx=*/true, ext_warn>;
+multiclass CXX26Compat<string message, bit ext_warn = true> : CompatWarning<message, 26, /*cxx=*/true, ext_warn, "2c">;
+multiclass CXX29Compat<string message, bit ext_warn = true> : CompatWarning<message, 29, /*cxx=*/true, ext_warn, "2d">;
+
+// C compatibility warnings generally use Extension rather than ExtWarn.
+multiclass C99Compat<string message, bit ext_warn = false> : CompatWarning<message, 99, /*cxx=*/false, ext_warn>;
+multiclass C11Compat<string message, bit ext_warn = false> : CompatWarning<message, 11, /*cxx=*/false, ext_warn>;
+multiclass C17Compat<string message, bit ext_warn = false> : CompatWarning<message, 17, /*cxx=*/false, ext_warn>;
+multiclass C23Compat<string message, bit ext_warn = false> : CompatWarning<message, 23, /*cxx=*/false, ext_warn>;
+multiclass C2yCompat<string message, bit ext_warn = false> : CompatWarning<message, 29, /*cxx=*/false, ext_warn, "2y", "2y">;
 
 // Definitions for Diagnostics.
 include "DiagnosticASTKinds.td"
diff --git a/clang/include/clang/Basic/DiagnosticIDs.h b/clang/include/clang/Basic/DiagnosticIDs.h
index 63b5e6a28aac0..f71e47b5cba70 100644
--- a/clang/include/clang/Basic/DiagnosticIDs.h
+++ b/clang/include/clang/Basic/DiagnosticIDs.h
@@ -485,8 +485,8 @@ class DiagnosticIDs : public RefCountedBase<DiagnosticIDs> {
 
   /// Get the appropriate diagnostic Id to use for issuing a compatibility
   /// diagnostic. For use by the various DiagCompat() helpers.
-  static unsigned getCXXCompatDiagId(const LangOptions &LangOpts,
-                                     unsigned CompatDiagId);
+  static unsigned getCompatDiagId(const LangOptions &LangOpts,
+                                  unsigned CompatDiagId);
 
   /// Return true if either of the following two conditions hold:
   /// 1. \p Loc is in a system header and the diagnostic kind \p DiagID does
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 86b765fdf1fab..ab3a2de281bb0 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12,6 +12,17 @@
 
 let Component = "Sema" in {
 let CategoryName = "Semantic Issue" in {
+// C23 compatibility with C17.
+defm restrict_on_array_of_pointers : C23Compat<"'restrict' qualifier on an array of pointers is">;
+defm non_local_variable_decl_in_for : C23Compat<"declaration of non-local variable in 'for' loop is">;
+defm non_variable_decl_in_for : C23Compat<"non-variable declaration in 'for' loop is">;
+
+// C2y compatibility with C23.
+defm imaginary_constant : C2yCompat<"imaginary constants are">;
+defm alignof_incomplete_array : C2yCompat<"'alignof' on an incomplete array type is">;
+defm increment_complex : C2yCompat<"'%select{--|++}0' on an object of complex type is">;
+defm assoc_type_incomplete : C2yCompat<"use of incomplete type %0 in a '_Generic' association is">;
+
 // C++11 compatibility with C++98.
 defm nonclass_type_friend : CXX11Compat<"non-class friend type %0 is">;
 defm static_data_member_in_union : CXX11Compat<"static data member %0 in union is">;
@@ -66,8 +77,9 @@ defm decomp_decl_cond : CXX26Compat<"structured binding declaration in a conditi
 
 // Compatibility warnings duplicated across multiple language versions.
 foreach std = [14, 20, 23] in {
-  defm cxx#std#_constexpr_body_invalid_stmt : CXXCompat<
-    "use of this statement in a constexpr %select{function|constructor}0 is", std>;
+  defm cxx#std#_constexpr_body_invalid_stmt : CompatWarning<
+    "use of this statement in a constexpr %select{function|constructor}0 is",
+    std, /*cxx=*/true>;
 }
 
 def note_previous_decl : Note<"%0 declared here">;
@@ -318,11 +330,6 @@ def ext_designated_init_brace_elision : ExtWarn<
 // Declarations.
 def ext_plain_complex : ExtWarn<
   "plain '_Complex' requires a type specifier; assuming '_Complex double'">;
-def warn_c23_compat_imaginary_constant : Warning<
-  "imaginary constants are incompatible with C standards before C2y">,
-  DefaultIgnore, InGroup<CPre2yCompat>;
-def ext_c2y_imaginary_constant : Extension<
-  "imaginary constants are a C2y extension">, InGroup<C2y>;
 def ext_gnu_imaginary_constant : Extension<
   "imaginary constants are a GNU extension">, InGroup<GNUImaginaryConstant>;
 def ext_integer_complex : Extension<
@@ -3801,12 +3808,6 @@ def warn_alignment_not_power_of_two : Warning<
   InGroup<DiagGroup<"non-power-of-two-alignment">>;
 def err_alignment_dependent_typedef_name : Error<
   "requested alignment is dependent but declaration is not dependent">;
-def ext_c2y_alignof_incomplete_array : Extension<
-  "'alignof' on an incomplete array type is a C2y extension">,
-  InGroup<C2y>;
-def warn_c2y_compat_alignof_incomplete_array : Warning<
-  "'alignof' on an incomplete array type is incompatible with C standards "
-  "before C2y">, InGroup<CPre2yCompat>, DefaultIgnore;
 
 def warn_alignment_builtin_useless : Warning<
   "%select{aligning a value|the result of checking whether a value is aligned}0"
@@ -7882,11 +7883,6 @@ def warn_c23_compat_utf8_string : Warning<
 def note_cxx20_c23_compat_utf8_string_remove_u8 : Note<
   "remove 'u8' prefix to avoid a change of behavior; "
   "Clang encodes unprefixed narrow string literals as UTF-8">;
-def warn_c23_compat_restrict_on_array_of_pointers : Warning<
-  "'restrict' qualifier on an array of pointers is incompatible with C standards before C23">,
-  InGroup<CPre23Compat>, DefaultIgnore;
-def ext_restrict_on_array_of_pointers_c23 : Extension<
-  "'restrict' qualifier on an array of pointers is a C23 extension">, InGroup<C23>;
 def err_array_init_different_type : Error<
   "cannot initialize array %diff{of type $ with array of type $|"
   "with different type of array}0,1">;
@@ -8335,12 +8331,6 @@ def note_gnu_counted_by_void_ptr_use_sized_by
            "to suppress this warning">;
 def err_readonly_message_assignment : Error<
   "assigning to 'readonly' return result of an Objective-C message not allowed">;
-def ext_c2y_increment_complex : Extension<
-  "'%select{--|++}0' on an object of complex type is a C2y extension">,
-  InGroup<C2y>;
-def warn_c2y_compat_increment_complex : Warning<
-  "'%select{--|++}0' on an object of complex type is incompatible with C "
-  "standards before C2y">, InGroup<CPre2yCompat>, DefaultIgnore;
 def ext_integer_complement_complex : Extension<
   "ISO C does not support '~' for complex conjugation of %0">;
 def err_nosetter_property_assignment : Error<
@@ -11188,13 +11178,6 @@ def warn_type_safety_null_pointer_required : Warning<
   "specified %0 type tag requires a null pointer">, InGroup<TypeSafety>;
 
 // Generic selections.
-def ext_assoc_type_incomplete : Extension<
-  "incomplete type %0 in a '_Generic' association is a C2y extension">,
-  InGroup<C2y>;
-def warn_c2y_compat_assoc_type_incomplete : Warning<
-  "use of incomplete type %0 in a '_Generic' association is incompatible with "
-  "C standards before C2y">,
-  InGroup<CPre2yCompat>, DefaultIgnore;
 def err_assoc_type_nonobject : Error<
   "type %0 in generic association not an object type">;
 def err_assoc_type_variably_modified : Error<
@@ -11589,22 +11572,6 @@ def err_non_local_variable_decl_in_for : Error<
 def err_non_variable_decl_in_for : Error<
   "non-variable declaration in 'for' loop">;
 
-def ext_c23_non_local_variable_decl_in_for : Extension<
-  "declaration of non-local variable in 'for' loop is a C23 extension">,
-  InGroup<C23>;
-
-def warn_c17_non_local_variable_decl_in_for : Warning<
-  "declaration of non-local variable in 'for' loop is incompatible with C standards before C23">,
-  DefaultIgnore, InGroup<CPre23Compat>;
-
-def ext_c23_non_variable_decl_in_for : Extension<
-  "non-variable declaration in 'for' loop is a C23 extension">,
-  InGroup<C23>;
-
-def warn_c17_non_variable_decl_in_for : Warning<
-  "non-variable declaration in 'for' loop is incompatible with C standards before C23">,
-  DefaultIgnore, InGroup<CPre23Compat>;
-
 def err_toomany_element_decls : Error<
   "only one element declaration is allowed">;
 def err_selector_element_not_lvalue : Error<
diff --git a/clang/lib/Basic/DiagnosticIDs.cpp b/clang/lib/Basic/DiagnosticIDs.cpp
index 6445aa6f4ecb1..92f96d1b9ad90 100644
--- a/clang/lib/Basic/DiagnosticIDs.cpp
+++ b/clang/lib/Basic/DiagnosticIDs.cpp
@@ -876,8 +876,8 @@ StringRef DiagnosticIDs::getNearestOption(diag::Flavor Flavor,
   return Best;
 }
 
-unsigned DiagnosticIDs::getCXXCompatDiagId(const LangOptions &LangOpts,
-                                           unsigned CompatDiagId) {
+unsigned DiagnosticIDs::getCompatDiagId(const LangOptions &LangOpts,
+                                        unsigned CompatDiagId) {
   struct CompatDiag {
     unsigned StdVer;
     unsigned DiagId;
@@ -888,10 +888,17 @@ unsigned DiagnosticIDs::getCXXCompatDiagId(const LangOptions &LangOpts,
   // actual numbers don't really matter for this, but the definitions of the
   // compat diags in the Tablegen file use the standard version number (i.e.
   // 98, 11, 14, etc.), so we base the encoding here on that.
+  //
+  // Likewise, for C, we have C99 < C11 < C17 < C23 < C29.
+  //
+  // We do end up with some overlap between C and C++ here, e.g. 2011 is used
+  // for both C11 and C++11, but this doesn't matter since we're never in e.g.
+  // C11 and C++11 mode at the same time (additionally, we should only ever
+  // be issuing C compatibility diagnostics in C mode and likewise for C++).
 #define DIAG_COMPAT_IDS_BEGIN()
 #define DIAG_COMPAT_IDS_END()
 #define DIAG_COMPAT_ID(Value, Name, Std, Diag, DiagPre)                        \
-  {Std == 98 ? 1998 : 2000 + Std, diag::Diag, diag::DiagPre},
+  {Std >= 98 ? 1900 + Std : 2000 + Std, diag::Diag, diag::DiagPre},
   static constexpr CompatDiag Diags[]{
 #include "clang/Basic/DiagnosticAllCompatIDs.inc"
   };
@@ -902,6 +909,20 @@ unsigned DiagnosticIDs::getCXXCompatDiagId(const LangOptions &LangOpts,
   assert(CompatDiagId < std::size(Diags) && "Invalid compat diag id");
 
   unsigned StdVer = [&] {
+    if (!LangOpts.CPlusPlus) {
+      if (LangOpts.C2y)
+        return 2029;
+      if (LangOpts.C23)
+        return 2023;
+      if (LangOpts.C17)
+        return 2017;
+      if (LangOpts.C11)
+        return 2011;
+      if (LangOpts.C99)
+        return 1999;
+      return 1989;
+    }
+
     if (LangOpts.CPlusPlus29)
       return 2029;
     if (LangOpts.CPlusPlus26)
diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index 5e1fd4df1a3f0..f30cd68bf1e6c 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -95,8 +95,7 @@ DiagnosticBuilder Parser::Diag(const Token &Tok, unsigned DiagID) {
 
 DiagnosticBuilder Parser::DiagCompat(SourceLocation Loc,
                                      unsigned CompatDiagId) {
-  return Diag(Loc,
-              DiagnosticIDs::getCXXCompatDiagId(getLangOpts(), CompatDiagId));
+  return Diag(Loc, DiagnosticIDs::getCompatDiagId(getLangOpts(), CompatDiagId));
 }
 
 DiagnosticBuilder Parser::DiagCompat(const Token &Tok, unsigned CompatDiagId) {
diff --git a/clang/lib/Sema/SemaBase.cpp b/clang/lib/Sema/SemaBase.cpp
index 5524ff50fce85..e16ec2883b89c 100644
--- a/clang/lib/Sema/SemaBase.cpp
+++ b/clang/lib/Sema/SemaBase.cpp
@@ -97,7 +97,6 @@ Sema::SemaDiagnosticBuilder SemaBase::Diag(SourceLocation Loc,
 
 SemaBase::SemaDiagnosticBuilder SemaBase::DiagCompat(SourceLocation Loc,
                                                      unsigned CompatDiagId) {
-  return Diag(Loc,
-              DiagnosticIDs::getCXXCompatDiagId(getLangOpts(), CompatDiagId));
+  return Diag(Loc, DiagnosticIDs::getCompatDiagId(getLangOpts(), CompatDiagId));
 }
 } // namespace clang
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 592cb1d588370..3bfc228843ae6 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -1922,8 +1922,8 @@ ExprResult Sema::CreateGenericSelectionExpr(
         // earlier because GCC does so.
         unsigned D = 0;
         if (ControllingExpr && Types[i]->getType()->isIncompleteType())
-          D = LangOpts.C2y ? diag::warn_c2y_compat_assoc_type_incomplete
-                           : diag::ext_assoc_type_incomplete;
+          D = LangOpts.C2y ? diag::compat_c2y_assoc_type_incomplete
+                           : diag::compat_pre_c2y_assoc_type_incomplete;
         else if (ControllingExpr && !Types[i]->getType()->isObjectType())
           D = diag::err_assoc_type_nonobject;
         else if (Types[i]->getType()->isVariablyModifiedType())
@@ -4258,14 +4258,10 @@ ExprResult Sema::ActOnNumericConstant(const Token &Tok, Scope *UDLScope) {
                                         Context.getComplexType(Res->getType()));
 
     // In C++, this is a GNU extension. In C, it's a C2y extension.
-    unsigned DiagId;
     if (getLangOpts().CPlusPlus)
-      DiagId = diag::ext_gnu_imaginary_constant;
-    else if (getLangOpts().C2y)
-      DiagId = diag::warn_c23_compat_imaginary_constant;
+      Diag(Tok.getLocation(), diag::ext_gnu_imaginary_constant);
     else
-      DiagId = diag::ext_c2y_imaginary_constant;
-    Diag(Tok.getLocation(), DiagId);
+      DiagCompat(Tok.getLocation(), diag_compat::imaginary_constant);
   }
   return Res;
 }
@@ -4732,9 +4728,7 @@ bool Sema::CheckUnaryExprOrTypeTraitOperand(QualType ExprType,
     // trait to an incomplete array is an extension.
     if (ExprKind == UETT_AlignOf && !getLangOpts().CPlusPlus &&
         ExprType->isIncompleteArrayType())
-      Diag(OpLoc, getLangOpts().C2y
-                      ? diag::warn_c2y_compat_alignof_incomplete_array
-                      : diag::ext_c2y_alignof_incomplete_array);
+      DiagCompat(OpLoc, diag_compat::alignof_incomplete_array);
     ExprType = Context.getBaseElementType(ExprType);
   }
 
@@ -14822,8 +14816,7 @@ static QualType CheckIncrementDecrementOperand(Sema &S, Expr *Op,
       return QualType();
   } else if (ResType->isAnyComplexType()) {
     // C99 does not support ++/-- on complex types, we allow as an extension.
-    S.Diag(OpLoc, S.getLangOpts().C2y ? diag::warn_c2y_compat_increment_complex
-                                      : diag::ext_c2y_increment_complex)
+    S.DiagCompat(OpLoc, diag_compat::increment_complex)
         << IsInc << Op->getSourceRange();
   } else if (ResType->isPlaceholderType()) {
     ExprResult PR = S.CheckPlaceholderExpr(Op);
diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index 27fee88d20c60..b6592656ca108 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -2267,10 +2267,8 @@ StmtResult Sema::ActOnForStmt(SourceLocation ForLoc, SourceLocation LParenLoc,
         if (VarDecl *VD = dyn_cast<VarDecl>(DI)) {
           VarDeclSeen = true;
           if (VD->isLocalVarDecl() && !VD->hasLocalStorage())
-            Diag(DI->getLocation(),
-                 getLangOpts().C23
-                     ? diag::warn_c17_non_local_variable_decl_in_for
-                     : diag::ext_c23_non_local_variable_decl_in_for);
+            DiagCompat(DI->getLocation(),
+    ...
[truncated]

@Sirraide

Copy link
Copy Markdown
Member Author

CI failures look unrelated; it seems main is just broken atm

@yronglin yronglin left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you working on this! LGTM with a nit.

multiclass CXX29Compat<string message, bit ext_warn = true> : CompatWarning<message, 29, /*cxx=*/true, ext_warn, "2d">;

// C compatibility warnings generally use Extension rather than ExtWarn.
multiclass C99Compat<string message, bit ext_warn = false> : CompatWarning<message, 99, /*cxx=*/false, ext_warn>;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Can we define a CCompat or something else, then we can avoid write a cxx= for every c compatibility warnings.

@bassiounix

Copy link
Copy Markdown
Member

Just leaving a comment here to get notified when this gets merged!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang:diagnostics New/improved warning or error message in Clang, but not in clang-tidy or static analyzer clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants