#include "clang/Lex/MacroArgs.h"
#include "clang/Lex/LexDiagnostic.h"
#include "clang/Lex/MacroInfo.h"
#include "clang/Lex/Preprocessor.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/SaveAndRestore.h"
#include <algorithm>
using namespace clang;
MacroArgs *MacroArgs::create(const MacroInfo *MI,
ArrayRef<Token> UnexpArgTokens,
bool VarargsElided, Preprocessor &PP) {
assert(MI->isFunctionLike() &&
"Can't have args for an object-like macro!");
MacroArgs **ResultEnt = nullptr;
unsigned ClosestMatch = ~0U;
for (MacroArgs **Entry = &PP.MacroArgCache; *Entry;
Entry = &(*Entry)->ArgCache) {
if ((*Entry)->NumUnexpArgTokens >= UnexpArgTokens.size() &&
(*Entry)->NumUnexpArgTokens < ClosestMatch) {
ResultEnt = Entry;
if ((*Entry)->NumUnexpArgTokens == UnexpArgTokens.size())
break;
ClosestMatch = (*Entry)->NumUnexpArgTokens;
}
}
MacroArgs *Result;
if (!ResultEnt) {
Result = new (
llvm::safe_malloc(totalSizeToAlloc<Token>(UnexpArgTokens.size())))
MacroArgs(UnexpArgTokens.size(), VarargsElided, MI->getNumParams());
} else {
Result = *ResultEnt;
*ResultEnt = Result->ArgCache;
Result->NumUnexpArgTokens = UnexpArgTokens.size();
Result->VarargsElided = VarargsElided;
Result->NumMacroArgs = MI->getNumParams();
}
if (!UnexpArgTokens.empty()) {
static_assert(std::is_trivial<Token>::value,
"assume trivial copyability if copying into the "
"uninitialized array (as opposed to reusing a cached "
"MacroArgs)");
std::copy(UnexpArgTokens.begin(), UnexpArgTokens.end(),
Result->getTrailingObjects<Token>());
}
return Result;
}
void MacroArgs::destroy(Preprocessor &PP) {
for (unsigned i = 0, e = PreExpArgTokens.size(); i != e; ++i)
PreExpArgTokens[i].clear();
ArgCache = PP.MacroArgCache;
PP.MacroArgCache = this;
}
MacroArgs *MacroArgs::deallocate() {
MacroArgs *Next = ArgCache;
this->~MacroArgs();
static_assert(std::is_trivially_destructible<Token>::value,
"assume trivially destructible and forego destructors");
free(this);
return Next;
}
unsigned MacroArgs::getArgLength(const Token *ArgPtr) {
unsigned NumArgTokens = 0;
for (; ArgPtr->isNot(tok::eof); ++ArgPtr)
++NumArgTokens;
return NumArgTokens;
}
const Token *MacroArgs::getUnexpArgument(unsigned Arg) const {
assert(Arg < getNumMacroArguments() && "Invalid arg #");
const Token *Start = getTrailingObjects<Token>();
const Token *Result = Start;
for (; Arg; ++Result) {
assert(Result < Start+NumUnexpArgTokens && "Invalid arg #");
if (Result->is(tok::eof))
--Arg;
}
assert(Result < Start+NumUnexpArgTokens && "Invalid arg #");
return Result;
}
bool MacroArgs::invokedWithVariadicArgument(const MacroInfo *const MI,
Preprocessor &PP) {
if (!MI->isVariadic())
return false;
const int VariadicArgIndex = getNumMacroArguments() - 1;
return getPreExpArgument(VariadicArgIndex, PP).front().isNot(tok::eof);
}
bool MacroArgs::ArgNeedsPreexpansion(const Token *ArgTok,
Preprocessor &PP) const {
for (; ArgTok->isNot(tok::eof); ++ArgTok)
if (IdentifierInfo *II = ArgTok->getIdentifierInfo())
if (II->hasMacroDefinition())
return true;
return false;
}
const std::vector<Token> &MacroArgs::getPreExpArgument(unsigned Arg,
Preprocessor &PP) {
assert(Arg < getNumMacroArguments() && "Invalid argument number!");
if (PreExpArgTokens.size() < getNumMacroArguments())
PreExpArgTokens.resize(getNumMacroArguments());
std::vector<Token> &Result = PreExpArgTokens[Arg];
if (!Result.empty()) return Result;
SaveAndRestore<bool> PreExpandingMacroArgs(PP.InMacroArgPreExpansion, true);
const Token *AT = getUnexpArgument(Arg);
unsigned NumToks = getArgLength(AT)+1;
PP.EnterTokenStream(AT, NumToks, false ,
false , false );
do {
Result.push_back(Token());
Token &Tok = Result.back();
PP.Lex(Tok);
} while (Result.back().isNot(tok::eof));
if (PP.InCachingLexMode())
PP.ExitCachingLexMode();
PP.RemoveTopOfLexerStack();
return Result;
}
Token MacroArgs::StringifyArgument(const Token *ArgToks,
Preprocessor &PP, bool Charify,
SourceLocation ExpansionLocStart,
SourceLocation ExpansionLocEnd) {
Token Tok;
Tok.startToken();
Tok.setKind(Charify ? tok::char_constant : tok::string_literal);
const Token *ArgTokStart = ArgToks;
SmallString<128> Result;
Result += "\"";
bool isFirst = true;
for (; ArgToks->isNot(tok::eof); ++ArgToks) {
const Token &Tok = *ArgToks;
if (!isFirst && (Tok.hasLeadingSpace() || Tok.isAtStartOfLine()))
Result += ' ';
isFirst = false;
if (tok::isStringLiteral(Tok.getKind()) || Tok.is(tok::char_constant) || Tok.is(tok::wide_char_constant) || Tok.is(tok::utf8_char_constant) || Tok.is(tok::utf16_char_constant) || Tok.is(tok::utf32_char_constant)) { bool Invalid = false;
std::string TokStr = PP.getSpelling(Tok, &Invalid);
if (!Invalid) {
std::string Str = Lexer::Stringify(TokStr);
Result.append(Str.begin(), Str.end());
}
} else if (Tok.is(tok::code_completion)) {
PP.CodeCompleteNaturalLanguage();
} else {
unsigned CurStrLen = Result.size();
Result.resize(CurStrLen+Tok.getLength());
const char *BufPtr = Result.data() + CurStrLen;
bool Invalid = false;
unsigned ActualTokLen = PP.getSpelling(Tok, BufPtr, &Invalid);
if (!Invalid) {
if (ActualTokLen && BufPtr != &Result[CurStrLen])
memcpy(&Result[CurStrLen], BufPtr, ActualTokLen);
if (ActualTokLen != Tok.getLength())
Result.resize(CurStrLen+ActualTokLen);
}
}
}
if (Result.back() == '\\') {
unsigned FirstNonSlash = Result.size()-2;
while (Result[FirstNonSlash] == '\\')
--FirstNonSlash;
if ((Result.size()-1-FirstNonSlash) & 1) {
PP.Diag(ArgToks[-1], diag::pp_invalid_string_literal);
Result.pop_back(); }
}
Result += '"';
if (Charify) {
Result[0] = '\'';
Result[Result.size()-1] = '\'';
bool isBad = false;
if (Result.size() == 3)
isBad = Result[1] == '\''; else
isBad = (Result.size() != 4 || Result[1] != '\\');
if (isBad) {
PP.Diag(ArgTokStart[0], diag::err_invalid_character_to_charify);
Result = "' '"; }
}
PP.CreateString(Result, Tok,
ExpansionLocStart, ExpansionLocEnd);
return Tok;
}