[clang] 1e48ef2 - Implement #pragma clang final extension
Chris Bieneman via cfe-commits
cfe-commits at lists.llvm.org
Mon Sep 27 12:11:28 PDT 2021
Author: Chris Bieneman
Date: 2021-09-27T14:11:16-05:00
New Revision: 1e48ef20358fb097f43d9b133e33bd23723d79c2
URL: https://github.com/llvm/llvm-project/commit/1e48ef20358fb097f43d9b133e33bd23723d79c2
DIFF: https://github.com/llvm/llvm-project/commit/1e48ef20358fb097f43d9b133e33bd23723d79c2.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.
Reviewed By: aaron.ballman
Differential Revision: https://reviews.llvm.org/D108567
Added:
clang/test/Lexer/Inputs/final-macro.h
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/include/clang/Lex/Preprocessor.h
clang/lib/Lex/PPDirectives.cpp
clang/lib/Lex/Pragma.cpp
clang/lib/Lex/Preprocessor.cpp
clang/test/Lexer/Inputs/unsafe-macro.h
clang/test/Lexer/deprecate-macro.c
clang/test/Lexer/pedantic-macro-interplay.c
Removed:
################################################################################
diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index 28791a48a6c0d..231c20dab1caa 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -3944,6 +3944,24 @@ will expansion of the macro within the main source file. 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)
+
+ #define FINAL_MACRO // warning: FINAL_MACRO is marked final and should not be redefined
+ #undef FINAL_MACRO // warning: FINAL_MACRO is marked final and should not be undefined
+
+This is useful for enforcing system-provided macros that should not be altered
+in user headers or code. This is controlled by ``-Wpedantic-macros``. Final
+macros will always warn on redefinition, including situations with identical
+bodies and in system headers.
+
Extended Integer Types
======================
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index f48598eec3262..23e9144ea0084 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -648,6 +648,7 @@ def KeywordAsMacro : DiagGroup<"keyword-macro">;
def ReservedIdAsMacro : DiagGroup<"reserved-macro-identifier">;
def ReservedIdAsMacroAlias : DiagGroup<"reserved-id-macro", [ReservedIdAsMacro]>;
def RestrictExpansionMacro : DiagGroup<"restrict-expansion">;
+def FinalMacro : DiagGroup<"final-macro">;
// Just silence warnings about -Wstrict-aliasing for now.
def : DiagGroup<"strict-aliasing=0">;
@@ -1322,4 +1323,5 @@ def PedanticMacros : DiagGroup<"pedantic-macros",
[DeprecatedPragma,
MacroRedefined,
BuiltinMacroRedefined,
- RestrictExpansionMacro]>;
+ RestrictExpansionMacro,
+ FinalMacro]>;
diff --git a/clang/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td
index faa84774167b9..763b7b0086369 100644
--- a/clang/include/clang/Basic/DiagnosticLexKinds.td
+++ b/clang/include/clang/Basic/DiagnosticLexKinds.td
@@ -551,7 +551,13 @@ def warn_pragma_restrict_expansion_macro_use :
// - Note for macro annotations.
def note_pp_macro_annotation :
- Note<"macro marked '%select{deprecated|restrict_expansion}0' here">;
+ Note<"macro marked '%select{deprecated|restrict_expansion|final}0' here">;
+
+// - #pragma clang final(...)
+def warn_pragma_final_macro :
+ ExtWarn<"macro %0 has been marked as final and should not be "
+ "%select{undefined|redefined}1">,
+ InGroup<FinalMacro>;
// - #pragma execution_character_set(...)
def warn_pragma_exec_charset_expected :
diff --git a/clang/include/clang/Basic/IdentifierTable.h b/clang/include/clang/Basic/IdentifierTable.h
index 9c4833b278294..d620138125569 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 IsRestrictExpansion : 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), IsRestrictExpansion(false) {}
+ IsDeprecatedMacro(false), IsRestrictExpansion(false), IsFinal(false) {}
public:
IdentifierInfo(const IdentifierInfo &) = delete;
@@ -189,10 +192,14 @@ 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;
+ // If this is a final macro, make the deprecation and header unsafe bits
+ // stick around after the undefinition so they apply to any redefinitions.
+ if (!IsFinal) {
+ // Because calling the setters of these calls recomputes, just set them
+ // manually to avoid recomputing a bunch of times.
+ IsDeprecatedMacro = false;
+ IsRestrictExpansion = false;
+ }
RecomputeNeedsHandleIdentifier();
}
}
@@ -227,6 +234,10 @@ 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/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h
index 1c7573ec87e14..b8b0890e3243d 100644
--- a/clang/include/clang/Lex/Preprocessor.h
+++ b/clang/include/clang/Lex/Preprocessor.h
@@ -793,12 +793,35 @@ class Preprocessor {
/// 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;
+ struct MacroAnnotationInfo {
+ SourceLocation Location;
+ std::string Message;
+ };
+
+ struct MacroAnnotations {
+ llvm::Optional<MacroAnnotationInfo> DeprecationInfo;
+ llvm::Optional<MacroAnnotationInfo> RestrictExpansionInfo;
+ llvm::Optional<SourceLocation> FinalAnnotationLoc;
+
+ static MacroAnnotations makeDeprecation(SourceLocation Loc,
+ std::string Msg) {
+ return MacroAnnotations{MacroAnnotationInfo{Loc, std::move(Msg)},
+ llvm::None, llvm::None};
+ }
+
+ static MacroAnnotations makeRestrictExpansion(SourceLocation Loc,
+ std::string Msg) {
+ return MacroAnnotations{
+ llvm::None, MacroAnnotationInfo{Loc, std::move(Msg)}, llvm::None};
+ }
+
+ static MacroAnnotations makeFinal(SourceLocation Loc) {
+ return MacroAnnotations{llvm::None, llvm::None, Loc};
+ }
+ };
- /// Usage warning for macros marked by #pragma clang restrict_expansion.
- llvm::DenseMap<const IdentifierInfo *, MsgLocationPair>
- RestrictExpansionMacroMsgs;
+ /// Warning information for macro annotations.
+ llvm::DenseMap<const IdentifierInfo *, MacroAnnotations> AnnotationInfos;
/// A "freelist" of MacroArg objects that can be
/// reused for quick allocation.
@@ -2402,39 +2425,56 @@ class Preprocessor {
/// warnings.
void markMacroAsUsed(MacroInfo *MI);
- void addMacroDeprecationMsg(const IdentifierInfo *II, std::string Msg) {
- MacroDeprecationMsgs.insert(std::make_pair(II, Msg));
- }
-
- llvm::Optional<std::string> getMacroDeprecationMsg(const IdentifierInfo *II) {
- auto MsgEntry = MacroDeprecationMsgs.find(II);
- if (MsgEntry == MacroDeprecationMsgs.end())
- return llvm::None;
- return MsgEntry->second;
+ void addMacroDeprecationMsg(const IdentifierInfo *II, std::string Msg,
+ SourceLocation AnnotationLoc) {
+ auto Annotations = AnnotationInfos.find(II);
+ if (Annotations == AnnotationInfos.end())
+ AnnotationInfos.insert(std::make_pair(
+ II,
+ MacroAnnotations::makeDeprecation(AnnotationLoc, std::move(Msg))));
+ else
+ Annotations->second.DeprecationInfo =
+ MacroAnnotationInfo{AnnotationLoc, std::move(Msg)};
}
void addRestrictExpansionMsg(const IdentifierInfo *II, std::string Msg,
SourceLocation AnnotationLoc) {
- RestrictExpansionMacroMsgs.insert(
- std::make_pair(II, std::make_pair(std::move(Msg), AnnotationLoc)));
+ auto Annotations = AnnotationInfos.find(II);
+ if (Annotations == AnnotationInfos.end())
+ AnnotationInfos.insert(
+ std::make_pair(II, MacroAnnotations::makeRestrictExpansion(
+ AnnotationLoc, std::move(Msg))));
+ else
+ Annotations->second.RestrictExpansionInfo =
+ MacroAnnotationInfo{AnnotationLoc, std::move(Msg)};
+ }
+
+ void addFinalLoc(const IdentifierInfo *II, SourceLocation AnnotationLoc) {
+ auto Annotations = AnnotationInfos.find(II);
+ if (Annotations == AnnotationInfos.end())
+ AnnotationInfos.insert(
+ std::make_pair(II, MacroAnnotations::makeFinal(AnnotationLoc)));
+ else
+ Annotations->second.FinalAnnotationLoc = AnnotationLoc;
}
- MsgLocationPair getRestrictExpansionMsg(const IdentifierInfo *II) {
- return RestrictExpansionMacroMsgs.find(II)->second;
+ const MacroAnnotations &getMacroAnnotations(const IdentifierInfo *II) const {
+ return AnnotationInfos.find(II)->second;
}
- void emitMacroExpansionWarnings(const Token &Identifier) {
+ void emitMacroExpansionWarnings(const Token &Identifier) const {
if (Identifier.getIdentifierInfo()->isDeprecatedMacro())
emitMacroDeprecationWarning(Identifier);
if (Identifier.getIdentifierInfo()->isRestrictExpansion() &&
!SourceMgr.isInMainFile(Identifier.getLocation()))
- emitMacroUnsafeHeaderWarning(Identifier);
+ emitRestrictExpansionWarning(Identifier);
}
private:
- void emitMacroDeprecationWarning(const Token &Identifier);
- void emitMacroUnsafeHeaderWarning(const Token &Identifier);
+ void emitMacroDeprecationWarning(const Token &Identifier) const;
+ void emitRestrictExpansionWarning(const Token &Identifier) const;
+ void emitFinalMacroWarning(const Token &Identifier, bool IsUndef) const;
Optional<unsigned>
getSkippedRangeForExcludedConditionalBlock(SourceLocation HashLoc);
diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp
index 7ea5cb1eb073b..b53ff12bbaa65 100644
--- a/clang/lib/Lex/PPDirectives.cpp
+++ b/clang/lib/Lex/PPDirectives.cpp
@@ -2865,6 +2865,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())
+ emitFinalMacroWarning(MacroNameTok, /*IsUndef=*/false);
+
// If we are supposed to keep comments in #defines, reenable comment saving
// mode.
if (CurLexer) CurLexer->SetCommentRetentionState(KeepMacroComments);
@@ -2907,6 +2913,12 @@ void Preprocessor::HandleDefineDirective(
// Finally, if this identifier already had a macro defined for it, verify that
// the macro bodies are identical, and issue diagnostics if they are not.
if (const MacroInfo *OtherMI=getMacroInfo(MacroNameTok.getIdentifierInfo())) {
+ // Final macros are hard-mode: they always warn. Even if the bodies are
+ // identical. Even if they are in system headers. Even if they are things we
+ // would silently allow in the past.
+ if (MacroNameTok.getIdentifierInfo()->isFinal())
+ emitFinalMacroWarning(MacroNameTok, /*IsUndef=*/false);
+
// In Objective-C, ignore attempts to directly redefine the builtin
// definitions of the ownership qualifiers. It's still possible to
// #undef them.
@@ -2936,6 +2948,7 @@ void Preprocessor::HandleDefineDirective(
// then don't bother calling MacroInfo::isIdenticalTo.
if (!getDiagnostics().getSuppressSystemWarnings() ||
!SourceMgr.isInSystemHeader(DefineTok.getLocation())) {
+
if (!OtherMI->isUsed() && OtherMI->isWarnIfUnused())
Diag(OtherMI->getDefinitionLoc(), diag::pp_macro_not_used);
@@ -3013,6 +3026,9 @@ void Preprocessor::HandleUndefDirective() {
auto MD = getMacroDefinition(II);
UndefMacroDirective *Undef = nullptr;
+ if (II->isFinal())
+ emitFinalMacroWarning(MacroNameTok, /*IsUndef=*/true);
+
// 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 e5bbc7524115b..a6cb5895b3bd8 100644
--- a/clang/lib/Lex/Pragma.cpp
+++ b/clang/lib/Lex/Pragma.cpp
@@ -1990,7 +1990,7 @@ static IdentifierInfo *HandleMacroAnnotationPragma(Preprocessor &PP, Token &Tok,
IdentifierInfo *II = Tok.getIdentifierInfo();
if (!II->hasMacroDefinition()) {
- PP.Diag(Tok, diag::err_pp_visibility_non_macro) << II->getName();
+ PP.Diag(Tok, diag::err_pp_visibility_non_macro) << II;
return nullptr;
}
@@ -2025,8 +2025,8 @@ struct PragmaDeprecatedHandler : public PragmaHandler {
if (IdentifierInfo *II = HandleMacroAnnotationPragma(
PP, Tok, "#pragma clang deprecated", MessageString)) {
II->setIsDeprecatedMacro(true);
- if (!MessageString.empty())
- PP.addMacroDeprecationMsg(II, std::move(MessageString));
+ PP.addMacroDeprecationMsg(II, std::move(MessageString),
+ Tok.getLocation());
}
}
};
@@ -2053,6 +2053,47 @@ struct PragmaRestrictExpansionHandler : 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;
+ return;
+ }
+
+ PP.Lex(Tok);
+ if (Tok.isNot(tok::r_paren)) {
+ PP.Diag(Tok, diag::err_expected) << ")";
+ return;
+ }
+ II->setIsFinal(true);
+ PP.addFinalLoc(II, Tok.getLocation());
+ }
+};
+
} // namespace
/// RegisterBuiltinPragmas - Install the standard preprocessor pragmas:
@@ -2084,6 +2125,7 @@ void Preprocessor::RegisterBuiltinPragmas() {
AddPragmaHandler("clang", new PragmaAssumeNonNullHandler());
AddPragmaHandler("clang", new PragmaDeprecatedHandler());
AddPragmaHandler("clang", new PragmaRestrictExpansionHandler());
+ AddPragmaHandler("clang", new PragmaFinalHandler());
// #pragma clang module ...
auto *ModuleHandler = new PragmaNamespace("module");
diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp
index 97615d89cefc4..bf0b85e038f05 100644
--- a/clang/lib/Lex/Preprocessor.cpp
+++ b/clang/lib/Lex/Preprocessor.cpp
@@ -1409,25 +1409,46 @@ bool Preprocessor::HandleComment(Token &result, SourceRange Comment) {
return true;
}
-void Preprocessor::emitMacroDeprecationWarning(const Token &Identifier) {
- auto DepMsg = getMacroDeprecationMsg(Identifier.getIdentifierInfo());
- if (!DepMsg)
+void Preprocessor::emitMacroDeprecationWarning(const Token &Identifier) const {
+ const MacroAnnotations &A =
+ getMacroAnnotations(Identifier.getIdentifierInfo());
+ assert(A.DeprecationInfo &&
+ "Macro deprecation warning without recorded annotation!");
+ const MacroAnnotationInfo &Info = *A.DeprecationInfo;
+ if (Info.Message.empty())
Diag(Identifier, diag::warn_pragma_deprecated_macro_use)
<< Identifier.getIdentifierInfo() << 0;
else
Diag(Identifier, diag::warn_pragma_deprecated_macro_use)
- << Identifier.getIdentifierInfo() << 1 << *DepMsg;
+ << Identifier.getIdentifierInfo() << 1 << Info.Message;
+ Diag(Info.Location, diag::note_pp_macro_annotation) << 0;
}
-void Preprocessor::emitMacroUnsafeHeaderWarning(const Token &Identifier) {
- auto DepMsg = getRestrictExpansionMsg(Identifier.getIdentifierInfo());
- if (DepMsg.first.empty())
+void Preprocessor::emitRestrictExpansionWarning(const Token &Identifier) const {
+ const MacroAnnotations &A =
+ getMacroAnnotations(Identifier.getIdentifierInfo());
+ assert(A.RestrictExpansionInfo &&
+ "Macro restricted expansion warning without recorded annotation!");
+ const MacroAnnotationInfo &Info = *A.RestrictExpansionInfo;
+ if (Info.Message.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;
+ << Identifier.getIdentifierInfo() << 1 << Info.Message;
+ Diag(Info.Location, diag::note_pp_macro_annotation) << 1;
+}
+
+void Preprocessor::emitFinalMacroWarning(const Token &Identifier,
+ bool IsUndef) const {
+ const MacroAnnotations &A =
+ getMacroAnnotations(Identifier.getIdentifierInfo());
+ assert(A.FinalAnnotationLoc &&
+ "Final macro warning without recorded annotation!");
+
+ Diag(Identifier, diag::warn_pragma_final_macro)
+ << Identifier.getIdentifierInfo() << (IsUndef ? 0 : 1);
+ Diag(*A.FinalAnnotationLoc, diag::note_pp_macro_annotation) << 2;
}
ModuleLoader::~ModuleLoader() = default;
diff --git a/clang/test/Lexer/Inputs/final-macro.h b/clang/test/Lexer/Inputs/final-macro.h
new file mode 100644
index 0000000000000..c616c7b902c0b
--- /dev/null
+++ b/clang/test/Lexer/Inputs/final-macro.h
@@ -0,0 +1,4 @@
+// expected-warning at +2{{macro 'Foo' has been marked as deprecated}}
+// expected-warning at +1{{macro 'Foo' has been marked as unsafe for use in headers}}
+#if Foo
+#endif
diff --git a/clang/test/Lexer/Inputs/unsafe-macro.h b/clang/test/Lexer/Inputs/unsafe-macro.h
index a1b1e7e555a73..8a66e23b7f05b 100644
--- a/clang/test/Lexer/Inputs/unsafe-macro.h
+++ b/clang/test/Lexer/Inputs/unsafe-macro.h
@@ -4,7 +4,7 @@
// expected-error at +1{{expected identifier}}
#pragma clang restrict_expansion(4
-// expected-error at +1{{no macro named foo}}
+// expected-error at +1{{no macro named 'foo'}}
#pragma clang restrict_expansion(foo)
diff --git a/clang/test/Lexer/deprecate-macro.c b/clang/test/Lexer/deprecate-macro.c
index c45d5fc22d822..8814fbdb8f948 100644
--- a/clang/test/Lexer/deprecate-macro.c
+++ b/clang/test/Lexer/deprecate-macro.c
@@ -6,10 +6,11 @@
// expected-error at +1{{expected identifier}}
#pragma clang deprecated(4
-// expected-error at +1{{no macro named foo}}
+// expected-error at +1{{no macro named 'foo'}}
#pragma clang deprecated(foo)
#define bar 1
+// expected-note at +1{{macro marked 'deprecated' here}}
#pragma clang deprecated(bar, "bar is deprecated use 1")
// expected-warning at +1{{macro 'bar' has been marked as deprecated: bar is deprecated use 1}}
@@ -17,6 +18,14 @@
#endif
#define foo 1
+// expected-note at +8{{macro marked 'deprecated' here}}
+// expected-note at +7{{macro marked 'deprecated' here}}
+// expected-note at +6{{macro marked 'deprecated' here}}
+// expected-note at +5{{macro marked 'deprecated' here}}
+// expected-note at +4{{macro marked 'deprecated' here}}
+// expected-note at +3{{macro marked 'deprecated' here}}
+// expected-note at +2{{macro marked 'deprecated' here}}
+// expected-note at +1{{macro marked 'deprecated' here}}
#pragma clang deprecated(foo)
// expected-error at +1{{expected )}}
@@ -39,7 +48,7 @@
#endif
int main(int argc, char** argv) {
- // expected-error at +1{{no macro named main}}
+// expected-error at +1{{no macro named 'main'}}
#pragma clang deprecated(main)
// expected-warning at +1{{macro 'foo' has been marked as deprecated}}
diff --git a/clang/test/Lexer/final-macro.c b/clang/test/Lexer/final-macro.c
new file mode 100644
index 0000000000000..498773d9dfdf5
--- /dev/null
+++ b/clang/test/Lexer/final-macro.c
@@ -0,0 +1,45 @@
+// RUN: %clang_cc1 -Wfinal-macro %s -fsyntax-only -verify
+
+// Test warning production
+#define Foo 1
+// expected-note at +1 4{{macro marked 'final' here}}
+#pragma clang final(Foo)
+
+// expected-warning at +2{{macro 'Foo' has been marked as final and should not be redefined}}
+// expected-note at +1{{previous definition is here}}
+#define Foo 1
+
+// expected-warning at +2{{macro 'Foo' has been marked as final and should not be redefined}}
+// 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
+
+// no diagnostics triggered by these pragmas.
+#pragma clang deprecated(Foo)
+#pragma clang restrict_expansion(Foo)
diff --git a/clang/test/Lexer/pedantic-macro-interplay.c b/clang/test/Lexer/pedantic-macro-interplay.c
index af002d3027013..5544577929b62 100644
--- a/clang/test/Lexer/pedantic-macro-interplay.c
+++ b/clang/test/Lexer/pedantic-macro-interplay.c
@@ -11,4 +11,17 @@
// 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
+
+#define Foo 1
+#pragma clang final(Foo)
+// expected-note at +2{{macro marked 'deprecated' here}}
+// expected-note at +1{{macro marked 'deprecated' here}}
+#pragma clang deprecated(Foo)
+// expected-note at +1{{macro marked 'restrict_expansion' here}}
+#pragma clang restrict_expansion(Foo)
+
+// Test that unsafe_header and deprecated markings stick around after the undef
+#include "Inputs/final-macro.h"
+
+// expected-warning at +1{{macro 'Foo' has been marked as deprecated}}
+const int X = Foo;
More information about the cfe-commits
mailing list