diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -204,6 +204,11 @@ ``-Wimplicit-int``. - ``-Wmisexpect`` warns when the branch weights collected during profiling conflict with those added by ``llvm.expect``. +- 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 `_. Non-comprehensive list of changes in this release ------------------------------------------------- diff --git a/clang/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td --- a/clang/include/clang/Basic/DiagnosticLexKinds.td +++ b/clang/include/clang/Basic/DiagnosticLexKinds.td @@ -694,6 +694,23 @@ "#line number greater than 32767 is incompatible with C++98">, InGroup, DefaultIgnore; +def warn_c2x_compat_pp_directive : Warning< + "use of a '#%select{|elifdef|elifndef}0' directive " + "is incompatible with C standards before C2x">, + InGroup, DefaultIgnore; +def ext_c2x_pp_directive : ExtWarn< + "use of a '#%select{|elifdef|elifndef}0' directive " + "is a C2x extension">, + InGroup; +def warn_cxx2b_compat_pp_directive : Warning< + "use of a '#%select{|elifdef|elifndef}0' directive " + "is incompatible with C++ standards before C++2b">, + InGroup, DefaultIgnore; +def ext_cxx2b_pp_directive : ExtWarn< + "use of a '#%select{|elifdef|elifndef}0' directive " + "is a C++2b extension">, + InGroup; + 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 --- a/clang/lib/Lex/PPDirectives.cpp +++ b/clang/lib/Lex/PPDirectives.cpp @@ -652,6 +652,17 @@ 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) @@ -3255,6 +3266,23 @@ : 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 --- a/clang/test/Lexer/Inputs/unsafe-macro-2.h +++ b/clang/test/Lexer/Inputs/unsafe-macro-2.h @@ -40,6 +40,7 @@ #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 @@ #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 @@ #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 diff --git a/clang/test/Lexer/deprecate-macro.c b/clang/test/Lexer/deprecate-macro.c --- a/clang/test/Lexer/deprecate-macro.c +++ b/clang/test/Lexer/deprecate-macro.c @@ -79,6 +79,7 @@ #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 @@ #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 diff --git a/clang/test/Preprocessor/elifdef.c b/clang/test/Preprocessor/elifdef.c --- a/clang/test/Preprocessor/elifdef.c +++ b/clang/test/Preprocessor/elifdef.c @@ -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 diff --git a/clang/test/Preprocessor/ext-pp-directive.c b/clang/test/Preprocessor/ext-pp-directive.c new file mode 100644 --- /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@-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}} diff --git a/clang/test/Preprocessor/if_warning.c b/clang/test/Preprocessor/if_warning.c --- a/clang/test/Preprocessor/if_warning.c +++ b/clang/test/Preprocessor/if_warning.c @@ -5,6 +5,7 @@ #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 @@ // PR3938 +// expected-warning@+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 --- a/clang/test/Preprocessor/ifdef-recover.c +++ b/clang/test/Preprocessor/ifdef-recover.c @@ -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 diff --git a/clang/test/Preprocessor/macro_misc.c b/clang/test/Preprocessor/macro_misc.c --- 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@-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 --- 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@+1{{missing '('}} #define V1(...) __VA_OPT__