Author: Chris Bieneman Date: 2021-07-29T11:19:25-05:00 New Revision: 267dc10d53f124dceeccee042f61e6e6624f6d4c
URL: https://github.com/llvm/llvm-project/commit/267dc10d53f124dceeccee042f61e6e6624f6d4c DIFF: https://github.com/llvm/llvm-project/commit/267dc10d53f124dceeccee042f61e6e6624f6d4c.diff LOG: Implement #pragma clang final extension This patch adds a new preprocessor extension ``#pragma clang final`` which enables warning on undefinition and re-definition of macros. The intent of this warning is to extend beyond ``-Wmacro-redefined`` to warn against any and all alterations to macros that are marked `final`. This warning is part of the ``-Wpedantic-macros`` diagnostics group. Added: clang/test/Lexer/final-macro.c Modified: clang/docs/LanguageExtensions.rst clang/include/clang/Basic/DiagnosticGroups.td clang/include/clang/Basic/DiagnosticLexKinds.td clang/include/clang/Basic/IdentifierTable.h clang/lib/Lex/PPDirectives.cpp clang/lib/Lex/Pragma.cpp Removed: ################################################################################ diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index 952db626535f..542222cb8164 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -3926,6 +3926,23 @@ headers with ABI stability requirements. For example: This warning is controlled by ``-Wpedantic-macros``. +Final Macros +============ + +Clang supports the pragma ``#pragma clang final``, which can be used to +mark macros as final, meaning they cannot be undef'd or re-defined. For example: + +.. code-block:: c + #define FINAL_MACRO 1 + #pragma clang final(FINAL_MACRO) + + #undef FINAL_MACRO // warning: FINAL_MACRO is marked final and should not be undefined + #define FINAL_MACRO // warning: FINAL_MACRO is marked final and should not be redefined + +This is useful for enforcing system-provided macros that should not be altered +in user headers or code. This is controlled by ``-Wabi-stability`` and implies +the related ``-Wmacro-redefined``. + Extended Integer Types ====================== diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index bd1cc3c7ff3c..341e120cf139 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -1308,4 +1308,4 @@ def OpenCLCoreFeaturesDiagGroup : DiagGroup<"pedantic-core-features">; // Warnings and extensions to make preprocessor macro usage pedantic def PedanticMacros : DiagGroup<"pedantic-macros", - [DeprecatedPragma, MacroRedefined, BuiltinMacroRedefined]>; + [DeprecatedPragma, MacroRedefined, BuiltinMacroRedefined]>; \ No newline at end of file diff --git a/clang/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td index ea4f0b2d5bb0..e9db43e236e2 100644 --- a/clang/include/clang/Basic/DiagnosticLexKinds.td +++ b/clang/include/clang/Basic/DiagnosticLexKinds.td @@ -530,6 +530,12 @@ def warn_pragma_header_unsafe_macro_use : "%select{|: %2}1">, InGroup<PedanticMacros>; +// - #pragma clang final(...) +def warn_pragma_final_macro : + ExtWarn<"macro %0 has been marked as final and should not be " + "%select{un|re}1defined">, + InGroup<PedanticMacros>; + // - #pragma execution_character_set(...) def warn_pragma_exec_charset_expected : ExtWarn<"#pragma execution_character_set expected '%0'">, diff --git a/clang/include/clang/Basic/IdentifierTable.h b/clang/include/clang/Basic/IdentifierTable.h index 599d10f8bd00..67b824e79464 100644 --- a/clang/include/clang/Basic/IdentifierTable.h +++ b/clang/include/clang/Basic/IdentifierTable.h @@ -127,7 +127,10 @@ class alignas(IdentifierInfoAlignment) IdentifierInfo { // True if this macro is unsafe in headers unsigned IsHeaderUnsafe : 1; - // 23 bits left in a 64-bit word. + // True if this macro is final + unsigned IsFinal : 1; + + // 22 bits left in a 64-bit word. // Managed by the language front-end. void *FETokenInfo = nullptr; @@ -141,7 +144,7 @@ class alignas(IdentifierInfoAlignment) IdentifierInfo { NeedsHandleIdentifier(false), IsFromAST(false), ChangedAfterLoad(false), FEChangedAfterLoad(false), RevertedTokenID(false), OutOfDate(false), IsModulesImport(false), IsMangledOpenMPVariantName(false), - IsDeprecatedMacro(false), IsHeaderUnsafe(false) {} + IsDeprecatedMacro(false), IsHeaderUnsafe(false), IsFinal(false) {} public: IdentifierInfo(const IdentifierInfo &) = delete; @@ -227,6 +230,12 @@ class alignas(IdentifierInfoAlignment) IdentifierInfo { RecomputeNeedsHandleIdentifier(); } + bool isFinal() const { return IsFinal; } + + void setIsFinal(bool Val) { + IsFinal = Val; + } + /// If this is a source-language token (e.g. 'for'), this API /// can be used to cause the lexer to map identifiers to source-language /// tokens. diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp index 60075d09280d..1e5b9a671633 100644 --- a/clang/lib/Lex/PPDirectives.cpp +++ b/clang/lib/Lex/PPDirectives.cpp @@ -2861,6 +2861,12 @@ void Preprocessor::HandleDefineDirective( if (MacroNameTok.is(tok::eod)) return; + IdentifierInfo *II = MacroNameTok.getIdentifierInfo(); + // Issue a final pragma warning if we're defining a macro that was has been + // undefined and is being redefined. + if (!II->hasMacroDefinition() && II->hadMacroDefinition() && II->isFinal()) + Diag(MacroNameTok, diag::warn_pragma_final_macro) << II << 1; + // If we are supposed to keep comments in #defines, reenable comment saving // mode. if (CurLexer) CurLexer->SetCommentRetentionState(KeepMacroComments); @@ -3009,6 +3015,9 @@ void Preprocessor::HandleUndefDirective() { auto MD = getMacroDefinition(II); UndefMacroDirective *Undef = nullptr; + if (II->isFinal()) + Diag(MacroNameTok, diag::warn_pragma_final_macro) << II << 0; + // If the macro is not defined, this is a noop undef. if (const MacroInfo *MI = MD.getMacroInfo()) { if (!MI->isUsed() && MI->isWarnIfUnused()) diff --git a/clang/lib/Lex/Pragma.cpp b/clang/lib/Lex/Pragma.cpp index 2f59dd6f2d4c..23ba3b8f1b84 100644 --- a/clang/lib/Lex/Pragma.cpp +++ b/clang/lib/Lex/Pragma.cpp @@ -1991,6 +1991,46 @@ struct PragmaHeaderUnsafeHandler : public PragmaHandler { } }; +/// "\#pragma clang final(...)" +/// +/// The syntax is +/// \code +/// #pragma clang final(MACRO_NAME) +/// \endcode +struct PragmaFinalHandler : public PragmaHandler { + PragmaFinalHandler() : PragmaHandler("final") {} + + void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer, + Token &Tok) override { + std::string Macro; + + PP.Lex(Tok); + if (Tok.isNot(tok::l_paren)) { + PP.Diag(Tok, diag::err_expected) << "("; + return; + } + + PP.LexUnexpandedToken(Tok); + if (!Tok.is(tok::identifier)) { + PP.Diag(Tok, diag::err_expected) << tok::identifier; + return; + } + IdentifierInfo *II = Tok.getIdentifierInfo(); + + if (!II->hasMacroDefinition()) { + PP.Diag(Tok, diag::err_pp_visibility_non_macro) << II->getName(); + return; + } + + PP.Lex(Tok); + if (Tok.isNot(tok::r_paren)) { + PP.Diag(Tok, diag::err_expected) << ")"; + return; + } + II->setIsFinal(true); + } +}; + } // namespace /// RegisterBuiltinPragmas - Install the standard preprocessor pragmas: @@ -2021,6 +2061,7 @@ void Preprocessor::RegisterBuiltinPragmas() { AddPragmaHandler("clang", new PragmaAssumeNonNullHandler()); AddPragmaHandler("clang", new PragmaDeprecatedHandler()); AddPragmaHandler("clang", new PragmaHeaderUnsafeHandler()); + AddPragmaHandler("clang", new PragmaFinalHandler()); // #pragma clang module ... auto *ModuleHandler = new PragmaNamespace("module"); diff --git a/clang/test/Lexer/final-macro.c b/clang/test/Lexer/final-macro.c new file mode 100644 index 000000000000..11b0a268ac32 --- /dev/null +++ b/clang/test/Lexer/final-macro.c @@ -0,0 +1,36 @@ +// RUN: %clang_cc1 -Wpedantic-macros %s -fsyntax-only -verify + +// Test warning production +// expected-note@+1{{previous definition is here}} +#define Foo 1 +#pragma clang final(Foo) + +// expected-warning@+1{{'Foo' macro redefined}} +#define Foo 2 + +// expected-warning@+1{{redefining builtin macro}} +#define __TIME__ 1 + +// expected-warning@+1{{undefining builtin macro}} +#undef __TIMESTAMP__ + +// expected-warning@+1{{macro 'Foo' has been marked as final and should not be undefined}} +#undef Foo +// expected-warning@+1{{macro 'Foo' has been marked as final and should not be redefined}} +#define Foo 3 + +// Test parse errors +// expected-error@+1{{expected (}} +#pragma clang final + +// expected-error@+1{{expected )}} +#pragma clang final(Foo + +// expected-error@+1{{no macro named Baz}} +#pragma clang final(Baz) + +// expected-error@+1{{expected identifier}} +#pragma clang final(4) + +// expected-error@+1{{expected (}} +#pragma clang final Baz _______________________________________________ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits