beanz updated this revision to Diff 361831.
beanz added a comment.
Fixing handling of #ifdef, #ifndef, and defined()
All tests pass this time :)
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D106732/new/
https://reviews.llvm.org/D106732
Files:
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/PPExpressions.cpp
clang/lib/Lex/PPMacroExpansion.cpp
clang/lib/Lex/Pragma.cpp
clang/test/Lexer/deprecate-macro.c
Index: clang/test/Lexer/deprecate-macro.c
===================================================================
--- /dev/null
+++ clang/test/Lexer/deprecate-macro.c
@@ -0,0 +1,60 @@
+// RUN: %clang_cc1 -Wdeprecated %s -fsyntax-only -verify
+
+// expected-error@+1{{expected (}}
+#pragma clang deprecated
+
+// expected-error@+1{{expected identifier}}
+#pragma clang deprecated(4
+
+// expected-error@+1{{no macro named foo}}
+#pragma clang deprecated(foo)
+
+#define bar 1
+#pragma clang deprecated(bar, "bar is deprecated use 1")
+
+// expected-warning@+1{{macro 'bar' has been marked as deprecated: bar is deprecated use 1}}
+#if bar
+#endif
+
+#define foo 1
+#pragma clang deprecated(foo)
+
+// expected-error@+1{{expected )}}
+#pragma clang deprecated(foo
+
+// expected-warning@+1{{macro 'foo' has been marked as deprecated}}
+#if foo
+#endif
+
+// expected-warning@+1{{macro 'foo' has been marked as deprecated}}
+#if defined(foo)
+#endif
+
+// expected-warning@+1{{macro 'foo' has been marked as deprecated}}
+#ifdef foo
+#endif
+
+// expected-warning@+1{{macro 'foo' has been marked as deprecated}}
+#ifndef foo
+#endif
+
+int main(int argc, char** argv) {
+ // expected-error@+1{{no macro named main}}
+#pragma clang deprecated(main)
+
+ // expected-warning@+1{{macro 'foo' has been marked as deprecated}}
+ return foo;
+}
+
+#define frobble 1
+#pragma clang deprecated(frobble)
+
+// not-expected-warning@+1{{macro 'frobble' has been marked as deprecated}}
+#undef frobble // Expect no diagnostics here
+
+// not-expected-warning@+1{{macro 'frobble' has been marked as deprecated}}
+#define frobble 1 // How about here given that this was undefined?
+
+// not-expected-warning@+1{{macro 'frobble' has been marked as deprecated}}
+#if defined(frobble)
+#endif
Index: clang/lib/Lex/Pragma.cpp
===================================================================
--- clang/lib/Lex/Pragma.cpp
+++ clang/lib/Lex/Pragma.cpp
@@ -1911,6 +1911,56 @@
}
};
+/// "\#pragma clang deprecate()"
+///
+/// The syntax is
+/// \code
+/// #pragma clang deprecate(MACRO_NAME [, Message])
+/// \endcode
+struct PragmaDeprecatedHandler : public PragmaHandler {
+ PragmaDeprecatedHandler() : PragmaHandler("deprecated") {}
+
+ 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;
+ }
+
+ 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.is(tok::comma)) {
+ PP.Lex(Tok);
+ if (!PP.FinishLexStringLiteral(Tok, MessageString,
+ "#pragma clang deprecated",
+ /*AllowMacroExpansion=*/true))
+ return;
+ }
+
+ if (Tok.isNot(tok::r_paren)) {
+ PP.Diag(Tok, diag::err_expected) << ")";
+ return;
+ }
+
+ II->setIsDeprecatedMacro(true);
+ PP.addMacroDeprecationMsg(II, std::move(MessageString));
+ }
+};
+
} // namespace
/// RegisterBuiltinPragmas - Install the standard preprocessor pragmas:
@@ -1939,6 +1989,7 @@
AddPragmaHandler("clang", new PragmaDiagnosticHandler("clang"));
AddPragmaHandler("clang", new PragmaARCCFCodeAuditedHandler());
AddPragmaHandler("clang", new PragmaAssumeNonNullHandler());
+ AddPragmaHandler("clang", new PragmaDeprecatedHandler());
// #pragma clang module ...
auto *ModuleHandler = new PragmaNamespace("module");
Index: clang/lib/Lex/PPMacroExpansion.cpp
===================================================================
--- clang/lib/Lex/PPMacroExpansion.cpp
+++ clang/lib/Lex/PPMacroExpansion.cpp
@@ -471,6 +471,16 @@
/// expanded as a macro, handle it and return the next token as 'Identifier'.
bool Preprocessor::HandleMacroExpandedIdentifier(Token &Identifier,
const MacroDefinition &M) {
+ 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;
+ }
+
MacroInfo *MI = M.getMacroInfo();
// If this is a macro expansion in the "#if !defined(x)" line for the file,
Index: clang/lib/Lex/PPExpressions.cpp
===================================================================
--- clang/lib/Lex/PPExpressions.cpp
+++ clang/lib/Lex/PPExpressions.cpp
@@ -133,6 +133,15 @@
Result.Val.setIsUnsigned(false); // Result is signed intmax_t.
DT.IncludedUndefinedIds = !Macro;
+ if (II->isDeprecatedMacro()) {
+ auto DepMsg = PP.getMacroDeprecationMsg(II);
+ if (!DepMsg)
+ PP.Diag(PeekTok, diag::warn_pragma_deprecated_macro_use) << II << 0;
+ else
+ PP.Diag(PeekTok, diag::warn_pragma_deprecated_macro_use)
+ << II << 1 << *DepMsg;
+ }
+
// If there is a macro, mark it used.
if (Result.Val != 0 && ValueLive)
PP.markMacroAsUsed(Macro.getMacroInfo());
Index: clang/lib/Lex/PPDirectives.cpp
===================================================================
--- clang/lib/Lex/PPDirectives.cpp
+++ clang/lib/Lex/PPDirectives.cpp
@@ -3038,6 +3038,16 @@
Token MacroNameTok;
ReadMacroName(MacroNameTok);
+ if (MacroNameTok.getIdentifierInfo()->isDeprecatedMacro()) {
+ auto DepMsg = getMacroDeprecationMsg(MacroNameTok.getIdentifierInfo());
+ if (!DepMsg)
+ Diag(MacroNameTok, diag::warn_pragma_deprecated_macro_use)
+ << MacroNameTok.getIdentifierInfo() << 0;
+ else
+ Diag(MacroNameTok, diag::warn_pragma_deprecated_macro_use)
+ << MacroNameTok.getIdentifierInfo() << 1 << *DepMsg;
+ }
+
// Error reading macro name? If so, diagnostic already issued.
if (MacroNameTok.is(tok::eod)) {
// Skip code until we get to #endif. This helps with recovery by not
Index: clang/include/clang/Lex/Preprocessor.h
===================================================================
--- clang/include/clang/Lex/Preprocessor.h
+++ clang/include/clang/Lex/Preprocessor.h
@@ -786,6 +786,9 @@
using WarnUnusedMacroLocsTy = llvm::SmallDenseSet<SourceLocation, 32>;
WarnUnusedMacroLocsTy WarnUnusedMacroLocs;
+ /// Deprecation messages for macros provided in #pragma clang deprecated
+ llvm::DenseMap<const IdentifierInfo *, std::string> MacroDeprecationMsgs;
+
/// A "freelist" of MacroArg objects that can be
/// reused for quick allocation.
MacroArgs *MacroArgCache = nullptr;
@@ -2385,6 +2388,17 @@
/// 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;
+ }
+
private:
Optional<unsigned>
getSkippedRangeForExcludedConditionalBlock(SourceLocation HashLoc);
Index: clang/include/clang/Basic/IdentifierTable.h
===================================================================
--- clang/include/clang/Basic/IdentifierTable.h
+++ clang/include/clang/Basic/IdentifierTable.h
@@ -121,7 +121,10 @@
// True if this is a mangled OpenMP variant name.
unsigned IsMangledOpenMPVariantName : 1;
- // 28 bits left in a 64-bit word.
+ // True if this is a deprecated macro
+ unsigned IsDeprecatedMacro : 1;
+
+ // 24 bits left in a 64-bit word.
// Managed by the language front-end.
void *FETokenInfo = nullptr;
@@ -134,7 +137,8 @@
IsPoisoned(false), IsCPPOperatorKeyword(false),
NeedsHandleIdentifier(false), IsFromAST(false), ChangedAfterLoad(false),
FEChangedAfterLoad(false), RevertedTokenID(false), OutOfDate(false),
- IsModulesImport(false), IsMangledOpenMPVariantName(false) {}
+ IsModulesImport(false), IsMangledOpenMPVariantName(false),
+ IsDeprecatedMacro(false) {}
public:
IdentifierInfo(const IdentifierInfo &) = delete;
@@ -183,6 +187,7 @@
HadMacro = true;
} else {
RecomputeNeedsHandleIdentifier();
+ setIsDeprecatedMacro(false);
}
}
/// Returns true if this identifier was \#defined to some value at any
@@ -192,6 +197,18 @@
return HadMacro;
}
+ bool isDeprecatedMacro() const { return IsDeprecatedMacro; }
+
+ void setIsDeprecatedMacro(bool Val) {
+ if (IsDeprecatedMacro == Val)
+ return;
+ IsDeprecatedMacro = 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.
Index: clang/include/clang/Basic/DiagnosticLexKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticLexKinds.td
+++ clang/include/clang/Basic/DiagnosticLexKinds.td
@@ -519,6 +519,11 @@
ExtWarn<"#pragma warning expected a warning number">,
InGroup<UnknownPragmas>;
+// - #pragma deprecate(...)
+def warn_pragma_deprecated_macro_use :
+ ExtWarn<"macro %0 has been marked as deprecated%select{|: %2}1">,
+ InGroup<DeprecatedPragma>;
+
// - #pragma execution_character_set(...)
def warn_pragma_exec_charset_expected :
ExtWarn<"#pragma execution_character_set expected '%0'">,
Index: clang/include/clang/Basic/DiagnosticGroups.td
===================================================================
--- clang/include/clang/Basic/DiagnosticGroups.td
+++ clang/include/clang/Basic/DiagnosticGroups.td
@@ -184,6 +184,7 @@
def DeprecatedVolatile : DiagGroup<"deprecated-volatile">;
def DeprecatedWritableStr : DiagGroup<"deprecated-writable-strings",
[CXX11CompatDeprecatedWritableStr]>;
+def DeprecatedPragma : DiagGroup<"deprecated-pragma">;
// FIXME: Why is DeprecatedImplementations not in this group?
def Deprecated : DiagGroup<"deprecated", [DeprecatedAnonEnumEnumConversion,
DeprecatedArrayCompare,
@@ -198,6 +199,7 @@
DeprecatedEnumEnumConversion,
DeprecatedEnumFloatConversion,
DeprecatedIncrementBool,
+ DeprecatedPragma,
DeprecatedRegister,
DeprecatedThisCapture,
DeprecatedVolatile,
Index: clang/docs/LanguageExtensions.rst
===================================================================
--- clang/docs/LanguageExtensions.rst
+++ clang/docs/LanguageExtensions.rst
@@ -3864,6 +3864,24 @@
as ``__builtin_dynamic_object_size(buffer, 0)``, Clang will fold it into
``size``, providing some extra runtime safety.
+Deprecating Macros
+==================
+
+Clang supports the pragma ``#pragma clang deprecated``, which can be used to
+provide deprecation warnings for macro uses. For example:
+
+.. code-block:: c
+ #define MIN(x, y) x < y ? x : y
+ #pragma clang deprecated(MIN, "use std::min instead")
+
+ void min(int a, int b) {
+ return MIN(a, b); // warning: MIN is deprecated: use std::min instead
+ }
+
+``#pragma clang deprecated`` should be preferred for this purpose over
+``#pragma GCC warning`` because the warning can be controlled with
+``-Wdeprecated``.
+
Extended Integer Types
======================
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits