[clang] a1545f5 - Warn if using `elifdef` & `elifndef` in not C2x & C++2b mode

Aaron Ballman via cfe-commits cfe-commits at lists.llvm.org
Thu May 12 06:26:54 PDT 2022


Author: Ken Matsui
Date: 2022-05-12T09:26:44-04:00
New Revision: a1545f51a9ef299ca6c6716bd80b862f360453ab

URL: https://github.com/llvm/llvm-project/commit/a1545f51a9ef299ca6c6716bd80b862f360453ab
DIFF: https://github.com/llvm/llvm-project/commit/a1545f51a9ef299ca6c6716bd80b862f360453ab.diff

LOG: Warn if using `elifdef` & `elifndef` in not C2x & C++2b mode

This adds an extension warning when using the preprocessor conditionals
in a language mode they're not officially supported in, and an opt-in
warning for compatibility with previous standards.

Fixes #55306
Differential Revision: https://reviews.llvm.org/D125178

Added: 
    clang/test/Preprocessor/ext-pp-directive.c

Modified: 
    clang/docs/ReleaseNotes.rst
    clang/include/clang/Basic/DiagnosticLexKinds.td
    clang/lib/Lex/PPDirectives.cpp
    clang/test/Lexer/Inputs/unsafe-macro-2.h
    clang/test/Lexer/deprecate-macro.c
    clang/test/Preprocessor/elifdef.c
    clang/test/Preprocessor/if_warning.c
    clang/test/Preprocessor/ifdef-recover.c
    clang/test/Preprocessor/macro_misc.c
    clang/test/Preprocessor/macro_vaopt_check.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 6d2a1b8885ce9..e7dd0d1d6e910 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -217,6 +217,12 @@ Improvements to Clang's diagnostics
 - Added the ``-Wgnu-line-marker`` diagnostic flag (grouped under the ``-Wgnu``
   flag) which is a portability warning about use of GNU linemarker preprocessor
   directives. Fixes `Issue 55067 <https://github.com/llvm/llvm-project/issues/55067>`_.
+- Using ``#elifdef`` and ``#elifndef`` that are incompatible with C/C++
+  standards before C2x/C++2b are now warned via ``-pedantic``. Additionally,
+  on such language mode, ``-Wpre-c2x-compat`` and ``-Wpre-c++2b-compat``
+  diagnostic flags report a compatibility issue.
+  Fixes `Issue 55306 <https://github.com/llvm/llvm-project/issues/55306>`_.
+
 
 Non-comprehensive list of changes in this release
 -------------------------------------------------

diff  --git a/clang/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td
index 01866d9d0eb7b..3622da94f67e2 100644
--- a/clang/include/clang/Basic/DiagnosticLexKinds.td
+++ b/clang/include/clang/Basic/DiagnosticLexKinds.td
@@ -698,6 +698,23 @@ def warn_cxx98_compat_pp_line_too_big : Warning<
   "#line number greater than 32767 is incompatible with C++98">,
   InGroup<CXX98CompatPedantic>, DefaultIgnore;
 
+def warn_c2x_compat_pp_directive : Warning<
+  "use of a '#%select{<BUG IF SEEN>|elifdef|elifndef}0' directive "
+  "is incompatible with C standards before C2x">,
+  InGroup<CPre2xCompat>, DefaultIgnore;
+def ext_c2x_pp_directive : ExtWarn<
+  "use of a '#%select{<BUG IF SEEN>|elifdef|elifndef}0' directive "
+  "is a C2x extension">,
+  InGroup<C2x>;
+def warn_cxx2b_compat_pp_directive : Warning<
+  "use of a '#%select{<BUG IF SEEN>|elifdef|elifndef}0' directive "
+  "is incompatible with C++ standards before C++2b">,
+  InGroup<CXXPre2bCompat>, DefaultIgnore;
+def ext_cxx2b_pp_directive : ExtWarn<
+  "use of a '#%select{<BUG IF SEEN>|elifdef|elifndef}0' directive "
+  "is a C++2b extension">,
+  InGroup<CXX2b>;
+
 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/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp
