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