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