mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-25 10:06:07 +00:00
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
This commit is contained in:
parent
28a0b94d22
commit
a1545f51a9
@ -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
|
||||
-------------------------------------------------
|
||||
|
@ -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'">;
|
||||
|
@ -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).
|
||||
|
@ -40,6 +40,7 @@ const int z = UNSAFE_MACRO_2;
|
||||
#ifdef baz
|
||||
#elifdef UNSAFE_MACRO
|
||||
// expected-warning@-1{{macro 'UNSAFE_MACRO' has been marked as unsafe for use in headers: Don't use this!}}
|
||||
// expected-warning@-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@-2{{macro 'UNSAFE_MACRO' has been marked as unsafe for use in headers: Don't use this!}}
|
||||
// expected-warning@-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@+1{{macro 'UNSAFE_MACRO' has been marked as unsafe for use in headers: Don't use this!}}
|
||||
// not-expected-warning@+2{{macro 'UNSAFE_MACRO' has been marked as unsafe for use in headers: Don't use this!}}
|
||||
// expected-warning@+1{{use of a '#elifndef' directive is a C2x extension}}
|
||||
#elifndef UNSAFE_MACRO
|
||||
#endif
|
||||
|
||||
#ifdef frobble
|
||||
// not-expected-warning@+1{{macro 'UNSAFE_MACRO' has been marked as unsafe for use in headers: Don't use this!}}
|
||||
// not-expected-warning@+2{{macro 'UNSAFE_MACRO' has been marked as unsafe for use in headers: Don't use this!}}
|
||||
// expected-warning@+1{{use of a '#elifdef' directive is a C2x extension}}
|
||||
#elifdef UNSAFE_MACRO
|
||||
#endif
|
||||
|
||||
|
@ -79,6 +79,7 @@ int main(int argc, char** argv) {
|
||||
#ifdef baz
|
||||
#elifdef foo
|
||||
// expected-warning@-1{{macro 'foo' has been marked as deprecated}}
|
||||
// expected-warning@-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@-2{{macro 'foo' has been marked as deprecated}}
|
||||
// expected-warning@-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@+1{{macro 'foo' has been marked as deprecated}}
|
||||
// not-expected-warning@+2{{macro 'foo' has been marked as deprecated}}
|
||||
// expected-warning@+1{{use of a '#elifndef' directive is a C2x extension}}
|
||||
#elifndef foo
|
||||
#endif
|
||||
|
||||
#ifdef frobble
|
||||
// not-expected-warning@+1{{macro 'foo' has been marked as deprecated}}
|
||||
// not-expected-warning@+2{{macro 'foo' has been marked as deprecated}}
|
||||
// expected-warning@+1{{use of a '#elifdef' directive is a C2x extension}}
|
||||
#elifdef foo
|
||||
#endif
|
||||
|
||||
|
@ -1,24 +1,28 @@
|
||||
// RUN: %clang_cc1 %s -Eonly -verify
|
||||
|
||||
/* expected-warning@+2 {{use of a '#elifdef' directive is a C2x extension}} */
|
||||
#ifdef FOO
|
||||
#elifdef BAR
|
||||
#error "did not expect to get here"
|
||||
#endif
|
||||
|
||||
/* expected-error@+4 {{"got it"}} */
|
||||
/* expected-error@+5 {{"got it"}} */
|
||||
/* expected-warning@+2 {{use of a '#elifdef' directive is a C2x extension}} */
|
||||
#ifdef FOO
|
||||
#elifdef BAR
|
||||
#else
|
||||
#error "got it"
|
||||
#endif
|
||||
|
||||
/* expected-error@+3 {{"got it"}} */
|
||||
/* expected-error@+4 {{"got it"}} */
|
||||
/* expected-warning@+2 {{use of a '#elifndef' directive is a C2x extension}} */
|
||||
#ifdef FOO
|
||||
#elifndef BAR
|
||||
#error "got it"
|
||||
#endif
|
||||
|
||||
/* expected-error@+3 {{"got it"}} */
|
||||
/* expected-error@+4 {{"got it"}} */
|
||||
/* expected-warning@+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@+3 {{"got it"}} */
|
||||
/* expected-error@+4 {{"got it"}} */
|
||||
/* expected-warning@+2 {{use of a '#elifdef' directive is a C2x extension}} */
|
||||
#ifdef FOO
|
||||
#elifdef BAR
|
||||
#error "got it"
|
||||
#endif
|
||||
#undef BAR
|
||||
|
||||
/* expected-warning@+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@+4 {{"got it"}} */
|
||||
/* expected-error@+5 {{"got it"}} */
|
||||
/* expected-warning@+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@+3 {{"got it"}} */
|
||||
/* expected-error@+4 {{"got it"}} */
|
||||
/* expected-warning@+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@+3 {{"got it"}} */
|
||||
/* expected-error@+4 {{"got it"}} */
|
||||
/* expected-warning@+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@+3 {{"got it"}} */
|
||||
/* expected-error@+4 {{"got it"}} */
|
||||
/* expected-warning@+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@+6 {{"got it"}} */
|
||||
/* expected-error@+7 {{"got it"}} */
|
||||
/* expected-warning@+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@+3 {{#elifdef after #else}} */
|
||||
/* expected-error@+4 {{#elifdef after #else}} */
|
||||
/* expected-warning@+3 {{use of a '#elifdef' directive is a C2x extension}} */
|
||||
#ifdef FOO
|
||||
#else
|
||||
#elifdef BAR
|
||||
#endif
|
||||
|
||||
/* expected-error@+3 {{#elifndef after #else}} */
|
||||
/* expected-error@+4 {{#elifndef after #else}} */
|
||||
/* expected-warning@+3 {{use of a '#elifndef' directive is a C2x extension}} */
|
||||
#ifdef FOO
|
||||
#else
|
||||
#elifndef BAR
|
||||
#endif
|
||||
|
||||
/* expected-warning@+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@+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@+2 {{"got it"}} */
|
||||
/* expected-error@+4 {{"got it"}} */
|
||||
/* expected-warning@+4 {{use of a '#elifdef' directive is a C2x extension}} */
|
||||
/* expected-warning@+4 {{use of a '#elifndef' directive is a C2x extension}} */
|
||||
#ifndef FOO
|
||||
#error "got it"
|
||||
#elifdef
|
||||
|
59
clang/test/Preprocessor/ext-pp-directive.c
Normal file
59
clang/test/Preprocessor/ext-pp-directive.c
Normal file
@ -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@-3 {{use of a '#elifdef' directive is a C2x extension}}
|
||||
// pre-c2x-compat-warning@-4 {{use of a '#elifdef' directive is incompatible with C standards before C2x}}
|
||||
|
||||
// For C++
|
||||
// pre-cpp2b-pedantic-warning@-7 {{use of a '#elifdef' directive is a C++2b extension}}
|
||||
// pre-cpp2b-compat-warning@-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@-3 {{use of a '#elifndef' directive is a C2x extension}}
|
||||
// pre-c2x-compat-warning@-4 {{use of a '#elifndef' directive is incompatible with C standards before C2x}}
|
||||
|
||||
// For C++
|
||||
// pre-cpp2b-pedantic-warning@-7 {{use of a '#elifndef' directive is a C++2b extension}}
|
||||
// pre-cpp2b-compat-warning@-8 {{use of a '#elifndef' directive is incompatible with C++ standards before C++2b}}
|
@ -5,6 +5,7 @@ extern int x;
|
||||
#if foo // expected-error {{'foo' is not defined, evaluates to 0}}
|
||||
#endif
|
||||
|
||||
// expected-warning@+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@+3 {{use of a '#elifdef' directive is a C2x extension}}
|
||||
#if 0
|
||||
#ifdef D
|
||||
#elifdef D
|
||||
|
@ -19,12 +19,14 @@
|
||||
#if f(2
|
||||
#endif
|
||||
|
||||
/* expected-error@+2 {{macro name missing}} */
|
||||
/* expected-error@+3 {{macro name missing}} */
|
||||
/* expected-warning@+2 {{use of a '#elifdef' directive is a C2x extension}} */
|
||||
#ifdef FOO
|
||||
#elifdef
|
||||
#endif
|
||||
|
||||
/* expected-error@+2 {{macro name must be an identifier}} */
|
||||
/* expected-error@+3 {{macro name must be an identifier}} */
|
||||
/* expected-warning@+2 {{use of a '#elifdef' directive is a C2x extension}} */
|
||||
#ifdef FOO
|
||||
#elifdef !
|
||||
#endif
|
||||
|
@ -4,6 +4,7 @@
|
||||
#ifdef defined
|
||||
#elifdef defined
|
||||
#endif
|
||||
// expected-warning@-2 {{use of a '#elifdef' directive is a C2x extension}}
|
||||
|
||||
|
||||
|
||||
|
@ -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@+1{{missing '('}}
|
||||
#define V1(...) __VA_OPT__
|
||||
|
Loading…
x
Reference in New Issue
Block a user