[llvm-branch-commits] [clang] 267dc10 - Implement #pragma clang final extension
Chris Bieneman via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Thu Jul 29 09:24:45 PDT 2021
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 at +1{{previous definition is here}}
+#define Foo 1
+#pragma clang final(Foo)
+
+// expected-warning at +1{{'Foo' macro redefined}}
+#define Foo 2
+
+// expected-warning at +1{{redefining builtin macro}}
+#define __TIME__ 1
+
+// expected-warning at +1{{undefining builtin macro}}
+#undef __TIMESTAMP__
+
+// expected-warning at +1{{macro 'Foo' has been marked as final and should not be undefined}}
+#undef Foo
+// expected-warning at +1{{macro 'Foo' has been marked as final and should not be redefined}}
+#define Foo 3
+
+// Test parse errors
+// expected-error at +1{{expected (}}
+#pragma clang final
+
+// expected-error at +1{{expected )}}
+#pragma clang final(Foo
+
+// expected-error at +1{{no macro named Baz}}
+#pragma clang final(Baz)
+
+// expected-error at +1{{expected identifier}}
+#pragma clang final(4)
+
+// expected-error at +1{{expected (}}
+#pragma clang final Baz
More information about the llvm-branch-commits
mailing list