123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231 |
- //===- 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"
- #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); });
- }
- Optional<StringRef>
- MacroExpansionContext::getExpandedText(SourceLocation MacroExpansionLoc) const {
- if (MacroExpansionLoc.isMacroID())
- return llvm::None;
- // If there was no macro expansion at that location, return None.
- if (ExpansionRanges.find_as(MacroExpansionLoc) == ExpansionRanges.end())
- return llvm::None;
- // 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();
- }
- Optional<StringRef>
- MacroExpansionContext::getOriginalText(SourceLocation MacroExpansionLoc) const {
- if (MacroExpansionLoc.isMacroID())
- return llvm::None;
- const auto It = ExpansionRanges.find_as(MacroExpansionLoc);
- if (It == ExpansionRanges.end())
- return llvm::None;
- 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);
- }
|