Home | History | Annotate | Line # | Download | only in Analysis
      1  1.1  joerg //===- MacroExpansionContext.cpp - Macro expansion information --*- C++ -*-===//
      2  1.1  joerg //
      3  1.1  joerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
      4  1.1  joerg // See https://llvm.org/LICENSE.txt for license information.
      5  1.1  joerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
      6  1.1  joerg //
      7  1.1  joerg //===----------------------------------------------------------------------===//
      8  1.1  joerg 
      9  1.1  joerg #include "clang/Analysis/MacroExpansionContext.h"
     10  1.1  joerg #include "llvm/Support/Debug.h"
     11  1.1  joerg 
     12  1.1  joerg #define DEBUG_TYPE "macro-expansion-context"
     13  1.1  joerg 
     14  1.1  joerg static void dumpTokenInto(const clang::Preprocessor &PP, clang::raw_ostream &OS,
     15  1.1  joerg                           clang::Token Tok);
     16  1.1  joerg 
     17  1.1  joerg namespace clang {
     18  1.1  joerg namespace detail {
     19  1.1  joerg class MacroExpansionRangeRecorder : public PPCallbacks {
     20  1.1  joerg   const Preprocessor &PP;
     21  1.1  joerg   SourceManager &SM;
     22  1.1  joerg   MacroExpansionContext::ExpansionRangeMap &ExpansionRanges;
     23  1.1  joerg 
     24  1.1  joerg public:
     25  1.1  joerg   explicit MacroExpansionRangeRecorder(
     26  1.1  joerg       const Preprocessor &PP, SourceManager &SM,
     27  1.1  joerg       MacroExpansionContext::ExpansionRangeMap &ExpansionRanges)
     28  1.1  joerg       : PP(PP), SM(SM), ExpansionRanges(ExpansionRanges) {}
     29  1.1  joerg 
     30  1.1  joerg   void MacroExpands(const Token &MacroName, const MacroDefinition &MD,
     31  1.1  joerg                     SourceRange Range, const MacroArgs *Args) override {
     32  1.1  joerg     // Ignore annotation tokens like: _Pragma("pack(push, 1)")
     33  1.1  joerg     if (MacroName.getIdentifierInfo()->getName() == "_Pragma")
     34  1.1  joerg       return;
     35  1.1  joerg 
     36  1.1  joerg     SourceLocation MacroNameBegin = SM.getExpansionLoc(MacroName.getLocation());
     37  1.1  joerg     assert(MacroNameBegin == SM.getExpansionLoc(Range.getBegin()));
     38  1.1  joerg 
     39  1.1  joerg     const SourceLocation ExpansionEnd = [Range, &SM = SM, &MacroName] {
     40  1.1  joerg       // If the range is empty, use the length of the macro.
     41  1.1  joerg       if (Range.getBegin() == Range.getEnd())
     42  1.1  joerg         return SM.getExpansionLoc(
     43  1.1  joerg             MacroName.getLocation().getLocWithOffset(MacroName.getLength()));
     44  1.1  joerg 
     45  1.1  joerg       // Include the last character.
     46  1.1  joerg       return SM.getExpansionLoc(Range.getEnd()).getLocWithOffset(1);
     47  1.1  joerg     }();
     48  1.1  joerg 
     49  1.1  joerg     (void)PP;
     50  1.1  joerg     LLVM_DEBUG(llvm::dbgs() << "MacroExpands event: '";
     51  1.1  joerg                dumpTokenInto(PP, llvm::dbgs(), MacroName);
     52  1.1  joerg                llvm::dbgs()
     53  1.1  joerg                << "' with length " << MacroName.getLength() << " at ";
     54  1.1  joerg                MacroNameBegin.print(llvm::dbgs(), SM);
     55  1.1  joerg                llvm::dbgs() << ", expansion end at ";
     56  1.1  joerg                ExpansionEnd.print(llvm::dbgs(), SM); llvm::dbgs() << '\n';);
     57  1.1  joerg 
     58  1.1  joerg     // If the expansion range is empty, use the identifier of the macro as a
     59  1.1  joerg     // range.
     60  1.1  joerg     MacroExpansionContext::ExpansionRangeMap::iterator It;
     61  1.1  joerg     bool Inserted;
     62  1.1  joerg     std::tie(It, Inserted) =
     63  1.1  joerg         ExpansionRanges.try_emplace(MacroNameBegin, ExpansionEnd);
     64  1.1  joerg     if (Inserted) {
     65  1.1  joerg       LLVM_DEBUG(llvm::dbgs() << "maps ";
     66  1.1  joerg                  It->getFirst().print(llvm::dbgs(), SM); llvm::dbgs() << " to ";
     67  1.1  joerg                  It->getSecond().print(llvm::dbgs(), SM);
     68  1.1  joerg                  llvm::dbgs() << '\n';);
     69  1.1  joerg     } else {
     70  1.1  joerg       if (SM.isBeforeInTranslationUnit(It->getSecond(), ExpansionEnd)) {
     71  1.1  joerg         It->getSecond() = ExpansionEnd;
     72  1.1  joerg         LLVM_DEBUG(
     73  1.1  joerg             llvm::dbgs() << "remaps "; It->getFirst().print(llvm::dbgs(), SM);
     74  1.1  joerg             llvm::dbgs() << " to "; It->getSecond().print(llvm::dbgs(), SM);
     75  1.1  joerg             llvm::dbgs() << '\n';);
     76  1.1  joerg       }
     77  1.1  joerg     }
     78  1.1  joerg   }
     79  1.1  joerg };
     80  1.1  joerg } // namespace detail
     81  1.1  joerg } // namespace clang
     82  1.1  joerg 
     83  1.1  joerg using namespace clang;
     84  1.1  joerg 
     85  1.1  joerg MacroExpansionContext::MacroExpansionContext(const LangOptions &LangOpts)
     86  1.1  joerg     : LangOpts(LangOpts) {}
     87  1.1  joerg 
     88  1.1  joerg void MacroExpansionContext::registerForPreprocessor(Preprocessor &NewPP) {
     89  1.1  joerg   PP = &NewPP;
     90  1.1  joerg   SM = &NewPP.getSourceManager();
     91  1.1  joerg 
     92  1.1  joerg   // Make sure that the Preprocessor does not outlive the MacroExpansionContext.
     93  1.1  joerg   PP->addPPCallbacks(std::make_unique<detail::MacroExpansionRangeRecorder>(
     94  1.1  joerg       *PP, *SM, ExpansionRanges));
     95  1.1  joerg   // Same applies here.
     96  1.1  joerg   PP->setTokenWatcher([this](const Token &Tok) { onTokenLexed(Tok); });
     97  1.1  joerg }
     98  1.1  joerg 
     99  1.1  joerg Optional<StringRef>
    100  1.1  joerg MacroExpansionContext::getExpandedText(SourceLocation MacroExpansionLoc) const {
    101  1.1  joerg   if (MacroExpansionLoc.isMacroID())
    102  1.1  joerg     return llvm::None;
    103  1.1  joerg 
    104  1.1  joerg   // If there was no macro expansion at that location, return None.
    105  1.1  joerg   if (ExpansionRanges.find_as(MacroExpansionLoc) == ExpansionRanges.end())
    106  1.1  joerg     return llvm::None;
    107  1.1  joerg 
    108  1.1  joerg   // There was macro expansion, but resulted in no tokens, return empty string.
    109  1.1  joerg   const auto It = ExpandedTokens.find_as(MacroExpansionLoc);
    110  1.1  joerg   if (It == ExpandedTokens.end())
    111  1.1  joerg     return StringRef{""};
    112  1.1  joerg 
    113  1.1  joerg   // Otherwise we have the actual token sequence as string.
    114  1.1  joerg   return StringRef{It->getSecond()};
    115  1.1  joerg }
    116  1.1  joerg 
    117  1.1  joerg Optional<StringRef>
    118  1.1  joerg MacroExpansionContext::getOriginalText(SourceLocation MacroExpansionLoc) const {
    119  1.1  joerg   if (MacroExpansionLoc.isMacroID())
    120  1.1  joerg     return llvm::None;
    121  1.1  joerg 
    122  1.1  joerg   const auto It = ExpansionRanges.find_as(MacroExpansionLoc);
    123  1.1  joerg   if (It == ExpansionRanges.end())
    124  1.1  joerg     return llvm::None;
    125  1.1  joerg 
    126  1.1  joerg   assert(It->getFirst() != It->getSecond() &&
    127  1.1  joerg          "Every macro expansion must cover a non-empty range.");
    128  1.1  joerg 
    129  1.1  joerg   return Lexer::getSourceText(
    130  1.1  joerg       CharSourceRange::getCharRange(It->getFirst(), It->getSecond()), *SM,
    131  1.1  joerg       LangOpts);
    132  1.1  joerg }
    133  1.1  joerg 
    134  1.1  joerg void MacroExpansionContext::dumpExpansionRanges() const {
    135  1.1  joerg   dumpExpansionRangesToStream(llvm::dbgs());
    136  1.1  joerg }
    137  1.1  joerg void MacroExpansionContext::dumpExpandedTexts() const {
    138  1.1  joerg   dumpExpandedTextsToStream(llvm::dbgs());
    139  1.1  joerg }
    140  1.1  joerg 
    141  1.1  joerg void MacroExpansionContext::dumpExpansionRangesToStream(raw_ostream &OS) const {
    142  1.1  joerg   std::vector<std::pair<SourceLocation, SourceLocation>> LocalExpansionRanges;
    143  1.1  joerg   LocalExpansionRanges.reserve(ExpansionRanges.size());
    144  1.1  joerg   for (const auto &Record : ExpansionRanges)
    145  1.1  joerg     LocalExpansionRanges.emplace_back(
    146  1.1  joerg         std::make_pair(Record.getFirst(), Record.getSecond()));
    147  1.1  joerg   llvm::sort(LocalExpansionRanges);
    148  1.1  joerg 
    149  1.1  joerg   OS << "\n=============== ExpansionRanges ===============\n";
    150  1.1  joerg   for (const auto &Record : LocalExpansionRanges) {
    151  1.1  joerg     OS << "> ";
    152  1.1  joerg     Record.first.print(OS, *SM);
    153  1.1  joerg     OS << ", ";
    154  1.1  joerg     Record.second.print(OS, *SM);
    155  1.1  joerg     OS << '\n';
    156  1.1  joerg   }
    157  1.1  joerg }
    158  1.1  joerg 
    159  1.1  joerg void MacroExpansionContext::dumpExpandedTextsToStream(raw_ostream &OS) const {
    160  1.1  joerg   std::vector<std::pair<SourceLocation, MacroExpansionText>>
    161  1.1  joerg       LocalExpandedTokens;
    162  1.1  joerg   LocalExpandedTokens.reserve(ExpandedTokens.size());
    163  1.1  joerg   for (const auto &Record : ExpandedTokens)
    164  1.1  joerg     LocalExpandedTokens.emplace_back(
    165  1.1  joerg         std::make_pair(Record.getFirst(), Record.getSecond()));
    166  1.1  joerg   llvm::sort(LocalExpandedTokens);
    167  1.1  joerg 
    168  1.1  joerg   OS << "\n=============== ExpandedTokens ===============\n";
    169  1.1  joerg   for (const auto &Record : LocalExpandedTokens) {
    170  1.1  joerg     OS << "> ";
    171  1.1  joerg     Record.first.print(OS, *SM);
    172  1.1  joerg     OS << " -> '" << Record.second << "'\n";
    173  1.1  joerg   }
    174  1.1  joerg }
    175  1.1  joerg 
    176  1.1  joerg static void dumpTokenInto(const Preprocessor &PP, raw_ostream &OS, Token Tok) {
    177  1.1  joerg   assert(Tok.isNot(tok::raw_identifier));
    178  1.1  joerg 
    179  1.1  joerg   // Ignore annotation tokens like: _Pragma("pack(push, 1)")
    180  1.1  joerg   if (Tok.isAnnotation())
    181  1.1  joerg     return;
    182  1.1  joerg 
    183  1.1  joerg   if (IdentifierInfo *II = Tok.getIdentifierInfo()) {
    184  1.1  joerg     // FIXME: For now, we don't respect whitespaces between macro expanded
    185  1.1  joerg     // tokens. We just emit a space after every identifier to produce a valid
    186  1.1  joerg     // code for `int a ;` like expansions.
    187  1.1  joerg     //              ^-^-- Space after the 'int' and 'a' identifiers.
    188  1.1  joerg     OS << II->getName() << ' ';
    189  1.1  joerg   } else if (Tok.isLiteral() && !Tok.needsCleaning() && Tok.getLiteralData()) {
    190  1.1  joerg     OS << StringRef(Tok.getLiteralData(), Tok.getLength());
    191  1.1  joerg   } else {
    192  1.1  joerg     char Tmp[256];
    193  1.1  joerg     if (Tok.getLength() < sizeof(Tmp)) {
    194  1.1  joerg       const char *TokPtr = Tmp;
    195  1.1  joerg       // FIXME: Might use a different overload for cleaner callsite.
    196  1.1  joerg       unsigned Len = PP.getSpelling(Tok, TokPtr);
    197  1.1  joerg       OS.write(TokPtr, Len);
    198  1.1  joerg     } else {
    199  1.1  joerg       OS << "<too long token>";
    200  1.1  joerg     }
    201  1.1  joerg   }
    202  1.1  joerg }
    203  1.1  joerg 
    204  1.1  joerg void MacroExpansionContext::onTokenLexed(const Token &Tok) {
    205  1.1  joerg   SourceLocation SLoc = Tok.getLocation();
    206  1.1  joerg   if (SLoc.isFileID())
    207  1.1  joerg     return;
    208  1.1  joerg 
    209  1.1  joerg   LLVM_DEBUG(llvm::dbgs() << "lexed macro expansion token '";
    210  1.1  joerg              dumpTokenInto(*PP, llvm::dbgs(), Tok); llvm::dbgs() << "' at ";
    211  1.1  joerg              SLoc.print(llvm::dbgs(), *SM); llvm::dbgs() << '\n';);
    212  1.1  joerg 
    213  1.1  joerg   // Remove spelling location.
    214  1.1  joerg   SourceLocation CurrExpansionLoc = SM->getExpansionLoc(SLoc);
    215  1.1  joerg 
    216  1.1  joerg   MacroExpansionText TokenAsString;
    217  1.1  joerg   llvm::raw_svector_ostream OS(TokenAsString);
    218  1.1  joerg 
    219  1.1  joerg   // FIXME: Prepend newlines and space to produce the exact same output as the
    220  1.1  joerg   // preprocessor would for this token.
    221  1.1  joerg 
    222  1.1  joerg   dumpTokenInto(*PP, OS, Tok);
    223  1.1  joerg 
    224  1.1  joerg   ExpansionMap::iterator It;
    225  1.1  joerg   bool Inserted;
    226  1.1  joerg   std::tie(It, Inserted) =
    227  1.1  joerg       ExpandedTokens.try_emplace(CurrExpansionLoc, std::move(TokenAsString));
    228  1.1  joerg   if (!Inserted)
    229  1.1  joerg     It->getSecond().append(TokenAsString);
    230  1.1  joerg }
    231  1.1  joerg 
    232