Path: blob/main/contrib/llvm-project/clang/lib/Analysis/MacroExpansionContext.cpp
35233 views
//===- MacroExpansionContext.cpp - Macro expansion information --*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "clang/Analysis/MacroExpansionContext.h"9#include "llvm/Support/Debug.h"10#include <optional>1112#define DEBUG_TYPE "macro-expansion-context"1314static void dumpTokenInto(const clang::Preprocessor &PP, llvm::raw_ostream &OS,15clang::Token Tok);1617namespace clang {18namespace detail {19class MacroExpansionRangeRecorder : public PPCallbacks {20const Preprocessor &PP;21SourceManager &SM;22MacroExpansionContext::ExpansionRangeMap &ExpansionRanges;2324public:25explicit MacroExpansionRangeRecorder(26const Preprocessor &PP, SourceManager &SM,27MacroExpansionContext::ExpansionRangeMap &ExpansionRanges)28: PP(PP), SM(SM), ExpansionRanges(ExpansionRanges) {}2930void MacroExpands(const Token &MacroName, const MacroDefinition &MD,31SourceRange Range, const MacroArgs *Args) override {32// Ignore annotation tokens like: _Pragma("pack(push, 1)")33if (MacroName.getIdentifierInfo()->getName() == "_Pragma")34return;3536SourceLocation MacroNameBegin = SM.getExpansionLoc(MacroName.getLocation());37assert(MacroNameBegin == SM.getExpansionLoc(Range.getBegin()));3839const SourceLocation ExpansionEnd = [Range, &SM = SM, &MacroName] {40// If the range is empty, use the length of the macro.41if (Range.getBegin() == Range.getEnd())42return SM.getExpansionLoc(43MacroName.getLocation().getLocWithOffset(MacroName.getLength()));4445// Include the last character.46return SM.getExpansionLoc(Range.getEnd()).getLocWithOffset(1);47}();4849(void)PP;50LLVM_DEBUG(llvm::dbgs() << "MacroExpands event: '";51dumpTokenInto(PP, llvm::dbgs(), MacroName);52llvm::dbgs()53<< "' with length " << MacroName.getLength() << " at ";54MacroNameBegin.print(llvm::dbgs(), SM);55llvm::dbgs() << ", expansion end at ";56ExpansionEnd.print(llvm::dbgs(), SM); llvm::dbgs() << '\n';);5758// If the expansion range is empty, use the identifier of the macro as a59// range.60MacroExpansionContext::ExpansionRangeMap::iterator It;61bool Inserted;62std::tie(It, Inserted) =63ExpansionRanges.try_emplace(MacroNameBegin, ExpansionEnd);64if (Inserted) {65LLVM_DEBUG(llvm::dbgs() << "maps ";66It->getFirst().print(llvm::dbgs(), SM); llvm::dbgs() << " to ";67It->getSecond().print(llvm::dbgs(), SM);68llvm::dbgs() << '\n';);69} else {70if (SM.isBeforeInTranslationUnit(It->getSecond(), ExpansionEnd)) {71It->getSecond() = ExpansionEnd;72LLVM_DEBUG(73llvm::dbgs() << "remaps "; It->getFirst().print(llvm::dbgs(), SM);74llvm::dbgs() << " to "; It->getSecond().print(llvm::dbgs(), SM);75llvm::dbgs() << '\n';);76}77}78}79};80} // namespace detail81} // namespace clang8283using namespace clang;8485MacroExpansionContext::MacroExpansionContext(const LangOptions &LangOpts)86: LangOpts(LangOpts) {}8788void MacroExpansionContext::registerForPreprocessor(Preprocessor &NewPP) {89PP = &NewPP;90SM = &NewPP.getSourceManager();9192// Make sure that the Preprocessor does not outlive the MacroExpansionContext.93PP->addPPCallbacks(std::make_unique<detail::MacroExpansionRangeRecorder>(94*PP, *SM, ExpansionRanges));95// Same applies here.96PP->setTokenWatcher([this](const Token &Tok) { onTokenLexed(Tok); });97}9899std::optional<StringRef>100MacroExpansionContext::getExpandedText(SourceLocation MacroExpansionLoc) const {101if (MacroExpansionLoc.isMacroID())102return std::nullopt;103104// If there was no macro expansion at that location, return std::nullopt.105if (ExpansionRanges.find_as(MacroExpansionLoc) == ExpansionRanges.end())106return std::nullopt;107108// There was macro expansion, but resulted in no tokens, return empty string.109const auto It = ExpandedTokens.find_as(MacroExpansionLoc);110if (It == ExpandedTokens.end())111return StringRef{""};112113// Otherwise we have the actual token sequence as string.114return It->getSecond().str();115}116117std::optional<StringRef>118MacroExpansionContext::getOriginalText(SourceLocation MacroExpansionLoc) const {119if (MacroExpansionLoc.isMacroID())120return std::nullopt;121122const auto It = ExpansionRanges.find_as(MacroExpansionLoc);123if (It == ExpansionRanges.end())124return std::nullopt;125126assert(It->getFirst() != It->getSecond() &&127"Every macro expansion must cover a non-empty range.");128129return Lexer::getSourceText(130CharSourceRange::getCharRange(It->getFirst(), It->getSecond()), *SM,131LangOpts);132}133134void MacroExpansionContext::dumpExpansionRanges() const {135dumpExpansionRangesToStream(llvm::dbgs());136}137void MacroExpansionContext::dumpExpandedTexts() const {138dumpExpandedTextsToStream(llvm::dbgs());139}140141void MacroExpansionContext::dumpExpansionRangesToStream(raw_ostream &OS) const {142std::vector<std::pair<SourceLocation, SourceLocation>> LocalExpansionRanges;143LocalExpansionRanges.reserve(ExpansionRanges.size());144for (const auto &Record : ExpansionRanges)145LocalExpansionRanges.emplace_back(146std::make_pair(Record.getFirst(), Record.getSecond()));147llvm::sort(LocalExpansionRanges);148149OS << "\n=============== ExpansionRanges ===============\n";150for (const auto &Record : LocalExpansionRanges) {151OS << "> ";152Record.first.print(OS, *SM);153OS << ", ";154Record.second.print(OS, *SM);155OS << '\n';156}157}158159void MacroExpansionContext::dumpExpandedTextsToStream(raw_ostream &OS) const {160std::vector<std::pair<SourceLocation, MacroExpansionText>>161LocalExpandedTokens;162LocalExpandedTokens.reserve(ExpandedTokens.size());163for (const auto &Record : ExpandedTokens)164LocalExpandedTokens.emplace_back(165std::make_pair(Record.getFirst(), Record.getSecond()));166llvm::sort(LocalExpandedTokens);167168OS << "\n=============== ExpandedTokens ===============\n";169for (const auto &Record : LocalExpandedTokens) {170OS << "> ";171Record.first.print(OS, *SM);172OS << " -> '" << Record.second << "'\n";173}174}175176static void dumpTokenInto(const Preprocessor &PP, raw_ostream &OS, Token Tok) {177assert(Tok.isNot(tok::raw_identifier));178179// Ignore annotation tokens like: _Pragma("pack(push, 1)")180if (Tok.isAnnotation())181return;182183if (IdentifierInfo *II = Tok.getIdentifierInfo()) {184// FIXME: For now, we don't respect whitespaces between macro expanded185// tokens. We just emit a space after every identifier to produce a valid186// code for `int a ;` like expansions.187// ^-^-- Space after the 'int' and 'a' identifiers.188OS << II->getName() << ' ';189} else if (Tok.isLiteral() && !Tok.needsCleaning() && Tok.getLiteralData()) {190OS << StringRef(Tok.getLiteralData(), Tok.getLength());191} else {192char Tmp[256];193if (Tok.getLength() < sizeof(Tmp)) {194const char *TokPtr = Tmp;195// FIXME: Might use a different overload for cleaner callsite.196unsigned Len = PP.getSpelling(Tok, TokPtr);197OS.write(TokPtr, Len);198} else {199OS << "<too long token>";200}201}202}203204void MacroExpansionContext::onTokenLexed(const Token &Tok) {205SourceLocation SLoc = Tok.getLocation();206if (SLoc.isFileID())207return;208209LLVM_DEBUG(llvm::dbgs() << "lexed macro expansion token '";210dumpTokenInto(*PP, llvm::dbgs(), Tok); llvm::dbgs() << "' at ";211SLoc.print(llvm::dbgs(), *SM); llvm::dbgs() << '\n';);212213// Remove spelling location.214SourceLocation CurrExpansionLoc = SM->getExpansionLoc(SLoc);215216MacroExpansionText TokenAsString;217llvm::raw_svector_ostream OS(TokenAsString);218219// FIXME: Prepend newlines and space to produce the exact same output as the220// preprocessor would for this token.221222dumpTokenInto(*PP, OS, Tok);223224ExpansionMap::iterator It;225bool Inserted;226std::tie(It, Inserted) =227ExpandedTokens.try_emplace(CurrExpansionLoc, std::move(TokenAsString));228if (!Inserted)229It->getSecond().append(TokenAsString);230}231232233234