https://github.com/llvm-beanz created https://github.com/llvm/llvm-project/pull/121025
Have you ever had that horrible realization that some header you included defines a macro that matches a commonly used word that appears throughout your codebase? What kind of horrible person would define `max` or `min` as a macro and put it into a public header that ships in an SDK?!?! Well, I have the solution for you! Enter "Macro Scopes": with this new preprocessor extension you can wrap pesky includes with `#pragma clang scope push` and `#pragma clang scope pop` to protect your carefully curated source from preprocessor macros that bleed from your dependencies. >From 5433a9134beb8faf1e2b662c96f76d7e8bc814de Mon Sep 17 00:00:00 2001 From: Chris Bieneman <chris.biene...@me.com> Date: Mon, 23 Dec 2024 21:22:28 -0600 Subject: [PATCH] Add `pragma clang scope [push|pop]` Have you ever had that horrible realization that some header you included defines a macro that matches a commonly used word that appears throughout your codebase? What kind of horrible person would define `max` or `min` as a macro and put it into a public header that ships in an SDK?!?! Well, I have the solution for you! Enter "Macro Scopes": with this new preprocessor extension you can wrap pesky includes with `#pragma clang scope push` and `#pragma clang scope pop` to protect your carefully curated source from preprocessor macros that bleed from your dependencies. --- clang/docs/LanguageExtensions.rst | 34 ++ .../include/clang/Basic/DiagnosticLexKinds.td | 4 +- clang/include/clang/Lex/Preprocessor.h | 7 + clang/lib/Lex/PPMacroExpansion.cpp | 499 ++++++++++-------- clang/lib/Lex/Pragma.cpp | 25 +- .../Inputs/SomeHeaderThatDefinesAwfulThings.h | 1 + clang/test/Lexer/pragma-scope.c | 36 ++ 7 files changed, 369 insertions(+), 237 deletions(-) create mode 100644 clang/test/Lexer/Inputs/SomeHeaderThatDefinesAwfulThings.h create mode 100644 clang/test/Lexer/pragma-scope.c diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index cc5f1d4ddf4477..a81fa833eafdc9 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -5738,6 +5738,40 @@ 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. +Macro Scope +=========== + +Clang supports the pragma ``#pragma clang scope`` which is provided with an +argument ``push`` or ``pop`` to denote entering and leaving macro scopes. On +entering a macro scope all macro definitions and undefinitions are recorded so +that they can be reverted on leaving the scope. + +.. code-block:: c + + #define NUM_DOGGOS 2 + + #pragma clang scope push + #define NUM_DOGGOS 3 + #pragma clang scope pop // NUM_DOGGOS is restored to 2 + + #pragma clang scope push + #undef NUM_DOGGOS + #pragma clang scope pop // NUM_DOGGOS is restored to 2 + + #undef NUM_DOGGOS + #pragma clang scope push + #define NUM_DOGGOS 1 + #pragma clang scope pop // NUM_DOGGOS is restored to undefined + +A macro scope can be used to wrap header includes to isolate headers from +leaking macros to the outer source file. + +.. code-block:: c + + #pragma clang scope push + #include <SomeSystemHeader.h> + #pragma clang scope pop // None of the defines from the included header persist. + Line Control ============ diff --git a/clang/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td index 959376b0847216..a1f57aafb51bf7 100644 --- a/clang/include/clang/Basic/DiagnosticLexKinds.td +++ b/clang/include/clang/Basic/DiagnosticLexKinds.td @@ -693,8 +693,8 @@ def warn_pragma_diagnostic_invalid : ExtWarn<"pragma diagnostic expected 'error', 'warning', 'ignored', 'fatal'," " 'push', or 'pop'">, InGroup<UnknownPragmas>; -def warn_pragma_diagnostic_cannot_pop : - ExtWarn<"pragma diagnostic pop could not pop, no matching push">, +def warn_pragma_cannot_pop : + ExtWarn<"pragma %select{diagnostic|scope}0 pop could not pop, no matching push">, InGroup<UnknownPragmas>; def warn_pragma_diagnostic_invalid_option : ExtWarn<"pragma diagnostic expected option name (e.g. \"-Wundef\")">, diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h index 3d223c345ea156..96240533deff5e 100644 --- a/clang/include/clang/Lex/Preprocessor.h +++ b/clang/include/clang/Lex/Preprocessor.h @@ -1059,6 +1059,10 @@ class Preprocessor { /// Warning information for macro annotations. llvm::DenseMap<const IdentifierInfo *, MacroAnnotations> AnnotationInfos; + using MacroScopeVec = llvm::SmallVector<std::pair<IdentifierInfo *, MacroDirective *> >; + MacroScopeVec *CurScope = nullptr; + llvm::SmallVector<MacroScopeVec> MacroScopeStack; + /// A "freelist" of MacroArg objects that can be /// reused for quick allocation. MacroArgs *MacroArgCache = nullptr; @@ -2896,6 +2900,9 @@ class Preprocessor { AnnotationInfos[II].FinalAnnotationLoc = AnnotationLoc; } + void pushMacroScope(); + void popMacroScope(SourceLocation Loc); + const MacroAnnotations &getMacroAnnotations(const IdentifierInfo *II) const { return AnnotationInfos.find(II)->second; } diff --git a/clang/lib/Lex/PPMacroExpansion.cpp b/clang/lib/Lex/PPMacroExpansion.cpp index 347c13da0ad215..f47a2eb1a37caf 100644 --- a/clang/lib/Lex/PPMacroExpansion.cpp +++ b/clang/lib/Lex/PPMacroExpansion.cpp @@ -66,7 +66,35 @@ Preprocessor::getLocalMacroDirectiveHistory(const IdentifierInfo *II) const { : Pos->second.getLatest(); } -void Preprocessor::appendMacroDirective(IdentifierInfo *II, MacroDirective *MD){ +void Preprocessor::pushMacroScope() { + MacroScopeStack.emplace_back(MacroScopeVec()); + CurScope = &MacroScopeStack.back(); +} + +void Preprocessor::popMacroScope(SourceLocation Loc) { + if (!CurScope) { + Diag(Loc, diag::warn_pragma_cannot_pop) << /*scope*/ 1; + return; + } + + for (auto It = CurScope->rbegin(); It != CurScope->rend(); ++It) { + MacroDirective *Prev = It->second->getPrevious(); + if (Prev && Prev->getKind() == MacroDirective::MD_Define) { + DefMacroDirective *MD = + AllocateDefMacroDirective(Prev->getMacroInfo(), Loc); + appendMacroDirective(It->first, MD); + } else { + UndefMacroDirective *Undef = AllocateUndefMacroDirective(Loc); + appendMacroDirective(It->first, Undef); + } + } + // Unwind macro stack... + MacroScopeStack.pop_back(); + CurScope = MacroScopeStack.empty() ? nullptr : &MacroScopeStack.back(); +} + +void Preprocessor::appendMacroDirective(IdentifierInfo *II, + MacroDirective *MD) { assert(MD && "MacroDirective should be non-zero!"); assert(!MD->getPrevious() && "Already attached to a MacroDirective history."); @@ -76,6 +104,9 @@ void Preprocessor::appendMacroDirective(IdentifierInfo *II, MacroDirective *MD){ StoredMD.setLatest(MD); StoredMD.overrideActiveModuleMacros(*this, II); + if (CurScope) + CurScope->push_back(std::make_pair(II,MD)); + if (needModuleMacros()) { // Track that we created a new macro directive, so we know we should // consider building a ModuleMacro for it when we get to the end of @@ -254,7 +285,7 @@ void Preprocessor::updateModuleMacroInfo(const IdentifierInfo *II, } void Preprocessor::dumpMacroInfo(const IdentifierInfo *II) { - ArrayRef<ModuleMacro*> Leaf; + ArrayRef<ModuleMacro *> Leaf; auto LeafIt = LeafModuleMacros.find(II); if (LeafIt != LeafModuleMacros.end()) Leaf = LeafIt->second; @@ -281,11 +312,11 @@ void Preprocessor::dumpMacroInfo(const IdentifierInfo *II) { } // Dump module macros. - llvm::DenseSet<ModuleMacro*> Active; + llvm::DenseSet<ModuleMacro *> Active; for (auto *MM : State ? State->getActiveModuleMacros(*this, II) : ArrayRef<ModuleMacro *>()) Active.insert(MM); - llvm::DenseSet<ModuleMacro*> Visited; + llvm::DenseSet<ModuleMacro *> Visited; llvm::SmallVector<ModuleMacro *, 16> Worklist(Leaf); while (!Worklist.empty()) { auto *MM = Worklist.pop_back_val(); @@ -394,7 +425,8 @@ static bool isTrivialSingleTokenExpansion(const MacroInfo *MI, IdentifierInfo *II = MI->getReplacementToken(0).getIdentifierInfo(); // If the token isn't an identifier, it's always literally expanded. - if (!II) return true; + if (!II) + return true; // If the information about this identifier is out of date, update it from // the external source. @@ -411,7 +443,8 @@ static bool isTrivialSingleTokenExpansion(const MacroInfo *MI, // If this is an object-like macro invocation, it is safe to trivially expand // it. - if (MI->isObjectLike()) return true; + if (MI->isObjectLike()) + return true; // If this is a function-like macro invocation, it's safe to trivially expand // as long as the identifier is not a macro argument. @@ -467,7 +500,8 @@ bool Preprocessor::HandleMacroExpandedIdentifier(Token &Identifier, // If this is a macro expansion in the "#if !defined(x)" line for the file, // then the macro could expand to different things in other contexts, we need // to disable the optimization in this case. - if (CurPPLexer) CurPPLexer->MIOpt.ExpandedMacro(); + if (CurPPLexer) + CurPPLexer->MIOpt.ExpandedMacro(); // If this is a builtin macro, like __LINE__ or _Pragma, handle it specially. if (MI->isBuiltinMacro()) { @@ -502,7 +536,8 @@ bool Preprocessor::HandleMacroExpandedIdentifier(Token &Identifier, ArgMacro = nullptr; // If there was an error parsing the arguments, bail out. - if (!Args) return true; + if (!Args) + return true; ++NumFnMacroExpanded; } else { @@ -540,13 +575,13 @@ bool Preprocessor::HandleMacroExpandedIdentifier(Token &Identifier, // If the macro definition is ambiguous, complain. if (M.isAmbiguous()) { Diag(Identifier, diag::warn_pp_ambiguous_macro) - << Identifier.getIdentifierInfo(); + << Identifier.getIdentifierInfo(); Diag(MI->getDefinitionLoc(), diag::note_pp_ambiguous_macro_chosen) - << Identifier.getIdentifierInfo(); + << Identifier.getIdentifierInfo(); M.forAllDefinitions([&](const MacroInfo *OtherMI) { if (OtherMI != MI) Diag(OtherMI->getDefinitionLoc(), diag::note_pp_ambiguous_macro_other) - << Identifier.getIdentifierInfo(); + << Identifier.getIdentifierInfo(); }); } @@ -556,7 +591,8 @@ bool Preprocessor::HandleMacroExpandedIdentifier(Token &Identifier, // expansion stack, only to take it right back off. if (MI->getNumTokens() == 0) { // No need for arg info. - if (Args) Args->destroy(*this); + if (Args) + Args->destroy(*this); // Propagate whitespace info as if we had pushed, then popped, // a macro context. @@ -572,7 +608,8 @@ bool Preprocessor::HandleMacroExpandedIdentifier(Token &Identifier, // "#define VAL 42". // No need for arg info. - if (Args) Args->destroy(*this); + if (Args) + Args->destroy(*this); // Propagate the isAtStartOfLine/hasLeadingSpace markers of the macro // identifier to the expanded token. @@ -583,14 +620,14 @@ bool Preprocessor::HandleMacroExpandedIdentifier(Token &Identifier, Identifier = MI->getReplacementToken(0); // Restore the StartOfLine/LeadingSpace markers. - Identifier.setFlagValue(Token::StartOfLine , isAtStartOfLine); + Identifier.setFlagValue(Token::StartOfLine, isAtStartOfLine); Identifier.setFlagValue(Token::LeadingSpace, hasLeadingSpace); // Update the tokens location to include both its expansion and physical // locations. SourceLocation Loc = - SourceMgr.createExpansionLoc(Identifier.getLocation(), ExpandLoc, - ExpansionEnd,Identifier.getLength()); + SourceMgr.createExpansionLoc(Identifier.getLocation(), ExpandLoc, + ExpansionEnd, Identifier.getLength()); Identifier.setLocation(Loc); // If this is a disabled macro or #define X X, we must mark the result as @@ -617,10 +654,7 @@ bool Preprocessor::HandleMacroExpandedIdentifier(Token &Identifier, return false; } -enum Bracket { - Brace, - Paren -}; +enum Bracket { Brace, Paren }; /// CheckMatchedBrackets - Returns true if the braces and parentheses in the /// token vector are properly nested. @@ -728,8 +762,8 @@ static bool GenerateNewArgTokens(Preprocessor &PP, TempToken.setLocation(Loc); TempToken.setLength(0); NewTokens.push_back(TempToken); - ParenHints.push_back(SourceRange(ArgStartIterator->getLocation(), - Loc)); + ParenHints.push_back( + SourceRange(ArgStartIterator->getLocation(), Loc)); } // Copy separator token @@ -797,7 +831,7 @@ MacroArgs *Preprocessor::ReadMacroCallArgumentList(Token &MacroName, if (!ContainsCodeCompletionTok) { Diag(MacroName, diag::err_unterm_macro_invoc); Diag(MI->getDefinitionLoc(), diag::note_macro_here) - << MacroName.getIdentifierInfo(); + << MacroName.getIdentifierInfo(); // Do not lose the EOF/EOD. Return it to the client. MacroName = Tok; return nullptr; @@ -811,8 +845,7 @@ MacroArgs *Preprocessor::ReadMacroCallArgumentList(Token &MacroName, // If we found the ) token, the macro arg list is done. if (NumParens-- == 0) { MacroEnd = Tok.getLocation(); - if (!ArgTokens.empty() && - ArgTokens.back().commaAfterElided()) { + if (!ArgTokens.empty() && ArgTokens.back().commaAfterElided()) { FoundElidedComma = true; } break; @@ -911,7 +944,7 @@ MacroArgs *Preprocessor::ReadMacroCallArgumentList(Token &MacroName, // Emitting it at the , could be far away from the macro name. Diag(TooManyArgsLoc, diag::err_too_many_args_in_macro_invoc); Diag(MI->getDefinitionLoc(), diag::note_macro_here) - << MacroName.getIdentifierInfo(); + << MacroName.getIdentifierInfo(); // Commas from braced initializer lists will be treated as argument // separators inside macros. Attempt to correct for this with parentheses. @@ -924,9 +957,8 @@ MacroArgs *Preprocessor::ReadMacroCallArgumentList(Token &MacroName, if (!GenerateNewArgTokens(*this, ArgTokens, FixedArgTokens, FixedNumArgs, ParenHints, InitLists)) { if (!InitLists.empty()) { - DiagnosticBuilder DB = - Diag(MacroName, - diag::note_init_list_at_beginning_of_macro_argument); + DiagnosticBuilder DB = Diag( + MacroName, diag::note_init_list_at_beginning_of_macro_argument); for (SourceRange Range : InitLists) DB << Range; } @@ -968,8 +1000,8 @@ MacroArgs *Preprocessor::ReadMacroCallArgumentList(Token &MacroName, // the macro expects one argument (the argument is just empty). isVarargsElided = MI->isVariadic(); } else if ((FoundElidedComma || MI->isVariadic()) && - (NumActuals+1 == MinArgsExpected || // A(x, ...) -> A(X) - (NumActuals == 0 && MinArgsExpected == 2))) {// A(x,...) -> A() + (NumActuals + 1 == MinArgsExpected || // A(x, ...) -> A(X) + (NumActuals == 0 && MinArgsExpected == 2))) { // A(x,...) -> A() // Varargs where the named vararg parameter is missing: OK as extension. // #define A(x, ...) // A("blah") @@ -992,7 +1024,7 @@ MacroArgs *Preprocessor::ReadMacroCallArgumentList(Token &MacroName, ID = diag::ext_c_missing_varargs_arg; Diag(Tok, ID); Diag(MI->getDefinitionLoc(), diag::note_macro_here) - << MacroName.getIdentifierInfo(); + << MacroName.getIdentifierInfo(); } // Remember this occurred, allowing us to elide the comma when used for @@ -1006,7 +1038,7 @@ MacroArgs *Preprocessor::ReadMacroCallArgumentList(Token &MacroName, // Otherwise, emit the error. Diag(Tok, diag::err_too_few_args_in_macro_invoc); Diag(MI->getDefinitionLoc(), diag::note_macro_here) - << MacroName.getIdentifierInfo(); + << MacroName.getIdentifierInfo(); return nullptr; } @@ -1028,7 +1060,7 @@ MacroArgs *Preprocessor::ReadMacroCallArgumentList(Token &MacroName, // Emitting it at the , could be far away from the macro name. Diag(MacroName, diag::err_too_many_args_in_macro_invoc); Diag(MI->getDefinitionLoc(), diag::note_macro_here) - << MacroName.getIdentifierInfo(); + << MacroName.getIdentifierInfo(); return nullptr; } @@ -1047,8 +1079,8 @@ Token *Preprocessor::cacheMacroExpandedTokens(TokenLexer *tokLexer, return nullptr; size_t newIndex = MacroExpandedTokens.size(); - bool cacheNeedsToGrow = tokens.size() > - MacroExpandedTokens.capacity()-MacroExpandedTokens.size(); + bool cacheNeedsToGrow = tokens.size() > MacroExpandedTokens.capacity() - + MacroExpandedTokens.size(); MacroExpandedTokens.append(tokens.begin(), tokens.end()); if (cacheNeedsToGrow) { @@ -1090,9 +1122,9 @@ static void ComputeDATE_TIME(SourceLocation &DATELoc, SourceLocation &TIMELoc, TM = std::localtime(&TT); } - static const char * const Months[] = { - "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec" - }; + static const char *const Months[] = {"Jan", "Feb", "Mar", "Apr", + "May", "Jun", "Jul", "Aug", + "Sep", "Oct", "Nov", "Dec"}; { SmallString<32> TmpBuffer; @@ -1160,8 +1192,8 @@ static bool HasExtension(const Preprocessor &PP, StringRef Extension) { Extension.size() >= 4) Extension = Extension.substr(2, Extension.size() - 4); - // Because we inherit the feature list from HasFeature, this string switch - // must be less restrictive than HasFeature's. + // Because we inherit the feature list from HasFeature, this string switch + // must be less restrictive than HasFeature's. #define EXTENSION(Name, Predicate) .Case(#Name, Predicate) return llvm::StringSwitch<bool>(Extension) #include "clang/Basic/Features.def" @@ -1376,17 +1408,15 @@ bool Preprocessor::EvaluateHasIncludeNext(Token &Tok, IdentifierInfo *II) { /// Process single-argument builtin feature-like macros that return /// integer values. -static void EvaluateFeatureLikeBuiltinMacro(llvm::raw_svector_ostream& OS, - Token &Tok, IdentifierInfo *II, - Preprocessor &PP, bool ExpandArgs, - llvm::function_ref< - int(Token &Tok, - bool &HasLexedNextTok)> Op) { +static void EvaluateFeatureLikeBuiltinMacro( + llvm::raw_svector_ostream &OS, Token &Tok, IdentifierInfo *II, + Preprocessor &PP, bool ExpandArgs, + llvm::function_ref<int(Token &Tok, bool &HasLexedNextTok)> Op) { // Parse the initial '('. PP.LexUnexpandedToken(Tok); if (Tok.isNot(tok::l_paren)) { - PP.Diag(Tok.getLocation(), diag::err_pp_expected_after) << II - << tok::l_paren; + PP.Diag(Tok.getLocation(), diag::err_pp_expected_after) + << II << tok::l_paren; // Provide a dummy '0' value on output stream to elide further errors. if (!Tok.isOneOf(tok::eof, tok::eod)) { @@ -1409,64 +1439,64 @@ static void EvaluateFeatureLikeBuiltinMacro(llvm::raw_svector_ostream& OS, else PP.LexUnexpandedToken(Tok); -already_lexed: + already_lexed: switch (Tok.getKind()) { - case tok::eof: - case tok::eod: - // Don't provide even a dummy value if the eod or eof marker is - // reached. Simply provide a diagnostic. - PP.Diag(Tok.getLocation(), diag::err_unterm_macro_invoc); - return; + case tok::eof: + case tok::eod: + // Don't provide even a dummy value if the eod or eof marker is + // reached. Simply provide a diagnostic. + PP.Diag(Tok.getLocation(), diag::err_unterm_macro_invoc); + return; - case tok::comma: - if (!SuppressDiagnostic) { - PP.Diag(Tok.getLocation(), diag::err_too_many_args_in_macro_invoc); - SuppressDiagnostic = true; - } - continue; + case tok::comma: + if (!SuppressDiagnostic) { + PP.Diag(Tok.getLocation(), diag::err_too_many_args_in_macro_invoc); + SuppressDiagnostic = true; + } + continue; - case tok::l_paren: - ++ParenDepth; - if (Result) - break; - if (!SuppressDiagnostic) { - PP.Diag(Tok.getLocation(), diag::err_pp_nested_paren) << II; - SuppressDiagnostic = true; - } + case tok::l_paren: + ++ParenDepth; + if (Result) + break; + if (!SuppressDiagnostic) { + PP.Diag(Tok.getLocation(), diag::err_pp_nested_paren) << II; + SuppressDiagnostic = true; + } + continue; + + case tok::r_paren: + if (--ParenDepth > 0) continue; - case tok::r_paren: - if (--ParenDepth > 0) - continue; - - // The last ')' has been reached; return the value if one found or - // a diagnostic and a dummy value. - if (Result) { - OS << *Result; - // For strict conformance to __has_cpp_attribute rules, use 'L' - // suffix for dated literals. - if (*Result > 1) - OS << 'L'; - } else { - OS << 0; - if (!SuppressDiagnostic) - PP.Diag(Tok.getLocation(), diag::err_too_few_args_in_macro_invoc); - } - Tok.setKind(tok::numeric_constant); - return; + // The last ')' has been reached; return the value if one found or + // a diagnostic and a dummy value. + if (Result) { + OS << *Result; + // For strict conformance to __has_cpp_attribute rules, use 'L' + // suffix for dated literals. + if (*Result > 1) + OS << 'L'; + } else { + OS << 0; + if (!SuppressDiagnostic) + PP.Diag(Tok.getLocation(), diag::err_too_few_args_in_macro_invoc); + } + Tok.setKind(tok::numeric_constant); + return; - default: { - // Parse the macro argument, if one not found so far. - if (Result) - break; + default: { + // Parse the macro argument, if one not found so far. + if (Result) + break; - bool HasLexedNextToken = false; - Result = Op(Tok, HasLexedNextToken); - ResultTok = Tok; - if (HasLexedNextToken) - goto already_lexed; - continue; - } + bool HasLexedNextToken = false; + Result = Op(Tok, HasLexedNextToken); + ResultTok = Tok; + if (HasLexedNextToken) + goto already_lexed; + continue; + } } // Diagnose missing ')'. @@ -1486,8 +1516,7 @@ static void EvaluateFeatureLikeBuiltinMacro(llvm::raw_svector_ostream& OS, /// Helper function to return the IdentifierInfo structure of a Token /// or generate a diagnostic if none available. -static IdentifierInfo *ExpectFeatureIdentifierInfo(Token &Tok, - Preprocessor &PP, +static IdentifierInfo *ExpectFeatureIdentifierInfo(Token &Tok, Preprocessor &PP, signed DiagID) { IdentifierInfo *II; if (!Tok.isAnnotation() && (II = Tok.getIdentifierInfo())) @@ -1573,7 +1602,7 @@ static bool isTargetVariantOS(const TargetInfo &TI, const IdentifierInfo *II) { /// Implements the __is_target_variant_environment builtin macro. static bool isTargetVariantEnvironment(const TargetInfo &TI, - const IdentifierInfo *II) { + const IdentifierInfo *II) { if (TI.getTriple().isOSDarwin()) { const llvm::Triple *VariantTriple = TI.getDarwinTargetVariantTriple(); if (!VariantTriple) @@ -1669,7 +1698,7 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) { PresumedLoc PLoc = SourceMgr.getPresumedLoc(Loc); // __LINE__ expands to a simple numeric value. - OS << (PLoc.isValid()? PLoc.getLine() : 1); + OS << (PLoc.isValid() ? PLoc.getLine() : 1); Tok.setKind(tok::numeric_constant); } else if (II == Ident__FILE__ || II == Ident__BASE_FILE__ || II == Ident__FILE_NAME__) { @@ -1711,9 +1740,8 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) { ComputeDATE_TIME(DATELoc, TIMELoc, *this); Tok.setKind(tok::string_literal); Tok.setLength(strlen("\"Mmm dd yyyy\"")); - Tok.setLocation(SourceMgr.createExpansionLoc(DATELoc, Tok.getLocation(), - Tok.getLocation(), - Tok.getLength())); + Tok.setLocation(SourceMgr.createExpansionLoc( + DATELoc, Tok.getLocation(), Tok.getLocation(), Tok.getLength())); return; } else if (II == Ident__TIME__) { Diag(Tok.getLocation(), diag::warn_pp_date_time); @@ -1721,9 +1749,8 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) { ComputeDATE_TIME(DATELoc, TIMELoc, *this); Tok.setKind(tok::string_literal); Tok.setLength(strlen("\"hh:mm:ss\"")); - Tok.setLocation(SourceMgr.createExpansionLoc(TIMELoc, Tok.getLocation(), - Tok.getLocation(), - Tok.getLength())); + Tok.setLocation(SourceMgr.createExpansionLoc( + TIMELoc, Tok.getLocation(), Tok.getLocation(), Tok.getLength())); return; } else if (II == Ident__INCLUDE_LEVEL__) { // Compute the presumed include depth of this token. This can be affected @@ -1784,68 +1811,71 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) { OS << CounterValue++; Tok.setKind(tok::numeric_constant); } else if (II == Ident__has_feature) { - EvaluateFeatureLikeBuiltinMacro(OS, Tok, II, *this, false, - [this](Token &Tok, bool &HasLexedNextToken) -> int { - IdentifierInfo *II = ExpectFeatureIdentifierInfo(Tok, *this, - diag::err_feature_check_malformed); - return II && HasFeature(*this, II->getName()); - }); + EvaluateFeatureLikeBuiltinMacro( + OS, Tok, II, *this, false, + [this](Token &Tok, bool &HasLexedNextToken) -> int { + IdentifierInfo *II = ExpectFeatureIdentifierInfo( + Tok, *this, diag::err_feature_check_malformed); + return II && HasFeature(*this, II->getName()); + }); } else if (II == Ident__has_extension) { - EvaluateFeatureLikeBuiltinMacro(OS, Tok, II, *this, false, - [this](Token &Tok, bool &HasLexedNextToken) -> int { - IdentifierInfo *II = ExpectFeatureIdentifierInfo(Tok, *this, - diag::err_feature_check_malformed); - return II && HasExtension(*this, II->getName()); - }); + EvaluateFeatureLikeBuiltinMacro( + OS, Tok, II, *this, false, + [this](Token &Tok, bool &HasLexedNextToken) -> int { + IdentifierInfo *II = ExpectFeatureIdentifierInfo( + Tok, *this, diag::err_feature_check_malformed); + return II && HasExtension(*this, II->getName()); + }); } else if (II == Ident__has_builtin) { - EvaluateFeatureLikeBuiltinMacro(OS, Tok, II, *this, false, - [this](Token &Tok, bool &HasLexedNextToken) -> int { - IdentifierInfo *II = ExpectFeatureIdentifierInfo(Tok, *this, - diag::err_feature_check_malformed); - if (!II) - return false; - else if (II->getBuiltinID() != 0) { - switch (II->getBuiltinID()) { - case Builtin::BI__builtin_cpu_is: - return getTargetInfo().supportsCpuIs(); - case Builtin::BI__builtin_cpu_init: - return getTargetInfo().supportsCpuInit(); - case Builtin::BI__builtin_cpu_supports: - return getTargetInfo().supportsCpuSupports(); - case Builtin::BI__builtin_operator_new: - case Builtin::BI__builtin_operator_delete: - // denotes date of behavior change to support calling arbitrary - // usual allocation and deallocation functions. Required by libc++ - return 201802; - default: - return Builtin::evaluateRequiredTargetFeatures( - getBuiltinInfo().getRequiredFeatures(II->getBuiltinID()), - getTargetInfo().getTargetOpts().FeatureMap); + EvaluateFeatureLikeBuiltinMacro( + OS, Tok, II, *this, false, + [this](Token &Tok, bool &HasLexedNextToken) -> int { + IdentifierInfo *II = ExpectFeatureIdentifierInfo( + Tok, *this, diag::err_feature_check_malformed); + if (!II) + return false; + else if (II->getBuiltinID() != 0) { + switch (II->getBuiltinID()) { + case Builtin::BI__builtin_cpu_is: + return getTargetInfo().supportsCpuIs(); + case Builtin::BI__builtin_cpu_init: + return getTargetInfo().supportsCpuInit(); + case Builtin::BI__builtin_cpu_supports: + return getTargetInfo().supportsCpuSupports(); + case Builtin::BI__builtin_operator_new: + case Builtin::BI__builtin_operator_delete: + // denotes date of behavior change to support calling arbitrary + // usual allocation and deallocation functions. Required by libc++ + return 201802; + default: + return Builtin::evaluateRequiredTargetFeatures( + getBuiltinInfo().getRequiredFeatures(II->getBuiltinID()), + getTargetInfo().getTargetOpts().FeatureMap); + } + return true; + } else if (IsBuiltinTrait(Tok)) { + return true; + } else if (II->getTokenID() != tok::identifier && + II->getName().starts_with("__builtin_")) { + return true; + } else { + return llvm::StringSwitch<bool>(II->getName()) + // Report builtin templates as being builtins. + .Case("__make_integer_seq", getLangOpts().CPlusPlus) + .Case("__type_pack_element", getLangOpts().CPlusPlus) + .Case("__builtin_common_type", getLangOpts().CPlusPlus) + // Likewise for some builtin preprocessor macros. + // FIXME: This is inconsistent; we usually suggest detecting + // builtin macros via #ifdef. Don't add more cases here. + .Case("__is_target_arch", true) + .Case("__is_target_vendor", true) + .Case("__is_target_os", true) + .Case("__is_target_environment", true) + .Case("__is_target_variant_os", true) + .Case("__is_target_variant_environment", true) + .Default(false); } - return true; - } else if (IsBuiltinTrait(Tok)) { - return true; - } else if (II->getTokenID() != tok::identifier && - II->getName().starts_with("__builtin_")) { - return true; - } else { - return llvm::StringSwitch<bool>(II->getName()) - // Report builtin templates as being builtins. - .Case("__make_integer_seq", getLangOpts().CPlusPlus) - .Case("__type_pack_element", getLangOpts().CPlusPlus) - .Case("__builtin_common_type", getLangOpts().CPlusPlus) - // Likewise for some builtin preprocessor macros. - // FIXME: This is inconsistent; we usually suggest detecting - // builtin macros via #ifdef. Don't add more cases here. - .Case("__is_target_arch", true) - .Case("__is_target_vendor", true) - .Case("__is_target_os", true) - .Case("__is_target_environment", true) - .Case("__is_target_variant_os", true) - .Case("__is_target_variant_environment", true) - .Default(false); - } - }); + }); } else if (II == Ident__has_constexpr_builtin) { EvaluateFeatureLikeBuiltinMacro( OS, Tok, II, *this, false, @@ -1859,37 +1889,40 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) { this->getBuiltinInfo().isConstantEvaluated(BuiltinOp); }); } else if (II == Ident__is_identifier) { - EvaluateFeatureLikeBuiltinMacro(OS, Tok, II, *this, false, - [](Token &Tok, bool &HasLexedNextToken) -> int { - return Tok.is(tok::identifier); - }); + EvaluateFeatureLikeBuiltinMacro( + OS, Tok, II, *this, false, + [](Token &Tok, bool &HasLexedNextToken) -> int { + return Tok.is(tok::identifier); + }); } else if (II == Ident__has_attribute) { - EvaluateFeatureLikeBuiltinMacro(OS, Tok, II, *this, true, - [this](Token &Tok, bool &HasLexedNextToken) -> int { - IdentifierInfo *II = ExpectFeatureIdentifierInfo(Tok, *this, - diag::err_feature_check_malformed); - return II ? hasAttribute(AttributeCommonInfo::Syntax::AS_GNU, nullptr, - II, getTargetInfo(), getLangOpts()) - : 0; - }); + EvaluateFeatureLikeBuiltinMacro( + OS, Tok, II, *this, true, + [this](Token &Tok, bool &HasLexedNextToken) -> int { + IdentifierInfo *II = ExpectFeatureIdentifierInfo( + Tok, *this, diag::err_feature_check_malformed); + return II ? hasAttribute(AttributeCommonInfo::Syntax::AS_GNU, nullptr, + II, getTargetInfo(), getLangOpts()) + : 0; + }); } else if (II == Ident__has_declspec) { - EvaluateFeatureLikeBuiltinMacro(OS, Tok, II, *this, true, - [this](Token &Tok, bool &HasLexedNextToken) -> int { - IdentifierInfo *II = ExpectFeatureIdentifierInfo(Tok, *this, - diag::err_feature_check_malformed); - if (II) { - const LangOptions &LangOpts = getLangOpts(); - return LangOpts.DeclSpecKeyword && - hasAttribute(AttributeCommonInfo::Syntax::AS_Declspec, nullptr, - II, getTargetInfo(), LangOpts); - } + EvaluateFeatureLikeBuiltinMacro( + OS, Tok, II, *this, true, + [this](Token &Tok, bool &HasLexedNextToken) -> int { + IdentifierInfo *II = ExpectFeatureIdentifierInfo( + Tok, *this, diag::err_feature_check_malformed); + if (II) { + const LangOptions &LangOpts = getLangOpts(); + return LangOpts.DeclSpecKeyword && + hasAttribute(AttributeCommonInfo::Syntax::AS_Declspec, + nullptr, II, getTargetInfo(), LangOpts); + } - return false; - }); - } else if (II == Ident__has_cpp_attribute || - II == Ident__has_c_attribute) { + return false; + }); + } else if (II == Ident__has_cpp_attribute || II == Ident__has_c_attribute) { bool IsCXX = II == Ident__has_cpp_attribute; - EvaluateFeatureLikeBuiltinMacro(OS, Tok, II, *this, true, + EvaluateFeatureLikeBuiltinMacro( + OS, Tok, II, *this, true, [&](Token &Tok, bool &HasLexedNextToken) -> int { IdentifierInfo *ScopeII = nullptr; IdentifierInfo *II = ExpectFeatureIdentifierInfo( @@ -1917,8 +1950,7 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) { getLangOpts()) : 0; }); - } else if (II == Ident__has_include || - II == Ident__has_include_next) { + } else if (II == Ident__has_include || II == Ident__has_include_next) { // The argument to these two builtins should be a parenthesized // file name string literal using angle brackets (<>) or // double-quotes (""). @@ -1945,44 +1977,45 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) { OS << static_cast<int>(Value); } else if (II == Ident__has_warning) { // The argument should be a parenthesized string literal. - EvaluateFeatureLikeBuiltinMacro(OS, Tok, II, *this, false, - [this](Token &Tok, bool &HasLexedNextToken) -> int { - std::string WarningName; - SourceLocation StrStartLoc = Tok.getLocation(); - - HasLexedNextToken = Tok.is(tok::string_literal); - if (!FinishLexStringLiteral(Tok, WarningName, "'__has_warning'", - /*AllowMacroExpansion=*/false)) - return false; + EvaluateFeatureLikeBuiltinMacro( + OS, Tok, II, *this, false, + [this](Token &Tok, bool &HasLexedNextToken) -> int { + std::string WarningName; + SourceLocation StrStartLoc = Tok.getLocation(); - // FIXME: Should we accept "-R..." flags here, or should that be - // handled by a separate __has_remark? - if (WarningName.size() < 3 || WarningName[0] != '-' || - WarningName[1] != 'W') { - Diag(StrStartLoc, diag::warn_has_warning_invalid_option); - return false; - } + HasLexedNextToken = Tok.is(tok::string_literal); + if (!FinishLexStringLiteral(Tok, WarningName, "'__has_warning'", + /*AllowMacroExpansion=*/false)) + return false; + + // FIXME: Should we accept "-R..." flags here, or should that be + // handled by a separate __has_remark? + if (WarningName.size() < 3 || WarningName[0] != '-' || + WarningName[1] != 'W') { + Diag(StrStartLoc, diag::warn_has_warning_invalid_option); + return false; + } - // 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. - SmallVector<diag::kind, 10> Diags; - return !getDiagnostics().getDiagnosticIDs()-> - getDiagnosticsInGroup(diag::Flavor::WarningOrError, - WarningName.substr(2), Diags); - }); + // 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. + SmallVector<diag::kind, 10> Diags; + return !getDiagnostics().getDiagnosticIDs()->getDiagnosticsInGroup( + diag::Flavor::WarningOrError, WarningName.substr(2), Diags); + }); } else if (II == Ident__building_module) { // The argument to this builtin should be an identifier. The // builtin evaluates to 1 when that identifier names the module we are // currently building. - EvaluateFeatureLikeBuiltinMacro(OS, Tok, II, *this, false, - [this](Token &Tok, bool &HasLexedNextToken) -> int { - IdentifierInfo *II = ExpectFeatureIdentifierInfo(Tok, *this, - diag::err_expected_id_building_module); - return getLangOpts().isCompilingModule() && II && - (II->getName() == getLangOpts().CurrentModule); - }); + EvaluateFeatureLikeBuiltinMacro( + OS, Tok, II, *this, false, + [this](Token &Tok, bool &HasLexedNextToken) -> int { + IdentifierInfo *II = ExpectFeatureIdentifierInfo( + Tok, *this, diag::err_expected_id_building_module); + return getLangOpts().isCompilingModule() && II && + (II->getName() == getLangOpts().CurrentModule); + }); } else if (II == Ident__MODULE__) { // The current module as an identifier. OS << getLangOpts().CurrentModule; @@ -1998,7 +2031,7 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) { if (Tok.isNot(tok::l_paren)) { // No '(', use end of last token. Diag(getLocForEndOfToken(Loc), diag::err_pp_expected_after) - << II << tok::l_paren; + << II << tok::l_paren; // If the next token isn't valid as our argument, we can't recover. if (!Tok.isAnnotation() && Tok.getIdentifierInfo()) Tok.setKind(tok::identifier); @@ -2020,7 +2053,7 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) { Tok.setKind(tok::identifier); } else { Diag(Tok.getLocation(), diag::err_pp_identifier_arg_not_identifier) - << Tok.getKind(); + << Tok.getKind(); // Don't walk past anything that's not a real token. if (Tok.isOneOf(tok::eof, tok::eod) || Tok.isAnnotation()) return; @@ -2031,7 +2064,7 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) { LexNonComment(RParen); if (RParen.isNot(tok::r_paren)) { Diag(getLocForEndOfToken(Tok.getLocation()), diag::err_pp_expected_after) - << Tok.getKind() << tok::r_paren; + << Tok.getKind() << tok::r_paren; Diag(LParenLoc, diag::note_matching) << tok::l_paren; } return; diff --git a/clang/lib/Lex/Pragma.cpp b/clang/lib/Lex/Pragma.cpp index e339ca84222784..c4dec81e109f28 100644 --- a/clang/lib/Lex/Pragma.cpp +++ b/clang/lib/Lex/Pragma.cpp @@ -1294,7 +1294,7 @@ struct PragmaDiagnosticHandler : public PragmaHandler { if (II->isStr("pop")) { if (!PP.getDiagnostics().popMappings(DiagLoc)) - PP.Diag(Tok, diag::warn_pragma_diagnostic_cannot_pop); + PP.Diag(Tok, diag::warn_pragma_cannot_pop) << /*diagnostic*/ 0; else if (Callbacks) Callbacks->PragmaDiagnosticPop(DiagLoc, Namespace); @@ -1416,7 +1416,7 @@ struct PragmaWarningHandler : public PragmaHandler { // #pragma warning( pop ) PP.Lex(Tok); if (!PP.getDiagnostics().popMappings(DiagLoc)) - PP.Diag(Tok, diag::warn_pragma_diagnostic_cannot_pop); + PP.Diag(Tok, diag::warn_pragma_cannot_pop) << /*diagnostic*/ 0; else if (Callbacks) Callbacks->PragmaWarningPop(DiagLoc); } else { @@ -2122,6 +2122,26 @@ struct PragmaFinalHandler : public PragmaHandler { } }; +struct PragmaScopeHandler : public PragmaHandler { + PragmaScopeHandler() : PragmaHandler("scope") {} + + void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer, + Token &Tok) override { + // Lex the 'push' or 'pop'. + PP.LexUnexpandedToken(Tok); + const IdentifierInfo *PushPop = Tok.getIdentifierInfo(); + if (PushPop && PushPop->isStr("push")) + PP.pushMacroScope(); + else if (PushPop && PushPop->isStr("pop")) + PP.popMacroScope(Tok.getLocation()); + else + PP.Diag(Tok, diag::err_expected) << "'push' or 'pop'"; + PP.Lex(Tok); + if (Tok.isNot(tok::eod)) + PP.Diag(Tok, diag::ext_pp_extra_tokens_at_eol) << "pragma"; + } +}; + } // namespace /// RegisterBuiltinPragmas - Install the standard preprocessor pragmas: @@ -2153,6 +2173,7 @@ void Preprocessor::RegisterBuiltinPragmas() { AddPragmaHandler("clang", new PragmaDeprecatedHandler()); AddPragmaHandler("clang", new PragmaRestrictExpansionHandler()); AddPragmaHandler("clang", new PragmaFinalHandler()); + AddPragmaHandler("clang", new PragmaScopeHandler()); // #pragma clang module ... auto *ModuleHandler = new PragmaNamespace("module"); diff --git a/clang/test/Lexer/Inputs/SomeHeaderThatDefinesAwfulThings.h b/clang/test/Lexer/Inputs/SomeHeaderThatDefinesAwfulThings.h new file mode 100644 index 00000000000000..ac100af24a6868 --- /dev/null +++ b/clang/test/Lexer/Inputs/SomeHeaderThatDefinesAwfulThings.h @@ -0,0 +1 @@ +#define max(l, r) l > r ? l : r diff --git a/clang/test/Lexer/pragma-scope.c b/clang/test/Lexer/pragma-scope.c new file mode 100644 index 00000000000000..5304f206178e90 --- /dev/null +++ b/clang/test/Lexer/pragma-scope.c @@ -0,0 +1,36 @@ +// RUN: %clang_cc1 %s -fsyntax-only -isystem %S/Inputs -verify + +#define Foo 1 + +#pragma clang scope push +#undef Foo +#pragma clang scope pop + +#ifndef Foo +#error "Foo is still defined!" +#endif + +#define Bar 1 // expected-note{{previous definition is here}} +#pragma clang scope push +#define Bar 2 // expected-warning{{'Bar' macro redefined}} +#pragma clang scope pop + +#if Bar != 1 +#error "Bar is set back to 1" +#endif + +#pragma clang scope push +#include <SomeHeaderThatDefinesAwfulThings.h> +#pragma clang scope pop + +#ifdef max +#error "Nobody should ever define max as a macro!" +#endif + +#pragma clang scope pop // expected-warning{{pragma scope pop could not pop, no matching push}} + +#pragma clang scope enter // expected-error{{expected 'push' or 'pop'}} + +#pragma clang scope push pop // expected-warning{{extra tokens at end of #pragma directive}} + +#pragma clang scope () // expected-warning{{expected 'push' or 'pop'}} expected-warning{{extra tokens at end of #pragma directive}} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits