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