Skip to content

Commit

Permalink
[analyzer][PlistMacroExpansion] Part 3.: Macro arguments are expanded
Browse files Browse the repository at this point in the history
This part focuses on expanding macro arguments.

Differential Revision: https://reviews.llvm.org/D52795

llvm-svn: 347629
  • Loading branch information
Szelethus committed Nov 27, 2018
1 parent 0b35afd commit 08d92e4
Show file tree
Hide file tree
Showing 3 changed files with 4,110 additions and 110 deletions.
219 changes: 197 additions & 22 deletions clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -723,15 +723,24 @@ void PlistDiagnostics::FlushDiagnosticsImpl(

namespace {

struct MacroNameAndInfo {
using ExpArgTokens = llvm::SmallVector<Token, 2>;

/// Maps unexpanded macro arguments to expanded arguments. A macro argument may
/// need to expanded further when it is nested inside another macro.
class MacroArgMap : public std::map<const IdentifierInfo *, ExpArgTokens> {
public:
void expandFromPrevMacro(const MacroArgMap &Super);
};

struct MacroNameAndArgs {
std::string Name;
const MacroInfo *MI = nullptr;
MacroArgMap Args;

MacroNameAndInfo(std::string N, const MacroInfo *MI)
: Name(std::move(N)), MI(MI) {}
MacroNameAndArgs(std::string N, const MacroInfo *MI, MacroArgMap M)
: Name(std::move(N)), MI(MI), Args(std::move(M)) {}
};

/// Helper class for printing tokens.
class TokenPrinter {
llvm::raw_ostream &OS;
const Preprocessor &PP;
Expand All @@ -751,12 +760,51 @@ class TokenPrinter {

} // end of anonymous namespace

/// The implementation method of getMacroExpansion: It prints the expansion of
/// a macro to \p Printer, and returns with the name of the macro.
///
/// Since macros can be nested in one another, this function may call itself
/// recursively.
///
/// Unfortunately, macro arguments have to expanded manually. To understand why,
/// observe the following example:
///
/// #define PRINT(x) print(x)
/// #define DO_SOMETHING(str) PRINT(str)
///
/// DO_SOMETHING("Cute panda cubs.");
///
/// As we expand the last line, we'll immediately replace PRINT(str) with
/// print(x). The information that both 'str' and 'x' refers to the same string
/// is an information we have to forward, hence the argument \p PrevArgs.
static std::string getMacroNameAndPrintExpansion(TokenPrinter &Printer,
SourceLocation MacroLoc,
const Preprocessor &PP);

/// Retrieves the name of the macro and its MacroInfo.
static MacroNameAndInfo getMacroNameAndInfo(SourceLocation ExpanLoc,
const Preprocessor &PP,
const MacroArgMap &PrevArgs);

/// Retrieves the name of the macro and what it's arguments expand into
/// at \p ExpanLoc.
///
/// For example, for the following macro expansion:
///
/// #define SET_TO_NULL(x) x = 0
/// #define NOT_SUSPICIOUS(a) \
/// { \
/// int b = 0; \
/// } \
/// SET_TO_NULL(a)
///
/// int *ptr = new int(4);
/// NOT_SUSPICIOUS(&ptr);
/// *ptr = 5;
///
/// When \p ExpanLoc references the last line, the macro name "NOT_SUSPICIOUS"
/// and the MacroArgMap map { (a, &ptr) } will be returned.
///
/// When \p ExpanLoc references "SET_TO_NULL(a)" within the definition of
/// "NOT_SUSPICOUS", the macro name "SET_TO_NULL" and the MacroArgMap map
/// { (x, a) } will be returned.
static MacroNameAndArgs getMacroNameAndArgs(SourceLocation ExpanLoc,
const Preprocessor &PP);

/// Retrieves the ')' token that matches '(' \p It points to.
Expand All @@ -781,21 +829,26 @@ static ExpansionInfo getExpandedMacro(SourceLocation MacroLoc,
llvm::SmallString<200> ExpansionBuf;
llvm::raw_svector_ostream OS(ExpansionBuf);
TokenPrinter Printer(OS, PP);
std::string MacroName = getMacroNameAndPrintExpansion(Printer, MacroLoc, PP);
std::string MacroName =
getMacroNameAndPrintExpansion(Printer, MacroLoc, PP, MacroArgMap{});
return { MacroName, OS.str() };
}

static std::string getMacroNameAndPrintExpansion(TokenPrinter &Printer,
SourceLocation MacroLoc,
const Preprocessor &PP) {
const Preprocessor &PP,
const MacroArgMap &PrevArgs) {

const SourceManager &SM = PP.getSourceManager();

MacroNameAndInfo Info = getMacroNameAndInfo(SM.getExpansionLoc(MacroLoc), PP);
const MacroInfo *MI = Info.MI;
MacroNameAndArgs Info = getMacroNameAndArgs(SM.getExpansionLoc(MacroLoc), PP);

// Manually expand its arguments from the previous macro.
Info.Args.expandFromPrevMacro(PrevArgs);

// Iterate over the macro's tokens and stringify them.
for (auto It = MI->tokens_begin(), E = MI->tokens_end(); It != E; ++It) {
for (auto It = Info.MI->tokens_begin(), E = Info.MI->tokens_end(); It != E;
++It) {
Token T = *It;

// If this token is not an identifier, we only need to print it.
Expand All @@ -812,7 +865,7 @@ static std::string getMacroNameAndPrintExpansion(TokenPrinter &Printer,
// macro.
if (const MacroInfo *MI =
getMacroInfoForLocation(PP, SM, II, T.getLocation())) {
getMacroNameAndPrintExpansion(Printer, T.getLocation(), PP);
getMacroNameAndPrintExpansion(Printer, T.getLocation(), PP, Info.Args);

// If this is a function-like macro, skip its arguments, as
// getExpandedMacro() already printed them. If this is the case, let's
Expand All @@ -822,15 +875,46 @@ static std::string getMacroNameAndPrintExpansion(TokenPrinter &Printer,
continue;
}

// If control reached here, then this token isn't a macro identifier, print
// it.
// If this token is the current macro's argument, we should expand it.
auto ArgMapIt = Info.Args.find(II);
if (ArgMapIt != Info.Args.end()) {
for (MacroInfo::tokens_iterator ArgIt = ArgMapIt->second.begin(),
ArgEnd = ArgMapIt->second.end();
ArgIt != ArgEnd; ++ArgIt) {

// These tokens may still be macros, if that is the case, handle it the
// same way we did above.
const auto *ArgII = ArgIt->getIdentifierInfo();
if (!ArgII) {
Printer.printToken(*ArgIt);
continue;
}

const auto *MI = PP.getMacroInfo(ArgII);
if (!MI) {
Printer.printToken(*ArgIt);
continue;
}

getMacroNameAndPrintExpansion(Printer, ArgIt->getLocation(), PP,
Info.Args);
if (MI->getNumParams() != 0)
ArgIt = getMatchingRParen(++ArgIt, ArgEnd);
}
continue;
}

// TODO: Handle tok::hash and tok::hashhash.

// If control reached here, then this token isn't a macro identifier, nor an
// unexpanded macro argument that we need to handle, print it.
Printer.printToken(T);
}

return Info.Name;
}

static MacroNameAndInfo getMacroNameAndInfo(SourceLocation ExpanLoc,
static MacroNameAndArgs getMacroNameAndArgs(SourceLocation ExpanLoc,
const Preprocessor &PP) {

const SourceManager &SM = PP.getSourceManager();
Expand All @@ -857,7 +941,66 @@ static MacroNameAndInfo getMacroNameAndInfo(SourceLocation ExpanLoc,
const MacroInfo *MI = getMacroInfoForLocation(PP, SM, II, ExpanLoc);
assert(MI && "The macro must've been defined at it's expansion location!");

return { MacroName, MI };
// Acquire the macro's arguments.
//
// The rough idea here is to lex from the first left parentheses to the last
// right parentheses, and map the macro's unexpanded arguments to what they
// will be expanded to. An expanded macro argument may contain several tokens
// (like '3 + 4'), so we'll lex until we find a tok::comma or tok::r_paren, at
// which point we start lexing the next argument or finish.
ArrayRef<const IdentifierInfo *> MacroArgs = MI->params();
if (MacroArgs.empty())
return { MacroName, MI, {} };

RawLexer.LexFromRawLexer(TheTok);
assert(TheTok.is(tok::l_paren) &&
"The token after the macro's identifier token should be '('!");

MacroArgMap Args;

// When the macro's argument is a function call, like
// CALL_FN(someFunctionName(param1, param2))
// we will find tok::l_paren, tok::r_paren, and tok::comma that do not divide
// actual macro arguments, or do not represent the macro argument's closing
// parentheses, so we'll count how many parentheses aren't closed yet.
int ParenthesesDepth = 1;

for (const IdentifierInfo *UnexpArgII : MacroArgs) {
MacroArgMap::mapped_type ExpandedArgTokens;

// Lex the first token of the next macro parameter.
RawLexer.LexFromRawLexer(TheTok);

while (TheTok.isNot(tok::comma) || ParenthesesDepth != 1) {
assert(TheTok.isNot(tok::eof) &&
"EOF encountered while looking for expanded macro args!");

if (TheTok.is(tok::l_paren))
++ParenthesesDepth;

if (TheTok.is(tok::r_paren))
--ParenthesesDepth;

if (ParenthesesDepth == 0)
break;

if (TheTok.is(tok::raw_identifier))
PP.LookUpIdentifierInfo(TheTok);

ExpandedArgTokens.push_back(TheTok);
RawLexer.LexFromRawLexer(TheTok);
}

Args.emplace(UnexpArgII, std::move(ExpandedArgTokens));
}

// TODO: The condition really should be TheTok.is(tok::r_paren), but variadic
// macro arguments are not handled yet.
assert(TheTok.isOneOf(tok::r_paren, tok::comma) &&
"Expanded macro argument acquisition failed! After the end of the loop"
" this token should be ')'!");

return { MacroName, MI, Args };
}

static MacroInfo::tokens_iterator getMatchingRParen(
Expand All @@ -867,8 +1010,8 @@ static MacroInfo::tokens_iterator getMatchingRParen(
assert(It->is(tok::l_paren) && "This token should be '('!");

// Skip until we find the closing ')'.
int ParanthesesDepth = 1;
while (ParanthesesDepth != 0) {
int ParenthesesDepth = 1;
while (ParenthesesDepth != 0) {
++It;

assert(It->isNot(tok::eof) &&
Expand All @@ -877,10 +1020,10 @@ static MacroInfo::tokens_iterator getMatchingRParen(
"End of the macro definition reached before finding ')'!");

if (It->is(tok::l_paren))
++ParanthesesDepth;
++ParenthesesDepth;

if (It->is(tok::r_paren))
--ParanthesesDepth;
--ParenthesesDepth;
}
return It;
}
Expand All @@ -897,6 +1040,38 @@ static const MacroInfo *getMacroInfoForLocation(const Preprocessor &PP,
return MD->findDirectiveAtLoc(Loc, SM).getMacroInfo();
}

void MacroArgMap::expandFromPrevMacro(const MacroArgMap &Super) {

for (value_type &Pair : *this) {
ExpArgTokens &CurrExpArgTokens = Pair.second;

// For each token in the expanded macro argument.
auto It = CurrExpArgTokens.begin();
while (It != CurrExpArgTokens.end()) {
if (It->isNot(tok::identifier)) {
++It;
continue;
}

const auto *II = It->getIdentifierInfo();
assert(II);

// Is this an argument that "Super" expands further?
if (!Super.count(II)) {
++It;
continue;
}

const ExpArgTokens &SuperExpArgTokens = Super.at(II);

It = CurrExpArgTokens.insert(
It, SuperExpArgTokens.begin(), SuperExpArgTokens.end());
std::advance(It, SuperExpArgTokens.size());
It = CurrExpArgTokens.erase(It);
}
}
}

void TokenPrinter::printToken(const Token &Tok) {
// If the tokens were already space separated, or if they must be to avoid
// them being implicitly pasted, add a space between them.
Expand Down
Loading

0 comments on commit 08d92e4

Please sign in to comment.