6ad0788c33
This patch replaces (llvm::|)Optional< with std::optional<. I'll post a separate patch to remove #include "llvm/ADT/Optional.h". This is part of an effort to migrate from llvm::Optional to std::optional: https://discourse.llvm.org/t/deprecating-llvm-optional-x-hasvalue-getvalue-getvalueor/63716
233 lines
8.1 KiB
C++
233 lines
8.1 KiB
C++
//===- MacroExpansionContext.cpp - Macro expansion information --*- C++ -*-===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "clang/Analysis/MacroExpansionContext.h"
|
|
#include "llvm/Support/Debug.h"
|
|
#include <optional>
|
|
|
|
#define DEBUG_TYPE "macro-expansion-context"
|
|
|
|
static void dumpTokenInto(const clang::Preprocessor &PP, clang::raw_ostream &OS,
|
|
clang::Token Tok);
|
|
|
|
namespace clang {
|
|
namespace detail {
|
|
class MacroExpansionRangeRecorder : public PPCallbacks {
|
|
const Preprocessor &PP;
|
|
SourceManager &SM;
|
|
MacroExpansionContext::ExpansionRangeMap &ExpansionRanges;
|
|
|
|
public:
|
|
explicit MacroExpansionRangeRecorder(
|
|
const Preprocessor &PP, SourceManager &SM,
|
|
MacroExpansionContext::ExpansionRangeMap &ExpansionRanges)
|
|
: PP(PP), SM(SM), ExpansionRanges(ExpansionRanges) {}
|
|
|
|
void MacroExpands(const Token &MacroName, const MacroDefinition &MD,
|
|
SourceRange Range, const MacroArgs *Args) override {
|
|
// Ignore annotation tokens like: _Pragma("pack(push, 1)")
|
|
if (MacroName.getIdentifierInfo()->getName() == "_Pragma")
|
|
return;
|
|
|
|
SourceLocation MacroNameBegin = SM.getExpansionLoc(MacroName.getLocation());
|
|
assert(MacroNameBegin == SM.getExpansionLoc(Range.getBegin()));
|
|
|
|
const SourceLocation ExpansionEnd = [Range, &SM = SM, &MacroName] {
|
|
// If the range is empty, use the length of the macro.
|
|
if (Range.getBegin() == Range.getEnd())
|
|
return SM.getExpansionLoc(
|
|
MacroName.getLocation().getLocWithOffset(MacroName.getLength()));
|
|
|
|
// Include the last character.
|
|
return SM.getExpansionLoc(Range.getEnd()).getLocWithOffset(1);
|
|
}();
|
|
|
|
(void)PP;
|
|
LLVM_DEBUG(llvm::dbgs() << "MacroExpands event: '";
|
|
dumpTokenInto(PP, llvm::dbgs(), MacroName);
|
|
llvm::dbgs()
|
|
<< "' with length " << MacroName.getLength() << " at ";
|
|
MacroNameBegin.print(llvm::dbgs(), SM);
|
|
llvm::dbgs() << ", expansion end at ";
|
|
ExpansionEnd.print(llvm::dbgs(), SM); llvm::dbgs() << '\n';);
|
|
|
|
// If the expansion range is empty, use the identifier of the macro as a
|
|
// range.
|
|
MacroExpansionContext::ExpansionRangeMap::iterator It;
|
|
bool Inserted;
|
|
std::tie(It, Inserted) =
|
|
ExpansionRanges.try_emplace(MacroNameBegin, ExpansionEnd);
|
|
if (Inserted) {
|
|
LLVM_DEBUG(llvm::dbgs() << "maps ";
|
|
It->getFirst().print(llvm::dbgs(), SM); llvm::dbgs() << " to ";
|
|
It->getSecond().print(llvm::dbgs(), SM);
|
|
llvm::dbgs() << '\n';);
|
|
} else {
|
|
if (SM.isBeforeInTranslationUnit(It->getSecond(), ExpansionEnd)) {
|
|
It->getSecond() = ExpansionEnd;
|
|
LLVM_DEBUG(
|
|
llvm::dbgs() << "remaps "; It->getFirst().print(llvm::dbgs(), SM);
|
|
llvm::dbgs() << " to "; It->getSecond().print(llvm::dbgs(), SM);
|
|
llvm::dbgs() << '\n';);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
} // namespace detail
|
|
} // namespace clang
|
|
|
|
using namespace clang;
|
|
|
|
MacroExpansionContext::MacroExpansionContext(const LangOptions &LangOpts)
|
|
: LangOpts(LangOpts) {}
|
|
|
|
void MacroExpansionContext::registerForPreprocessor(Preprocessor &NewPP) {
|
|
PP = &NewPP;
|
|
SM = &NewPP.getSourceManager();
|
|
|
|
// Make sure that the Preprocessor does not outlive the MacroExpansionContext.
|
|
PP->addPPCallbacks(std::make_unique<detail::MacroExpansionRangeRecorder>(
|
|
*PP, *SM, ExpansionRanges));
|
|
// Same applies here.
|
|
PP->setTokenWatcher([this](const Token &Tok) { onTokenLexed(Tok); });
|
|
}
|
|
|
|
std::optional<StringRef>
|
|
MacroExpansionContext::getExpandedText(SourceLocation MacroExpansionLoc) const {
|
|
if (MacroExpansionLoc.isMacroID())
|
|
return std::nullopt;
|
|
|
|
// If there was no macro expansion at that location, return std::nullopt.
|
|
if (ExpansionRanges.find_as(MacroExpansionLoc) == ExpansionRanges.end())
|
|
return std::nullopt;
|
|
|
|
// There was macro expansion, but resulted in no tokens, return empty string.
|
|
const auto It = ExpandedTokens.find_as(MacroExpansionLoc);
|
|
if (It == ExpandedTokens.end())
|
|
return StringRef{""};
|
|
|
|
// Otherwise we have the actual token sequence as string.
|
|
return It->getSecond().str();
|
|
}
|
|
|
|
std::optional<StringRef>
|
|
MacroExpansionContext::getOriginalText(SourceLocation MacroExpansionLoc) const {
|
|
if (MacroExpansionLoc.isMacroID())
|
|
return std::nullopt;
|
|
|
|
const auto It = ExpansionRanges.find_as(MacroExpansionLoc);
|
|
if (It == ExpansionRanges.end())
|
|
return std::nullopt;
|
|
|
|
assert(It->getFirst() != It->getSecond() &&
|
|
"Every macro expansion must cover a non-empty range.");
|
|
|
|
return Lexer::getSourceText(
|
|
CharSourceRange::getCharRange(It->getFirst(), It->getSecond()), *SM,
|
|
LangOpts);
|
|
}
|
|
|
|
void MacroExpansionContext::dumpExpansionRanges() const {
|
|
dumpExpansionRangesToStream(llvm::dbgs());
|
|
}
|
|
void MacroExpansionContext::dumpExpandedTexts() const {
|
|
dumpExpandedTextsToStream(llvm::dbgs());
|
|
}
|
|
|
|
void MacroExpansionContext::dumpExpansionRangesToStream(raw_ostream &OS) const {
|
|
std::vector<std::pair<SourceLocation, SourceLocation>> LocalExpansionRanges;
|
|
LocalExpansionRanges.reserve(ExpansionRanges.size());
|
|
for (const auto &Record : ExpansionRanges)
|
|
LocalExpansionRanges.emplace_back(
|
|
std::make_pair(Record.getFirst(), Record.getSecond()));
|
|
llvm::sort(LocalExpansionRanges);
|
|
|
|
OS << "\n=============== ExpansionRanges ===============\n";
|
|
for (const auto &Record : LocalExpansionRanges) {
|
|
OS << "> ";
|
|
Record.first.print(OS, *SM);
|
|
OS << ", ";
|
|
Record.second.print(OS, *SM);
|
|
OS << '\n';
|
|
}
|
|
}
|
|
|
|
void MacroExpansionContext::dumpExpandedTextsToStream(raw_ostream &OS) const {
|
|
std::vector<std::pair<SourceLocation, MacroExpansionText>>
|
|
LocalExpandedTokens;
|
|
LocalExpandedTokens.reserve(ExpandedTokens.size());
|
|
for (const auto &Record : ExpandedTokens)
|
|
LocalExpandedTokens.emplace_back(
|
|
std::make_pair(Record.getFirst(), Record.getSecond()));
|
|
llvm::sort(LocalExpandedTokens);
|
|
|
|
OS << "\n=============== ExpandedTokens ===============\n";
|
|
for (const auto &Record : LocalExpandedTokens) {
|
|
OS << "> ";
|
|
Record.first.print(OS, *SM);
|
|
OS << " -> '" << Record.second << "'\n";
|
|
}
|
|
}
|
|
|
|
static void dumpTokenInto(const Preprocessor &PP, raw_ostream &OS, Token Tok) {
|
|
assert(Tok.isNot(tok::raw_identifier));
|
|
|
|
// Ignore annotation tokens like: _Pragma("pack(push, 1)")
|
|
if (Tok.isAnnotation())
|
|
return;
|
|
|
|
if (IdentifierInfo *II = Tok.getIdentifierInfo()) {
|
|
// FIXME: For now, we don't respect whitespaces between macro expanded
|
|
// tokens. We just emit a space after every identifier to produce a valid
|
|
// code for `int a ;` like expansions.
|
|
// ^-^-- Space after the 'int' and 'a' identifiers.
|
|
OS << II->getName() << ' ';
|
|
} else if (Tok.isLiteral() && !Tok.needsCleaning() && Tok.getLiteralData()) {
|
|
OS << StringRef(Tok.getLiteralData(), Tok.getLength());
|
|
} else {
|
|
char Tmp[256];
|
|
if (Tok.getLength() < sizeof(Tmp)) {
|
|
const char *TokPtr = Tmp;
|
|
// FIXME: Might use a different overload for cleaner callsite.
|
|
unsigned Len = PP.getSpelling(Tok, TokPtr);
|
|
OS.write(TokPtr, Len);
|
|
} else {
|
|
OS << "<too long token>";
|
|
}
|
|
}
|
|
}
|
|
|
|
void MacroExpansionContext::onTokenLexed(const Token &Tok) {
|
|
SourceLocation SLoc = Tok.getLocation();
|
|
if (SLoc.isFileID())
|
|
return;
|
|
|
|
LLVM_DEBUG(llvm::dbgs() << "lexed macro expansion token '";
|
|
dumpTokenInto(*PP, llvm::dbgs(), Tok); llvm::dbgs() << "' at ";
|
|
SLoc.print(llvm::dbgs(), *SM); llvm::dbgs() << '\n';);
|
|
|
|
// Remove spelling location.
|
|
SourceLocation CurrExpansionLoc = SM->getExpansionLoc(SLoc);
|
|
|
|
MacroExpansionText TokenAsString;
|
|
llvm::raw_svector_ostream OS(TokenAsString);
|
|
|
|
// FIXME: Prepend newlines and space to produce the exact same output as the
|
|
// preprocessor would for this token.
|
|
|
|
dumpTokenInto(*PP, OS, Tok);
|
|
|
|
ExpansionMap::iterator It;
|
|
bool Inserted;
|
|
std::tie(It, Inserted) =
|
|
ExpandedTokens.try_emplace(CurrExpansionLoc, std::move(TokenAsString));
|
|
if (!Inserted)
|
|
It->getSecond().append(TokenAsString);
|
|
}
|
|
|