[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