aboutsummaryrefslogtreecommitdiff
path: root/clang/lib/Lex
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib/Lex')
-rw-r--r--clang/lib/Lex/PPDirectives.cpp477
-rw-r--r--clang/lib/Lex/PPExpressions.cpp49
-rw-r--r--clang/lib/Lex/PPMacroExpansion.cpp111
-rw-r--r--clang/lib/Lex/TokenConcatenation.cpp5
4 files changed, 626 insertions, 16 deletions
diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp
index 8e73864..b7ee0c0 100644
--- a/clang/lib/Lex/PPDirectives.cpp
+++ b/clang/lib/Lex/PPDirectives.cpp
@@ -19,6 +19,7 @@
#include "clang/Basic/Module.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/TargetInfo.h"
#include "clang/Basic/TokenKinds.h"
#include "clang/Lex/CodeCompletionHandler.h"
#include "clang/Lex/HeaderSearch.h"
@@ -39,6 +40,7 @@
#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/AlignOf.h"
@@ -82,8 +84,7 @@ Preprocessor::AllocateVisibilityMacroDirective(SourceLocation Loc,
/// Read and discard all tokens remaining on the current line until
/// the tok::eod token is found.
-SourceRange Preprocessor::DiscardUntilEndOfDirective() {
- Token Tmp;
+SourceRange Preprocessor::DiscardUntilEndOfDirective(Token &Tmp) {
SourceRange Res;
LexUnexpandedToken(Tmp);
@@ -1073,6 +1074,74 @@ OptionalFileEntryRef Preprocessor::LookupFile(
return std::nullopt;
}
+OptionalFileEntryRef
+Preprocessor::LookupEmbedFile(StringRef Filename, bool isAngled, bool OpenFile,
+ const FileEntry *LookupFromFile) {
+ FileManager &FM = this->getFileManager();
+ if (llvm::sys::path::is_absolute(Filename)) {
+ // lookup path or immediately fail
+ llvm::Expected<FileEntryRef> ShouldBeEntry =
+ FM.getFileRef(Filename, OpenFile);
+ return llvm::expectedToOptional(std::move(ShouldBeEntry));
+ }
+
+ auto SeparateComponents = [](SmallVectorImpl<char> &LookupPath,
+ StringRef StartingFrom, StringRef FileName,
+ bool RemoveInitialFileComponentFromLookupPath) {
+ llvm::sys::path::native(StartingFrom, LookupPath);
+ if (RemoveInitialFileComponentFromLookupPath)
+ llvm::sys::path::remove_filename(LookupPath);
+ if (!LookupPath.empty() &&
+ !llvm::sys::path::is_separator(LookupPath.back())) {
+ LookupPath.push_back(llvm::sys::path::get_separator().front());
+ }
+ LookupPath.append(FileName.begin(), FileName.end());
+ };
+
+ // Otherwise, it's search time!
+ SmallString<512> LookupPath;
+ // Non-angled lookup
+ if (!isAngled) {
+ if (LookupFromFile) {
+ // Use file-based lookup.
+ StringRef FullFileDir = LookupFromFile->tryGetRealPathName();
+ if (!FullFileDir.empty()) {
+ SeparateComponents(LookupPath, FullFileDir, Filename, true);
+ llvm::Expected<FileEntryRef> ShouldBeEntry =
+ FM.getFileRef(LookupPath, OpenFile);
+ if (ShouldBeEntry)
+ return llvm::expectedToOptional(std::move(ShouldBeEntry));
+ llvm::consumeError(ShouldBeEntry.takeError());
+ }
+ }
+
+ // Otherwise, do working directory lookup.
+ LookupPath.clear();
+ auto MaybeWorkingDirEntry = FM.getDirectoryRef(".");
+ if (MaybeWorkingDirEntry) {
+ DirectoryEntryRef WorkingDirEntry = *MaybeWorkingDirEntry;
+ StringRef WorkingDir = WorkingDirEntry.getName();
+ if (!WorkingDir.empty()) {
+ SeparateComponents(LookupPath, WorkingDir, Filename, false);
+ llvm::Expected<FileEntryRef> ShouldBeEntry =
+ FM.getFileRef(LookupPath, OpenFile);
+ if (ShouldBeEntry)
+ return llvm::expectedToOptional(std::move(ShouldBeEntry));
+ llvm::consumeError(ShouldBeEntry.takeError());
+ }
+ }
+ }
+
+ for (const auto &Entry : PPOpts->EmbedEntries) {
+ LookupPath.clear();
+ SeparateComponents(LookupPath, Entry, Filename, false);
+ llvm::Expected<FileEntryRef> ShouldBeEntry =
+ FM.getFileRef(LookupPath, OpenFile);
+ return llvm::expectedToOptional(std::move(ShouldBeEntry));
+ }
+ return std::nullopt;
+}
+
//===----------------------------------------------------------------------===//
// Preprocessor Directive Handling.
//===----------------------------------------------------------------------===//
@@ -1168,6 +1237,7 @@ void Preprocessor::HandleDirective(Token &Result) {
case tok::pp_include_next:
case tok::pp___include_macros:
case tok::pp_pragma:
+ case tok::pp_embed:
Diag(Result, diag::err_embedded_directive) << II->getName();
Diag(*ArgMacro, diag::note_macro_expansion_here)
<< ArgMacro->getIdentifierInfo();
@@ -1282,6 +1352,11 @@ void Preprocessor::HandleDirective(Token &Result) {
return HandleIdentSCCSDirective(Result);
case tok::pp_sccs:
return HandleIdentSCCSDirective(Result);
+ case tok::pp_embed:
+ return HandleEmbedDirective(SavedHash.getLocation(), Result,
+ getCurrentFileLexer()
+ ? *getCurrentFileLexer()->getFileEntry()
+ : static_cast<FileEntry *>(nullptr));
case tok::pp_assert:
//isExtension = true; // FIXME: implement #assert
break;
@@ -3543,3 +3618,401 @@ void Preprocessor::HandleElifFamilyDirective(Token &ElifToken,
HashToken.getLocation(), CI.IfLoc, /*Foundnonskip*/ true,
/*FoundElse*/ CI.FoundElse, ElifToken.getLocation());
}
+
+std::optional<LexEmbedParametersResult>
+Preprocessor::LexEmbedParameters(Token &CurTok, bool ForHasEmbed) {
+ LexEmbedParametersResult Result{};
+ SmallVector<Token, 2> ParameterTokens;
+ tok::TokenKind EndTokenKind = ForHasEmbed ? tok::r_paren : tok::eod;
+ Result.ParamRange = {CurTok.getLocation(), CurTok.getLocation()};
+
+ auto DiagMismatchedBracesAndSkipToEOD =
+ [&](tok::TokenKind Expected,
+ std::pair<tok::TokenKind, SourceLocation> Matches) {
+ Result.ParamRange.setEnd(CurTok.getEndLoc());
+ Diag(CurTok, diag::err_expected) << Expected;
+ Diag(Matches.second, diag::note_matching) << Matches.first;
+ if (CurTok.isNot(tok::eod))
+ DiscardUntilEndOfDirective(CurTok);
+ };
+
+ auto ExpectOrDiagAndSkipToEOD = [&](tok::TokenKind Kind) {
+ if (CurTok.isNot(Kind)) {
+ Result.ParamRange.setEnd(CurTok.getEndLoc());
+ Diag(CurTok, diag::err_expected) << Kind;
+ if (CurTok.isNot(tok::eod))
+ DiscardUntilEndOfDirective(CurTok);
+ return false;
+ }
+ return true;
+ };
+
+ // C23 6.10:
+ // pp-parameter-name:
+ // pp-standard-parameter
+ // pp-prefixed-parameter
+ //
+ // pp-standard-parameter:
+ // identifier
+ //
+ // pp-prefixed-parameter:
+ // identifier :: identifier
+ auto LexPPParameterName = [&]() -> std::optional<std::string> {
+ // We expect the current token to be an identifier; if it's not, things
+ // have gone wrong.
+ if (!ExpectOrDiagAndSkipToEOD(tok::identifier))
+ return std::nullopt;
+
+ const IdentifierInfo *Prefix = CurTok.getIdentifierInfo();
+
+ // Lex another token; it is either a :: or we're done with the parameter
+ // name.
+ LexNonComment(CurTok);
+ if (CurTok.is(tok::coloncolon)) {
+ // We found a ::, so lex another identifier token.
+ LexNonComment(CurTok);
+ if (!ExpectOrDiagAndSkipToEOD(tok::identifier))
+ return std::nullopt;
+
+ const IdentifierInfo *Suffix = CurTok.getIdentifierInfo();
+
+ // Lex another token so we're past the name.
+ LexNonComment(CurTok);
+ return (llvm::Twine(Prefix->getName()) + "::" + Suffix->getName()).str();
+ }
+ return Prefix->getName().str();
+ };
+
+ // C23 6.10p5: In all aspects, a preprocessor standard parameter specified by
+ // this document as an identifier pp_param and an identifier of the form
+ // __pp_param__ shall behave the same when used as a preprocessor parameter,
+ // except for the spelling.
+ auto NormalizeParameterName = [](StringRef Name) {
+ if (Name.size() > 4 && Name.starts_with("__") && Name.ends_with("__"))
+ return Name.substr(2, Name.size() - 4);
+ return Name;
+ };
+
+ auto LexParenthesizedIntegerExpr = [&]() -> std::optional<size_t> {
+ // we have a limit parameter and its internals are processed using
+ // evaluation rules from #if.
+ if (!ExpectOrDiagAndSkipToEOD(tok::l_paren))
+ return std::nullopt;
+
+ // We do not consume the ( because EvaluateDirectiveExpression will lex
+ // the next token for us.
+ IdentifierInfo *ParameterIfNDef = nullptr;
+ bool EvaluatedDefined;
+ DirectiveEvalResult LimitEvalResult = EvaluateDirectiveExpression(
+ ParameterIfNDef, CurTok, EvaluatedDefined, /*CheckForEOD=*/false);
+
+ if (!LimitEvalResult.Value) {
+ // If there was an error evaluating the directive expression, we expect
+ // to be at the end of directive token.
+ assert(CurTok.is(tok::eod) && "expect to be at the end of directive");
+ return std::nullopt;
+ }
+
+ if (!ExpectOrDiagAndSkipToEOD(tok::r_paren))
+ return std::nullopt;
+
+ // Eat the ).
+ LexNonComment(CurTok);
+
+ // C23 6.10.3.2p2: The token defined shall not appear within the constant
+ // expression.
+ if (EvaluatedDefined) {
+ Diag(CurTok, diag::err_defined_in_pp_embed);
+ return std::nullopt;
+ }
+
+ if (LimitEvalResult.Value) {
+ const llvm::APSInt &Result = *LimitEvalResult.Value;
+ if (Result.isNegative()) {
+ Diag(CurTok, diag::err_requires_positive_value)
+ << toString(Result, 10) << /*positive*/ 0;
+ return std::nullopt;
+ }
+ return Result.getLimitedValue();
+ }
+ return std::nullopt;
+ };
+
+ auto GetMatchingCloseBracket = [](tok::TokenKind Kind) {
+ switch (Kind) {
+ case tok::l_paren:
+ return tok::r_paren;
+ case tok::l_brace:
+ return tok::r_brace;
+ case tok::l_square:
+ return tok::r_square;
+ default:
+ llvm_unreachable("should not get here");
+ }
+ };
+
+ auto LexParenthesizedBalancedTokenSoup =
+ [&](llvm::SmallVectorImpl<Token> &Tokens) {
+ std::vector<std::pair<tok::TokenKind, SourceLocation>> BracketStack;
+
+ // We expect the current token to be a left paren.
+ if (!ExpectOrDiagAndSkipToEOD(tok::l_paren))
+ return false;
+ LexNonComment(CurTok); // Eat the (
+
+ bool WaitingForInnerCloseParen = false;
+ while (CurTok.isNot(tok::eod) &&
+ (WaitingForInnerCloseParen ||
+ (!WaitingForInnerCloseParen && CurTok.isNot(tok::r_paren)))) {
+ switch (CurTok.getKind()) {
+ default: // Shutting up diagnostics about not fully-covered switch.
+ break;
+ case tok::l_paren:
+ WaitingForInnerCloseParen = true;
+ [[fallthrough]];
+ case tok::l_brace:
+ case tok::l_square:
+ BracketStack.push_back({CurTok.getKind(), CurTok.getLocation()});
+ break;
+ case tok::r_paren:
+ WaitingForInnerCloseParen = false;
+ [[fallthrough]];
+ case tok::r_brace:
+ case tok::r_square: {
+ tok::TokenKind Matching =
+ GetMatchingCloseBracket(BracketStack.back().first);
+ if (BracketStack.empty() || CurTok.getKind() != Matching) {
+ DiagMismatchedBracesAndSkipToEOD(Matching, BracketStack.back());
+ return false;
+ }
+ BracketStack.pop_back();
+ } break;
+ }
+ Tokens.push_back(CurTok);
+ LexNonComment(CurTok);
+ }
+
+ // When we're done, we want to eat the closing paren.
+ if (!ExpectOrDiagAndSkipToEOD(tok::r_paren))
+ return false;
+
+ LexNonComment(CurTok); // Eat the )
+ return true;
+ };
+
+ LexNonComment(CurTok); // Prime the pump.
+ while (!CurTok.isOneOf(EndTokenKind, tok::eod)) {
+ SourceLocation ParamStartLoc = CurTok.getLocation();
+ std::optional<std::string> ParamName = LexPPParameterName();
+ if (!ParamName)
+ return std::nullopt;
+ StringRef Parameter = NormalizeParameterName(*ParamName);
+
+ // Lex the parameters (dependent on the parameter type we want!).
+ //
+ // C23 6.10.3.Xp1: The X standard embed parameter may appear zero times or
+ // one time in the embed parameter sequence.
+ if (Parameter == "limit") {
+ if (Result.MaybeLimitParam)
+ Diag(CurTok, diag::err_pp_embed_dup_params) << Parameter;
+
+ std::optional<size_t> Limit = LexParenthesizedIntegerExpr();
+ if (!Limit)
+ return std::nullopt;
+ Result.MaybeLimitParam =
+ PPEmbedParameterLimit{*Limit, {ParamStartLoc, CurTok.getLocation()}};
+ } else if (Parameter == "clang::offset") {
+ if (Result.MaybeOffsetParam)
+ Diag(CurTok, diag::err_pp_embed_dup_params) << Parameter;
+
+ std::optional<size_t> Offset = LexParenthesizedIntegerExpr();
+ if (!Offset)
+ return std::nullopt;
+ Result.MaybeOffsetParam = PPEmbedParameterOffset{
+ *Offset, {ParamStartLoc, CurTok.getLocation()}};
+ } else if (Parameter == "prefix") {
+ if (Result.MaybePrefixParam)
+ Diag(CurTok, diag::err_pp_embed_dup_params) << Parameter;
+
+ SmallVector<Token, 4> Soup;
+ if (!LexParenthesizedBalancedTokenSoup(Soup))
+ return std::nullopt;
+ Result.MaybePrefixParam = PPEmbedParameterPrefix{
+ std::move(Soup), {ParamStartLoc, CurTok.getLocation()}};
+ } else if (Parameter == "suffix") {
+ if (Result.MaybeSuffixParam)
+ Diag(CurTok, diag::err_pp_embed_dup_params) << Parameter;
+
+ SmallVector<Token, 4> Soup;
+ if (!LexParenthesizedBalancedTokenSoup(Soup))
+ return std::nullopt;
+ Result.MaybeSuffixParam = PPEmbedParameterSuffix{
+ std::move(Soup), {ParamStartLoc, CurTok.getLocation()}};
+ } else if (Parameter == "if_empty") {
+ if (Result.MaybeIfEmptyParam)
+ Diag(CurTok, diag::err_pp_embed_dup_params) << Parameter;
+
+ SmallVector<Token, 4> Soup;
+ if (!LexParenthesizedBalancedTokenSoup(Soup))
+ return std::nullopt;
+ Result.MaybeIfEmptyParam = PPEmbedParameterIfEmpty{
+ std::move(Soup), {ParamStartLoc, CurTok.getLocation()}};
+ } else {
+ ++Result.UnrecognizedParams;
+
+ // If there's a left paren, we need to parse a balanced token sequence
+ // and just eat those tokens.
+ if (CurTok.is(tok::l_paren)) {
+ SmallVector<Token, 4> Soup;
+ if (!LexParenthesizedBalancedTokenSoup(Soup))
+ return std::nullopt;
+ }
+ if (!ForHasEmbed) {
+ Diag(CurTok, diag::err_pp_unknown_parameter) << 1 << Parameter;
+ return std::nullopt;
+ }
+ }
+ }
+ Result.ParamRange.setEnd(CurTok.getLocation());
+ return Result;
+}
+
+void Preprocessor::HandleEmbedDirectiveImpl(
+ SourceLocation HashLoc, StringRef ResolvedFilename,
+ const LexEmbedParametersResult &Params, StringRef BinaryContents) {
+ if (BinaryContents.empty()) {
+ // If we have no binary contents, the only thing we need to emit are the
+ // if_empty tokens, if any.
+ // FIXME: this loses AST fidelity; nothing in the compiler will see that
+ // these tokens came from #embed. We have to hack around this when printing
+ // preprocessed output. The same is true for prefix and suffix tokens.
+ if (Params.MaybeIfEmptyParam) {
+ ArrayRef<Token> Toks = Params.MaybeIfEmptyParam->Tokens;
+ size_t TokCount = Toks.size();
+ auto NewToks = std::make_unique<Token[]>(TokCount);
+ llvm::copy(Toks, NewToks.get());
+ EnterTokenStream(std::move(NewToks), TokCount, true, true);
+ }
+ return;
+ }
+
+ size_t NumPrefixToks = Params.PrefixTokenCount(),
+ NumSuffixToks = Params.SuffixTokenCount();
+ size_t TotalNumToks = 1 + NumPrefixToks + NumSuffixToks;
+ size_t CurIdx = 0;
+ auto Toks = std::make_unique<Token[]>(TotalNumToks);
+
+ // Add the prefix tokens, if any.
+ if (Params.MaybePrefixParam) {
+ llvm::copy(Params.MaybePrefixParam->Tokens, &Toks[CurIdx]);
+ CurIdx += NumPrefixToks;
+ }
+
+ EmbedAnnotationData *Data = new (BP) EmbedAnnotationData;
+ Data->FileName = ResolvedFilename;
+ Data->BinaryData = BinaryContents;
+
+ Toks[CurIdx].startToken();
+ Toks[CurIdx].setKind(tok::annot_embed);
+ Toks[CurIdx].setAnnotationRange(HashLoc);
+ Toks[CurIdx++].setAnnotationValue(Data);
+
+ // Now add the suffix tokens, if any.
+ if (Params.MaybeSuffixParam) {
+ llvm::copy(Params.MaybeSuffixParam->Tokens, &Toks[CurIdx]);
+ CurIdx += NumSuffixToks;
+ }
+
+ assert(CurIdx == TotalNumToks && "Calculated the incorrect number of tokens");
+ EnterTokenStream(std::move(Toks), TotalNumToks, true, true);
+}
+
+void Preprocessor::HandleEmbedDirective(SourceLocation HashLoc, Token &EmbedTok,
+ const FileEntry *LookupFromFile) {
+ // Give the usual extension/compatibility warnings.
+ if (LangOpts.C23)
+ Diag(EmbedTok, diag::warn_compat_pp_embed_directive);
+ else
+ Diag(EmbedTok, diag::ext_pp_embed_directive)
+ << (LangOpts.CPlusPlus ? /*Clang*/ 1 : /*C23*/ 0);
+
+ // Parse the filename header
+ Token FilenameTok;
+ if (LexHeaderName(FilenameTok))
+ return;
+
+ if (FilenameTok.isNot(tok::header_name)) {
+ Diag(FilenameTok.getLocation(), diag::err_pp_expects_filename);
+ if (FilenameTok.isNot(tok::eod))
+ DiscardUntilEndOfDirective();
+ return;
+ }
+
+ // Parse the optional sequence of
+ // directive-parameters:
+ // identifier parameter-name-list[opt] directive-argument-list[opt]
+ // directive-argument-list:
+ // '(' balanced-token-sequence ')'
+ // parameter-name-list:
+ // '::' identifier parameter-name-list[opt]
+ Token CurTok;
+ std::optional<LexEmbedParametersResult> Params =
+ LexEmbedParameters(CurTok, /*ForHasEmbed=*/false);
+
+ assert((Params || CurTok.is(tok::eod)) &&
+ "expected success or to be at the end of the directive");
+ if (!Params)
+ return;
+
+ // Now, splat the data out!
+ SmallString<128> FilenameBuffer;
+ StringRef Filename = getSpelling(FilenameTok, FilenameBuffer);
+ StringRef OriginalFilename = Filename;
+ bool isAngled =
+ GetIncludeFilenameSpelling(FilenameTok.getLocation(), Filename);
+ // If GetIncludeFilenameSpelling set the start ptr to null, there was an
+ // error.
+ assert(!Filename.empty());
+ OptionalFileEntryRef MaybeFileRef =
+ this->LookupEmbedFile(Filename, isAngled, true, LookupFromFile);
+ if (!MaybeFileRef) {
+ // could not find file
+ if (Callbacks && Callbacks->EmbedFileNotFound(OriginalFilename)) {
+ return;
+ }
+ Diag(FilenameTok, diag::err_pp_file_not_found) << Filename;
+ return;
+ }
+ std::optional<llvm::MemoryBufferRef> MaybeFile =
+ getSourceManager().getMemoryBufferForFileOrNone(*MaybeFileRef);
+ if (!MaybeFile) {
+ // could not find file
+ Diag(FilenameTok, diag::err_cannot_open_file)
+ << Filename << "a buffer to the contents could not be created";
+ return;
+ }
+ StringRef BinaryContents = MaybeFile->getBuffer();
+
+ // The order is important between 'offset' and 'limit'; we want to offset
+ // first and then limit second; otherwise we may reduce the notional resource
+ // size to something too small to offset into.
+ if (Params->MaybeOffsetParam) {
+ // FIXME: just like with the limit() and if_empty() parameters, this loses
+ // source fidelity in the AST; it has no idea that there was an offset
+ // involved.
+ // offsets all the way to the end of the file make for an empty file.
+ BinaryContents = BinaryContents.substr(Params->MaybeOffsetParam->Offset);
+ }
+
+ if (Params->MaybeLimitParam) {
+ // FIXME: just like with the clang::offset() and if_empty() parameters,
+ // this loses source fidelity in the AST; it has no idea there was a limit
+ // involved.
+ BinaryContents = BinaryContents.substr(0, Params->MaybeLimitParam->Limit);
+ }
+
+ if (Callbacks)
+ Callbacks->EmbedDirective(HashLoc, Filename, isAngled, MaybeFileRef,
+ *Params);
+ HandleEmbedDirectiveImpl(HashLoc, Filename, *Params, BinaryContents);
+}
diff --git a/clang/lib/Lex/PPExpressions.cpp b/clang/lib/Lex/PPExpressions.cpp
index f267efabd..8bb82bd 100644
--- a/clang/lib/Lex/PPExpressions.cpp
+++ b/clang/lib/Lex/PPExpressions.cpp
@@ -870,7 +870,9 @@ static bool EvaluateDirectiveSubExpr(PPValue &LHS, unsigned MinPrec,
/// may occur after a #if or #elif directive. If the expression is equivalent
/// to "!defined(X)" return X in IfNDefMacro.
Preprocessor::DirectiveEvalResult
-Preprocessor::EvaluateDirectiveExpression(IdentifierInfo *&IfNDefMacro) {
+Preprocessor::EvaluateDirectiveExpression(IdentifierInfo *&IfNDefMacro,
+ Token &Tok, bool &EvaluatedDefined,
+ bool CheckForEoD) {
SaveAndRestore PPDir(ParsingIfOrElifDirective, true);
// Save the current state of 'DisableMacroExpansion' and reset it to false. If
// 'DisableMacroExpansion' is true, then we must be in a macro argument list
@@ -882,7 +884,6 @@ Preprocessor::EvaluateDirectiveExpression(IdentifierInfo *&IfNDefMacro) {
DisableMacroExpansion = false;
// Peek ahead one token.
- Token Tok;
LexNonComment(Tok);
// C99 6.10.1p3 - All expressions are evaluated as intmax_t or uintmax_t.
@@ -895,7 +896,7 @@ Preprocessor::EvaluateDirectiveExpression(IdentifierInfo *&IfNDefMacro) {
// Parse error, skip the rest of the macro line.
SourceRange ConditionRange = ExprStartLoc;
if (Tok.isNot(tok::eod))
- ConditionRange = DiscardUntilEndOfDirective();
+ ConditionRange = DiscardUntilEndOfDirective(Tok);
// Restore 'DisableMacroExpansion'.
DisableMacroExpansion = DisableMacroExpansionAtStartOfDirective;
@@ -903,11 +904,14 @@ Preprocessor::EvaluateDirectiveExpression(IdentifierInfo *&IfNDefMacro) {
// We cannot trust the source range from the value because there was a
// parse error. Track the range manually -- the end of the directive is the
// end of the condition range.
- return {false,
+ return {std::nullopt,
+ false,
DT.IncludedUndefinedIds,
{ExprStartLoc, ConditionRange.getEnd()}};
}
+ EvaluatedDefined = DT.State != DefinedTracker::Unknown;
+
// If we are at the end of the expression after just parsing a value, there
// must be no (unparenthesized) binary operators involved, so we can exit
// directly.
@@ -919,7 +923,10 @@ Preprocessor::EvaluateDirectiveExpression(IdentifierInfo *&IfNDefMacro) {
// Restore 'DisableMacroExpansion'.
DisableMacroExpansion = DisableMacroExpansionAtStartOfDirective;
- return {ResVal.Val != 0, DT.IncludedUndefinedIds, ResVal.getRange()};
+ bool IsNonZero = ResVal.Val != 0;
+ SourceRange ValRange = ResVal.getRange();
+ return {std::move(ResVal.Val), IsNonZero, DT.IncludedUndefinedIds,
+ ValRange};
}
// Otherwise, we must have a binary operator (e.g. "#if 1 < 2"), so parse the
@@ -928,21 +935,37 @@ Preprocessor::EvaluateDirectiveExpression(IdentifierInfo *&IfNDefMacro) {
Tok, true, DT.IncludedUndefinedIds, *this)) {
// Parse error, skip the rest of the macro line.
if (Tok.isNot(tok::eod))
- DiscardUntilEndOfDirective();
+ DiscardUntilEndOfDirective(Tok);
// Restore 'DisableMacroExpansion'.
DisableMacroExpansion = DisableMacroExpansionAtStartOfDirective;
- return {false, DT.IncludedUndefinedIds, ResVal.getRange()};
+ SourceRange ValRange = ResVal.getRange();
+ return {std::nullopt, false, DT.IncludedUndefinedIds, ValRange};
}
- // If we aren't at the tok::eod token, something bad happened, like an extra
- // ')' token.
- if (Tok.isNot(tok::eod)) {
- Diag(Tok, diag::err_pp_expected_eol);
- DiscardUntilEndOfDirective();
+ if (CheckForEoD) {
+ // If we aren't at the tok::eod token, something bad happened, like an extra
+ // ')' token.
+ if (Tok.isNot(tok::eod)) {
+ Diag(Tok, diag::err_pp_expected_eol);
+ DiscardUntilEndOfDirective(Tok);
+ }
}
+ EvaluatedDefined = EvaluatedDefined || DT.State != DefinedTracker::Unknown;
+
// Restore 'DisableMacroExpansion'.
DisableMacroExpansion = DisableMacroExpansionAtStartOfDirective;
- return {ResVal.Val != 0, DT.IncludedUndefinedIds, ResVal.getRange()};
+ bool IsNonZero = ResVal.Val != 0;
+ SourceRange ValRange = ResVal.getRange();
+ return {std::move(ResVal.Val), IsNonZero, DT.IncludedUndefinedIds, ValRange};
+}
+
+Preprocessor::DirectiveEvalResult
+Preprocessor::EvaluateDirectiveExpression(IdentifierInfo *&IfNDefMacro,
+ bool CheckForEoD) {
+ Token Tok;
+ bool EvaluatedDefined;
+ return EvaluateDirectiveExpression(IfNDefMacro, Tok, EvaluatedDefined,
+ CheckForEoD);
}
diff --git a/clang/lib/Lex/PPMacroExpansion.cpp b/clang/lib/Lex/PPMacroExpansion.cpp
index f085b94..3913ff0 100644
--- a/clang/lib/Lex/PPMacroExpansion.cpp
+++ b/clang/lib/Lex/PPMacroExpansion.cpp
@@ -380,6 +380,7 @@ void Preprocessor::RegisterBuiltinMacros() {
Ident__has_c_attribute = nullptr;
Ident__has_declspec = RegisterBuiltinMacro(*this, "__has_declspec_attribute");
+ Ident__has_embed = RegisterBuiltinMacro(*this, "__has_embed");
Ident__has_include = RegisterBuiltinMacro(*this, "__has_include");
Ident__has_include_next = RegisterBuiltinMacro(*this, "__has_include_next");
Ident__has_warning = RegisterBuiltinMacro(*this, "__has_warning");
@@ -1279,6 +1280,105 @@ static bool EvaluateHasIncludeCommon(Token &Tok, IdentifierInfo *II,
return File.has_value();
}
+/// EvaluateHasEmbed - Process a '__has_embed("foo" params...)' expression.
+/// Returns a filled optional with the value if successful; otherwise, empty.
+EmbedResult Preprocessor::EvaluateHasEmbed(Token &Tok, IdentifierInfo *II) {
+ // These expressions are only allowed within a preprocessor directive.
+ if (!this->isParsingIfOrElifDirective()) {
+ Diag(Tok, diag::err_pp_directive_required) << II;
+ // Return a valid identifier token.
+ assert(Tok.is(tok::identifier));
+ Tok.setIdentifierInfo(II);
+ return EmbedResult::Invalid;
+ }
+
+ // Ensure we have a '('.
+ LexUnexpandedToken(Tok);
+ if (Tok.isNot(tok::l_paren)) {
+ Diag(Tok, diag::err_pp_expected_after) << II << tok::l_paren;
+ // If the next token looks like a filename or the start of one,
+ // assume it is and process it as such.
+ return EmbedResult::Invalid;
+ }
+
+ // Save '(' location for possible missing ')' message and then lex the header
+ // name token for the embed resource.
+ SourceLocation LParenLoc = Tok.getLocation();
+ if (this->LexHeaderName(Tok))
+ return EmbedResult::Invalid;
+
+ if (Tok.isNot(tok::header_name)) {
+ Diag(Tok.getLocation(), diag::err_pp_expects_filename);
+ return EmbedResult::Invalid;
+ }
+
+ SourceLocation FilenameLoc = Tok.getLocation();
+ Token FilenameTok = Tok;
+
+ std::optional<LexEmbedParametersResult> Params =
+ this->LexEmbedParameters(Tok, /*ForHasEmbed=*/true);
+ assert((Params || Tok.is(tok::eod)) &&
+ "expected success or to be at the end of the directive");
+
+ if (!Params)
+ return EmbedResult::Invalid;
+
+ if (Params->UnrecognizedParams > 0)
+ return EmbedResult::NotFound;
+
+ if (!Tok.is(tok::r_paren)) {
+ Diag(this->getLocForEndOfToken(FilenameLoc), diag::err_pp_expected_after)
+ << II << tok::r_paren;
+ Diag(LParenLoc, diag::note_matching) << tok::l_paren;
+ if (Tok.isNot(tok::eod))
+ DiscardUntilEndOfDirective();
+ return EmbedResult::Invalid;
+ }
+
+ SmallString<128> FilenameBuffer;
+ StringRef Filename = this->getSpelling(FilenameTok, FilenameBuffer);
+ bool isAngled =
+ this->GetIncludeFilenameSpelling(FilenameTok.getLocation(), Filename);
+ // If GetIncludeFilenameSpelling set the start ptr to null, there was an
+ // error.
+ assert(!Filename.empty());
+ const FileEntry *LookupFromFile =
+ this->getCurrentFileLexer() ? *this->getCurrentFileLexer()->getFileEntry()
+ : static_cast<FileEntry *>(nullptr);
+ OptionalFileEntryRef MaybeFileEntry =
+ this->LookupEmbedFile(Filename, isAngled, false, LookupFromFile);
+ if (Callbacks) {
+ Callbacks->HasEmbed(LParenLoc, Filename, isAngled, MaybeFileEntry);
+ }
+ if (!MaybeFileEntry)
+ return EmbedResult::NotFound;
+
+ size_t FileSize = MaybeFileEntry->getSize();
+ // First, "offset" into the file (this reduces the amount of data we can read
+ // from the file).
+ if (Params->MaybeOffsetParam) {
+ if (Params->MaybeOffsetParam->Offset > FileSize)
+ FileSize = 0;
+ else
+ FileSize -= Params->MaybeOffsetParam->Offset;
+ }
+
+ // Second, limit the data from the file (this also reduces the amount of data
+ // we can read from the file).
+ if (Params->MaybeLimitParam) {
+ if (Params->MaybeLimitParam->Limit > FileSize)
+ FileSize = 0;
+ else
+ FileSize = Params->MaybeLimitParam->Limit;
+ }
+
+ // If we have no data left to read, the file is empty, otherwise we have the
+ // expected resource.
+ if (FileSize == 0)
+ return EmbedResult::Empty;
+ return EmbedResult::Found;
+}
+
bool Preprocessor::EvaluateHasInclude(Token &Tok, IdentifierInfo *II) {
return EvaluateHasIncludeCommon(Tok, II, *this, nullptr, nullptr);
}
@@ -1820,6 +1920,17 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) {
return;
OS << (int)Value;
Tok.setKind(tok::numeric_constant);
+ } else if (II == Ident__has_embed) {
+ // The argument to these two builtins should be a parenthesized
+ // file name string literal using angle brackets (<>) or
+ // double-quotes (""), optionally followed by a series of
+ // arguments similar to form like attributes.
+ EmbedResult Value = EvaluateHasEmbed(Tok, II);
+ if (Value == EmbedResult::Invalid)
+ return;
+
+ Tok.setKind(tok::numeric_constant);
+ 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,
diff --git a/clang/lib/Lex/TokenConcatenation.cpp b/clang/lib/Lex/TokenConcatenation.cpp
index 1b3201b..865879d 100644
--- a/clang/lib/Lex/TokenConcatenation.cpp
+++ b/clang/lib/Lex/TokenConcatenation.cpp
@@ -193,9 +193,12 @@ bool TokenConcatenation::AvoidConcat(const Token &PrevPrevTok,
if (Tok.isAnnotation()) {
// Modules annotation can show up when generated automatically for includes.
assert(Tok.isOneOf(tok::annot_module_include, tok::annot_module_begin,
- tok::annot_module_end) &&
+ tok::annot_module_end, tok::annot_embed) &&
"unexpected annotation in AvoidConcat");
+
ConcatInfo = 0;
+ if (Tok.is(tok::annot_embed))
+ return true;
}
if (ConcatInfo == 0)