[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