xref: /netbsd-src/external/apache2/llvm/dist/clang/lib/Analysis/MacroExpansionContext.cpp (revision e038c9c4676b0f19b1b7dd08a940c6ed64a6d5ae)
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