[clang] 43de869 - Implement #pragma clang restrict_expansion

Chris Bieneman via cfe-commits cfe-commits at lists.llvm.org
Mon Aug 23 09:46:51 PDT 2021


Author: Chris Bieneman
Date: 2021-08-23T09:46:38-07:00
New Revision: 43de869d77f77eedf9f8760f94940a249d2b5a41

URL: https://github.com/llvm/llvm-project/commit/43de869d77f77eedf9f8760f94940a249d2b5a41
DIFF: https://github.com/llvm/llvm-project/commit/43de869d77f77eedf9f8760f94940a249d2b5a41.diff

LOG: Implement #pragma clang restrict_expansion

This patch adds `#pragma clang restrict_expansion ` to enable flagging
macros as unsafe for header use. This is to allow macros that may have
ABI implications to be avoided in headers that have ABI stability
promises.

Using macros in headers (particularly public headers) can cause a
variety of issues relating to ABI and modules. This new pragma logs
warnings when using annotated macros outside the main source file.

This warning is added under a new diagnostics group -Wpedantic-macros

Reviewed By: aaron.ballman

Differential Revision: https://reviews.llvm.org/D107095

Added: 
    clang/test/Lexer/Inputs/pedantic-macro-interplay.h
    clang/test/Lexer/Inputs/unsafe-macro-2.h
    clang/test/Lexer/Inputs/unsafe-macro.h
    clang/test/Lexer/pedantic-macro-interplay.c
    clang/test/Lexer/unsafe-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/include/clang/Lex/Preprocessor.h
    clang/lib/Lex/Pragma.cpp
    clang/lib/Lex/Preprocessor.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index 087aa66c1b7af..fada65c487bc5 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -3931,6 +3931,40 @@ provide deprecation warnings for macro uses. For example:
 ``#pragma GCC warning`` because the warning can be controlled with
 ``-Wdeprecated``.
 
+Restricted Expansion Macros
+===========================
+
+Clang supports the pragma ``#pragma clang restrict_expansion``, which can be
+used restrict macro expansion in headers. This can be valuable when providing
+headers with ABI stability requirements. Any expansion of the annotated macro
+processed by the preprocessor after the ``#pragma`` annotation will log a
+warning. Redefining the macro or undefining the macro will not be diagnosed, nor
+will expansion of the macro within the main source file. For example:
+
+.. code-block:: c
+
+   #define TARGET_ARM 1
+   #pragma clang restrict_expansion(TARGET_ARM, "<reason>")
+
+   /// Foo.h
+   struct Foo {
+   #if TARGET_ARM // warning: TARGET_ARM is marked unsafe in headers: <reason>
+     uint32_t X;
+   #else
+     uint64_t X;
+   #endif
+   };
+
+   /// main.c
+   #include "foo.h"
+   #if TARGET_ARM // No warning in main source file
+   X_TYPE uint32_t
+   #else
+   X_TYPE uint64_t
+   #endif
+
+This warning is controlled by ``-Wpedantic-macros``.
+
 Extended Integer Types
 ======================
 

diff  --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 17b5f419ef58c..753a3d5546e2f 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -647,6 +647,7 @@ def AmbiguousMacro : DiagGroup<"ambiguous-macro">;
 def KeywordAsMacro : DiagGroup<"keyword-macro">;
 def ReservedIdAsMacro : DiagGroup<"reserved-macro-identifier">;
 def ReservedIdAsMacroAlias : DiagGroup<"reserved-id-macro", [ReservedIdAsMacro]>;
+def RestrictExpansionMacro : DiagGroup<"restrict-expansion">;
 
 // Just silence warnings about -Wstrict-aliasing for now.
 def : DiagGroup<"strict-aliasing=0">;
@@ -1311,3 +1312,10 @@ def WebAssemblyExceptionSpec : DiagGroup<"wasm-exception-spec">;
 def RTTI : DiagGroup<"rtti">;
 
 def OpenCLCoreFeaturesDiagGroup : DiagGroup<"pedantic-core-features">;
+
+// Warnings and extensions to make preprocessor macro usage pedantic.
+def PedanticMacros : DiagGroup<"pedantic-macros",
+                    [DeprecatedPragma,
+                     MacroRedefined,
+                     BuiltinMacroRedefined,
+                     RestrictExpansionMacro]>;

