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