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