From: Ted Kremenek Date: Wed, 12 Oct 2011 19:46:30 +0000 (+0000) Subject: Implement built-in macro '__has_warning', which allows one to query if a warning... X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=d768150ef57f617c8d9fef48f3c92e8f21698024;p=clang Implement built-in macro '__has_warning', which allows one to query if a warning flag is valid. Fixes . git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@141802 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/docs/LanguageExtensions.html b/docs/LanguageExtensions.html index d8a57df486..ef57a659c4 100644 --- a/docs/LanguageExtensions.html +++ b/docs/LanguageExtensions.html @@ -300,6 +300,23 @@ and will issue a warning if used in the top-level compilation file. A warning will also be issued if an absolute path is used in the file argument.

+ + +

__has_warning

+ + +

This function-like macro takes a string literal that represents a command + line option for a warning and returns true if that is a valid warning + option.

+ +
+
+#if __has_warning("-Wformat")
+...
+#endif
+
+
+

Builtin Macros

diff --git a/include/clang/Basic/DiagnosticGroups.td b/include/clang/Basic/DiagnosticGroups.td index 8020dd163d..55e8831281 100644 --- a/include/clang/Basic/DiagnosticGroups.td +++ b/include/clang/Basic/DiagnosticGroups.td @@ -73,6 +73,7 @@ def : DiagGroup<"int-to-pointer-cast">; def : DiagGroup<"invalid-pch">; def LiteralRange : DiagGroup<"literal-range">; def LocalTypeTemplateArgs : DiagGroup<"local-type-template-args">; +def MalformedWarningCheck : DiagGroup<"malformed-warning-check">; def Main : DiagGroup<"main">; def MissingBraces : DiagGroup<"missing-braces">; def MissingDeclarations: DiagGroup<"missing-declarations">; diff --git a/include/clang/Basic/DiagnosticIDs.h b/include/clang/Basic/DiagnosticIDs.h index e158bf52b5..16d9b39985 100644 --- a/include/clang/Basic/DiagnosticIDs.h +++ b/include/clang/Basic/DiagnosticIDs.h @@ -251,7 +251,6 @@ public: static diag_iterator diags_begin(); static diag_iterator diags_end(); -private: /// \brief Get the set of all diagnostic IDs in the group with the given name. /// /// \param Diags [out] - On return, the diagnostics in the group. @@ -259,6 +258,7 @@ private: bool getDiagnosticsInGroup(StringRef Group, llvm::SmallVectorImpl &Diags) const; +private: /// \brief Get the set of all diagnostic IDs in the given group. /// /// \param Diags [out] - On return, the diagnostics in the group. diff --git a/include/clang/Basic/DiagnosticLexKinds.td b/include/clang/Basic/DiagnosticLexKinds.td index 2d6cbe8fc3..6655472057 100644 --- a/include/clang/Basic/DiagnosticLexKinds.td +++ b/include/clang/Basic/DiagnosticLexKinds.td @@ -242,6 +242,13 @@ def err_pp_used_poisoned_id : Error<"attempt to use a poisoned identifier">; def err_feature_check_malformed : Error< "builtin feature check macro requires a parenthesized identifier">; +def err_warning_check_malformed : Error< + "builtin warning check macro requires a parenthesized string">, + InGroup; +def warn_has_warning_invalid_option : + ExtWarn<"__has_warning expected option name (e.g. \"-Wundef\")">, + InGroup; + def err__Pragma_malformed : Error< "_Pragma takes a parenthesized string literal">; def err_pragma_comment_malformed : Error< diff --git a/include/clang/Lex/Preprocessor.h b/include/clang/Lex/Preprocessor.h index 7e64fc4d8a..8b7743316f 100644 --- a/include/clang/Lex/Preprocessor.h +++ b/include/clang/Lex/Preprocessor.h @@ -93,6 +93,7 @@ class Preprocessor : public llvm::RefCountedBase { IdentifierInfo *Ident__has_attribute; // __has_attribute IdentifierInfo *Ident__has_include; // __has_include IdentifierInfo *Ident__has_include_next; // __has_include_next + IdentifierInfo *Ident__has_warning; // __has_warning SourceLocation DATELoc, TIMELoc; unsigned CounterValue; // Next __COUNTER__ value. diff --git a/lib/Lex/PPMacroExpansion.cpp b/lib/Lex/PPMacroExpansion.cpp index 5bdebf93b3..486ffbeb9c 100644 --- a/lib/Lex/PPMacroExpansion.cpp +++ b/lib/Lex/PPMacroExpansion.cpp @@ -21,6 +21,7 @@ #include "clang/Lex/LexDiagnostic.h" #include "clang/Lex/CodeCompletionHandler.h" #include "clang/Lex/ExternalPreprocessorSource.h" +#include "clang/Lex/LiteralSupport.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Config/config.h" @@ -92,6 +93,7 @@ void Preprocessor::RegisterBuiltinMacros() { Ident__has_attribute = RegisterBuiltinMacro(*this, "__has_attribute"); Ident__has_include = RegisterBuiltinMacro(*this, "__has_include"); Ident__has_include_next = RegisterBuiltinMacro(*this, "__has_include_next"); + Ident__has_warning = RegisterBuiltinMacro(*this, "__has_warning"); // Microsoft Extensions. if (Features.MicrosoftExt) @@ -1016,6 +1018,73 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) { Value = EvaluateHasIncludeNext(Tok, II, *this); OS << (int)Value; Tok.setKind(tok::numeric_constant); + } else if (II == Ident__has_warning) { + // The argument should be a parenthesized string literal. + // The argument to these builtins should be a parenthesized identifier. + SourceLocation StartLoc = Tok.getLocation(); + bool IsValid = false; + bool Value = false; + // Read the '('. + Lex(Tok); + do { + if (Tok.is(tok::l_paren)) { + // Read the string. + Lex(Tok); + + // We need at least one string literal. + if (!Tok.is(tok::string_literal)) { + StartLoc = Tok.getLocation(); + IsValid = false; + // Eat tokens until ')'. + do Lex(Tok); while (!(Tok.is(tok::r_paren) || Tok.is(tok::eod))); + break; + } + + // String concatenation allows multiple strings, which can even come + // from macro expansion. + SmallVector StrToks; + while (Tok.is(tok::string_literal)) { + StrToks.push_back(Tok); + LexUnexpandedToken(Tok); + } + + // Is the end a ')'? + if (!(IsValid = Tok.is(tok::r_paren))) + break; + + // Concatenate and parse the strings. + StringLiteralParser Literal(&StrToks[0], StrToks.size(), *this); + assert(Literal.isAscii() && "Didn't allow wide strings in"); + if (Literal.hadError) + break; + if (Literal.Pascal) { + Diag(Tok, diag::warn_pragma_diagnostic_invalid); + break; + } + + StringRef WarningName(Literal.GetString()); + + if (WarningName.size() < 3 || WarningName[0] != '-' || + WarningName[1] != 'W') { + Diag(StrToks[0].getLocation(), diag::warn_has_warning_invalid_option); + break; + } + + // Finally, check if the warning flags maps to a diagnostic group. + // We construct a SmallVector here to talk to getDiagnosticIDs(). + // Although we don't use the result, this isn't a hot path, and not + // worth special casing. + llvm::SmallVector Diags; + Value = !getDiagnostics().getDiagnosticIDs()-> + getDiagnosticsInGroup(WarningName.substr(2), Diags); + } + } while (false); + + if (!IsValid) + Diag(StartLoc, diag::err_warning_check_malformed); + + OS << (int)Value; + Tok.setKind(tok::numeric_constant); } else { llvm_unreachable("Unknown identifier!"); }