index 1ef1ba16bcd31..7c7625ad50ba2 100644
--- a/clang/lib/Lex/PPDirectives.cpp
+++ b/clang/lib/Lex/PPDirectives.cpp
@@ -652,6 +652,17 @@ void Preprocessor::SkipExcludedConditionalBlock(SourceLocation HashTokenLoc,
         PPConditionalInfo &CondInfo = CurPPLexer->peekConditionalLevel();
         Token DirectiveToken = Tok;
 
+        // Warn if using `#elifdef` & `#elifndef` in not C2x & C++2b mode even
+        // if this branch is in a skipping block.
+        unsigned DiagID;
+        if (LangOpts.CPlusPlus)
+          DiagID = LangOpts.CPlusPlus2b ? diag::warn_cxx2b_compat_pp_directive
+                                        : diag::ext_cxx2b_pp_directive;
+        else
+          DiagID = LangOpts.C2x ? diag::warn_c2x_compat_pp_directive
+                                : diag::ext_c2x_pp_directive;
+        Diag(Tok, DiagID) << (IsElifDef ? PED_Elifdef : PED_Elifndef);
+
         // If this is a #elif with a #else before it, report the error.
         if (CondInfo.FoundElse)
           Diag(Tok, diag::pp_err_elif_after_else)
@@ -3259,6 +3270,23 @@ void Preprocessor::HandleElifFamilyDirective(Token &ElifToken,
                                                  : PED_Elifndef;
   ++NumElse;
 
+  // Warn if using `#elifdef` & `#elifndef` in not C2x & C++2b mode.
+  switch (DirKind) {
+  case PED_Elifdef:
+  case PED_Elifndef:
+    unsigned DiagID;
+    if (LangOpts.CPlusPlus)
+      DiagID = LangOpts.CPlusPlus2b ? diag::warn_cxx2b_compat_pp_directive
+                                    : diag::ext_cxx2b_pp_directive;
+    else
+      DiagID = LangOpts.C2x ? diag::warn_c2x_compat_pp_directive
+                            : diag::ext_c2x_pp_directive;
+    Diag(ElifToken, DiagID) << DirKind;
+    break;
+  default:
+    break;
+  }
+
   // #elif directive in a non-skipping conditional... start skipping.
   // We don't care what the condition is, because we will always skip it (since
   // the block immediately before it was included).

diff  --git a/clang/test/Lexer/Inputs/unsafe-macro-2.h b/clang/test/Lexer/Inputs/unsafe-macro-2.h
index f906c8328d4c0..e7254bc007a56 100644
--- a/clang/test/Lexer/Inputs/unsafe-macro-2.h
+++ b/clang/test/Lexer/Inputs/unsafe-macro-2.h
@@ -40,6 +40,7 @@ const int z = UNSAFE_MACRO_2;
 #ifdef baz
 #elifdef UNSAFE_MACRO
 // expected-warning at -1{{macro 'UNSAFE_MACRO' has been marked as unsafe for use in headers: Don't use this!}}
+// expected-warning at -2{{use of a '#elifdef' directive is a C2x extension}}
 #endif
 
 // Test that we diagnose on #elifndef.
@@ -47,6 +48,7 @@ const int z = UNSAFE_MACRO_2;
 #elifndef UNSAFE_MACRO
 #endif
 // expected-warning at -2{{macro 'UNSAFE_MACRO' has been marked as unsafe for use in headers: Don't use this!}}
+// expected-warning at -3{{use of a '#elifndef' directive is a C2x extension}}
 
 // FIXME: These cases are currently not handled because clang doesn't expand
 // conditions on skipped #elif* blocks. See the FIXME notes in
@@ -55,12 +57,14 @@ const int z = UNSAFE_MACRO_2;
 #define frobble
 
 #ifdef frobble
-// not-expected-warning at +1{{macro 'UNSAFE_MACRO' has been marked as unsafe for use in headers: Don't use this!}}
+// not-expected-warning at +2{{macro 'UNSAFE_MACRO' has been marked as unsafe for use in headers: Don't use this!}}
+// expected-warning at +1{{use of a '#elifndef' directive is a C2x extension}}
 #elifndef UNSAFE_MACRO
 #endif
 
 #ifdef frobble
-// not-expected-warning at +1{{macro 'UNSAFE_MACRO' has been marked as unsafe for use in headers: Don't use this!}}
+// not-expected-warning at +2{{macro 'UNSAFE_MACRO' has been marked as unsafe for use in headers: Don't use this!}}
+// expected-warning at +1{{use of a '#elifdef' directive is a C2x extension}}
 #elifdef UNSAFE_MACRO
 #endif
 

diff  --git a/clang/test/Lexer/deprecate-macro.c b/clang/test/Lexer/deprecate-macro.c
index 8814fbdb8f948..1f08093b17a4e 100644
--- a/clang/test/Lexer/deprecate-macro.c
+++ b/clang/test/Lexer/deprecate-macro.c
@@ -79,6 +79,7 @@ int main(int argc, char** argv) {
 #ifdef baz
 #elifdef foo
 // expected-warning at -1{{macro 'foo' has been marked as deprecated}}
+// expected-warning at -2{{use of a '#elifdef' directive is a C2x extension}}
 #endif
 
 // Test that we diagnose on #elifndef.
@@ -86,18 +87,21 @@ int main(int argc, char** argv) {
 #elifndef foo
 #endif
 // expected-warning at -2{{macro 'foo' has been marked as deprecated}}
+// expected-warning at -3{{use of a '#elifndef' directive is a C2x extension}}
 
 // FIXME: These cases are currently not handled because clang doesn't expand
 // conditions on skipped #elif* blocks. See the FIXME notes in
 // Preprocessor::SkipExcludedConditionalBlock.
 
 #ifdef frobble
-// not-expected-warning at +1{{macro 'foo' has been marked as deprecated}}
+// not-expected-warning at +2{{macro 'foo' has been marked as deprecated}}
+// expected-warning at +1{{use of a '#elifndef' directive is a C2x extension}}
 #elifndef foo
 #endif
 
 #ifdef frobble
-// not-expected-warning at +1{{macro 'foo' has been marked as deprecated}}
+// not-expected-warning at +2{{macro 'foo' has been marked as deprecated}}
+// expected-warning at +1{{use of a '#elifdef' directive is a C2x extension}}
 #elifdef foo
 #endif
 

diff  --git a/clang/test/Preprocessor/elifdef.c b/clang/test/Preprocessor/elifdef.c
index 6bc467d70011f..900d21e6548e0 100644
--- a/clang/test/Preprocessor/elifdef.c
+++ b/clang/test/Preprocessor/elifdef.c
@@ -1,24 +1,28 @@
 // RUN: %clang_cc1 %s -Eonly -verify
 
+/* expected-warning at +2 {{use of a '#elifdef' directive is a C2x extension}} */
 #ifdef FOO
 #elifdef BAR
 #error "did not expect to get here"
 #endif
 
-/* expected-error at +4 {{"got it"}} */
+/* expected-error at +5 {{"got it"}} */
+/* expected-warning at +2 {{use of a '#elifdef' directive is a C2x extension}} */
 #ifdef FOO
 #elifdef BAR
 #else
 #error "got it"
 #endif
 
-/* expected-error at +3 {{"got it"}} */
+/* expected-error at +4 {{"got it"}} */
+/* expected-warning at +2 {{use of a '#elifndef' directive is a C2x extension}} */
 #ifdef FOO
 #elifndef BAR
 #error "got it"
 #endif
 
-/* expected-error at +3 {{"got it"}} */
+/* expected-error at +4 {{"got it"}} */
+/* expected-warning at +2 {{use of a '#elifndef' directive is a C2x extension}} */
 #ifdef FOO
 #elifndef BAR
 #error "got it"
@@ -27,32 +31,37 @@
 #endif
 
 #define BAR
-/* expected-error at +3 {{"got it"}} */
+/* expected-error at +4 {{"got it"}} */
+/* expected-warning at +2 {{use of a '#elifdef' directive is a C2x extension}} */
 #ifdef FOO
 #elifdef BAR
 #error "got it"
 #endif
 #undef BAR
 
+/* expected-warning at +2 {{use of a '#elifdef' directive is a C2x extension}} */
 #ifdef FOO
 #elifdef BAR // test that comments aren't an issue
 #error "did not expect to get here"
 #endif
 
-/* expected-error at +4 {{"got it"}} */
+/* expected-error at +5 {{"got it"}} */
+/* expected-warning at +2 {{use of a '#elifdef' directive is a C2x extension}} */
 #ifdef FOO
 #elifdef BAR // test that comments aren't an issue
 #else
 #error "got it"
 #endif
 
-/* expected-error at +3 {{"got it"}} */
+/* expected-error at +4 {{"got it"}} */
+/* expected-warning at +2 {{use of a '#elifndef' directive is a C2x extension}} */
 #ifdef FOO
 #elifndef BAR // test that comments aren't an issue
 #error "got it"
 #endif
 
-/* expected-error at +3 {{"got it"}} */
+/* expected-error at +4 {{"got it"}} */
+/* expected-warning at +2 {{use of a '#elifndef' directive is a C2x extension}} */
 #ifdef FOO
 #elifndef BAR // test that comments aren't an issue
 #error "got it"
@@ -61,7 +70,8 @@
 #endif
 
 #define BAR
-/* expected-error at +3 {{"got it"}} */
+/* expected-error at +4 {{"got it"}} */
+/* expected-warning at +2 {{use of a '#elifdef' directive is a C2x extension}} */
 #ifdef FOO
 #elifdef BAR // test that comments aren't an issue
 #error "got it"
@@ -69,7 +79,8 @@
 #undef BAR
 
 #define BAR
-/* expected-error at +6 {{"got it"}} */
+/* expected-error at +7 {{"got it"}} */
+/* expected-warning at +3 {{use of a '#elifndef' directive is a C2x extension}} */
 #ifdef FOO
 #error "did not expect to get here"
 #elifndef BAR
@@ -79,27 +90,33 @@
 #endif
 #undef BAR
 
-/* expected-error at +3 {{#elifdef after #else}} */
+/* expected-error at +4 {{#elifdef after #else}} */
+/* expected-warning at +3 {{use of a '#elifdef' directive is a C2x extension}} */
 #ifdef FOO
 #else
 #elifdef BAR
 #endif
 
-/* expected-error at +3 {{#elifndef after #else}} */
+/* expected-error at +4 {{#elifndef after #else}} */
+/* expected-warning at +3 {{use of a '#elifndef' directive is a C2x extension}} */
 #ifdef FOO
 #else
 #elifndef BAR
 #endif
 
+/* expected-warning at +1 {{use of a '#elifdef' directive is a C2x extension}} */
 #elifdef FOO /* expected-error {{#elifdef without #if}} */
 #endif       /* expected-error {{#endif without #if}} */
 
+/* expected-warning at +1 {{use of a '#elifndef' directive is a C2x extension}} */
 #elifndef FOO /* expected-error {{#elifndef without #if}} */
 #endif        /* expected-error {{#endif without #if}} */
 
 /* Note, we do not expect errors about the missing macro name in the skipped
    blocks. This is consistent with #elif behavior. */
-/* expected-error at +2 {{"got it"}} */
+/* expected-error at +4 {{"got it"}} */
+/* expected-warning at +4 {{use of a '#elifdef' directive is a C2x extension}} */
+/* expected-warning at +4 {{use of a '#elifndef' directive is a C2x extension}} */
 #ifndef FOO
 #error "got it"
 #elifdef

diff  --git a/clang/test/Preprocessor/ext-pp-directive.c b/clang/test/Preprocessor/ext-pp-directive.c
new file mode 100644
index 0000000000000..45f7ecba3022b
--- /dev/null
+++ b/clang/test/Preprocessor/ext-pp-directive.c
@@ -0,0 +1,59 @@
+// For C
+// RUN: %clang_cc1 -std=c99 -fsyntax-only -verify=pre-c2x-pedantic -pedantic %s
+// RUN: %clang_cc1 -std=c2x -fsyntax-only -verify=pre-c2x-compat -Wpre-c2x-compat %s
+// RUN: not %clang_cc1 -std=c99 -fsyntax-only -verify %s
+// RUN: not %clang_cc1 -std=c2x -fsyntax-only -verify -pedantic %s
+// RUN: not %clang_cc1 -std=c2x -fsyntax-only -verify %s
+
+// For C++
+// RUN: %clang_cc1 -x c++ -fsyntax-only -verify=pre-cpp2b-pedantic -pedantic %s
+// RUN: %clang_cc1 -x c++ -std=c++2b -fsyntax-only -verify=pre-cpp2b-compat -Wpre-c++2b-compat %s
+// RUN: not %clang_cc1 -x c++ -fsyntax-only -verify %s
+// RUN: not %clang_cc1 -x c++ -std=c++2b -fsyntax-only -verify -pedantic %s
+// RUN: not %clang_cc1 -x c++ -std=c++2b -fsyntax-only -verify %s
+
+int x;
+
+#if 1
+#elifdef A // #1
+#endif
+// For C
+// pre-c2x-pedantic-warning@#1 {{use of a '#elifdef' directive is a C2x extension}}
+// pre-c2x-compat-warning@#1 {{use of a '#elifdef' directive is incompatible with C standards before C2x}}
+
+// For C++
+// pre-cpp2b-pedantic-warning@#1 {{use of a '#elifdef' directive is a C++2b extension}}
+// pre-cpp2b-compat-warning@#1 {{use of a '#elifdef' directive is incompatible with C++ standards before C++2b}}
+
+#if 1
+#elifndef B // #2
+#endif
+// For C
+// pre-c2x-pedantic-warning@#2 {{use of a '#elifndef' directive is a C2x extension}}
+// pre-c2x-compat-warning@#2 {{use of a '#elifndef' directive is incompatible with C standards before C2x}}
+
+// For C++
+// pre-cpp2b-pedantic-warning@#2 {{use of a '#elifndef' directive is a C++2b extension}}
+// pre-cpp2b-compat-warning@#2 {{use of a '#elifndef' directive is incompatible with C++ standards before C++2b}}
+
+#if 0
+#elifdef C
+#endif
+// For C
+// pre-c2x-pedantic-warning at -3 {{use of a '#elifdef' directive is a C2x extension}}
+// pre-c2x-compat-warning at -4 {{use of a '#elifdef' directive is incompatible with C standards before C2x}}
+
+// For C++
+// pre-cpp2b-pedantic-warning at -7 {{use of a '#elifdef' directive is a C++2b extension}}
+// pre-cpp2b-compat-warning at -8 {{use of a '#elifdef' directive is incompatible with C++ standards before C++2b}}
+
+#if 0
+#elifndef D
+#endif
+// For C
+// pre-c2x-pedantic-warning at -3 {{use of a '#elifndef' directive is a C2x extension}}
+// pre-c2x-compat-warning at -4 {{use of a '#elifndef' directive is incompatible with C standards before C2x}}
+
+// For C++
+// pre-cpp2b-pedantic-warning at -7 {{use of a '#elifndef' directive is a C++2b extension}}
+// pre-cpp2b-compat-warning at -8 {{use of a '#elifndef' directive is incompatible with C++ standards before C++2b}}

diff  --git a/clang/test/Preprocessor/if_warning.c b/clang/test/Preprocessor/if_warning.c
index 511d10232445d..42c834b10aa00 100644
--- a/clang/test/Preprocessor/if_warning.c
+++ b/clang/test/Preprocessor/if_warning.c
@@ -5,6 +5,7 @@ extern int x;
 #if foo   // expected-error {{'foo' is not defined, evaluates to 0}}
 #endif
 
+// expected-warning at +2 {{use of a '#elifdef' directive is a C2x extension}}
 #ifdef foo
 #elifdef foo
 #endif
@@ -14,6 +15,7 @@ extern int x;
 
 
 // PR3938
+// expected-warning at +3 {{use of a '#elifdef' directive is a C2x extension}}
 #if 0
 #ifdef D
 #elifdef D

diff  --git a/clang/test/Preprocessor/ifdef-recover.c b/clang/test/Preprocessor/ifdef-recover.c
index b058851f6d1c1..33a53003bc2bc 100644
--- a/clang/test/Preprocessor/ifdef-recover.c
+++ b/clang/test/Preprocessor/ifdef-recover.c
@@ -19,12 +19,14 @@
 #if f(2
 #endif
 
-/* expected-error at +2 {{macro name missing}} */
+/* expected-error at +3 {{macro name missing}} */
+/* expected-warning at +2 {{use of a '#elifdef' directive is a C2x extension}} */
 #ifdef FOO
 #elifdef
 #endif
 
-/* expected-error at +2 {{macro name must be an identifier}} */
+/* expected-error at +3 {{macro name must be an identifier}} */
+/* expected-warning at +2 {{use of a '#elifdef' directive is a C2x extension}} */
 #ifdef FOO
 #elifdef !
 #endif

diff  --git a/clang/test/Preprocessor/macro_misc.c b/clang/test/Preprocessor/macro_misc.c
index 92cd614867683..f80ab7144d544 100644
--- a/clang/test/Preprocessor/macro_misc.c
+++ b/clang/test/Preprocessor/macro_misc.c
@@ -4,6 +4,7 @@
 #ifdef defined
 #elifdef defined
 #endif
+// expected-warning at -2 {{use of a '#elifdef' directive is a C2x extension}}
 
 
 

diff  --git a/clang/test/Preprocessor/macro_vaopt_check.cpp b/clang/test/Preprocessor/macro_vaopt_check.cpp
index a0bec14ded1aa..d0469d7e884cf 100644
--- a/clang/test/Preprocessor/macro_vaopt_check.cpp
+++ b/clang/test/Preprocessor/macro_vaopt_check.cpp
@@ -1,6 +1,6 @@
-// RUN: %clang_cc1 %s -Eonly -verify -Wno-all -pedantic -std=c++20
-// RUN: %clang_cc1 %s -Eonly -verify -Wno-all -pedantic -std=c++11
-// RUN: %clang_cc1 -x c %s -Eonly -verify -Wno-all -pedantic -std=c99
+// RUN: %clang_cc1 %s -Eonly -verify -Wno-all -Wno-c++2b-extensions -pedantic -std=c++20
+// RUN: %clang_cc1 %s -Eonly -verify -Wno-all -Wno-c++2b-extensions -pedantic -std=c++11
+// RUN: %clang_cc1 -x c %s -Eonly -verify -Wno-all -Wno-c2x-extensions -pedantic -std=c99
 
 //expected-error at +1{{missing '('}}
 #define V1(...) __VA_OPT__  


        


More information about the cfe-commits mailing list