diff  --git a/clang/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td
index c19adf104db1f..49c50fb27e624 100644
--- a/clang/include/clang/Basic/DiagnosticLexKinds.td
+++ b/clang/include/clang/Basic/DiagnosticLexKinds.td
@@ -528,6 +528,16 @@ def warn_pragma_deprecated_macro_use :
   ExtWarn<"macro %0 has been marked as deprecated%select{|: %2}1">,
   InGroup<DeprecatedPragma>;
 
+// - #pragma clang restrict_expansion(...)
+def warn_pragma_restrict_expansion_macro_use :
+  ExtWarn<"macro %0 has been marked as unsafe for use in headers"
+          "%select{|: %2}1">,
+  InGroup<RestrictExpansionMacro>;
+
+// - Note for macro annotations.
+def note_pp_macro_annotation :
+  Note<"macro marked '%select{deprecated|restrict_expansion}0' here">;
+
 // - #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 d75d43f0398d7..9c4833b278294 100644
--- a/clang/include/clang/Basic/IdentifierTable.h
+++ b/clang/include/clang/Basic/IdentifierTable.h
@@ -121,10 +121,13 @@ class alignas(IdentifierInfoAlignment) IdentifierInfo {
   // True if this is a mangled OpenMP variant name.
   unsigned IsMangledOpenMPVariantName : 1;
 
-  // True if this is a deprecated macro
+  // True if this is a deprecated macro.
   unsigned IsDeprecatedMacro : 1;
 
-  // 24 bits left in a 64-bit word.
+  // True if this macro is unsafe in headers.
+  unsigned IsRestrictExpansion : 1;
+
+  // 23 bits left in a 64-bit word.
 
   // Managed by the language front-end.
   void *FETokenInfo = nullptr;
@@ -138,7 +141,7 @@ class alignas(IdentifierInfoAlignment) IdentifierInfo {
         NeedsHandleIdentifier(false), IsFromAST(false), ChangedAfterLoad(false),
         FEChangedAfterLoad(false), RevertedTokenID(false), OutOfDate(false),
         IsModulesImport(false), IsMangledOpenMPVariantName(false),
-        IsDeprecatedMacro(false) {}
+        IsDeprecatedMacro(false), IsRestrictExpansion(false) {}
 
 public:
   IdentifierInfo(const IdentifierInfo &) = delete;
@@ -186,8 +189,11 @@ class alignas(IdentifierInfoAlignment) IdentifierInfo {
       NeedsHandleIdentifier = true;
       HadMacro = true;
     } else {
+      // Because calling the setters of these calls recomputes, just set them
+      // manually to avoid recomputing a bunch of times.
+      IsDeprecatedMacro = false;
+      IsRestrictExpansion = false;
       RecomputeNeedsHandleIdentifier();
-      setIsDeprecatedMacro(false);
     }
   }
   /// Returns true if this identifier was \#defined to some value at any
@@ -209,6 +215,18 @@ class alignas(IdentifierInfoAlignment) IdentifierInfo {
       RecomputeNeedsHandleIdentifier();
   }
 
+  bool isRestrictExpansion() const { return IsRestrictExpansion; }
+
+  void setIsRestrictExpansion(bool Val) {
+    if (IsRestrictExpansion == Val)
+      return;
+    IsRestrictExpansion = Val;
+    if (Val)
+      NeedsHandleIdentifier = true;
+    else
+      RecomputeNeedsHandleIdentifier();
+  }
+
   /// 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/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h
index 8ecd6a965cf86..a6c7d0bf0e5b8 100644
--- a/clang/include/clang/Lex/Preprocessor.h
+++ b/clang/include/clang/Lex/Preprocessor.h
@@ -15,6 +15,7 @@
 #define LLVM_CLANG_LEX_PREPROCESSOR_H
 
 #include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/DiagnosticIDs.h"
 #include "clang/Basic/IdentifierTable.h"
 #include "clang/Basic/LLVM.h"
 #include "clang/Basic/LangOptions.h"
@@ -791,9 +792,19 @@ class Preprocessor {
   using WarnUnusedMacroLocsTy = llvm::SmallDenseSet<SourceLocation, 32>;
   WarnUnusedMacroLocsTy WarnUnusedMacroLocs;
 
-  /// Deprecation messages for macros provided in #pragma clang deprecated
+  /// This is a pair of an optional message and source location used for pragmas
+  /// that annotate macros like pragma clang restrict_expansion and pragma clang
+  /// deprecated. This pair stores the optional message and the location of the
+  /// annotation pragma for use producing diagnostics and notes.
+  using MsgLocationPair = std::pair<std::string, SourceLocation>;
+
+  /// Deprecation messages for macros provided in #pragma clang deprecated.
   llvm::DenseMap<const IdentifierInfo *, std::string> MacroDeprecationMsgs;
 
+  /// Usage warning for macros marked by #pragma clang restrict_expansion.
+  llvm::DenseMap<const IdentifierInfo *, MsgLocationPair>
+      RestrictExpansionMacroMsgs;
+
   /// A "freelist" of MacroArg objects that can be
   /// reused for quick allocation.
   MacroArgs *MacroArgCache = nullptr;
@@ -2409,9 +2420,29 @@ class Preprocessor {
     return MsgEntry->second;
   }
 
-  void emitMacroExpansionWarnings(const Token &Identifier);
+  void addRestrictExpansionMsg(const IdentifierInfo *II, std::string Msg,
+                               SourceLocation AnnotationLoc) {
+    RestrictExpansionMacroMsgs.insert(
+        std::make_pair(II, std::make_pair(std::move(Msg), AnnotationLoc)));
+  }
+
+  MsgLocationPair getRestrictExpansionMsg(const IdentifierInfo *II) {
+    return RestrictExpansionMacroMsgs.find(II)->second;
+  }
+
+  void emitMacroExpansionWarnings(const Token &Identifier) {
+    if (Identifier.getIdentifierInfo()->isDeprecatedMacro())
+      emitMacroDeprecationWarning(Identifier);
+
+    if (Identifier.getIdentifierInfo()->isRestrictExpansion() &&
+        !SourceMgr.isInMainFile(Identifier.getLocation()))
+      emitMacroUnsafeHeaderWarning(Identifier);
+  }
 
 private:
+  void emitMacroDeprecationWarning(const Token &Identifier);
+  void emitMacroUnsafeHeaderWarning(const Token &Identifier);
+
   Optional<unsigned>
   getSkippedRangeForExcludedConditionalBlock(SourceLocation HashLoc);
 

diff  --git a/clang/lib/Lex/Pragma.cpp b/clang/lib/Lex/Pragma.cpp
index 4eadd99085923..e5bbc7524115b 100644
--- a/clang/lib/Lex/Pragma.cpp
+++ b/clang/lib/Lex/Pragma.cpp
@@ -1970,6 +1970,45 @@ struct PragmaRegionHandler : public PragmaHandler {
   }
 };
 
+/// This handles parsing pragmas that take a macro name and optional message
+static IdentifierInfo *HandleMacroAnnotationPragma(Preprocessor &PP, Token &Tok,
+                                                   const char *Pragma,
+                                                   std::string &MessageString) {
+  std::string Macro;
+
+  PP.Lex(Tok);
+  if (Tok.isNot(tok::l_paren)) {
+    PP.Diag(Tok, diag::err_expected) << "(";
+    return nullptr;
+  }
+
+  PP.LexUnexpandedToken(Tok);
+  if (!Tok.is(tok::identifier)) {
+    PP.Diag(Tok, diag::err_expected) << tok::identifier;
+    return nullptr;
+  }
+  IdentifierInfo *II = Tok.getIdentifierInfo();
+
+  if (!II->hasMacroDefinition()) {
+    PP.Diag(Tok, diag::err_pp_visibility_non_macro) << II->getName();
+    return nullptr;
+  }
+
+  PP.Lex(Tok);
+  if (Tok.is(tok::comma)) {
+    PP.Lex(Tok);
+    if (!PP.FinishLexStringLiteral(Tok, MessageString, Pragma,
+                                   /*AllowMacroExpansion=*/true))
+      return nullptr;
+  }
+
+  if (Tok.isNot(tok::r_paren)) {
+    PP.Diag(Tok, diag::err_expected) << ")";
+    return nullptr;
+  }
+  return II;
+}
+
 /// "\#pragma clang deprecated(...)"
 ///
 /// The syntax is
@@ -1981,43 +2020,36 @@ struct PragmaDeprecatedHandler : public PragmaHandler {
 
   void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer,
                     Token &Tok) override {
-    std::string Macro, MessageString;
-
-    PP.Lex(Tok);
-    if (Tok.isNot(tok::l_paren)) {
-      PP.Diag(Tok, diag::err_expected) << "(";
-      return;
-    }
+    std::string MessageString;
 
-    PP.LexUnexpandedToken(Tok);
-    if (!Tok.is(tok::identifier)) {
-      PP.Diag(Tok, diag::err_expected) << tok::identifier;
-      return;
+    if (IdentifierInfo *II = HandleMacroAnnotationPragma(
+            PP, Tok, "#pragma clang deprecated", MessageString)) {
+      II->setIsDeprecatedMacro(true);
+      if (!MessageString.empty())
+        PP.addMacroDeprecationMsg(II, std::move(MessageString));
     }
-    IdentifierInfo *II = Tok.getIdentifierInfo();
+  }
+};
 
-    if (!II->hasMacroDefinition()) {
-      PP.Diag(Tok, diag::err_pp_visibility_non_macro) << II->getName();
-      return;
-    }
+/// "\#pragma clang restrict_expansion(...)"
+///
+/// The syntax is
+/// \code
+///   #pragma clang restrict_expansion(MACRO_NAME [, Message])
+/// \endcode
+struct PragmaRestrictExpansionHandler : public PragmaHandler {
+  PragmaRestrictExpansionHandler() : PragmaHandler("restrict_expansion") {}
 
-    PP.Lex(Tok);
-    if (Tok.is(tok::comma)) {
-      PP.Lex(Tok);
-      if (!PP.FinishLexStringLiteral(Tok, MessageString,
-                                     "#pragma clang deprecated",
-                                     /*AllowMacroExpansion=*/true))
-        return;
-    }
+  void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer,
+                    Token &Tok) override {
+    std::string MessageString;
 
-    if (Tok.isNot(tok::r_paren)) {
-      PP.Diag(Tok, diag::err_expected) << ")";
-      return;
+    if (IdentifierInfo *II = HandleMacroAnnotationPragma(
+            PP, Tok, "#pragma clang restrict_expansion", MessageString)) {
+      II->setIsRestrictExpansion(true);
+      PP.addRestrictExpansionMsg(II, std::move(MessageString),
+                                 Tok.getLocation());
     }
-
-    II->setIsDeprecatedMacro(true);
-    if (!MessageString.empty())
-      PP.addMacroDeprecationMsg(II, std::move(MessageString));
   }
 };
 
@@ -2051,6 +2083,7 @@ void Preprocessor::RegisterBuiltinPragmas() {
   AddPragmaHandler("clang", new PragmaARCCFCodeAuditedHandler());
   AddPragmaHandler("clang", new PragmaAssumeNonNullHandler());
   AddPragmaHandler("clang", new PragmaDeprecatedHandler());
+  AddPragmaHandler("clang", new PragmaRestrictExpansionHandler());
 
   // #pragma clang module ...
   auto *ModuleHandler = new PragmaNamespace("module");

diff  --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp
index a0722a26cb780..d79637a68a8b1 100644
--- a/clang/lib/Lex/Preprocessor.cpp
+++ b/clang/lib/Lex/Preprocessor.cpp
@@ -1413,16 +1413,25 @@ bool Preprocessor::HandleComment(Token &result, SourceRange Comment) {
   return true;
 }
 
-void Preprocessor::emitMacroExpansionWarnings(const Token &Identifier) {
-  if (Identifier.getIdentifierInfo()->isDeprecatedMacro()) {
-    auto DepMsg = getMacroDeprecationMsg(Identifier.getIdentifierInfo());
-    if (!DepMsg)
-      Diag(Identifier, diag::warn_pragma_deprecated_macro_use)
-          << Identifier.getIdentifierInfo() << 0;
-    else
-      Diag(Identifier, diag::warn_pragma_deprecated_macro_use)
-          << Identifier.getIdentifierInfo() << 1 << *DepMsg;
-  }
+void Preprocessor::emitMacroDeprecationWarning(const Token &Identifier) {
+  auto DepMsg = getMacroDeprecationMsg(Identifier.getIdentifierInfo());
+  if (!DepMsg)
+    Diag(Identifier, diag::warn_pragma_deprecated_macro_use)
+        << Identifier.getIdentifierInfo() << 0;
+  else
+    Diag(Identifier, diag::warn_pragma_deprecated_macro_use)
+        << Identifier.getIdentifierInfo() << 1 << *DepMsg;
+}
+
+void Preprocessor::emitMacroUnsafeHeaderWarning(const Token &Identifier) {
+  auto DepMsg = getRestrictExpansionMsg(Identifier.getIdentifierInfo());
+  if (DepMsg.first.empty())
+    Diag(Identifier, diag::warn_pragma_restrict_expansion_macro_use)
+        << Identifier.getIdentifierInfo() << 0;
+  else
+    Diag(Identifier, diag::warn_pragma_restrict_expansion_macro_use)
+        << Identifier.getIdentifierInfo() << 1 << DepMsg.first;
+  Diag(DepMsg.second, diag::note_pp_macro_annotation) << 1;
 }
 
 ModuleLoader::~ModuleLoader() = default;

diff  --git a/clang/test/Lexer/Inputs/pedantic-macro-interplay.h b/clang/test/Lexer/Inputs/pedantic-macro-interplay.h
new file mode 100644
index 0000000000000..d33cd84979a32
--- /dev/null
+++ b/clang/test/Lexer/Inputs/pedantic-macro-interplay.h
@@ -0,0 +1,9 @@
+#define UNSAFE_MACRO 1
+#pragma clang restrict_expansion(UNSAFE_MACRO, "Don't use this!")
+// not-expected-warning at +1{{macro 'UNSAFE_MACRO' has been marked as unsafe for use in headers: Don't use this!}}
+#pragma clang deprecated(UNSAFE_MACRO, "Don't use this!")
+
+#define UNSAFE_MACRO_2 1
+#pragma clang deprecated(UNSAFE_MACRO_2, "Don't use this!")
+// not-expected-warning at +1{{macro 'UNSAFE_MACRO_2' has been marked as deprecated: Don't use this!}}
+#pragma clang restrict_expansion(UNSAFE_MACRO_2, "Don't use this!")

diff  --git a/clang/test/Lexer/Inputs/unsafe-macro-2.h b/clang/test/Lexer/Inputs/unsafe-macro-2.h
new file mode 100644
index 0000000000000..f906c8328d4c0
--- /dev/null
+++ b/clang/test/Lexer/Inputs/unsafe-macro-2.h
@@ -0,0 +1,70 @@
+// expected-warning at +1{{macro 'UNSAFE_MACRO' has been marked as unsafe for use in headers: Don't use this!}}
+#if UNSAFE_MACRO
+#endif
+
+// expected-warning at +1{{macro 'UNSAFE_MACRO' has been marked as unsafe for use in headers: Don't use this!}}
+#if defined(UNSAFE_MACRO)
+#endif
+
+// expected-warning at +1{{macro 'UNSAFE_MACRO' has been marked as unsafe for use in headers: Don't use this!}}
+#ifdef UNSAFE_MACRO
+#endif
+
+// expected-warning at +1{{macro 'UNSAFE_MACRO' has been marked as unsafe for use in headers: Don't use this!}}
+#ifndef UNSAFE_MACRO
+#endif
+
+// expected-warning at +1{{macro 'UNSAFE_MACRO' has been marked as unsafe for use in headers: Don't use this!}}
+const int x = UNSAFE_MACRO;
+
+// expected-warning at +1{{macro 'UNSAFE_MACRO_2' has been marked as unsafe for use in headers}}
+const int y = UNSAFE_MACRO_2;
+
+// not-expected-warning at +1{{macro 'UNSAFE_MACRO_2' has been marked as unsafe for use in headers}}
+#undef UNSAFE_MACRO_2
+// not-expected-warning at +1{{macro 'UNSAFE_MACRO_2' has been marked as unsafe for use in headers}}
+#define UNSAFE_MACRO_2 2
+
+// not-expected-warning at +1{{macro 'UNSAFE_MACRO_2' has been marked as unsafe for use in headers}}
+const int z = UNSAFE_MACRO_2;
+
+
+// Test that we diagnose on #elif.
+#if 0
+#elif UNSAFE_MACRO
+// expected-warning at -1{{macro 'UNSAFE_MACRO' has been marked as unsafe for use in headers: Don't use this!}}
+#endif
+
+
+// Test that we diagnose on #elifdef.
+#ifdef baz
+#elifdef UNSAFE_MACRO
+// expected-warning at -1{{macro 'UNSAFE_MACRO' has been marked as unsafe for use in headers: Don't use this!}}
+#endif
+
+// Test that we diagnose on #elifndef.
+#ifdef baz
+#elifndef UNSAFE_MACRO
+#endif
+// expected-warning at -2{{macro 'UNSAFE_MACRO' has been marked as unsafe for use in headers: Don't use this!}}
+
+// FIXME: These cases are currently not handled because clang doesn't expand
+// conditions on skipped #elif* blocks. See the FIXME notes in
+// Preprocessor::SkipExcludedConditionalBlock.
+
+#define frobble
+
+#ifdef frobble
+// not-expected-warning at +1{{macro 'UNSAFE_MACRO' has been marked as unsafe for use in headers: Don't use this!}}
+#elifndef UNSAFE_MACRO
+#endif
+
+#ifdef frobble
+// not-expected-warning at +1{{macro 'UNSAFE_MACRO' has been marked as unsafe for use in headers: Don't use this!}}
+#elifdef UNSAFE_MACRO
+#endif
+
+#if 1
+// not-expected-warning at +1{{macro 'UNSAFE_MACRO' has been marked as unsafe for use in headers: Don't use this!}}
+#elif UNSAFE_MACRO
+#endif

diff  --git a/clang/test/Lexer/Inputs/unsafe-macro.h b/clang/test/Lexer/Inputs/unsafe-macro.h
new file mode 100644
index 0000000000000..a1b1e7e555a73
--- /dev/null
+++ b/clang/test/Lexer/Inputs/unsafe-macro.h
@@ -0,0 +1,27 @@
+// expected-error at +1{{expected (}}
+#pragma clang restrict_expansion
+
+// expected-error at +1{{expected identifier}}
+#pragma clang restrict_expansion(4
+
+// expected-error at +1{{no macro named foo}}
+#pragma clang restrict_expansion(foo)
+
+
+#define UNSAFE_MACRO 1
+// expected-note at +8{{macro marked 'restrict_expansion' here}}
+// expected-note at +7{{macro marked 'restrict_expansion' here}}
+// expected-note at +6{{macro marked 'restrict_expansion' here}}
+// expected-note at +5{{macro marked 'restrict_expansion' here}}
+// expected-note at +4{{macro marked 'restrict_expansion' here}}
+// expected-note at +3{{macro marked 'restrict_expansion' here}}
+// expected-note at +2{{macro marked 'restrict_expansion' here}}
+// expected-note at +1{{macro marked 'restrict_expansion' here}} 
+#pragma clang restrict_expansion(UNSAFE_MACRO, "Don't use this!")
+
+#define UNSAFE_MACRO_2 2
+// expected-note at +1{{macro marked 'restrict_expansion' here}}
+#pragma clang restrict_expansion(UNSAFE_MACRO_2)
+
+// expected-error at +1{{expected )}}
+#pragma clang deprecated(UNSAFE_MACRO

diff  --git a/clang/test/Lexer/pedantic-macro-interplay.c b/clang/test/Lexer/pedantic-macro-interplay.c
new file mode 100644
index 0000000000000..af002d3027013
--- /dev/null
+++ b/clang/test/Lexer/pedantic-macro-interplay.c
@@ -0,0 +1,14 @@
+// RUN: %clang_cc1 -Wpedantic-macros %s -fsyntax-only -verify
+
+// This test verifies that the -Wpedantic-macros warnings don't fire in the
+// annotation pragmas themselves. This ensures you can annotate macros with
+// more than one of the pragmas without spewing warnings all over the code.
+
+#include "Inputs/pedantic-macro-interplay.h"
+
+#define UNSAFE_MACRO_2 1
+#pragma clang deprecated(UNSAFE_MACRO_2, "Don't use this!")
+// not-expected-warning at +1{{macro 'UNSAFE_MACRO_2' has been marked as deprecated: Don't use this!}}
+#pragma clang restrict_expansion(UNSAFE_MACRO_2, "Don't use this!")
+
+// expected-no-diagnostics

diff  --git a/clang/test/Lexer/unsafe-macro.c b/clang/test/Lexer/unsafe-macro.c
new file mode 100644
index 0000000000000..ddf0e2dcc6b43
--- /dev/null
+++ b/clang/test/Lexer/unsafe-macro.c
@@ -0,0 +1,7 @@
+// RUN: %clang_cc1 -Wrestrict-expansion %s -fsyntax-only -verify
+#include "Inputs/unsafe-macro.h"
+#include "Inputs/unsafe-macro-2.h"
+
+// not-expected-warning at +1{{macro 'UNSAFE_MACRO' has been marked as unsafe for use in headers: Don't use this!}}
+#if UNSAFE_MACRO
+#endif


        


More information about the cfe-commits mailing list