[clang] [Clang] Use Compat diagnostics for most extension/compatibility warnings (PR #216693)

Nikolas Klauser via cfe-commits cfe-commits at lists.llvm.org
Wed Sep 2 07:59:01 PDT 2026


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/216693

>From ab53f7b82e8162d3100be46a4406baf8a63f405e Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Mon, 17 Aug 2026 11:34:53 +0200
Subject: [PATCH 1/3] [Clang] Use Compat diagnostics for most
 extension/compatibility warnings

---
 .../clang/Basic/DiagnosticCommonKinds.td      |  16 +-
 .../include/clang/Basic/DiagnosticLexKinds.td |  36 +-
 .../clang/Basic/DiagnosticParseKinds.td       | 332 +++++-------------
 .../clang/Basic/DiagnosticSemaKinds.td        | 104 ++----
 clang/include/clang/Lex/Lexer.h               |   2 +
 clang/include/clang/Lex/Preprocessor.h        |   9 +
 clang/lib/Lex/Lexer.cpp                       |   9 +-
 clang/lib/Lex/PPDirectives.cpp                |  22 +-
 clang/lib/Lex/PPExpressions.cpp               |  13 +-
 clang/lib/Parse/ParseCXXInlineMethods.cpp     |  18 +-
 clang/lib/Parse/ParseDecl.cpp                 |  39 +-
 clang/lib/Parse/ParseDeclCXX.cpp              |  88 ++---
 clang/lib/Parse/ParseExpr.cpp                 |  17 +-
 clang/lib/Parse/ParseExprCXX.cpp              |  27 +-
 clang/lib/Parse/ParseObjc.cpp                 |   2 +-
 clang/lib/Parse/ParseOpenMP.cpp               |   2 +-
 clang/lib/Parse/ParseStmt.cpp                 |  42 +--
 clang/lib/Parse/ParseTemplate.cpp             |  16 +-
 clang/lib/Parse/Parser.cpp                    |   8 +-
 clang/lib/Sema/SemaDeclCXX.cpp                |  24 +-
 clang/lib/Sema/SemaExpr.cpp                   |  17 +-
 clang/lib/Sema/SemaLambda.cpp                 |  16 +-
 clang/lib/Sema/SemaStmt.cpp                   |   4 +-
 clang/lib/Sema/SemaTemplate.cpp               |  14 +-
 clang/lib/Sema/SemaTemplateVariadic.cpp       |   4 +-
 clang/lib/Sema/SemaType.cpp                   |   8 +-
 .../basic.lookup.qual/namespace.qual/p2.cpp   |   6 +-
 clang/test/CXX/drs/cwg0xx.cpp                 |  10 +-
 clang/test/CXX/drs/cwg4xx.cpp                 |   6 +-
 clang/test/CXX/stmt.stmt/stmt.select/p3.cpp   |  12 +-
 clang/test/Lexer/cxx2c-raw-strings.cpp        |   8 +-
 clang/test/Parser/cxx0x-in-cxx98.cpp          |   2 +-
 .../test/Parser/cxx0x-lambda-expressions.cpp  |   2 +-
 .../Parser/cxx11-user-defined-literals.cpp    |   2 +-
 .../cxx1z-nested-namespace-definition.cpp     |   2 +-
 clang/test/Sema/static-assert.c               |   2 +-
 clang/test/SemaCXX/cxx98-compat.cpp           |   4 +-
 clang/test/SemaCXX/static-assert-ext.cpp      |   4 +-
 38 files changed, 277 insertions(+), 672 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticCommonKinds.td b/clang/include/clang/Basic/DiagnosticCommonKinds.td
index 192fdf9299eb4..2c9ad1d324f28 100644
--- a/clang/include/clang/Basic/DiagnosticCommonKinds.td
+++ b/clang/include/clang/Basic/DiagnosticCommonKinds.td
@@ -11,6 +11,11 @@
 //===----------------------------------------------------------------------===//
 
 let Component = "Common" in {
+// C++11 compatibility with C++98.
+defm variadic_templates : CXX11Compat<"variadic templates are">;
+
+// C++23 compatibility with C++20 and earlier.
+defm size_t_suffix : CXX23Compat<"'size_t' suffix for literals is">;
 
 // Substitutions.
 
@@ -91,11 +96,6 @@ def warn_method_param_declaration : Warning<"redeclaration of method parameter %
 def err_invalid_storage_class_in_func_decl : Error<
   "invalid storage class specifier in function declarator">;
 def err_expected_namespace_name : Error<"expected namespace name">;
-def ext_variadic_templates : ExtWarn<
-  "variadic templates are a C++11 extension">, InGroup<CXX11>;
-def warn_cxx98_compat_variadic_templates :
-  Warning<"variadic templates are incompatible with C++98">,
-  InGroup<CXX98Compat>, DefaultIgnore;
 def err_default_special_members : Error<
   "only special member functions %select{|and comparison operators }0"
   "may be defaulted">;
@@ -223,12 +223,6 @@ def ext_cxx11_longlong : Extension<
 def warn_cxx98_compat_longlong : Warning<
   "'long long' is incompatible with C++98">,
   InGroup<CXX98CompatPedantic>, DefaultIgnore;
-def ext_cxx23_size_t_suffix : ExtWarn<
-  "'size_t' suffix for literals is a C++23 extension">,
-  InGroup<CXX23>;
-def warn_cxx20_compat_size_t_suffix : Warning<
-  "'size_t' suffix for literals is incompatible with C++ standards before "
-  "C++23">, InGroup<CXXPre23Compat>, DefaultIgnore;
 def err_cxx23_size_t_suffix: Error<
   "'size_t' suffix for literals is a C++23 feature">;
 def err_size_t_literal_too_large: Error<
diff --git a/clang/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td
index f0791ed486a74..bb2800e0cdbc1 100644
--- a/clang/include/clang/Basic/DiagnosticLexKinds.td
+++ b/clang/include/clang/Basic/DiagnosticLexKinds.td
@@ -11,6 +11,17 @@
 //===----------------------------------------------------------------------===//
 
 let Component = "Lex", CategoryName = "Lexical or Preprocessor Issue" in {
+// C23 compatibility with C17 and earlier.
+defm c23_pp_directive : C23Compat<
+  "use of a '#%select{<BUG IF SEEN>|elifdef|elifndef}0' directive is", /*ext_warn*/true>;
+
+// C++23 compatibility with C++20 and earlier.
+defm cxx23_pp_directive : CXX23Compat<
+  "use of a '#%select{<BUG IF SEEN>|elifdef|elifndef}0' directive is">;
+
+// C++26 compatibility with C++23 and earlier.
+defm raw_string_literal_character_set : CXX26Compat<
+  " '%0' in a raw string literal delimiter is", /*ext_warn*/false>;
 
 def null_in_char_or_string : Warning<
   "null character(s) preserved in %select{char|string}0 literal">,
@@ -113,14 +124,6 @@ def warn_cxx98_compat_raw_string_literal : Warning<
   "raw string literals are incompatible with C++98">,
   InGroup<CXX98Compat>, DefaultIgnore;
 
-def warn_cxx26_compat_raw_string_literal_character_set : Warning<
-  " '%0' in a raw string literal delimiter is incompatible "
-  "with standards before C++2c">,
-  InGroup<CXXPre26Compat>, DefaultIgnore;
-def ext_cxx26_raw_string_literal_character_set : Extension<
-  " '%0' in a raw string literal delimiter is a C++2c extension">,
-  InGroup<CXX26>, DefaultIgnore;
-
 def warn_multichar_character_literal : Warning<
   "multi-character character constant">, InGroup<MultiChar>;
 def warn_four_char_character_literal : Warning<
@@ -836,23 +839,6 @@ def warn_cxx98_compat_pp_line_too_big : Warning<
   "#line number greater than 32767 is incompatible with C++98">,
   InGroup<CXX98CompatPedantic>, DefaultIgnore;
 
-def warn_c23_compat_pp_directive : Warning<
-  "use of a '#%select{<BUG IF SEEN>|elifdef|elifndef}0' directive "
-  "is incompatible with C standards before C23">,
-  InGroup<CPre23Compat>, DefaultIgnore;
-def ext_c23_pp_directive : ExtWarn<
-  "use of a '#%select{<BUG IF SEEN>|elifdef|elifndef}0' directive "
-  "is a C23 extension">,
-  InGroup<C23>;
-def warn_cxx23_compat_pp_directive : Warning<
-  "use of a '#%select{<BUG IF SEEN>|elifdef|elifndef}0' directive "
-  "is incompatible with C++ standards before C++23">,
-  InGroup<CXXPre23Compat>, DefaultIgnore;
-def ext_cxx23_pp_directive : ExtWarn<
-  "use of a '#%select{<BUG IF SEEN>|elifdef|elifndef}0' directive "
-  "is a C++23 extension">,
-  InGroup<CXX23>;
-
 def err_pp_visibility_non_macro : Error<"no macro named %0">;
 
 def err_pp_arc_cf_code_audited_syntax : Error<"expected 'begin' or 'end'">;
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 6a48d74079f4e..60ba00e431208 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -12,15 +12,93 @@
 
 let Component = "Parse" in {
 let CategoryName = "Parse Issue" in {
-// C2y compatibility with C89.
+// C23 compatibility with C17 and earlier.
+defm c_label_at_end_of_compound_statement : C23Compat<
+  "label at end of compound statement is", /*ext_warn*/true>;
+defm c_static_assert_no_message : C23Compat<
+  "'_Static_assert' with no message is", /*ext_warn*/true>;
+defm c23_attributes : C23Compat<"[[]] attributes are">;
+defm c_enum_fixed_underlying_type : C23Compat<
+  "enumeration types with a fixed underlying type are">;
+defm label_followed_by_declaration : C23Compat<
+  "label followed by a declaration is">;
+
+// C2y compatibility with C23 and earlier.
 defm decl_statement : C2yCompat<"'%select{if|switch}0' declaration statements are">;
+defm generic_with_type_arg : C2yCompat<
+  "passing a type argument as the first operand to '_Generic' is">;
 
 // C++11 compatibility with C++98.
-defm enum_fixed_underlying_type : CXX11Compat<
+defm cxx_enum_fixed_underlying_type : CXX11Compat<
   "enumeration types with a fixed underlying type are",
   /*ext_warn=*/false>;
+defm for_range : CXX11Compat<"range-based for loop is">;
+defm scoped_enum : CXX11Compat<"scoped enumerations are">;
+defm rvalue_reference : CXX11Compat<"rvalue references are">;
+defm ref_qualifier : CXX11Compat<"reference qualifiers on functions are">;
+defm generalized_initializer_lists : CXX11Compat<
+  "generalized initializer lists are">;
+defm cxx11_attributes : CXX11Compat<"[[]] attributes are", /*ext_warn*/false>;
+defm defaulted_deleted_function : CXX11Compat<
+  "%select{defaulted|deleted}0 function definitions are">;
+defm nonstatic_member_init : CXX11Compat<
+  "default member initializer for non-static data member is">;
+defm alias_declaration : CXX11Compat<"alias declarations are">;
+defm override_control_keyword : CXX11Compat<"'%0' keyword is">;
+defm inline_namespace : CXX11Compat<"inline namespaces are">;
+
+// C++14 compatibility with C++11 and earlier.
+defm decltype_auto_type_specifier : CXX14Compat<
+  "'decltype(auto)' type specifier is">;
 }
 
+// C++17 compatibility with C++14 and earlier.
+defm cxx_static_assert_no_message : CXX17Compat<
+  "'static_assert' with no message is">;
+defm constexpr_if : CXX17Compat<"constexpr if is">;
+defm init_statement : CXX17Compat<
+  "'%select{if|switch}0' initialization statements are">;
+defm ns_enum_attribute : CXX17Compat<
+  "attributes on %select{a namespace|an enumerator}0 declaration are",
+  /*ext_warn*/false>;
+defm using_attribute_ns : CXX17Compat<
+  "default scope specifier for attributes is">;
+defm template_template_param_typename : CXX17Compat<
+  "template template parameter using 'typename' is">;
+defm fold_expression : CXX17Compat<"pack fold expression is">;
+defm multi_using_declaration : CXX17Compat<
+  "use of multiple declarators in a single using declaration is">;
+defm using_declaration_pack : CXX17Compat<
+  "pack expansion of using declaration is">;
+defm constexpr_on_lambda : CXX17Compat<"'constexpr' on lambda expressions is">;
+
+// C++20 compatibility with C++17 and earlier.
+defm using_enum_declaration : CXX20Compat<"using enum declaration is">;
+defm for_range_init_stmt : CXX20Compat<
+  "range-based for loop initialization statements are">;
+defm explicit_bool : CXX20Compat<"explicit(bool) is">;
+defm bitfield_member_init : CXX20Compat<
+  "default member initializer for bit-field is">;
+defm lambda_template_parameter_list : CXX20Compat<
+  "explicit template parameter list for lambdas is">;
+
+// C++23 compatibility with C++20 and earlier.
+defm cxx_label_at_end_of_compound_statement : CXX23Compat<
+  "label at end of compound statement is">;
+defm alias_in_init_statement : CXX23Compat<
+  "alias declaration in this context is">;
+defm consteval_if : CXX23Compat<"consteval if is">;
+defm static_lambda : CXX23Compat<"static lambdas are">;
+
+// C++26 compatibility with C++23 and earlier.
+defm static_assert_user_generated_message : CXX26Compat<
+  "'static_assert' with a user-generated message is">;
+defm attrs_on_binding : CXX26Compat<
+  "an attribute specifier sequence attached to a structured binding declaration is">;
+defm delete_with_message : CXX26Compat<"'= delete' with a message is", /*ext_warn*/false>;
+defm variadic_friends : CXX26Compat<"variadic 'friend' declarations are">;
+defm binding_pack : CXX26Compat<"structured binding packs are">;
+
 def err_asm_qualifier_ignored : Error<
   "expected 'volatile', 'inline', 'goto', or '('">, CatInlineAsm;
 def err_global_asm_qualifier_ignored : Error<
@@ -122,12 +200,6 @@ def ext_ms_c_enum_fixed_underlying_type : Extension<
 def ext_ms_c_empty_enum_type : Extension<
   "empty enumeration types are a Microsoft extension">,
   InGroup<MicrosoftEmptyEnum>;
-def ext_c23_enum_fixed_underlying_type : Extension<
-  "enumeration types with a fixed underlying type are a C23 extension">,
-  InGroup<C23>;
-def warn_c17_compat_enum_fixed_underlying_type : Warning<
-  "enumeration types with a fixed underlying type are incompatible with C standards before C23">,
-  DefaultIgnore, InGroup<CPre23Compat>;
 def ext_enum_base_in_type_specifier : ExtWarn<
   "non-defining declaration of enumeration with a fixed underlying type is "
   "only permitted as a standalone declaration"
@@ -138,11 +210,6 @@ def ext_elaborated_enum_class : ExtWarn<
   InGroup<DiagGroup<"elaborated-enum-class">>, DefaultError;
 def err_scoped_enum_missing_identifier : Error<
   "scoped enumeration requires a name">;
-def ext_scoped_enum : ExtWarn<
-  "scoped enumerations are a C++11 extension">, InGroup<CXX11>;
-def warn_cxx98_compat_scoped_enum : Warning<
-  "scoped enumerations are incompatible with C++98">,
-  InGroup<CXX98Compat>, DefaultIgnore;
 def err_anonymous_enum_bitfield : Error<
   "ISO C++ only allows ':' in member enumeration declaration to introduce "
   "a fixed underlying type, not an anonymous bit-field">;
@@ -166,12 +233,6 @@ def err_duplicate_default_assoc : Error<
   "duplicate default generic association">;
 def note_previous_default_assoc : Note<
   "previous default generic association is here">;
-def ext_c2y_generic_with_type_arg : Extension<
-  "passing a type argument as the first operand to '_Generic' is a C2y "
-  "extension">, InGroup<C2y>;
-def warn_c2y_compat_generic_with_type_arg : Warning<
-  "passing a type argument as the first operand to '_Generic' is incompatible "
-  "with C standards before C2y">, InGroup<CPre2yCompat>, DefaultIgnore;
 
 def ext_c99_feature : Extension<
   "'%0' is a C99 extension">, InGroup<C99>;
@@ -334,24 +395,6 @@ def note_missing_selector_name : Note<
 def note_force_empty_selector_name : Note<
   "or insert whitespace before ':' to use %0 as parameter name "
   "and have an empty entry in the selector">;
-def ext_c_label_followed_by_declaration : ExtWarn<
-  "label followed by a declaration is a C23 extension">,
-  InGroup<C23>;
-def warn_c23_compat_label_followed_by_declaration : Warning<
-  "label followed by a declaration is incompatible with C standards before "
-  "C23">, InGroup<CPre23Compat>, DefaultIgnore;
-def ext_c_label_end_of_compound_statement : ExtWarn<
-  "label at end of compound statement is a C23 extension">,
-   InGroup<C23>;
-def ext_cxx_label_end_of_compound_statement : ExtWarn<
-  "label at end of compound statement is a C++23 extension">,
-   InGroup<CXX23>;
-def warn_c23_compat_label_end_of_compound_statement : Warning<
-  "label at end of compound statement is incompatible with C standards before C23">,
-  InGroup<CPre23Compat>, DefaultIgnore;
-def warn_cxx20_compat_label_end_of_compound_statement : Warning<
-  "label at end of compound statement is incompatible with C++ standards before C++23">,
-  InGroup<CXXPre23Compat>, DefaultIgnore;
 def err_address_of_label_outside_fn : Error<
   "use of address-of-label extension outside of a function body">;
 def err_asm_operand_wide_string_literal : Error<
@@ -378,27 +421,6 @@ def err_invalid_reference_qualifier_application : Error<
   "'%0' qualifier may not be applied to a reference">;
 def err_illegal_decl_reference_to_reference : Error<
   "%0 declared as a reference to a reference">;
-def ext_rvalue_reference : ExtWarn<
-  "rvalue references are a C++11 extension">, InGroup<CXX11>;
-def warn_cxx98_compat_rvalue_reference : Warning<
-  "rvalue references are incompatible with C++98">,
-  InGroup<CXX98Compat>, DefaultIgnore;
-def ext_ref_qualifier : ExtWarn<
-  "reference qualifiers on functions are a C++11 extension">, InGroup<CXX11>;
-def warn_cxx98_compat_ref_qualifier : Warning<
-  "reference qualifiers on functions are incompatible with C++98">,
-  InGroup<CXX98Compat>, DefaultIgnore;
-def ext_inline_namespace : ExtWarn<
-  "inline namespaces are a C++11 feature">, InGroup<CXX11InlineNamespace>;
-def warn_cxx98_compat_inline_namespace : Warning<
-  "inline namespaces are incompatible with C++98">,
-  InGroup<CXX98Compat>, DefaultIgnore;
-def ext_generalized_initializer_lists : ExtWarn<
-  "generalized initializer lists are a C++11 extension">,
-  InGroup<CXX11>;
-def warn_cxx98_compat_generalized_initializer_lists : Warning<
-  "generalized initializer lists are incompatible with C++98">,
-  InGroup<CXX98Compat>, DefaultIgnore;
 def err_init_list_bin_op : Error<"initializer list cannot be used on the "
   "%select{left|right}0 hand side of operator '%1'">;
 def warn_cxx98_compat_trailing_return_type : Warning<
@@ -413,19 +435,9 @@ def err_requires_clause_inside_parens : Error<
 def ext_auto_storage_class : ExtWarn<
   "'auto' storage class specifier is not permitted in C++11, and will not "
   "be supported in future releases">, InGroup<DiagGroup<"auto-storage-class">>;
-def ext_decltype_auto_type_specifier : ExtWarn<
-  "'decltype(auto)' type specifier is a C++14 extension">, InGroup<CXX14>;
-def warn_cxx11_compat_decltype_auto_type_specifier : Warning<
-  "'decltype(auto)' type specifier is incompatible with C++ standards before "
-  "C++14">, InGroup<CXXPre14Compat>, DefaultIgnore;
 def ext_auto_type : Extension<
   "'__auto_type' is a GNU extension">,
   InGroup<GNUAutoType>;
-def ext_for_range : ExtWarn<
-  "range-based for loop is a C++11 extension">, InGroup<CXX11>;
-def warn_cxx98_compat_for_range : Warning<
-  "range-based for loop is incompatible with C++98">,
-  InGroup<CXX98Compat>, DefaultIgnore;
 def err_for_range_identifier : Error<
   "%select{range-based for loop|expansion statement}0 requires "
   "type for %select{loop|expansion}0 variable">;
@@ -503,24 +515,6 @@ def err_bool_redeclaration : Error<
 def warn_cxx98_compat_static_assert : Warning<
   "'static_assert' declarations are incompatible with C++98">,
   InGroup<CXX98Compat>, DefaultIgnore;
-def ext_cxx_static_assert_no_message : ExtWarn<
-  "'static_assert' with no message is a C++17 extension">, InGroup<CXX17>;
-def ext_c_static_assert_no_message : ExtWarn<
-  "'_Static_assert' with no message is a C23 extension">, InGroup<C23>;
-def warn_cxx14_compat_static_assert_no_message : Warning<
-  "'static_assert' with no message is incompatible with C++ standards before "
-  "C++17">,
-  DefaultIgnore, InGroup<CXXPre17Compat>;
-def warn_c17_compat_static_assert_no_message : Warning<
-  "'_Static_assert' with no message is incompatible with C standards before "
-  "C23">,
-  DefaultIgnore, InGroup<CPre23Compat>;
-def ext_cxx_static_assert_user_generated_message : ExtWarn<
-  "'static_assert' with a user-generated message is a C++26 extension">,
-  InGroup<CXX26>;
-def warn_cxx20_compat_static_assert_user_generated_message : Warning<
-  "'static_assert' with a user-generated message is incompatible with "
-  "C++ standards before C++26">, DefaultIgnore, InGroup<CXXPre26Compat>;
 def err_function_definition_not_allowed : Error<
   "function definition is not allowed here">;
 def err_expected_end_of_enumerator : Error<
@@ -535,15 +529,6 @@ def ext_decomp_decl_empty : ExtWarn<
 def err_function_parameter_limit_exceeded : Error<
   "too many function parameters; subsequent parameters will be ignored">;
 
-// C++26 structured bindings
-def ext_decl_attrs_on_binding : ExtWarn<
-  "an attribute specifier sequence attached to a structured binding declaration "
-  "is a C++2c extension">, InGroup<CXX26>;
-def warn_cxx23_compat_decl_attrs_on_binding : Warning<
-  "an attribute specifier sequence attached to a structured binding declaration "
-  "is incompatible with C++ standards before C++2c">,
-  InGroup<CXXPre26Compat>, DefaultIgnore;
-
 /// Objective-C parser diagnostics
 def err_expected_minus_or_plus : Error<
   "method type specifier must start with '-' or '+'">;
@@ -645,12 +630,6 @@ def err_expected_init_in_condition_lparen : Error<
   "variable declaration in condition cannot have a parenthesized initializer">;
 def err_extraneous_rparen_in_condition : Error<
   "extraneous ')' after condition, expected a statement">;
-def ext_alias_in_init_statement : ExtWarn<
-  "alias declaration in this context is a C++23 extension">,
-  InGroup<CXX23>;
-def warn_cxx20_alias_in_init_statement  : Warning<
-  "alias declaration in this context is incompatible with C++ standards before C++23">,
-  DefaultIgnore, InGroup<CXXPre23Compat>;
 def warn_dangling_else : Warning<
   "add explicit braces to avoid dangling else">,
   InGroup<DanglingElse>;
@@ -682,12 +661,6 @@ def warn_cxx98_compat_noexcept_decl : Warning<
 def err_expected_catch : Error<"expected catch">;
 def err_using_namespace_in_class : Error<
   "'using namespace' is not allowed in classes">;
-def warn_cxx17_compat_using_enum_declaration : Warning<
-  "using enum declaration is incompatible with C++ standards before C++20">,
-  InGroup<CXXPre20Compat>, DefaultIgnore;
-def ext_using_enum_declaration : ExtWarn<
-  "using enum declaration is a C++20 extension">,
-  InGroup<CXX20>;
 def err_using_enum_expect_identifier : Error<
   "using enum %select{requires an enum or typedef name|"
   "does not permit an elaborated enum specifier}0">;
@@ -726,30 +699,7 @@ def err_function_is_not_record : Error<
   "unexpected %0 in function call; perhaps remove the %0?">;
 def err_super_in_using_declaration : Error<
   "'__super' cannot be used with a using declaration">;
-def ext_constexpr_if : ExtWarn<
-  "constexpr if is a C++17 extension">, InGroup<CXX17>;
-def warn_cxx14_compat_constexpr_if : Warning<
-  "constexpr if is incompatible with C++ standards before C++17">,
-  DefaultIgnore, InGroup<CXXPre17Compat>;
-def ext_consteval_if : ExtWarn<
-  "consteval if is a C++23 extension">,
-   InGroup<CXX23>;
-def warn_cxx20_compat_consteval_if : Warning<
-  "consteval if is incompatible with C++ standards before C++23">,
-  InGroup<CXXPre23Compat>, DefaultIgnore;
 
-def ext_init_statement : ExtWarn<
-  "'%select{if|switch}0' initialization statements are a C++17 extension">,
-  InGroup<CXX17>;
-def warn_cxx14_compat_init_statement : Warning<
-  "%select{if|switch}0 initialization statements are incompatible with "
-  "C++ standards before C++17">, DefaultIgnore, InGroup<CXXPre17Compat>;
-def ext_for_range_init_stmt : ExtWarn<
-  "range-based for loop initialization statements are a C++20 extension">,
-  InGroup<CXX20>;
-def warn_cxx17_compat_for_range_init_stmt : Warning<
-  "range-based for loop initialization statements are incompatible with "
-  "C++ standards before C++20">, DefaultIgnore, InGroup<CXXPre20Compat>;
 def warn_empty_init_statement : Warning<
   "empty initialization statement of '%select{if|switch|range-based for}0' "
   "has no effect">, InGroup<EmptyInitStatement>, DefaultIgnore;
@@ -789,39 +739,14 @@ def ext_c_nullptr : Extension<
 def warn_wrong_clang_attr_namespace : Warning<
   "'__clang__' is a predefined macro name, not an attribute scope specifier; "
   "did you mean '_Clang' instead?">, InGroup<IgnoredAttributes>;
-def ext_ns_enum_attribute : Extension<
-  "attributes on %select{a namespace|an enumerator}0 declaration are "
-  "a C++17 extension">, InGroup<CXX17>;
-def warn_cxx14_compat_ns_enum_attribute : Warning<
-  "attributes on %select{a namespace|an enumerator}0 declaration are "
-  "incompatible with C++ standards before C++17">,
-  InGroup<CXXPre17CompatPedantic>, DefaultIgnore;
 def warn_cxx98_compat_alignas : Warning<"'alignas' is incompatible with C++98">,
   InGroup<CXX98Compat>, DefaultIgnore;
-def warn_cxx98_compat_attribute : Warning<
-  "[[]] attributes are incompatible with C++ standards before C++11">,
-  InGroup<CXX98Compat>, DefaultIgnore;
-def warn_ext_cxx11_attributes : Extension<
-  "[[]] attributes are a C++11 extension">,
-  InGroup<CXX11>;
-def warn_pre_c23_compat_attributes : Warning<
-  "[[]] attributes are incompatible with C standards before C23">,
-  DefaultIgnore, InGroup<CPre23Compat>;
-def warn_ext_c23_attributes : Extension<
-  "[[]] attributes are a C23 extension">,
-  InGroup<C23>;
 def err_cxx11_attribute_forbids_arguments : Error<
   "attribute %0 cannot have an argument list">;
 def err_attribute_requires_arguments : Error<
   "parentheses must be omitted if %0 attribute's argument list is empty">;
 def err_cxx11_attribute_forbids_ellipsis : Error<
   "attribute %0 cannot be used as an attribute pack">;
-def warn_cxx14_compat_using_attribute_ns : Warning<
-  "default scope specifier for attributes is incompatible with C++ standards "
-  "before C++17">, InGroup<CXXPre17Compat>, DefaultIgnore;
-def ext_using_attribute_ns : ExtWarn<
-  "default scope specifier for attributes is a C++17 extension">,
-  InGroup<CXX17>;
 def err_using_attribute_ns_conflict : Error<
   "attribute with scope specifier cannot follow default scope specifier">;
 def err_attributes_not_allowed : Error<"an attribute list cannot appear here">;
@@ -866,11 +791,6 @@ def err_assume_attr_expects_cond_expr : Error<
 def warn_cxx20_compat_explicit_bool : Warning<
   "this expression will be parsed as explicit(bool) in C++20">,
   InGroup<CXX20Compat>, DefaultIgnore;
-def warn_cxx17_compat_explicit_bool : Warning<
-  "explicit(bool) is incompatible with C++ standards before C++20">,
-  InGroup<CXXPre20Compat>, DefaultIgnore;
-def ext_explicit_bool : ExtWarn<"explicit(bool) is a C++20 extension">,
-  InGroup<CXX20>;
 
 /// C++ Templates
 def err_expected_template : Error<"expected template">;
@@ -881,13 +801,6 @@ def err_expected_comma_greater : Error<
 def err_class_on_template_template_param
     : Error<"template template parameter requires 'class'%select{| or "
             "'typename'}0 after the parameter list">;
-def ext_template_template_param_typename : ExtWarn<
-  "template template parameter using 'typename' is a C++17 extension">,
-  InGroup<CXX17>;
-def warn_cxx14_compat_template_template_param_typename : Warning<
-  "template template parameter using 'typename' is "
-  "incompatible with C++ standards before C++17">,
-  InGroup<CXXPre17Compat>, DefaultIgnore;
 def err_template_spec_syntax_non_template : Error<
   "identifier followed by '<' indicates a class template specialization but "
   "%0 %select{does not refer to a template|refers to a function template|"
@@ -994,12 +907,6 @@ def err_default_template_template_parameter_not_template : Error<
   "default template argument for a template template parameter must be a class "
   "template">;
 
-def ext_fold_expression : ExtWarn<
-  "pack fold expression is a C++17 extension">,
-  InGroup<CXX17>;
-def warn_cxx14_compat_fold_expression : Warning<
-  "pack fold expression is incompatible with C++ standards before C++17">,
-  InGroup<CXXPre17Compat>, DefaultIgnore;
 def err_expected_fold_operator : Error<
   "expected a foldable binary operator in fold expression">;
 def err_fold_operator_mismatch : Error<
@@ -1016,52 +923,16 @@ def err_missing_whitespace_digraph : Error<
   "%select{template name|addrspace_cast|const_cast|dynamic_cast|reinterpret_cast|static_cast}0"
   " which forms the digraph '<:' (aka '[') and a ':', did you mean '< ::'?">;
 
-def ext_defaulted_deleted_function : ExtWarn<
-  "%select{defaulted|deleted}0 function definitions are a C++11 extension">,
-  InGroup<CXX11>;
-def warn_cxx98_compat_defaulted_deleted_function : Warning<
-  "%select{defaulted|deleted}0 function definitions are incompatible with C++98">,
-  InGroup<CXX98Compat>, DefaultIgnore;
-
-def ext_delete_with_message : Extension<
-  "'= delete' with a message is a C++2c extension">, InGroup<CXX26>;
-def warn_cxx23_delete_with_message : Warning<
-  "'= delete' with a message is incompatible with C++ standards before C++2c">,
-  DefaultIgnore, InGroup<CXXPre26Compat>;
-
-def ext_variadic_friends : ExtWarn<
-  "variadic 'friend' declarations are a C++2c extension">, InGroup<CXX26>;
-def warn_cxx23_variadic_friends : Warning<
-  "variadic 'friend' declarations are incompatible with C++ standards before C++2c">,
-  DefaultIgnore, InGroup<CXXPre26Compat>;
-
 def err_friend_concept : Error<
   "friend declaration cannot be a concept">;
 
 // C++11 default member initialization
-def ext_nonstatic_member_init : ExtWarn<
-  "default member initializer for non-static data member is a C++11 "
-  "extension">, InGroup<CXX11>;
-def warn_cxx98_compat_nonstatic_member_init : Warning<
-  "default member initializer for non-static data members is incompatible with "
-  "C++98">, InGroup<CXX98Compat>, DefaultIgnore;
-def ext_bitfield_member_init: ExtWarn<
-  "default member initializer for bit-field is a C++20 extension">,
-  InGroup<CXX20>;
-def warn_cxx17_compat_bitfield_member_init: Warning<
-  "default member initializer for bit-field is incompatible with "
-  "C++ standards before C++20">, InGroup<CXXPre20Compat>, DefaultIgnore;
 def err_anon_bitfield_member_init : Error<
   "anonymous bit-field cannot have a default member initializer">;
 def err_incomplete_array_member_init: Error<
   "array bound cannot be deduced from a default member initializer">;
 
 // C++11 alias-declaration
-def ext_alias_declaration : ExtWarn<
-  "alias declarations are a C++11 extension">, InGroup<CXX11>;
-def warn_cxx98_compat_alias_declaration : Warning<
-  "alias declarations are incompatible with C++98">,
-  InGroup<CXX98Compat>, DefaultIgnore;
 def err_alias_declaration_not_identifier : Error<
   "name defined in alias declaration must be an identifier">;
 def err_alias_declaration_specialization : Error<
@@ -1069,26 +940,7 @@ def err_alias_declaration_specialization : Error<
 def err_alias_declaration_pack_expansion : Error<
   "alias declaration cannot be a pack expansion">;
 
-// C++17 using-declaration pack expansions
-def ext_multi_using_declaration : ExtWarn<
-  "use of multiple declarators in a single using declaration is "
-  "a C++17 extension">, InGroup<CXX17>;
-def warn_cxx17_compat_multi_using_declaration : Warning<
-  "use of multiple declarators in a single using declaration is "
-  "incompatible with C++ standards before C++17">,
-  InGroup<CXXPre17Compat>, DefaultIgnore;
-def ext_using_declaration_pack : ExtWarn<
-  "pack expansion of using declaration is a C++17 extension">, InGroup<CXX17>;
-def warn_cxx17_compat_using_declaration_pack : Warning<
-  "pack expansion using declaration is incompatible with C++ standards "
-  "before C++17">, InGroup<CXXPre17Compat>, DefaultIgnore;
-
 // C++11 override control
-def ext_override_control_keyword : ExtWarn<
-  "'%0' keyword is a C++11 extension">, InGroup<CXX11>;
-def warn_cxx98_compat_override_control_keyword : Warning<
-  "'%0' keyword is incompatible with C++98">,
-  InGroup<CXX98Compat>, DefaultIgnore;
 def err_override_control_interface : Error<
   "'%0' keyword not permitted with interface types">;
 def ext_ms_sealed_keyword : ExtWarn<
@@ -1144,12 +996,6 @@ def err_binding_multiple_ellipses : Error<
   "multiple packs in structured binding declaration">;
 def note_previous_ellipsis : Note<
   "previous binding pack specified here">;
-def ext_cxx_binding_pack : ExtWarn<
-  "structured binding packs are a C++2c extension ">,
-  InGroup<CXX26>;
-def warn_cxx23_compat_binding_pack : Warning<
-  "structured binding packs are incompatible with C++ standards before C++2c">,
-  InGroup<CXXPre26Compat>, DefaultIgnore;
 def err_capture_default_first : Error<
   "capture default must be first">;
 def ext_decl_attrs_on_lambda : ExtWarn<
@@ -1167,29 +1013,11 @@ def warn_cxx20_compat_decl_attrs_on_lambda : Warning<
 def err_expected_star_this_capture : Error<
   "expected 'this' following '*' in lambda capture list">;
 
-// C++17 constexpr lambda expressions
-def warn_cxx14_compat_constexpr_on_lambda : Warning<
-  "constexpr on lambda expressions is incompatible with C++ standards before C++17">,
-  InGroup<CXXPre17Compat>, DefaultIgnore;
-def ext_constexpr_on_lambda_cxx17 : ExtWarn<
-  "'constexpr' on lambda expressions is a C++17 extension">, InGroup<CXX17>;
-
 // C++20 template lambdas
-def ext_lambda_template_parameter_list: ExtWarn<
-  "explicit template parameter list for lambdas is a C++20 extension">,
-  InGroup<CXX20>;
-def warn_cxx17_compat_lambda_template_parameter_list: Warning<
-  "explicit template parameter list for lambdas is incompatible with "
-  "C++ standards before C++20">, InGroup<CXXPre20Compat>, DefaultIgnore;
 def err_lambda_template_parameter_list_empty : Error<
   "lambda template parameter list cannot be empty">;
 
 // C++23 static lambdas
-def err_static_lambda: ExtWarn<
-  "static lambdas are a C++23 extension">, InGroup<CXX23>;
-def warn_cxx20_compat_static_lambda : Warning<
-  "static lambdas are incompatible with C++ standards before C++23">,
-  InGroup<CXXPre23Compat>, DefaultIgnore;
 def err_static_mutable_lambda : Error<
   "lambda cannot be both mutable and static">;
 def err_static_lambda_captures : Error<
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index b314c17ad27bd..9b9b98ca5b5f0 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -31,6 +31,13 @@ defm templ_default_in_function_templ : CXX11Compat<
 defm template_arg_extra_parens : CXX11Compat<
   "parentheses around address non-type template argument are">;
 defm typename_outside_of_template : CXX11Compat<"'typename' outside of a template is">;
+defm template_arg_object_internal : CXX11Compat<
+  "non-type template argument referring to %select{function|object}0 %1 with "
+  "internal linkage is">;
+defm template_outside_of_template : CXX11Compat<
+  "use of 'template' keyword outside of a template is">;
+defm explicit_conversion_functions : CXX11Compat<
+  "explicit conversion functions are">;
 
 // C++14 compatibility with C++11 and earlier.
 defm constexpr_type_definition : CXX14Compat<
@@ -40,10 +47,14 @@ defm constexpr_local_var : CXX14Compat<
 defm constexpr_body_multiple_return : CXX14Compat<
   "multiple return statements in constexpr function is">;
 defm variable_template : CXX14Compat<"variable templates are">;
+defm init_capture : CXX14Compat<"initialized lambda captures are">;
 
 // C++17 compatibility with C++14 and earlier.
 defm decomp_decl : CXX17Compat<"structured binding declarations are">;
 defm inline_variable : CXX17Compat<"inline variables are">;
+defm for_range_begin_end_types_differ : CXX17Compat<
+  "'begin' and 'end' returning different types (%0 and %1) is">;
+defm star_this_lambda_capture : CXX17Compat<"by value capture of '*this' is">;
 
 // C++20 compatibility with C++17 and earlier.
 defm decomp_decl_spec
@@ -63,17 +74,27 @@ defm ctad_for_alias_templates
     : CXX20Compat<"class template argument deduction for alias templates is">;
 defm implicit_typename
     : CXX20Compat<"missing 'typename' prior to dependent type name %0 is">;
-
 defm auto_param : CXX20Compat<"'auto' parameters are">;
+defm using_decl_scoped_enumerator : CXX20Compat<
+  "using declaration naming a scoped enumerator is">;
+defm init_capture_pack : CXX20Compat<"initialized lambda capture packs are">;
+defm equals_this_lambda_capture : CXX20Compat<
+  "explicit capture of 'this' with a capture default of '=' is">;
+defm capture_binding : CXX20Compat<"captured structured bindings are">;
+defm defaulted_comparison : CXX20Compat<"defaulted comparison operators are">;
 
 // C++23 compatibility with C++20 and earlier.
 defm constexpr_static_var : CXX23Compat<
   "definition of a %select{static|thread_local}1 variable "
   "in a constexpr %select{function|constructor}0 "
   "is">;
+defm operator_overload_static : CXX23Compat<
+  "declaring overloaded %0 as 'static' is">;
 
 // C++26 compatibility with C++23 and earlier.
 defm decomp_decl_cond : CXX26Compat<"structured binding declaration in a condition is">;
+defm pack_indexing : CXX26Compat<"pack indexing is">;
+defm placeholder_var_definition : CXX26Compat<"placeholder variables are">;
 
 // Compatibility warnings duplicated across multiple language versions.
 foreach std = [14, 20, 23] in {
@@ -715,12 +736,6 @@ def err_using_decl_can_not_refer_to_namespace : Error<
   "using declaration cannot refer to a namespace">;
 def note_namespace_using_decl : Note<
   "did you mean 'using namespace'?">;
-def warn_cxx17_compat_using_decl_scoped_enumerator: Warning<
-  "using declaration naming a scoped enumerator is incompatible with "
-  "C++ standards before C++20">, InGroup<CXXPre20Compat>, DefaultIgnore;
-def ext_using_decl_scoped_enumerator : ExtWarn<
-  "using declaration naming a scoped enumerator is a C++20 extension">,
-  InGroup<CXX20>;
 def err_using_decl_constructor : Error<
   "using declaration cannot refer to a constructor">;
 def warn_cxx98_compat_using_decl_constructor : Warning<
@@ -2970,12 +2985,6 @@ def err_for_range_incomplete_type : Error<
   "cannot use incomplete type %0 as a range">;
 def err_for_range_iter_deduction_failure : Error<
   "cannot use type %0 as an iterator">;
-def ext_for_range_begin_end_types_differ : ExtWarn<
-  "'begin' and 'end' returning different types (%0 and %1) is a C++17 extension">,
-  InGroup<CXX17>;
-def warn_for_range_begin_end_types_differ : Warning<
-  "'begin' and 'end' returning different types (%0 and %1) is incompatible "
-  "with C++ standards before C++17">, InGroup<CXXPre17Compat>, DefaultIgnore;
 def note_in_for_range: Note<
   "when looking up '%select{begin|end}0' function for range expression "
   "of type %1">;
@@ -5789,13 +5798,6 @@ def err_template_arg_method : Error<
 def err_template_arg_object_no_linkage : Error<
   "non-type template argument refers to %select{function|object}0 %1 that "
   "does not have linkage">;
-def warn_cxx98_compat_template_arg_object_internal : Warning<
-  "non-type template argument referring to %select{function|object}0 %1 with "
-  "internal linkage is incompatible with C++98">,
-  InGroup<CXX98Compat>, DefaultIgnore;
-def ext_template_arg_object_internal : ExtWarn<
-  "non-type template argument referring to %select{function|object}0 %1 with "
-  "internal linkage is a C++11 extension">, InGroup<CXX11>;
 def err_template_arg_thread_local : Error<
   "non-type template argument refers to thread-local object">;
 def note_template_arg_internal_object : Note<
@@ -6245,11 +6247,6 @@ def note_referenced_type_template : Note<
   "%select{class|type alias}0 template declared here">;
 def err_template_kw_missing
     : Error<"missing 'template' keyword prior to dependent template name %0">;
-def ext_template_outside_of_template : ExtWarn<
-  "'template' keyword outside of a template">, InGroup<CXX11>;
-def warn_cxx98_compat_template_outside_of_template : Warning<
-  "use of 'template' keyword outside of a template is incompatible with C++98">,
-  InGroup<CXX98Compat>, DefaultIgnore;
 
 def err_non_type_template_in_nested_name_specifier : Error<
   "qualified name refers into a specialization of %select{function|variable}0 "
@@ -6320,12 +6317,6 @@ def err_expected_name_of_pack : Error<
 def err_pack_index_out_of_bound : Error<
   "invalid index %0 for pack %1 of size %2">;
 
-def ext_pack_indexing : ExtWarn<
-  "pack indexing is a C++2c extension">, InGroup<CXX26>;
-def warn_cxx23_pack_indexing : Warning<
-  "pack indexing is incompatible with C++ standards before C++2c">,
-  DefaultIgnore, InGroup<CXXPre26Compat>;
-
 def err_pack_outside_template : Error<
   "pack declaration outside of template">;
 
@@ -7533,11 +7524,6 @@ def err_using_placeholder_variable : Error<
   "ambiguous reference to placeholder '_', which is defined multiple times">;
 def note_reference_placeholder : Note<
   "placeholder declared here">;
-def ext_placeholder_var_definition : ExtWarn<
-  "placeholder variables are a C++2c extension">, InGroup<CXX26>;
-def warn_cxx23_placeholder_var_definition : Warning<
-  "placeholder variables are incompatible with C++ standards before C++2c">,
-  DefaultIgnore, InGroup<CXXPre26Compat>;
 
 def ext_sizeof_alignof_function_type : Extension<
   "invalid application of '%0' to a function type">, InGroup<PointerArith>;
@@ -8969,11 +8955,6 @@ let CategoryName = "Lambda Issue" in {
     "%select{| explicitly}1 captured here">;
 
   // C++14 lambda init-captures.
-  def warn_cxx11_compat_init_capture : Warning<
-    "initialized lambda captures are incompatible with C++ standards "
-    "before C++14">, InGroup<CXXPre14Compat>, DefaultIgnore;
-  def ext_init_capture : ExtWarn<
-    "initialized lambda captures are a C++14 extension">, InGroup<CXX14>;
   def err_init_capture_no_expression : Error<
     "initializer missing for lambda capture %0">;
   def err_init_capture_multiple_expressions : Error<
@@ -8985,11 +8966,6 @@ let CategoryName = "Lambda Issue" in {
     "cannot deduce type for lambda capture %0 from initializer of type %2">;
   def err_init_capture_deduction_failure_from_init_list : Error<
     "cannot deduce type for lambda capture %0 from initializer list">;
-  def warn_cxx17_compat_init_capture_pack : Warning<
-    "initialized lambda capture packs are incompatible with C++ standards "
-    "before C++20">, InGroup<CXXPre20Compat>, DefaultIgnore;
-  def ext_init_capture_pack : ExtWarn<
-    "initialized lambda pack captures are a C++20 extension">, InGroup<CXX20>;
 
   // C++14 generic lambdas.
   def warn_cxx11_compat_generic_lambda : Warning<
@@ -8998,24 +8974,11 @@ let CategoryName = "Lambda Issue" in {
   def err_lambda_explicit_temp_spec : Error<
     "a member of a lambda should not be explicitly %select{specialized|instantiated}0">;
 
-  // C++17 '*this' captures.
-  def warn_cxx14_compat_star_this_lambda_capture : Warning<
-    "by value capture of '*this' is incompatible with C++ standards before C++17">,
-     InGroup<CXXPre17Compat>, DefaultIgnore;
-  def ext_star_this_lambda_capture_cxx17 : ExtWarn<
-    "capture of '*this' by copy is a C++17 extension">, InGroup<CXX17>;
-
   // C++17 parameter shadows capture
   def err_parameter_shadow_capture : Error<
     "a lambda parameter cannot shadow an explicitly captured entity">;
 
   // C++20 [=, this] captures.
-  def warn_cxx17_compat_equals_this_lambda_capture : Warning<
-    "explicit capture of 'this' with a capture default of '=' is incompatible "
-    "with C++ standards before C++20">, InGroup<CXXPre20Compat>, DefaultIgnore;
-  def ext_equals_this_lambda_capture_cxx20 : ExtWarn<
-    "explicit capture of 'this' with a capture default of '=' "
-    "is a C++20 extension">, InGroup<CXX20>;
   def warn_deprecated_this_capture : Warning<
     "implicit capture of 'this' with a capture default of '=' is deprecated">,
     InGroup<DeprecatedThisCapture>;
@@ -10331,12 +10294,6 @@ def err_local_nested_class_invalid_scope : Error<
   "nested local class %0 must be defined in the same block scope as %1">;
 def err_capture_binding_openmp : Error<
   "capturing a structured binding is not yet supported in OpenMP">;
-def ext_capture_binding : ExtWarn<
-  "captured structured bindings are a C++20 extension">, InGroup<CXX20>;
-def warn_cxx17_compat_capture_binding : Warning<
-  "captured structured bindings are incompatible with "
-  "C++ standards before C++20">,
-  InGroup<CXXPre20Compat>, DefaultIgnore;
 
 def err_static_data_member_not_allowed_in_local_class : Error<
   "static data member %0 not allowed in local %sub{select_tag_type_kind}2 %1">;
@@ -10388,11 +10345,6 @@ def err_operator_overload_needs_class_or_enum : Error<
   "or enumeration type">;
 
 def err_operator_overload_variadic : Error<"overloaded %0 cannot be variadic">;
-def warn_cxx20_compat_operator_overload_static : Warning<
-  "declaring overloaded %0 as 'static' is incompatible with C++ standards "
-  "before C++23">, InGroup<CXXPre23Compat>, DefaultIgnore;
-def ext_operator_overload_static : ExtWarn<
-  "declaring overloaded %0 as 'static' is a C++23 extension">, InGroup<CXX23>;
 def err_operator_overload_static : Error<
   "overloaded %0 cannot be a static member function">;
 def err_operator_overload_default_arg : Error<
@@ -10535,13 +10487,6 @@ def warn_conv_to_void_not_used : Warning<
 def warn_not_compound_assign : Warning<
   "use of unary operator that may be intended as compound assignment (%0=)">;
 
-// C++11 explicit conversion operators
-def ext_explicit_conversion_functions : ExtWarn<
-  "explicit conversion functions are a C++11 extension">, InGroup<CXX11>;
-def warn_cxx98_compat_explicit_conversion_functions : Warning<
-  "explicit conversion functions are incompatible with C++98">,
-  InGroup<CXX98Compat>, DefaultIgnore;
-
 // C++11 defaulted functions
 def err_defaulted_special_member_params : Error<
   "an explicitly-defaulted %sub{select_special_member_kind}0 cannot "
@@ -10603,11 +10548,6 @@ def note_vbase_moved_here : Note<
 def select_defaulted_comparison_kind : TextSubstitution<
   "%select{<ERROR>|equality|three-way|equality|relational}0 comparison "
   "operator">;
-def ext_defaulted_comparison : ExtWarn<
-  "defaulted comparison operators are a C++20 extension">, InGroup<CXX20>;
-def warn_cxx17_compat_defaulted_comparison : Warning<
-  "defaulted comparison operators are incompatible with C++ standards "
-  "before C++20">, InGroup<CXXPre20Compat>, DefaultIgnore;
 def err_defaulted_comparison_template : Error<
   "comparison operator template cannot be defaulted">;
 def err_defaulted_comparison_num_args : Error<
diff --git a/clang/include/clang/Lex/Lexer.h b/clang/include/clang/Lex/Lexer.h
index b042e5fb088fa..92bd25888a7b6 100644
--- a/clang/include/clang/Lex/Lexer.h
+++ b/clang/include/clang/Lex/Lexer.h
@@ -294,6 +294,8 @@ class Lexer : public PreprocessorLexer {
   /// position in the current buffer into a SourceLocation object for rendering.
   DiagnosticBuilder Diag(const char *Loc, unsigned DiagID) const;
 
+  DiagnosticBuilder DiagCompat(const char *Loc, unsigned CompatDiagId) const;
+
   /// getSourceLocation - Return a source location identifier for the specified
   /// offset in the current file.
   SourceLocation getSourceLocation(const char *Loc, unsigned TokLen = 1) const;
diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h
index e752010dd2062..cf2acaa0168d0 100644
--- a/clang/include/clang/Lex/Preprocessor.h
+++ b/clang/include/clang/Lex/Preprocessor.h
@@ -2162,6 +2162,15 @@ class Preprocessor {
     return Diags->Report(Tok.getLocation(), DiagID);
   }
 
+  DiagnosticBuilder DiagCompat(SourceLocation Loc,
+                               unsigned CompatDiagID) const {
+    return Diag(Loc, DiagnosticIDs::getCompatDiagId(LangOpts, CompatDiagID));
+  }
+
+  DiagnosticBuilder DiagCompat(const Token &Tok, unsigned CompatDiagID) const {
+    return Diag(Tok, DiagnosticIDs::getCompatDiagId(LangOpts, CompatDiagID));
+  }
+
   /// Return the 'spelling' of the token at the given
   /// location; does not go up to the spelling location or down to the
   /// expansion location.
diff --git a/clang/lib/Lex/Lexer.cpp b/clang/lib/Lex/Lexer.cpp
index 37e33861c4470..46d61b1826633 100644
--- a/clang/lib/Lex/Lexer.cpp
+++ b/clang/lib/Lex/Lexer.cpp
@@ -1284,6 +1284,11 @@ DiagnosticBuilder Lexer::Diag(const char *Loc, unsigned DiagID) const {
   return PP->Diag(getSourceLocation(Loc), DiagID);
 }
 
+DiagnosticBuilder Lexer::DiagCompat(const char *Loc,
+                                    unsigned CompatDiagId) const {
+  return Diag(Loc, DiagnosticIDs::getCompatDiagId(LangOpts, CompatDiagId));
+}
+
 //===----------------------------------------------------------------------===//
 // Trigraph and Escaped Newline Handling Code.
 //===----------------------------------------------------------------------===//
@@ -2391,9 +2396,7 @@ bool Lexer::LexRawStringLiteral(Token &Result, const char *CurPtr,
     if (!isLexingRawMode() &&
         llvm::is_contained({'$', '@', '`'}, CurPtr[PrefixLen])) {
       const char *Pos = &CurPtr[PrefixLen];
-      Diag(Pos, LangOpts.CPlusPlus26
-                    ? diag::warn_cxx26_compat_raw_string_literal_character_set
-                    : diag::ext_cxx26_raw_string_literal_character_set)
+      DiagCompat(Pos, diag_compat::raw_string_literal_character_set)
           << StringRef(Pos, 1);
     }
     ++PrefixLen;
diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp
index ec387a4d582fc..e7e01873213dc 100644
--- a/clang/lib/Lex/PPDirectives.cpp
+++ b/clang/lib/Lex/PPDirectives.cpp
@@ -882,14 +882,9 @@ void Preprocessor::SkipExcludedConditionalBlock(SourceLocation HashTokenLoc,
 
         // Warn if using `#elifdef` & `#elifndef` in not C23 & C++23 mode even
         // if this branch is in a skipping block.
-        unsigned DiagID;
-        if (LangOpts.CPlusPlus)
-          DiagID = LangOpts.CPlusPlus23 ? diag::warn_cxx23_compat_pp_directive
-                                        : diag::ext_cxx23_pp_directive;
-        else
-          DiagID = LangOpts.C23 ? diag::warn_c23_compat_pp_directive
-                                : diag::ext_c23_pp_directive;
-        Diag(Tok, DiagID) << (IsElifDef ? PED_Elifdef : PED_Elifndef);
+        unsigned DiagID = LangOpts.CPlusPlus ? diag_compat::cxx23_pp_directive
+                                             : diag_compat::c23_pp_directive;
+        DiagCompat(Tok, DiagID) << (IsElifDef ? PED_Elifdef : PED_Elifndef);
 
         // If this is a #elif with a #else before it, report the error.
         if (CondInfo.FoundElse)
@@ -3698,14 +3693,9 @@ void Preprocessor::HandleElifFamilyDirective(Token &ElifToken,
   switch (DirKind) {
   case PED_Elifdef:
   case PED_Elifndef:
-    unsigned DiagID;
-    if (LangOpts.CPlusPlus)
-      DiagID = LangOpts.CPlusPlus23 ? diag::warn_cxx23_compat_pp_directive
-                                    : diag::ext_cxx23_pp_directive;
-    else
-      DiagID = LangOpts.C23 ? diag::warn_c23_compat_pp_directive
-                            : diag::ext_c23_pp_directive;
-    Diag(ElifToken, DiagID) << DirKind;
+    DiagCompat(ElifToken, LangOpts.CPlusPlus ? diag_compat::cxx23_pp_directive
+                                             : diag_compat::c23_pp_directive)
+        << DirKind;
     break;
   default:
     break;
diff --git a/clang/lib/Lex/PPExpressions.cpp b/clang/lib/Lex/PPExpressions.cpp
index 1040b83e8745d..eb614cba959a4 100644
--- a/clang/lib/Lex/PPExpressions.cpp
+++ b/clang/lib/Lex/PPExpressions.cpp
@@ -328,12 +328,13 @@ static bool EvaluateValue(PPValue &Result, Token &PeekTok, DefinedTracker &DT,
     }
 
     // 'z/uz' literals are a C++23 feature.
-    if (Literal.isSizeT)
-      PP.Diag(PeekTok, PP.getLangOpts().CPlusPlus
-                           ? PP.getLangOpts().CPlusPlus23
-                                 ? diag::warn_cxx20_compat_size_t_suffix
-                                 : diag::ext_cxx23_size_t_suffix
-                           : diag::err_cxx23_size_t_suffix);
+    if (Literal.isSizeT) {
+      if (PP.getLangOpts().CPlusPlus) {
+        PP.DiagCompat(PeekTok, diag_compat::size_t_suffix);
+      } else {
+        PP.Diag(PeekTok, diag::err_cxx23_size_t_suffix);
+      }
+    }
 
     // 'wb/uwb' literals are a C23 feature.
     // '__wb/__uwb' are a C++ extension.
diff --git a/clang/lib/Parse/ParseCXXInlineMethods.cpp b/clang/lib/Parse/ParseCXXInlineMethods.cpp
index 3f101feb26a6d..8612cfe119edf 100644
--- a/clang/lib/Parse/ParseCXXInlineMethods.cpp
+++ b/clang/lib/Parse/ParseCXXInlineMethods.cpp
@@ -32,9 +32,7 @@ StringLiteral *Parser::ParseCXXDeletedFunctionMessage() {
     ExprResult Res = ParseUnevaluatedStringLiteralExpression();
     if (Res.isUsable()) {
       Message = Res.getAs<StringLiteral>();
-      Diag(Message->getBeginLoc(), getLangOpts().CPlusPlus26
-                                       ? diag::warn_cxx23_delete_with_message
-                                       : diag::ext_delete_with_message)
+      DiagCompat(Message->getBeginLoc(), diag_compat::delete_with_message)
           << Message->getSourceRange();
     }
   } else {
@@ -102,10 +100,8 @@ NamedDecl *Parser::ParseCXXInlineMethodDef(
     bool Delete = false;
     SourceLocation KWLoc;
     if (TryConsumeToken(tok::kw_delete, KWLoc)) {
-      Diag(KWLoc, getLangOpts().CPlusPlus11
-                      ? diag::warn_cxx98_compat_defaulted_deleted_function
-                      : diag::ext_defaulted_deleted_function)
-        << 1 /* deleted */;
+      DiagCompat(KWLoc, diag_compat::defaulted_deleted_function)
+          << 1 /* deleted */;
       StringLiteral *Message = ParseCXXDeletedFunctionMessage();
       Actions.SetDeclDeleted(FnD, KWLoc, Message);
       Delete = true;
@@ -113,10 +109,8 @@ NamedDecl *Parser::ParseCXXInlineMethodDef(
         DeclAsFunction->setRangeEnd(PrevTokLocation);
       }
     } else if (TryConsumeToken(tok::kw_default, KWLoc)) {
-      Diag(KWLoc, getLangOpts().CPlusPlus11
-                      ? diag::warn_cxx98_compat_defaulted_deleted_function
-                      : diag::ext_defaulted_deleted_function)
-        << 0 /* defaulted */;
+      DiagCompat(KWLoc, diag_compat::defaulted_deleted_function)
+          << 0 /* defaulted */;
       Actions.SetDeclDefaulted(FnD, KWLoc);
       if (auto *DeclAsFunction = dyn_cast<FunctionDecl>(FnD)) {
         DeclAsFunction->setRangeEnd(PrevTokLocation);
@@ -434,7 +428,7 @@ void Parser::ParseLexedMethodDeclaration(LateParsedMethodDeclaration &LM) {
 
       ExprResult DefArgResult;
       if (getLangOpts().CPlusPlus11 && Tok.is(tok::l_brace)) {
-        Diag(Tok, diag::warn_cxx98_compat_generalized_initializer_lists);
+        Diag(Tok, diag::compat_cxx11_generalized_initializer_lists);
         DefArgResult = ParseBraceInitializer();
       } else
         DefArgResult = ParseAssignmentExpression();
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 2bbc76fc7c4df..2c52a517b09b3 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -441,7 +441,7 @@ bool Parser::ParseAttributeArgumentList(
     if (ArgsProperties.isStringLiteralArg(Arg)) {
       Expr = ParseUnevaluatedStringInAttribute(AttrName);
     } else if (getLangOpts().CPlusPlus11 && Tok.is(tok::l_brace)) {
-      Diag(Tok, diag::warn_cxx98_compat_generalized_initializer_lists);
+      Diag(Tok, diag::compat_cxx11_generalized_initializer_lists);
       Expr = ParseBraceInitializer();
     } else {
       Expr = ParseAssignmentExpression();
@@ -2727,7 +2727,7 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes(
   }
   case InitKind::CXXBraced: {
     // Parse C++0x braced-init-list.
-    Diag(Tok, diag::warn_cxx98_compat_generalized_initializer_lists);
+    Diag(Tok, diag::compat_cxx11_generalized_initializer_lists);
 
     InitializerScopeRAII InitScope(*this, D, ThisDecl);
 
@@ -4224,9 +4224,7 @@ void Parser::ParseDeclarationSpecifiers(
       ConsumeToken(); // kw_explicit
       if (Tok.is(tok::l_paren)) {
         if (getLangOpts().CPlusPlus20 || isExplicitBool() == TPResult::True) {
-          Diag(Tok.getLocation(), getLangOpts().CPlusPlus20
-                                      ? diag::warn_cxx17_compat_explicit_bool
-                                      : diag::ext_explicit_bool);
+          DiagCompat(Tok.getLocation(), diag_compat::explicit_bool);
 
           ExprResult ExplicitExpr(static_cast<Expr *>(nullptr));
           BalancedDelimiterTracker Tracker(*this, tok::l_paren);
@@ -5072,8 +5070,7 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS,
 
   // In C++11, recognize 'enum class' and 'enum struct'.
   if (Tok.isOneOf(tok::kw_class, tok::kw_struct) && getLangOpts().CPlusPlus) {
-    Diag(Tok, getLangOpts().CPlusPlus11 ? diag::warn_cxx98_compat_scoped_enum
-                                        : diag::ext_scoped_enum);
+    DiagCompat(Tok, diag_compat::scoped_enum);
     IsScopedUsingClassTag = Tok.is(tok::kw_class);
     ScopedEnumKWLoc = ConsumeToken();
 
@@ -5224,15 +5221,13 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS,
 
       if (!getLangOpts().ObjC) {
         if (getLangOpts().CPlusPlus)
-          DiagCompat(ColonLoc, diag_compat::enum_fixed_underlying_type)
+          DiagCompat(ColonLoc, diag_compat::cxx_enum_fixed_underlying_type)
               << BaseRange;
         else if (getLangOpts().MicrosoftExt && !getLangOpts().C23)
           Diag(ColonLoc, diag::ext_ms_c_enum_fixed_underlying_type)
               << BaseRange;
         else
-          Diag(ColonLoc, getLangOpts().C23
-                             ? diag::warn_c17_compat_enum_fixed_underlying_type
-                             : diag::ext_c23_enum_fixed_underlying_type)
+          DiagCompat(ColonLoc, diag_compat::c_enum_fixed_underlying_type)
               << BaseRange;
       }
     }
@@ -5480,9 +5475,7 @@ void Parser::ParseEnumBody(SourceLocation StartLoc, Decl *EnumDecl,
     MaybeParseGNUAttributes(attrs);
     if (isAllowedCXX11AttributeSpecifier()) {
       if (getLangOpts().CPlusPlus)
-        Diag(Tok.getLocation(), getLangOpts().CPlusPlus17
-                                    ? diag::warn_cxx14_compat_ns_enum_attribute
-                                    : diag::ext_ns_enum_attribute)
+        DiagCompat(Tok.getLocation(), diag_compat::ns_enum_attribute)
             << 1 /*enumerator*/;
       ParseCXX11Attributes(attrs);
     }
@@ -6599,9 +6592,7 @@ void Parser::ParseDeclaratorInternal(Declarator &D,
     // Complain about rvalue references in C++03, but then go on and build
     // the declarator.
     if (Kind == tok::ampamp)
-      Diag(Loc, getLangOpts().CPlusPlus11 ?
-           diag::warn_cxx98_compat_rvalue_reference :
-           diag::ext_rvalue_reference);
+      DiagCompat(Loc, diag_compat::rvalue_reference);
 
     // GNU-style and C++11 attributes are allowed here, as is restrict.
     ParseTypeQualifierListOpt(DS);
@@ -7080,8 +7071,7 @@ void Parser::ParseDecompositionDeclarator(Declarator &D) {
     SourceLocation EllipsisLoc;
 
     if (Tok.is(tok::ellipsis)) {
-      Diag(Tok, getLangOpts().CPlusPlus26 ? diag::warn_cxx23_compat_binding_pack
-                                          : diag::ext_cxx_binding_pack);
+      DiagCompat(Tok, diag_compat::binding_pack);
       if (PrevEllipsisLoc.isValid()) {
         Diag(Tok, diag::err_binding_multiple_ellipses);
         Diag(PrevEllipsisLoc, diag::note_previous_ellipsis);
@@ -7111,9 +7101,7 @@ void Parser::ParseDecompositionDeclarator(Declarator &D) {
     ParsedAttributes Attrs(AttrFactory);
     if (isCXX11AttributeSpecifier() !=
         CXX11AttributeKind::NotAttributeSpecifier) {
-      Diag(Tok, getLangOpts().CPlusPlus26
-                    ? diag::warn_cxx23_compat_decl_attrs_on_binding
-                    : diag::ext_decl_attrs_on_binding);
+      DiagCompat(Tok, diag_compat::attrs_on_binding);
       MaybeParseCXX11Attributes(Attrs);
     }
 
@@ -7481,10 +7469,7 @@ void Parser::ParseFunctionDeclarator(Declarator &D,
 bool Parser::ParseRefQualifier(bool &RefQualifierIsLValueRef,
                                SourceLocation &RefQualifierLoc) {
   if (Tok.isOneOf(tok::amp, tok::ampamp)) {
-    Diag(Tok, getLangOpts().CPlusPlus11 ?
-         diag::warn_cxx98_compat_ref_qualifier :
-         diag::ext_ref_qualifier);
-
+    DiagCompat(Tok, diag_compat::ref_qualifier);
     RefQualifierIsLValueRef = Tok.is(tok::amp);
     RefQualifierLoc = ConsumeToken();
     return true;
@@ -7775,7 +7760,7 @@ void Parser::ParseParameterDeclarationClause(
 
           ExprResult DefArgResult;
           if (getLangOpts().CPlusPlus11 && Tok.is(tok::l_brace)) {
-            Diag(Tok, diag::warn_cxx98_compat_generalized_initializer_lists);
+            Diag(Tok, diag::compat_cxx11_generalized_initializer_lists);
             DefArgResult = ParseBraceInitializer();
           } else {
             if (Tok.is(tok::l_paren) && NextToken().is(tok::l_brace)) {
diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index a3617c3db49c4..b88dd0e2912c0 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -57,9 +57,7 @@ Parser::DeclGroupPtrTy Parser::ParseNamespace(DeclaratorContext Context,
   while (MaybeParseGNUAttributes(attrs) || isAllowedCXX11AttributeSpecifier()) {
     if (isAllowedCXX11AttributeSpecifier()) {
       if (getLangOpts().CPlusPlus11)
-        Diag(Tok.getLocation(), getLangOpts().CPlusPlus17
-                                    ? diag::warn_cxx14_compat_ns_enum_attribute
-                                    : diag::ext_ns_enum_attribute)
+        DiagCompat(Tok.getLocation(), diag_compat::ns_enum_attribute)
             << 0 /*namespace*/;
       ParseCXX11Attributes(attrs);
     }
@@ -194,9 +192,7 @@ Parser::DeclGroupPtrTy Parser::ParseNamespace(DeclaratorContext Context,
 
   // If we're still good, complain about inline namespaces in non-C++0x now.
   if (InlineLoc.isValid())
-    Diag(InlineLoc, getLangOpts().CPlusPlus11
-                        ? diag::warn_cxx98_compat_inline_namespace
-                        : diag::ext_inline_namespace);
+    DiagCompat(InlineLoc, diag_compat::inline_namespace);
 
   // Enter a scope for the namespace.
   ParseScope NamespaceScope(this, Scope::DeclScope);
@@ -608,9 +604,7 @@ bool Parser::ParseUsingDeclarator(DeclaratorContext Context,
   }
 
   if (TryConsumeToken(tok::ellipsis, D.EllipsisLoc))
-    Diag(Tok.getLocation(), getLangOpts().CPlusPlus17
-                                ? diag::warn_cxx17_compat_using_declaration_pack
-                                : diag::ext_using_declaration_pack);
+    DiagCompat(Tok.getLocation(), diag_compat::using_declaration_pack);
 
   return false;
 }
@@ -625,10 +619,7 @@ Parser::DeclGroupPtrTy Parser::ParseUsingDeclaration(
 
   if (TryConsumeToken(tok::kw_enum, UELoc) && !InInitStatement) {
     // C++20 using-enum
-    Diag(UELoc, getLangOpts().CPlusPlus20
-                    ? diag::warn_cxx17_compat_using_enum_declaration
-                    : diag::ext_using_enum_declaration);
-
+    DiagCompat(UELoc, diag_compat::using_enum_declaration);
     DiagnoseCXX11AttributeExtension(PrefixAttrs);
 
     if (TemplateInfo.Kind != ParsedTemplateKind::NonTemplate) {
@@ -819,10 +810,7 @@ Parser::DeclGroupPtrTy Parser::ParseUsingDeclaration(
   }
 
   if (DeclsInGroup.size() > 1)
-    Diag(Tok.getLocation(),
-         getLangOpts().CPlusPlus17
-             ? diag::warn_cxx17_compat_multi_using_declaration
-             : diag::ext_multi_using_declaration);
+    DiagCompat(Tok.getLocation(), diag_compat::multi_using_declaration);
 
   // Eat ';'.
   DeclEnd = Tok.getLocation();
@@ -844,9 +832,7 @@ Decl *Parser::ParseAliasDeclarationAfterDeclarator(
     return nullptr;
   }
 
-  Diag(Tok.getLocation(), getLangOpts().CPlusPlus11
-                              ? diag::warn_cxx98_compat_alias_declaration
-                              : diag::ext_alias_declaration);
+  DiagCompat(Tok.getLocation(), diag_compat::alias_declaration);
 
   // Type alias templates cannot be specialized.
   int SpecKind = -1;
@@ -959,17 +945,11 @@ Decl *Parser::ParseStaticAssertDeclaration(SourceLocation &DeclEnd) {
 
   ExprResult AssertMessage;
   if (Tok.is(tok::r_paren)) {
-    unsigned DiagVal;
-    if (getLangOpts().CPlusPlus17)
-      DiagVal = diag::warn_cxx14_compat_static_assert_no_message;
-    else if (getLangOpts().CPlusPlus)
-      DiagVal = diag::ext_cxx_static_assert_no_message;
-    else if (getLangOpts().C23)
-      DiagVal = diag::warn_c17_compat_static_assert_no_message;
-    else
-      DiagVal = diag::ext_c_static_assert_no_message;
-    Diag(Tok, DiagVal) << getStaticAssertNoMessageFixIt(AssertExpr.get(),
-                                                        Tok.getLocation());
+    auto diag = getLangOpts().CPlusPlus
+                    ? diag_compat::cxx_static_assert_no_message
+                    : diag_compat::c_static_assert_no_message;
+    DiagCompat(Tok, diag) << getStaticAssertNoMessageFixIt(AssertExpr.get(),
+                                                           Tok.getLocation());
   } else {
     if (ExpectAndConsume(tok::comma)) {
       SkipUntil(tok::semi);
@@ -992,10 +972,7 @@ Decl *Parser::ParseStaticAssertDeclaration(SourceLocation &DeclEnd) {
     if (ParseAsExpression) {
       AssertMessage = ParseConstantExpressionInExprEvalContext();
       if (Tok.is(tok::r_paren)) {
-        Diag(Tok,
-             getLangOpts().CPlusPlus26
-                 ? diag::warn_cxx20_compat_static_assert_user_generated_message
-                 : diag::ext_cxx_static_assert_user_generated_message);
+        DiagCompat(Tok, diag_compat::static_assert_user_generated_message);
       } else {
         T.consumeClose();
         return nullptr;
@@ -1062,10 +1039,7 @@ SourceLocation Parser::ParseDecltypeSpecifier(DeclSpec &DS) {
     if (Tok.is(tok::kw_auto) && NextToken().is(tok::r_paren)) {
       // the typename-specifier in a function-style cast expression may
       // be 'auto' since C++23.
-      Diag(Tok.getLocation(),
-           getLangOpts().CPlusPlus14
-               ? diag::warn_cxx11_compat_decltype_auto_type_specifier
-               : diag::ext_decltype_auto_type_specifier);
+      DiagCompat(Tok.getLocation(), diag_compat::decltype_auto_type_specifier);
       ConsumeToken();
     } else {
       // Parse the expression
@@ -2470,10 +2444,7 @@ void Parser::ParseOptionalCXX11VirtSpecifierSeq(VirtSpecifiers &VS,
     } else if (Specifier == VirtSpecifiers::VS_GNU_Final) {
       Diag(Tok.getLocation(), diag::ext_warn_gnu_final);
     } else {
-      Diag(Tok.getLocation(),
-           getLangOpts().CPlusPlus11
-               ? diag::warn_cxx98_compat_override_control_keyword
-               : diag::ext_override_control_keyword)
+      DiagCompat(Tok.getLocation(), diag_compat::override_control_keyword)
           << VirtSpecifiers::getSpecifierName(Specifier);
     }
     ConsumeToken();
@@ -2869,9 +2840,7 @@ Parser::DeclGroupPtrTy Parser::ParseCXXClassMemberDeclaration(
   // Handle C++26's variadic friend declarations. These don't even have
   // declarators, so we get them out of the way early here.
   if (DS.isFriendSpecifiedFirst() && Tok.isOneOf(tok::comma, tok::ellipsis)) {
-    Diag(Tok.getLocation(), getLangOpts().CPlusPlus26
-                                ? diag::warn_cxx23_variadic_friends
-                                : diag::ext_variadic_friends);
+    DiagCompat(Tok.getLocation(), diag_compat::variadic_friends);
 
     SourceLocation FriendLoc = DS.getFriendSpecLoc();
     SmallVector<Decl *> Decls;
@@ -3094,9 +3063,7 @@ Parser::DeclGroupPtrTy Parser::ParseCXXClassMemberDeclaration(
                  TemplateInfo.Kind == ParsedTemplateKind::NonTemplate) {
         // It's a default member initializer.
         if (BitfieldSize.get())
-          Diag(Tok, getLangOpts().CPlusPlus20
-                        ? diag::warn_cxx17_compat_bitfield_member_init
-                        : diag::ext_bitfield_member_init);
+          DiagCompat(Tok, diag_compat::bitfield_member_init);
         HasInClassInit = Tok.is(tok::equal) ? ICIS_CopyInit : ICIS_ListInit;
       } else {
         HasStaticInitializer = true;
@@ -3160,9 +3127,7 @@ Parser::DeclGroupPtrTy Parser::ParseCXXClassMemberDeclaration(
     // Handle the initializer.
     if (HasInClassInit != ICIS_NoInit) {
       // The initializer was deferred; parse it and cache the tokens.
-      Diag(Tok, getLangOpts().CPlusPlus11
-                    ? diag::warn_cxx98_compat_nonstatic_member_init
-                    : diag::ext_nonstatic_member_init);
+      DiagCompat(Tok, diag_compat::nonstatic_member_init);
 
       if (DeclaratorInfo.isArrayOfUnknownBound()) {
         // C++11 [dcl.array]p3: An array bound may also be omitted when the
@@ -3587,9 +3552,7 @@ void Parser::ParseCXXMemberSpecification(SourceLocation RecordLoc,
         Diag(FinalLoc, diag::err_override_control_interface)
             << VirtSpecifiers::getSpecifierName(Specifier);
       else if (Specifier == VirtSpecifiers::VS_Final)
-        Diag(FinalLoc, getLangOpts().CPlusPlus11
-                           ? diag::warn_cxx98_compat_override_control_keyword
-                           : diag::ext_override_control_keyword)
+        DiagCompat(FinalLoc, diag_compat::override_control_keyword)
             << VirtSpecifiers::getSpecifierName(Specifier);
       else if (Specifier == VirtSpecifiers::VS_Sealed)
         Diag(FinalLoc, diag::ext_ms_sealed_keyword);
@@ -3864,7 +3827,7 @@ MemInitResult Parser::ParseMemInitializer(Decl *ConstructorDecl) {
 
   // Parse the '('.
   if (getLangOpts().CPlusPlus11 && Tok.is(tok::l_brace)) {
-    Diag(Tok, diag::warn_cxx98_compat_generalized_initializer_lists);
+    Diag(Tok, diag::compat_cxx11_generalized_initializer_lists);
 
     // FIXME: Add support for signature help inside initializer lists.
     ExprResult InitList = ParseBraceInitializer();
@@ -4605,13 +4568,8 @@ void Parser::ParseCXX11AttributeSpecifierInternal(ParsedAttributes &Attrs,
          "Not a double square bracket attribute list");
 
   SourceLocation OpenLoc = Tok.getLocation();
-  if (getLangOpts().CPlusPlus) {
-    Diag(OpenLoc, getLangOpts().CPlusPlus11 ? diag::warn_cxx98_compat_attribute
-                                            : diag::warn_ext_cxx11_attributes);
-  } else {
-    Diag(OpenLoc, getLangOpts().C23 ? diag::warn_pre_c23_compat_attributes
-                                    : diag::warn_ext_c23_attributes);
-  }
+  DiagCompat(OpenLoc, getLangOpts().CPlusPlus ? diag_compat::cxx11_attributes
+                                              : diag_compat::c23_attributes);
 
   ConsumeBracket();
   checkCompoundToken(OpenLoc, tok::l_square, CompoundToken::AttrBegin);
@@ -4620,9 +4578,7 @@ void Parser::ParseCXX11AttributeSpecifierInternal(ParsedAttributes &Attrs,
   SourceLocation CommonScopeLoc;
   IdentifierInfo *CommonScopeName = nullptr;
   if (Tok.is(tok::kw_using)) {
-    Diag(Tok.getLocation(), getLangOpts().CPlusPlus17
-                                ? diag::warn_cxx14_compat_using_attribute_ns
-                                : diag::ext_using_attribute_ns);
+    DiagCompat(Tok.getLocation(), diag_compat::using_attribute_ns);
     ConsumeToken();
 
     CommonScopeName = TryParseCXX11AttributeIdentifier(
diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp
index 87cd7a01451cf..cfc7c199cbdb5 100644
--- a/clang/lib/Parse/ParseExpr.cpp
+++ b/clang/lib/Parse/ParseExpr.cpp
@@ -525,8 +525,8 @@ Parser::ParseRHSOfBinaryExpression(ExprResult LHS, prec::Level MinPrec) {
 
     if (!RHS.isInvalid() && RHSIsInitList) {
       if (ThisPrec == prec::Assignment) {
-        Diag(OpToken, diag::warn_cxx98_compat_generalized_initializer_lists)
-          << Actions.getExprRange(RHS.get());
+        Diag(OpToken, diag::compat_cxx11_generalized_initializer_lists)
+            << Actions.getExprRange(RHS.get());
       } else if (ColonLoc.isValid()) {
         Diag(ColonLoc, diag::err_init_list_bin_op)
           << /*RHS*/1 << ":"
@@ -1386,7 +1386,7 @@ Parser::ParseCastExpression(CastParseKind ParseKind, bool isAddressOfOperand,
                          << DS.getSourceRange());
 
     if (Tok.is(tok::l_brace))
-      Diag(Tok, diag::warn_cxx98_compat_generalized_initializer_lists);
+      Diag(Tok, diag::compat_cxx11_generalized_initializer_lists);
 
     Res = ParseCXXTypeConstructExpression(DS);
     break;
@@ -1735,7 +1735,7 @@ Parser::ParsePostfixExpressionSuffix(ExprResult LHS) {
         if (!getLangOpts().CPlusPlus23) {
           ExprResult Idx;
           if (getLangOpts().CPlusPlus11 && Tok.is(tok::l_brace)) {
-            Diag(Tok, diag::warn_cxx98_compat_generalized_initializer_lists);
+            Diag(Tok, diag::compat_cxx11_generalized_initializer_lists);
             Idx = ParseBraceInitializer();
           } else {
             Idx = ParseExpression(); // May be a comma expression
@@ -3068,8 +3068,7 @@ ExprResult Parser::ParseGenericSelectionExpression() {
     }
     const auto *LIT = cast<LocInfoType>(ControllingType.get().get());
     SourceLocation Loc = LIT->getTypeSourceInfo()->getTypeLoc().getBeginLoc();
-    Diag(Loc, getLangOpts().C2y ? diag::warn_c2y_compat_generic_with_type_arg
-                                : diag::ext_c2y_generic_with_type_arg);
+    DiagCompat(Loc, diag_compat::generic_with_type_arg);
   } else {
     // C11 6.5.1.1p3 "The controlling expression of a generic selection is
     // not evaluated."
@@ -3178,9 +3177,7 @@ ExprResult Parser::ParseFoldExpression(ExprResult LHS,
     }
   }
 
-  Diag(EllipsisLoc, getLangOpts().CPlusPlus17
-                        ? diag::warn_cxx14_compat_fold_expression
-                        : diag::ext_fold_expression);
+  DiagCompat(EllipsisLoc, diag_compat::fold_expression);
 
   T.consumeClose();
   return Actions.ActOnCXXFoldExpr(getCurScope(), T.getOpenLocation(), LHS.get(),
@@ -3224,7 +3221,7 @@ bool Parser::ParseExpressionList(SmallVectorImpl<Expr *> &Exprs,
 
     ExprResult Expr;
     if (getLangOpts().CPlusPlus11 && Tok.is(tok::l_brace)) {
-      Diag(Tok, diag::warn_cxx98_compat_generalized_initializer_lists);
+      Diag(Tok, diag::compat_cxx11_generalized_initializer_lists);
       Expr = ParseBraceInitializer();
     } else
       Expr = ParseAssignmentExpression();
diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index f9a0dcc7d53af..559d61549f658 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -1130,9 +1130,7 @@ static void tryConsumeLambdaSpecifierToken(Parser &P,
 static void addStaticToLambdaDeclSpecifier(Parser &P, SourceLocation StaticLoc,
                                            DeclSpec &DS) {
   if (StaticLoc.isValid()) {
-    P.Diag(StaticLoc, !P.getLangOpts().CPlusPlus23
-                          ? diag::err_static_lambda
-                          : diag::warn_cxx20_compat_static_lambda);
+    P.DiagCompat(StaticLoc, diag_compat::static_lambda);
     const char *PrevSpec = nullptr;
     unsigned DiagID = 0;
     DS.SetStorageClassSpec(P.getActions(), DeclSpec::SCS_static, StaticLoc,
@@ -1147,9 +1145,7 @@ static void
 addConstexprToLambdaDeclSpecifier(Parser &P, SourceLocation ConstexprLoc,
                                   DeclSpec &DS) {
   if (ConstexprLoc.isValid()) {
-    P.Diag(ConstexprLoc, !P.getLangOpts().CPlusPlus17
-                             ? diag::ext_constexpr_on_lambda_cxx17
-                             : diag::warn_cxx14_compat_constexpr_on_lambda);
+    P.DiagCompat(ConstexprLoc, diag_compat::constexpr_on_lambda);
     const char *PrevSpec = nullptr;
     unsigned DiagID = 0;
     DS.SetConstexprSpec(ConstexprSpecKind::Constexpr, ConstexprLoc, PrevSpec,
@@ -1244,9 +1240,7 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
 
   MultiParseScope TemplateParamScope(*this);
   if (Tok.is(tok::less)) {
-    Diag(Tok, getLangOpts().CPlusPlus20
-                  ? diag::warn_cxx17_compat_lambda_template_parameter_list
-                  : diag::ext_lambda_template_parameter_list);
+    DiagCompat(Tok, diag_compat::lambda_template_parameter_list);
 
     SmallVector<NamedDecl*, 4> TemplateParams;
     SourceLocation LAngleLoc, RAngleLoc;
@@ -1857,10 +1851,7 @@ Parser::ParseAliasDeclarationInInitStatement(DeclaratorContext Context,
   if (!DG)
     return DG;
 
-  Diag(DeclStart, !getLangOpts().CPlusPlus23
-                      ? diag::ext_alias_in_init_statement
-                      : diag::warn_cxx20_alias_in_init_statement)
-      << SourceRange(DeclStart, DeclEnd);
+  DiagCompat(DeclStart, diag_compat::alias_in_init_statement);
 
   return DG;
 }
@@ -1903,9 +1894,7 @@ Sema::ConditionResult Parser::ParseCondition(StmtResult *InitStmt,
 
   const auto WarnOnInit = [this, &CK] {
     if (getLangOpts().CPlusPlus)
-      Diag(Tok.getLocation(), getLangOpts().CPlusPlus17
-                                  ? diag::warn_cxx14_compat_init_statement
-                                  : diag::ext_init_statement)
+      DiagCompat(Tok.getLocation(), diag_compat::init_statement)
           << (CK == Sema::ConditionKind::Switch);
     else
       DiagCompat(Tok.getLocation(), diag_compat::decl_statement)
@@ -2062,8 +2051,7 @@ Sema::ConditionResult Parser::ParseCondition(StmtResult *InitStmt,
 
   ExprResult InitExpr = ExprError();
   if (getLangOpts().CPlusPlus11 && Tok.is(tok::l_brace)) {
-    Diag(Tok.getLocation(),
-         diag::warn_cxx98_compat_generalized_initializer_lists);
+    Diag(Tok.getLocation(), diag::compat_cxx11_generalized_initializer_lists);
     InitExpr = ParseBraceInitializer();
   } else if (CopyInitialization) {
     PreferredType.enterVariableInit(Tok.getLocation(), DeclOut);
@@ -3015,8 +3003,7 @@ Parser::ParseCXXNewExpression(bool UseGlobal, SourceLocation Start) {
                                              ConstructorRParen,
                                              ConstructorArgs);
   } else if (Tok.is(tok::l_brace) && getLangOpts().CPlusPlus11) {
-    Diag(Tok.getLocation(),
-         diag::warn_cxx98_compat_generalized_initializer_lists);
+    Diag(Tok.getLocation(), diag::compat_cxx11_generalized_initializer_lists);
     Initializer = ParseBraceInitializer();
   }
   if (Initializer.isInvalid())
diff --git a/clang/lib/Parse/ParseObjc.cpp b/clang/lib/Parse/ParseObjc.cpp
index d01b0abf917cb..8cb39da3c9d40 100644
--- a/clang/lib/Parse/ParseObjc.cpp
+++ b/clang/lib/Parse/ParseObjc.cpp
@@ -2881,7 +2881,7 @@ Parser::ParseObjCMessageExpressionBody(SourceLocation LBracLoc,
 
       ExprResult Expr;
       if (getLangOpts().CPlusPlus11 && Tok.is(tok::l_brace)) {
-        Diag(Tok, diag::warn_cxx98_compat_generalized_initializer_lists);
+        Diag(Tok, diag::compat_cxx11_generalized_initializer_lists);
         Expr = ParseBraceInitializer();
       } else
         Expr = ParseAssignmentExpression();
diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp
index 30b6c64e69f4c..102128b657cc9 100644
--- a/clang/lib/Parse/ParseOpenMP.cpp
+++ b/clang/lib/Parse/ParseOpenMP.cpp
@@ -389,7 +389,7 @@ void Parser::ParseOpenMPReductionInitializerForDecl(VarDecl *OmpPrivParm) {
     }
   } else if (getLangOpts().CPlusPlus11 && Tok.is(tok::l_brace)) {
     // Parse C++0x braced-init-list.
-    Diag(Tok, diag::warn_cxx98_compat_generalized_initializer_lists);
+    Diag(Tok, diag::compat_cxx11_generalized_initializer_lists);
 
     ExprResult Init(ParseBraceInitializer());
 
diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index 219bcd980e860..78d0648c7b4d9 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -713,10 +713,8 @@ static void DiagnoseLabelFollowedByDecl(Parser &P, const Stmt *SubStmt) {
   // label that is followed by a declaration rather than a statement.
   if (!P.getLangOpts().CPlusPlus && !P.getLangOpts().MicrosoftExt &&
       isa<DeclStmt>(SubStmt)) {
-    P.Diag(SubStmt->getBeginLoc(),
-           P.getLangOpts().C23
-               ? diag::warn_c23_compat_label_followed_by_declaration
-               : diag::ext_c_label_followed_by_declaration);
+    P.DiagCompat(SubStmt->getBeginLoc(),
+                 diag_compat::label_followed_by_declaration);
   }
 }
 
@@ -1086,15 +1084,9 @@ void Parser::ParseCompoundStatementLeadingPragmas() {
 }
 
 void Parser::DiagnoseLabelAtEndOfCompoundStatement() {
-  if (getLangOpts().CPlusPlus) {
-    Diag(Tok, getLangOpts().CPlusPlus23
-                  ? diag::warn_cxx20_compat_label_end_of_compound_statement
-                  : diag::ext_cxx_label_end_of_compound_statement);
-  } else {
-    Diag(Tok, getLangOpts().C23
-                  ? diag::warn_c23_compat_label_end_of_compound_statement
-                  : diag::ext_c_label_end_of_compound_statement);
-  }
+  DiagCompat(Tok, getLangOpts().CPlusPlus
+                      ? diag_compat::cxx_label_at_end_of_compound_statement
+                      : diag_compat::c_label_at_end_of_compound_statement);
 }
 
 bool Parser::ConsumeNullStmt(StmtVector &Stmts) {
@@ -1473,8 +1465,7 @@ StmtResult Parser::ParseIfStatement(SourceLocation *TrailingElseLoc) {
   if (Tok.is(tok::kw_constexpr)) {
     // C23 supports constexpr keyword, but only for object definitions.
     if (getLangOpts().CPlusPlus) {
-      Diag(Tok, getLangOpts().CPlusPlus17 ? diag::warn_cxx14_compat_constexpr_if
-                                          : diag::ext_constexpr_if);
+      DiagCompat(Tok, diag_compat::constexpr_if);
       IsConstexpr = true;
       ConsumeToken();
     }
@@ -1484,8 +1475,7 @@ StmtResult Parser::ParseIfStatement(SourceLocation *TrailingElseLoc) {
     }
 
     if (Tok.is(tok::kw_consteval)) {
-      Diag(Tok, getLangOpts().CPlusPlus23 ? diag::warn_cxx20_compat_consteval_if
-                                          : diag::ext_consteval_if);
+      DiagCompat(Tok, diag_compat::consteval_if);
       IsConsteval = true;
       ConstevalLoc = ConsumeToken();
     } else if (Tok.is(tok::code_completion)) {
@@ -2128,9 +2118,7 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
           MightBeForRangeStmt ? &ForRangeInfo : nullptr);
       FirstPart = Actions.ActOnDeclStmt(DG, DeclStart, Tok.getLocation());
       if (ForRangeInfo.ParsedForRangeDecl()) {
-        Diag(ForRangeInfo.ColonLoc, getLangOpts().CPlusPlus11
-                                        ? diag::warn_cxx98_compat_for_range
-                                        : diag::ext_for_range);
+        DiagCompat(ForRangeInfo.ColonLoc, diag_compat::for_range);
         ForRangeInfo.LoopVar = FirstPart;
         FirstPart = StmtResult();
       } else if (Tok.is(tok::semi)) { // for (int x = 4;
@@ -2229,11 +2217,9 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
             /*MissingOK=*/true, MightBeForRangeStmt ? &ForRangeInfo : nullptr);
 
         if (ForRangeInfo.ParsedForRangeDecl()) {
-          Diag(FirstPart.get() ? FirstPart.get()->getBeginLoc()
-                               : ForRangeInfo.ColonLoc,
-               getLangOpts().CPlusPlus20
-                   ? diag::warn_cxx17_compat_for_range_init_stmt
-                   : diag::ext_for_range_init_stmt)
+          DiagCompat(FirstPart.get() ? FirstPart.get()->getBeginLoc()
+                                     : ForRangeInfo.ColonLoc,
+                     diag_compat::for_range_init_stmt)
               << (FirstPart.get() ? FirstPart.get()->getSourceRange()
                                   : SourceRange());
           if (EmptyInitStmtSemiLoc.isValid()) {
@@ -2497,11 +2483,7 @@ StmtResult Parser::ParseReturnStatement() {
     if (Tok.is(tok::l_brace) && getLangOpts().CPlusPlus) {
       R = ParseInitializer();
       if (R.isUsable())
-        Diag(R.get()->getBeginLoc(),
-             getLangOpts().CPlusPlus11
-                 ? diag::warn_cxx98_compat_generalized_initializer_lists
-                 : diag::ext_generalized_initializer_lists)
-            << R.get()->getSourceRange();
+        DiagCompat(R.get()->getBeginLoc(), diag_compat::generalized_initializer_lists);
     } else
       R = ParseExpression();
     if (R.isInvalid()) {
diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp
index 735a9bd1f9f1c..34d3e69071d3b 100644
--- a/clang/lib/Parse/ParseTemplate.cpp
+++ b/clang/lib/Parse/ParseTemplate.cpp
@@ -625,10 +625,7 @@ NamedDecl *Parser::ParseTypeParameter(unsigned Depth, unsigned Position) {
   // Grab the ellipsis (if given).
   SourceLocation EllipsisLoc;
   if (TryConsumeToken(tok::ellipsis, EllipsisLoc)) {
-    Diag(EllipsisLoc,
-         getLangOpts().CPlusPlus11
-           ? diag::warn_cxx98_compat_variadic_templates
-           : diag::ext_variadic_templates);
+    DiagCompat(EllipsisLoc, diag_compat::variadic_templates);
   }
 
   // Grab the template parameter name (if given)
@@ -736,10 +733,8 @@ NamedDecl *Parser::ParseTemplateTemplateParameter(unsigned Depth,
     if (Tok.is(tok::kw_typename)) {
       TypenameKeyword = true;
       Kind = TemplateNameKind::TNK_Type_template;
-      Diag(Tok.getLocation(),
-           getLangOpts().CPlusPlus17
-               ? diag::warn_cxx14_compat_template_template_param_typename
-               : diag::ext_template_template_param_typename)
+      DiagCompat(Tok.getLocation(),
+                 diag_compat::template_template_param_typename)
           << (!getLangOpts().CPlusPlus17
                   ? FixItHint::CreateReplacement(Tok.getLocation(), "class")
                   : FixItHint());
@@ -772,10 +767,7 @@ NamedDecl *Parser::ParseTemplateTemplateParameter(unsigned Depth,
 
   // Parse the ellipsis, if given.
   if (TryConsumeToken(tok::ellipsis, EllipsisLoc))
-    Diag(EllipsisLoc,
-         getLangOpts().CPlusPlus11
-           ? diag::warn_cxx98_compat_variadic_templates
-           : diag::ext_variadic_templates);
+    DiagCompat(EllipsisLoc, diag_compat::variadic_templates);
 
   // Get the identifier, if given.
   NameLoc = Tok.getLocation();
diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index d83b75072f844..da8d00edefcf5 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -1301,17 +1301,13 @@ Decl *Parser::ParseFunctionDefinition(ParsingDeclarator &D,
     assert(getLangOpts().CPlusPlus && "Only C++ function definitions have '='");
 
     if (TryConsumeToken(tok::kw_delete, KWLoc)) {
-      Diag(KWLoc, getLangOpts().CPlusPlus11
-                      ? diag::warn_cxx98_compat_defaulted_deleted_function
-                      : diag::ext_defaulted_deleted_function)
+      DiagCompat(KWLoc, diag_compat::defaulted_deleted_function)
           << 1 /* deleted */;
       BodyKind = Sema::FnBodyKind::Delete;
       DeletedMessage = ParseCXXDeletedFunctionMessage();
       D.SetRangeEnd(PrevTokLocation);
     } else if (TryConsumeToken(tok::kw_default, KWLoc)) {
-      Diag(KWLoc, getLangOpts().CPlusPlus11
-                      ? diag::warn_cxx98_compat_defaulted_deleted_function
-                      : diag::ext_defaulted_deleted_function)
+      DiagCompat(KWLoc, diag_compat::defaulted_deleted_function)
           << 0 /* defaulted */;
       BodyKind = Sema::FnBodyKind::Default;
       D.SetRangeEnd(PrevTokLocation);
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index dd95f9220bb9d..85ec63782802a 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -748,9 +748,7 @@ bool Sema::MergeCXXFunctionDecl(FunctionDecl *New, FunctionDecl *Old,
 }
 
 void Sema::DiagPlaceholderVariableDefinition(SourceLocation Loc) {
-  Diag(Loc, getLangOpts().CPlusPlus26
-                ? diag::warn_cxx23_placeholder_var_definition
-                : diag::ext_placeholder_var_definition);
+  DiagCompat(Loc, diag_compat::placeholder_var_definition);
 }
 
 NamedDecl *
@@ -11609,10 +11607,8 @@ void Sema::CheckConversionDeclarator(Declarator &D, QualType &R,
 
   // C++0x explicit conversion operators.
   if (DS.hasExplicitSpecifier() && !getLangOpts().CPlusPlus20)
-    Diag(DS.getExplicitSpecLoc(),
-         getLangOpts().CPlusPlus11
-             ? diag::warn_cxx98_compat_explicit_conversion_functions
-             : diag::ext_explicit_conversion_functions)
+    DiagCompat(DS.getExplicitSpecLoc(),
+               diag_compat::explicit_conversion_functions)
         << SourceRange(DS.getExplicitSpecRange());
 }
 
@@ -13664,10 +13660,7 @@ bool Sema::CheckUsingDeclQualifier(SourceLocation UsingLoc, bool HasTypename,
       // A using-declaration shall not name a scoped enumerator.
       // C++20 p1099 permits enumerators.
       if (EC && R && ED->isScoped())
-        Diag(SS.getBeginLoc(),
-             getLangOpts().CPlusPlus20
-                 ? diag::warn_cxx17_compat_using_decl_scoped_enumerator
-                 : diag::ext_using_decl_scoped_enumerator)
+        DiagCompat(SS.getBeginLoc(), diag_compat::using_decl_scoped_enumerator)
             << SS.getRange();
 
       // We want to consider the scope of the enumerator
@@ -17009,10 +17002,7 @@ bool Sema::CheckOverloadedOperatorDeclaration(FunctionDecl *FnDecl) {
   if (CXXMethodDecl *MethodDecl = dyn_cast<CXXMethodDecl>(FnDecl)) {
     if (MethodDecl->isStatic()) {
       if (Op == OO_Call || Op == OO_Subscript)
-        Diag(FnDecl->getLocation(),
-             (LangOpts.CPlusPlus23
-                  ? diag::warn_cxx20_compat_operator_overload_static
-                  : diag::ext_operator_overload_static))
+        DiagCompat(FnDecl->getLocation(), diag_compat::operator_overload_static)
             << FnDecl;
       else
         return Diag(FnDecl->getLocation(), diag::err_operator_overload_static)
@@ -18776,9 +18766,7 @@ void Sema::SetDeclDefaulted(Decl *Dcl, SourceLocation DefaultLoc) {
   // 'operator<=>' when parsing the '<=>' token.
   if (DefKind.isComparison() &&
       DefKind.asComparison() != DefaultedComparisonKind::ThreeWay) {
-    Diag(DefaultLoc, getLangOpts().CPlusPlus20
-                         ? diag::warn_cxx17_compat_defaulted_comparison
-                         : diag::ext_defaulted_comparison);
+    DiagCompat(DefaultLoc, diag_compat::defaulted_comparison);
   }
 
   FD->setDefaulted();
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index f25829ae676dc..1e81767ea7ae9 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -4058,12 +4058,12 @@ ExprResult Sema::ActOnNumericConstant(const Token &Tok, Scope *UDLScope) {
     QualType Ty;
 
     // 'z/uz' literals are a C++23 feature.
-    if (Literal.isSizeT)
-      Diag(Tok.getLocation(), getLangOpts().CPlusPlus
-                                  ? getLangOpts().CPlusPlus23
-                                        ? diag::warn_cxx20_compat_size_t_suffix
-                                        : diag::ext_cxx23_size_t_suffix
-                                  : diag::err_cxx23_size_t_suffix);
+    if (Literal.isSizeT) {
+      if (getLangOpts().CPlusPlus)
+        DiagCompat(Tok.getLocation(), diag_compat::size_t_suffix);
+      else
+        Diag(Tok.getLocation(), diag::err_cxx23_size_t_suffix);
+    }
 
     // 'wb/uwb' literals are a C23 feature. We support _BitInt as a type in C++,
     // but we do not currently support the suffix in C++ mode because it's not
@@ -19497,10 +19497,7 @@ static bool isVariableCapturable(CapturingScopeInfo *CSI, ValueDecl *Var,
         diagnoseUncapturableValueReferenceOrBinding(S, Loc, Var);
       return false;
     } else if (Diagnose && S.getLangOpts().CPlusPlus) {
-      S.Diag(Loc, S.LangOpts.CPlusPlus20
-                      ? diag::warn_cxx17_compat_capture_binding
-                      : diag::ext_capture_binding)
-          << Var;
+      S.DiagCompat(Loc, diag_compat::capture_binding) << Var;
       S.Diag(Var->getLocation(), diag::note_entity_declared_at) << Var;
     }
   }
diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp
index b97c6d8b95f62..a773f4bcfa11e 100644
--- a/clang/lib/Sema/SemaLambda.cpp
+++ b/clang/lib/Sema/SemaLambda.cpp
@@ -860,9 +860,7 @@ QualType Sema::buildLambdaInitCaptureInitialization(
   }
   if (EllipsisLoc.isValid()) {
     if (Init->containsUnexpandedParameterPack()) {
-      Diag(EllipsisLoc, getLangOpts().CPlusPlus20
-                            ? diag::warn_cxx17_compat_init_capture_pack
-                            : diag::ext_init_capture_pack);
+      DiagCompat(EllipsisLoc, diag_compat::init_capture_pack);
       DeductType = Context.getPackExpansionType(DeductType, NumExpansions,
                                                 /*ExpectPackInType=*/false);
       TLB.push<PackExpansionTypeLoc>(DeductType).setEllipsisLoc(EllipsisLoc);
@@ -1193,9 +1191,7 @@ void Sema::ActOnLambdaExpressionAfterIntroducer(LambdaIntroducer &Intro,
        PrevCaptureLoc = C->Loc, ++C) {
     if (C->Kind == LCK_This || C->Kind == LCK_StarThis) {
       if (C->Kind == LCK_StarThis)
-        Diag(C->Loc, !getLangOpts().CPlusPlus17
-                         ? diag::ext_star_this_lambda_capture_cxx17
-                         : diag::warn_cxx14_compat_star_this_lambda_capture);
+        DiagCompat(C->Loc, diag_compat::star_this_lambda_capture);
 
       // C++11 [expr.prim.lambda]p8:
       //   An identifier or this shall not appear more than once in a
@@ -1214,9 +1210,7 @@ void Sema::ActOnLambdaExpressionAfterIntroducer(LambdaIntroducer &Intro,
       //  "&identifier", "this", or "* this". [ Note: The form [&,this] is
       //  redundant but accepted for compatibility with ISO C++14. --end note ]
       if (Intro.Default == LCD_ByCopy && C->Kind != LCK_StarThis)
-        Diag(C->Loc, !getLangOpts().CPlusPlus20
-                         ? diag::ext_equals_this_lambda_capture_cxx20
-                         : diag::warn_cxx17_compat_equals_this_lambda_capture);
+        DiagCompat(C->Loc, diag_compat::equals_this_lambda_capture);
 
       // C++11 [expr.prim.lambda]p12:
       //   If this is captured by a local lambda expression, its nearest
@@ -1242,9 +1236,7 @@ void Sema::ActOnLambdaExpressionAfterIntroducer(LambdaIntroducer &Intro,
 
     ValueDecl *Var = nullptr;
     if (C->Init.isUsable()) {
-      Diag(C->Loc, getLangOpts().CPlusPlus14
-                       ? diag::warn_cxx11_compat_init_capture
-                       : diag::ext_init_capture);
+      DiagCompat(C->Loc, diag_compat::init_capture);
 
       // If the initializer expression is usable, but the InitCaptureType
       // is not, then an error has occurred - so ignore the capture for now.
diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index 1fde5a45977dd..331c1866e36ec 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -2990,9 +2990,7 @@ StmtResult Sema::BuildCXXForRangeStmt(
     SourceLocation RangeLoc = RangeVar->getLocation();
     QualType BeginType = BeginVar->getType(), EndType = EndVar->getType();
     if (!Context.hasSameType(BeginType, EndType)) {
-      Diag(RangeLoc, getLangOpts().CPlusPlus17
-                         ? diag::warn_for_range_begin_end_types_differ
-                         : diag::ext_for_range_begin_end_types_differ)
+      DiagCompat(RangeLoc, diag_compat::for_range_begin_end_types_differ)
           << BeginType << EndType;
       NoteForRangeBeginEndFunction(*this, BeginExpr, BEF_begin);
       NoteForRangeBeginEndFunction(*this, EndExpr, BEF_end);
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 643392833759d..e897159c416c8 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -5071,11 +5071,8 @@ TemplateNameKind Sema::ActOnTemplateName(Scope *S,
                                          TemplateTy &Result,
                                          bool AllowInjectedClassName) {
   if (TemplateKWLoc.isValid() && S && !S->getTemplateParamParent())
-    Diag(TemplateKWLoc,
-         getLangOpts().CPlusPlus11 ?
-           diag::warn_cxx98_compat_template_outside_of_template :
-           diag::ext_template_outside_of_template)
-      << FixItHint::CreateRemoval(TemplateKWLoc);
+    DiagCompat(TemplateKWLoc, diag_compat::template_outside_of_template)
+        << FixItHint::CreateRemoval(TemplateKWLoc);
 
   if (SS.isInvalid())
     return TNK_Non_template;
@@ -6924,13 +6921,10 @@ static bool CheckTemplateArgumentAddressOfObjectOrFunction(
 
   // Address / reference template args must have external linkage in C++98.
   if (Entity->getFormalLinkage() == Linkage::Internal) {
-    S.Diag(Arg->getBeginLoc(),
-           S.getLangOpts().CPlusPlus11
-               ? diag::warn_cxx98_compat_template_arg_object_internal
-               : diag::ext_template_arg_object_internal)
+    S.DiagCompat(Arg->getBeginLoc(), diag_compat::template_arg_object_internal)
         << !Func << Entity << Arg->getSourceRange();
     S.Diag(Entity->getLocation(), diag::note_template_arg_internal_object)
-      << !Func;
+        << !Func;
   } else if (!Entity->hasLinkage()) {
     S.Diag(Arg->getBeginLoc(), diag::err_template_arg_object_no_linkage)
         << !Func << Entity << Arg->getSourceRange();
diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp
index aa818aad7ff68..d48131c595980 100644
--- a/clang/lib/Sema/SemaTemplateVariadic.cpp
+++ b/clang/lib/Sema/SemaTemplateVariadic.cpp
@@ -1346,9 +1346,7 @@ ExprResult Sema::ActOnPackIndexingExpr(Scope *S, Expr *PackExpression,
   ExprResult Res =
       BuildPackIndexingExpr(PackExpression, EllipsisLoc, IndexExpr, RSquareLoc);
   if (!Res.isInvalid())
-    Diag(Res.get()->getBeginLoc(), getLangOpts().CPlusPlus26
-                                       ? diag::warn_cxx23_pack_indexing
-                                       : diag::ext_pack_indexing);
+    DiagCompat(Res.get()->getBeginLoc(), diag_compat::pack_indexing);
   return Res;
 }
 
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index f9033ecb48581..c27e0d95f42e0 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -5717,10 +5717,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
       if (T->containsUnexpandedParameterPack())
         T = Context.getPackExpansionType(T, std::nullopt);
       else
-        S.Diag(D.getEllipsisLoc(),
-               LangOpts.CPlusPlus11
-                 ? diag::warn_cxx98_compat_variadic_templates
-                 : diag::ext_variadic_templates);
+        S.DiagCompat(D.getEllipsisLoc(), diag_compat::variadic_templates);
       break;
 
     case DeclaratorContext::File:
@@ -10093,8 +10090,7 @@ QualType Sema::ActOnPackIndexingType(QualType Pattern, Expr *IndexExpr,
   QualType Type = BuildPackIndexingType(Pattern, IndexExpr, Loc, EllipsisLoc);
 
   if (!Type.isNull())
-    Diag(Loc, getLangOpts().CPlusPlus26 ? diag::warn_cxx23_pack_indexing
-                                        : diag::ext_pack_indexing);
+    DiagCompat(Loc, diag_compat::pack_indexing);
   return Type;
 }
 
diff --git a/clang/test/CXX/basic/basic.lookup/basic.lookup.qual/namespace.qual/p2.cpp b/clang/test/CXX/basic/basic.lookup/basic.lookup.qual/namespace.qual/p2.cpp
index 73c043fec4057..49ff4c7fa0ccf 100644
--- a/clang/test/CXX/basic/basic.lookup/basic.lookup.qual/namespace.qual/p2.cpp
+++ b/clang/test/CXX/basic/basic.lookup/basic.lookup.qual/namespace.qual/p2.cpp
@@ -22,10 +22,10 @@ namespace Numbers {
 void test() {
   int i = Ints::zero;
   Ints::f(i);
-  
+
   float f = Floats::zero;
   Floats::f(f);
-  
+
   double n = Numbers::zero; // expected-error {{reference to 'zero' is ambiguous}}
   Numbers::f(n); // expected-error{{call to 'f' is ambiguous}}
   Numbers::f(i);
@@ -74,7 +74,7 @@ namespace inline_ns {
   int x; // expected-note 2{{found}}
   inline namespace A {
 #if __cplusplus <= 199711L // C++03 or earlier
-  // expected-warning at -2 {{inline namespaces are a C++11 feature}}
+  // expected-warning at -2 {{inline namespaces are a C++11 extension}}
 #endif
 
     int x; // expected-note 2{{found}}
diff --git a/clang/test/CXX/drs/cwg0xx.cpp b/clang/test/CXX/drs/cwg0xx.cpp
index ea1daee25610c..05fe817e4fc66 100644
--- a/clang/test/CXX/drs/cwg0xx.cpp
+++ b/clang/test/CXX/drs/cwg0xx.cpp
@@ -451,11 +451,11 @@ namespace cwg30 { // cwg30: sup 468 c++11
   } a, *p = &a;
   // FIXME: It's not clear whether CWG468 applies to C++98 too.
   int x = A::template f<0>();
-  // cxx98-error at -1 {{'template' keyword outside of a template}}
+  // cxx98-error at -1 {{use of 'template' keyword outside of a template is a C++11 extension}}
   int y = a.template f<0>();
-  // cxx98-error at -1 {{'template' keyword outside of a template}}
+  // cxx98-error at -1 {{use of 'template' keyword outside of a template is a C++11 extension}}
   int z = p->template f<0>();
-  // cxx98-error at -1 {{'template' keyword outside of a template}}
+  // cxx98-error at -1 {{use of 'template' keyword outside of a template is a C++11 extension}}
 } // namespace cwg30
 
 namespace cwg31 { // cwg31: 2.8
@@ -1115,12 +1115,12 @@ namespace cwg68 { // cwg68: 2.8
   template<typename T> struct X {};
   struct ::cwg68::X<int> x1;
   struct ::cwg68::template X<int> x2;
-  // cxx98-error at -1 {{'template' keyword outside of a template}}
+  // cxx98-error at -1 {{use of 'template' keyword outside of a template is a C++11 extension}}
   struct Y {
     friend struct X<int>;
     friend struct ::cwg68::X<char>;
     friend struct ::cwg68::template X<double>;
-    // cxx98-error at -1 {{'template' keyword outside of a template}}
+    // cxx98-error at -1 {{use of 'template' keyword outside of a template is a C++11 extension}}
   };
   template<typename>
   struct Z {
diff --git a/clang/test/CXX/drs/cwg4xx.cpp b/clang/test/CXX/drs/cwg4xx.cpp
index 4f63b9ac34fb2..96a28100ee987 100644
--- a/clang/test/CXX/drs/cwg4xx.cpp
+++ b/clang/test/CXX/drs/cwg4xx.cpp
@@ -1035,8 +1035,8 @@ namespace cwg468 { // cwg468: 2.7 c++11
     };
   };
   int k = cwg468::template A<int>::template B<char>::C;
-  // cxx98-error at -1 {{'template' keyword outside of a template}}
-  // cxx98-error at -2 {{'template' keyword outside of a template}}
+  // cxx98-error at -1 {{use of 'template' keyword outside of a template is a C++11 extension}}
+  // cxx98-error at -2 {{use of 'template' keyword outside of a template is a C++11 extension}}
 } // namespace cwg468
 
 namespace cwg469 { // cwg469: no
@@ -1264,7 +1264,7 @@ namespace cwg482 { // cwg482: 3.5
   // expected-warning at -1 {{extra qualification on member 'f'}}
 
   inline namespace X {
-  // cxx98-error at -1 {{inline namespaces are a C++11 feature}}
+  // cxx98-error at -1 {{inline namespaces are a C++11 extension}}
     extern int b;
     void g();
     struct S;
diff --git a/clang/test/CXX/stmt.stmt/stmt.select/p3.cpp b/clang/test/CXX/stmt.stmt/stmt.select/p3.cpp
index 067039aa47393..579c361a7640d 100644
--- a/clang/test/CXX/stmt.stmt/stmt.select/p3.cpp
+++ b/clang/test/CXX/stmt.stmt/stmt.select/p3.cpp
@@ -25,9 +25,9 @@ void ifInitStatement() {
   if (Var + Var; true) {}
   if (; true) {}
 #ifdef CPP17
-  // expected-warning at -4 {{if initialization statements are incompatible with C++ standards before C++17}}
-  // expected-warning at -4 {{if initialization statements are incompatible with C++ standards before C++17}}
-  // expected-warning at -4 {{if initialization statements are incompatible with C++ standards before C++17}}
+  // expected-warning at -4 {{'if' initialization statements are incompatible with C++ standards before C++17}}
+  // expected-warning at -4 {{'if' initialization statements are incompatible with C++ standards before C++17}}
+  // expected-warning at -4 {{'if' initialization statements are incompatible with C++ standards before C++17}}
 #else
   // expected-warning at -8 {{'if' initialization statements are a C++17 extension}}
   // expected-warning at -8 {{'if' initialization statements are a C++17 extension}}
@@ -42,9 +42,9 @@ void switchInitStatement() {
   switch (Var + Var; Var) {}
   switch (; Var) {}
 #ifdef CPP17
-  // expected-warning at -4 {{switch initialization statements are incompatible with C++ standards before C++17}}
-  // expected-warning at -4 {{switch initialization statements are incompatible with C++ standards before C++17}}
-  // expected-warning at -4 {{switch initialization statements are incompatible with C++ standards before C++17}}
+  // expected-warning at -4 {{'switch' initialization statements are incompatible with C++ standards before C++17}}
+  // expected-warning at -4 {{'switch' initialization statements are incompatible with C++ standards before C++17}}
+  // expected-warning at -4 {{'switch' initialization statements are incompatible with C++ standards before C++17}}
 #else
   // expected-warning at -8 {{'switch' initialization statements are a C++17 extension}}
   // expected-warning at -8 {{'switch' initialization statements are a C++17 extension}}
diff --git a/clang/test/Lexer/cxx2c-raw-strings.cpp b/clang/test/Lexer/cxx2c-raw-strings.cpp
index cf114e57d8bb1..f74763aa951ba 100644
--- a/clang/test/Lexer/cxx2c-raw-strings.cpp
+++ b/clang/test/Lexer/cxx2c-raw-strings.cpp
@@ -6,9 +6,9 @@ int main() {
   //precxx26-warning at -1 {{'`' in a raw string literal delimiter is a C++2c extension}}
   //precxx26-warning at -2 {{'@' in a raw string literal delimiter is a C++2c extension}}
   //precxx26-warning at -3 {{'$' in a raw string literal delimiter is a C++2c extension}}
-  //cxx26-warning at -4 {{'`' in a raw string literal delimiter is incompatible with standards before C++2c}}
-  //cxx26-warning at -5 {{'@' in a raw string literal delimiter is incompatible with standards before C++2c}}
-  //cxx26-warning at -6 {{'$' in a raw string literal delimiter is incompatible with standards before C++2c}}
+  //cxx26-warning at -4 {{'`' in a raw string literal delimiter is incompatible with C++ standards before C++2c}}
+  //cxx26-warning at -5 {{'@' in a raw string literal delimiter is incompatible with C++ standards before C++2c}}
+  //cxx26-warning at -6 {{'$' in a raw string literal delimiter is incompatible with C++ standards before C++2c}}
 
   (void) R"\t()\t";
   // expected-error at -1 {{invalid character '\' in raw string delimiter}}
@@ -23,6 +23,6 @@ int main() {
   // expected-error at -2 {{expected expression}}
 
   (void) R"@(foo)@";
-  // cxx26-warning at -1 {{'@' in a raw string literal delimiter is incompatible with standards before C++2c}}
+  // cxx26-warning at -1 {{'@' in a raw string literal delimiter is incompatible with C++ standards before C++2c}}
   // precxx26-warning at -2 {{'@' in a raw string literal delimiter is a C++2c extension}}
 }
diff --git a/clang/test/Parser/cxx0x-in-cxx98.cpp b/clang/test/Parser/cxx0x-in-cxx98.cpp
index 10cb4d2fa53aa..90496df13b655 100644
--- a/clang/test/Parser/cxx0x-in-cxx98.cpp
+++ b/clang/test/Parser/cxx0x-in-cxx98.cpp
@@ -1,6 +1,6 @@
 // RUN: %clang_cc1 -std=c++98 -fsyntax-only -verify %s
 
-inline namespace N { // expected-warning{{inline namespaces are a C++11 feature}}
+inline namespace N { // expected-warning{{inline namespaces are a C++11 extension}}
 struct X {
   template<typename ...Args> // expected-warning{{variadic templates are a C++11 extension}}
   void f(Args &&...) &; // expected-warning{{rvalue references are a C++11 extension}} \
diff --git a/clang/test/Parser/cxx0x-lambda-expressions.cpp b/clang/test/Parser/cxx0x-lambda-expressions.cpp
index 5b57c7f638e8a..f2a1972f1fc9d 100644
--- a/clang/test/Parser/cxx0x-lambda-expressions.cpp
+++ b/clang/test/Parser/cxx0x-lambda-expressions.cpp
@@ -146,7 +146,7 @@ struct A {
 };
 
 struct S {
-  void mf() { A(([*this]{})); } // cxx17ext-warning {{'*this' by copy is a C++17 extension}}
+  void mf() { A(([*this]{})); } // cxx17ext-warning {{by value capture of '*this' is a C++17 extension}}
 };
 }
 
diff --git a/clang/test/Parser/cxx11-user-defined-literals.cpp b/clang/test/Parser/cxx11-user-defined-literals.cpp
index 6c15b8133b86f..cf477d8d32385 100644
--- a/clang/test/Parser/cxx11-user-defined-literals.cpp
+++ b/clang/test/Parser/cxx11-user-defined-literals.cpp
@@ -22,7 +22,7 @@ int f() {
 }
 
 static_assert(true, "foo"_bar); // expected-error {{no matching literal operator for call to 'operator""_bar'}}
-// expected-warning at -1 {{'static_assert' with a user-generated message is a C++26 extension}}
+// expected-warning at -1 {{'static_assert' with a user-generated message is a C++2c extension}}
 
 int cake() __attribute__((availability(macosx, unavailable, message = "is a lie"_x))); // expected-error {{user-defined suffix cannot be used here}}
 
diff --git a/clang/test/Parser/cxx1z-nested-namespace-definition.cpp b/clang/test/Parser/cxx1z-nested-namespace-definition.cpp
index b2d6d3253c70e..cbd61b4f15677 100644
--- a/clang/test/Parser/cxx1z-nested-namespace-definition.cpp
+++ b/clang/test/Parser/cxx1z-nested-namespace-definition.cpp
@@ -14,7 +14,7 @@ namespace foo1::foo2::foo3 {
 }
 
 #ifndef FIXIT
-inline namespace goo::bar { // expected-error {{nested namespace definition cannot be 'inline'}} expected-warning 0-1{{C++11 feature}}
+inline namespace goo::bar { // expected-error {{nested namespace definition cannot be 'inline'}} expected-warning 0-1{{C++11 extension}}
   int n;
 }
 
diff --git a/clang/test/Sema/static-assert.c b/clang/test/Sema/static-assert.c
index dd83cd7684d2c..2f1c00e53cbfd 100644
--- a/clang/test/Sema/static-assert.c
+++ b/clang/test/Sema/static-assert.c
@@ -31,7 +31,7 @@ _Static_assert(1, invalid); // ext-warning {{'_Static_assert' is a C11 extension
 // expected-error at -2 {{expected string literal for diagnostic message in static_assert}}
 #endif
 // cxx-error at -4 {{use of undeclared identifier 'invalid'}}
-// cxx-warning at -5 {{'static_assert' with a user-generated message is a C++26 extension}}
+// cxx-warning at -5 {{'static_assert' with a user-generated message is a C++2c extension}}
 
 struct A {
   int a;
diff --git a/clang/test/SemaCXX/cxx98-compat.cpp b/clang/test/SemaCXX/cxx98-compat.cpp
index 587c242271a02..a34fded504167 100644
--- a/clang/test/SemaCXX/cxx98-compat.cpp
+++ b/clang/test/SemaCXX/cxx98-compat.cpp
@@ -35,7 +35,7 @@ template<int ...I>  // expected-warning {{variadic templates are incompatible wi
 class Variadic3 {};
 
 alignas(8) int with_alignas; // expected-warning {{'alignas' is incompatible with C++98}}
-int with_attribute [[ ]]; // expected-warning {{[[]] attributes are incompatible with C++ standards before C++11}}
+int with_attribute [[ ]]; // expected-warning {{[[]] attributes are incompatible with C++98}}
 
 void Literals() {
   (void)u8"str"; // expected-warning {{unicode literals are incompatible with C++98}}
@@ -133,7 +133,7 @@ void RangeFor() {
 }
 
 struct InClassInit {
-  int n = 0; // expected-warning {{default member initializer for non-static data members is incompatible with C++98}}
+  int n = 0; // expected-warning {{default member initializer for non-static data member is incompatible with C++98}}
 };
 
 struct OverrideControlBase {
diff --git a/clang/test/SemaCXX/static-assert-ext.cpp b/clang/test/SemaCXX/static-assert-ext.cpp
index 05f7a0e96974a..a31034c56c606 100644
--- a/clang/test/SemaCXX/static-assert-ext.cpp
+++ b/clang/test/SemaCXX/static-assert-ext.cpp
@@ -22,7 +22,7 @@ struct X {
 
 static_assert(false, X());
 // since-cxx11-warning at -1 {{'static_assert' declarations are incompatible with C++98}}
-// precxx26-warning at -2 {{'static_assert' with a user-generated message is a C++26 extension}}
-// since-cxx26-warning at -3 {{'static_assert' with a user-generated message is incompatible with C++ standards before C++26}}
+// precxx26-warning at -2 {{'static_assert' with a user-generated message is a C++2c extension}}
+// since-cxx26-warning at -3 {{'static_assert' with a user-generated message is incompatible with C++ standards before C++2c}}
 // since-cxx11-error at -4 {{static assertion failed: b}}
 #endif

>From 9eada186d340ed83d87c44d3fa64058657163ca1 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Fri, 28 Aug 2026 10:05:36 +0200
Subject: [PATCH 2/3] Fix formatting

---
 clang/lib/Parse/ParseStmt.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index 78d0648c7b4d9..5e67cd551bff8 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -2483,7 +2483,8 @@ StmtResult Parser::ParseReturnStatement() {
     if (Tok.is(tok::l_brace) && getLangOpts().CPlusPlus) {
       R = ParseInitializer();
       if (R.isUsable())
-        DiagCompat(R.get()->getBeginLoc(), diag_compat::generalized_initializer_lists);
+        DiagCompat(R.get()->getBeginLoc(),
+                   diag_compat::generalized_initializer_lists);
     } else
       R = ParseExpression();
     if (R.isInvalid()) {

>From 97b8707b743eac3ea9ab1888f23018d48ab36a3e Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Wed, 2 Sep 2026 16:58:39 +0200
Subject: [PATCH 3/3] Fix typo

---
 clang/include/clang/Basic/DiagnosticLexKinds.td | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td
index c3ad1902f2d2b..a7e0a22488bf1 100644
--- a/clang/include/clang/Basic/DiagnosticLexKinds.td
+++ b/clang/include/clang/Basic/DiagnosticLexKinds.td
@@ -21,7 +21,7 @@ defm cxx23_pp_directive : CXX23Compat<
 
 // C++26 compatibility with C++23 and earlier.
 defm raw_string_literal_character_set : CXX26Compat<
-  " '%0' in a raw string literal delimiter is", /*ext_warn*/false>;
+  "'%0' in a raw string literal delimiter is", /*ext_warn*/false>;
 
 def null_in_char_or_string : Warning<
   "null character(s) preserved in %select{char|string}0 literal">,



More information about the cfe-commits mailing list