1 //===- MacroExpansionContext.cpp - Macro expansion information --*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 #include "clang/Analysis/MacroExpansionContext.h"
10 #include "llvm/Support/Debug.h"
13 #define DEBUG_TYPE "macro-expansion-context"
15 static void dumpTokenInto(const clang::Preprocessor
&PP
, clang::raw_ostream
&OS
,
20 class MacroExpansionRangeRecorder
: public PPCallbacks
{
21 const Preprocessor
&PP
;
23 MacroExpansionContext::ExpansionRangeMap
&ExpansionRanges
;
26 explicit MacroExpansionRangeRecorder(
27 const Preprocessor
&PP
, SourceManager
&SM
,
28 MacroExpansionContext::ExpansionRangeMap
&ExpansionRanges
)
29 : PP(PP
), SM(SM
), ExpansionRanges(ExpansionRanges
) {}
31 void MacroExpands(const Token
&MacroName
, const MacroDefinition
&MD
,
32 SourceRange Range
, const MacroArgs
*Args
) override
{
33 // Ignore annotation tokens like: _Pragma("pack(push, 1)")
34 if (MacroName
.getIdentifierInfo()->getName() == "_Pragma")
37 SourceLocation MacroNameBegin
= SM
.getExpansionLoc(MacroName
.getLocation());
38 assert(MacroNameBegin
== SM
.getExpansionLoc(Range
.getBegin()));
40 const SourceLocation ExpansionEnd
= [Range
, &SM
= SM
, &MacroName
] {
41 // If the range is empty, use the length of the macro.
42 if (Range
.getBegin() == Range
.getEnd())
43 return SM
.getExpansionLoc(
44 MacroName
.getLocation().getLocWithOffset(MacroName
.getLength()));
46 // Include the last character.
47 return SM
.getExpansionLoc(Range
.getEnd()).getLocWithOffset(1);
51 LLVM_DEBUG(llvm::dbgs() << "MacroExpands event: '";
52 dumpTokenInto(PP
, llvm::dbgs(), MacroName
);
54 << "' with length " << MacroName
.getLength() << " at ";
55 MacroNameBegin
.print(llvm::dbgs(), SM
);
56 llvm::dbgs() << ", expansion end at ";
57 ExpansionEnd
.print(llvm::dbgs(), SM
); llvm::dbgs() << '\n';);
59 // If the expansion range is empty, use the identifier of the macro as a
61 MacroExpansionContext::ExpansionRangeMap::iterator It
;
63 std::tie(It
, Inserted
) =
64 ExpansionRanges
.try_emplace(MacroNameBegin
, ExpansionEnd
);
66 LLVM_DEBUG(llvm::dbgs() << "maps ";
67 It
->getFirst().print(llvm::dbgs(), SM
); llvm::dbgs() << " to ";
68 It
->getSecond().print(llvm::dbgs(), SM
);
69 llvm::dbgs() << '\n';);
71 if (SM
.isBeforeInTranslationUnit(It
->getSecond(), ExpansionEnd
)) {
72 It
->getSecond() = ExpansionEnd
;
74 llvm::dbgs() << "remaps "; It
->getFirst().print(llvm::dbgs(), SM
);
75 llvm::dbgs() << " to "; It
->getSecond().print(llvm::dbgs(), SM
);
76 llvm::dbgs() << '\n';);
84 using namespace clang
;
86 MacroExpansionContext::MacroExpansionContext(const LangOptions
&LangOpts
)
87 : LangOpts(LangOpts
) {}
89 void MacroExpansionContext::registerForPreprocessor(Preprocessor
&NewPP
) {
91 SM
= &NewPP
.getSourceManager();
93 // Make sure that the Preprocessor does not outlive the MacroExpansionContext.
94 PP
->addPPCallbacks(std::make_unique
<detail::MacroExpansionRangeRecorder
>(
95 *PP
, *SM
, ExpansionRanges
));
97 PP
->setTokenWatcher([this](const Token
&Tok
) { onTokenLexed(Tok
); });
100 std::optional
<StringRef
>
101 MacroExpansionContext::getExpandedText(SourceLocation MacroExpansionLoc
) const {
102 if (MacroExpansionLoc
.isMacroID())
105 // If there was no macro expansion at that location, return std::nullopt.
106 if (ExpansionRanges
.find_as(MacroExpansionLoc
) == ExpansionRanges
.end())
109 // There was macro expansion, but resulted in no tokens, return empty string.
110 const auto It
= ExpandedTokens
.find_as(MacroExpansionLoc
);
111 if (It
== ExpandedTokens
.end())
112 return StringRef
{""};
114 // Otherwise we have the actual token sequence as string.
115 return It
->getSecond().str();
118 std::optional
<StringRef
>
119 MacroExpansionContext::getOriginalText(SourceLocation MacroExpansionLoc
) const {
120 if (MacroExpansionLoc
.isMacroID())
123 const auto It
= ExpansionRanges
.find_as(MacroExpansionLoc
);
124 if (It
== ExpansionRanges
.end())
127 assert(It
->getFirst() != It
->getSecond() &&
128 "Every macro expansion must cover a non-empty range.");
130 return Lexer::getSourceText(
131 CharSourceRange::getCharRange(It
->getFirst(), It
->getSecond()), *SM
,
135 void MacroExpansionContext::dumpExpansionRanges() const {
136 dumpExpansionRangesToStream(llvm::dbgs());
138 void MacroExpansionContext::dumpExpandedTexts() const {
139 dumpExpandedTextsToStream(llvm::dbgs());
142 void MacroExpansionContext::dumpExpansionRangesToStream(raw_ostream
&OS
) const {
143 std::vector
<std::pair
<SourceLocation
, SourceLocation
>> LocalExpansionRanges
;
144 LocalExpansionRanges
.reserve(ExpansionRanges
.size());
145 for (const auto &Record
: ExpansionRanges
)
146 LocalExpansionRanges
.emplace_back(
147 std::make_pair(Record
.getFirst(), Record
.getSecond()));
148 llvm::sort(LocalExpansionRanges
);
150 OS
<< "\n=============== ExpansionRanges ===============\n";
151 for (const auto &Record
: LocalExpansionRanges
) {
153 Record
.first
.print(OS
, *SM
);
155 Record
.second
.print(OS
, *SM
);
160 void MacroExpansionContext::dumpExpandedTextsToStream(raw_ostream
&OS
) const {
161 std::vector
<std::pair
<SourceLocation
, MacroExpansionText
>>
163 LocalExpandedTokens
.reserve(ExpandedTokens
.size());
164 for (const auto &Record
: ExpandedTokens
)
165 LocalExpandedTokens
.emplace_back(
166 std::make_pair(Record
.getFirst(), Record
.getSecond()));
167 llvm::sort(LocalExpandedTokens
);
169 OS
<< "\n=============== ExpandedTokens ===============\n";
170 for (const auto &Record
: LocalExpandedTokens
) {
172 Record
.first
.print(OS
, *SM
);
173 OS
<< " -> '" << Record
.second
<< "'\n";
177 static void dumpTokenInto(const Preprocessor
&PP
, raw_ostream
&OS
, Token Tok
) {
178 assert(Tok
.isNot(tok::raw_identifier
));
180 // Ignore annotation tokens like: _Pragma("pack(push, 1)")
181 if (Tok
.isAnnotation())
184 if (IdentifierInfo
*II
= Tok
.getIdentifierInfo()) {
185 // FIXME: For now, we don't respect whitespaces between macro expanded
186 // tokens. We just emit a space after every identifier to produce a valid
187 // code for `int a ;` like expansions.
188 // ^-^-- Space after the 'int' and 'a' identifiers.
189 OS
<< II
->getName() << ' ';
190 } else if (Tok
.isLiteral() && !Tok
.needsCleaning() && Tok
.getLiteralData()) {
191 OS
<< StringRef(Tok
.getLiteralData(), Tok
.getLength());
194 if (Tok
.getLength() < sizeof(Tmp
)) {
195 const char *TokPtr
= Tmp
;
196 // FIXME: Might use a different overload for cleaner callsite.
197 unsigned Len
= PP
.getSpelling(Tok
, TokPtr
);
198 OS
.write(TokPtr
, Len
);
200 OS
<< "<too long token>";
205 void MacroExpansionContext::onTokenLexed(const Token
&Tok
) {
206 SourceLocation SLoc
= Tok
.getLocation();
210 LLVM_DEBUG(llvm::dbgs() << "lexed macro expansion token '";
211 dumpTokenInto(*PP
, llvm::dbgs(), Tok
); llvm::dbgs() << "' at ";
212 SLoc
.print(llvm::dbgs(), *SM
); llvm::dbgs() << '\n';);
214 // Remove spelling location.
215 SourceLocation CurrExpansionLoc
= SM
->getExpansionLoc(SLoc
);
217 MacroExpansionText TokenAsString
;
218 llvm::raw_svector_ostream
OS(TokenAsString
);
220 // FIXME: Prepend newlines and space to produce the exact same output as the
221 // preprocessor would for this token.
223 dumpTokenInto(*PP
, OS
, Tok
);
225 ExpansionMap::iterator It
;
227 std::tie(It
, Inserted
) =
228 ExpandedTokens
.try_emplace(CurrExpansionLoc
, std::move(TokenAsString
));
230 It
->getSecond().append(TokenAsString
);