Coverage Report

Created: 2021-09-21 08:58

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/Analysis/MacroExpansionContext.cpp
Line
Count
Source (jump to first uncovered line)
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
14
      : PP(PP), SM(SM), ExpansionRanges(ExpansionRanges) {}
29
30
  void MacroExpands(const Token &MacroName, const MacroDefinition &MD,
31
140
                    SourceRange Range, const MacroArgs *Args) override {
32
    // Ignore annotation tokens like: _Pragma("pack(push, 1)")
33
140
    if (MacroName.getIdentifierInfo()->getName() == "_Pragma")
34
2
      return;
35
36
138
    SourceLocation MacroNameBegin = SM.getExpansionLoc(MacroName.getLocation());
37
138
    assert(MacroNameBegin == SM.getExpansionLoc(Range.getBegin()));
38
39
138
    const SourceLocation ExpansionEnd = [Range, &SM = SM, &MacroName] {
40
      // If the range is empty, use the length of the macro.
41
138
      if (Range.getBegin() == Range.getEnd())
42
58
        return SM.getExpansionLoc(
43
58
            MacroName.getLocation().getLocWithOffset(MacroName.getLength()));
44
45
      // Include the last character.
46
80
      return SM.getExpansionLoc(Range.getEnd()).getLocWithOffset(1);
47
138
    }();
48
49
138
    (void)PP;
50
138
    LLVM_DEBUG(llvm::dbgs() << "MacroExpands event: '";
51
138
               dumpTokenInto(PP, llvm::dbgs(), MacroName);
52
138
               llvm::dbgs()
53
138
               << "' with length " << MacroName.getLength() << " at ";
54
138
               MacroNameBegin.print(llvm::dbgs(), SM);
55
138
               llvm::dbgs() << ", expansion end at ";
56
138
               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
138
    MacroExpansionContext::ExpansionRangeMap::iterator It;
61
138
    bool Inserted;
62
138
    std::tie(It, Inserted) =
63
138
        ExpansionRanges.try_emplace(MacroNameBegin, ExpansionEnd);
64
138
    if (Inserted) {
65
77
      LLVM_DEBUG(llvm::dbgs() << "maps ";
66
77
                 It->getFirst().print(llvm::dbgs(), SM); llvm::dbgs() << " to ";
67
77
                 It->getSecond().print(llvm::dbgs(), SM);
68
77
                 llvm::dbgs() << '\n';);
69
77
    } else {
70
61
      if (SM.isBeforeInTranslationUnit(It->getSecond(), ExpansionEnd)) {
71
3
        It->getSecond() = ExpansionEnd;
72
3
        LLVM_DEBUG(
73
3
            llvm::dbgs() << "remaps "; It->getFirst().print(llvm::dbgs(), SM);
74
3
            llvm::dbgs() << " to "; It->getSecond().print(llvm::dbgs(), SM);
75
3
            llvm::dbgs() << '\n';);
76
3
      }
77
61
    }
78
138
  }
79
};
80
} // namespace detail
81
} // namespace clang
82
83
using namespace clang;
84
85
MacroExpansionContext::MacroExpansionContext(const LangOptions &LangOpts)
86
1.42k
    : LangOpts(LangOpts) {}
87
88
14
void MacroExpansionContext::registerForPreprocessor(Preprocessor &NewPP) {
89
14
  PP = &NewPP;
90
14
  SM = &NewPP.getSourceManager();
91
92
  // Make sure that the Preprocessor does not outlive the MacroExpansionContext.
93
14
  PP->addPPCallbacks(std::make_unique<detail::MacroExpansionRangeRecorder>(
94
14
      *PP, *SM, ExpansionRanges));
95
  // Same applies here.
96
1.46k
  PP->setTokenWatcher([this](const Token &Tok) { onTokenLexed(Tok); });
97
14
}
98
99
Optional<StringRef>
100
82
MacroExpansionContext::getExpandedText(SourceLocation MacroExpansionLoc) const {
101
82
  if (MacroExpansionLoc.isMacroID())
102
0
    return llvm::None;
103
104
  // If there was no macro expansion at that location, return None.
105
82
  if (ExpansionRanges.find_as(MacroExpansionLoc) == ExpansionRanges.end())
106
15
    return llvm::None;
107
108
  // There was macro expansion, but resulted in no tokens, return empty string.
109
67
  const auto It = ExpandedTokens.find_as(MacroExpansionLoc);
110
67
  if (It == ExpandedTokens.end())
111
7
    return StringRef{""};
112
113
  // Otherwise we have the actual token sequence as string.
114
60
  return It->getSecond().str();
115
67
}
116
117
Optional<StringRef>
118
83
MacroExpansionContext::getOriginalText(SourceLocation MacroExpansionLoc) const {
119
83
  if (MacroExpansionLoc.isMacroID())
120
0
    return llvm::None;
121
122
83
  const auto It = ExpansionRanges.find_as(MacroExpansionLoc);
123
83
  if (It == ExpansionRanges.end())
124
15
    return llvm::None;
125
126
68
  assert(It->getFirst() != It->getSecond() &&
127
68
         "Every macro expansion must cover a non-empty range.");
128
129
0
  return Lexer::getSourceText(
130
68
      CharSourceRange::getCharRange(It->getFirst(), It->getSecond()), *SM,
131
68
      LangOpts);
132
83
}
133
134
0
void MacroExpansionContext::dumpExpansionRanges() const {
135
0
  dumpExpansionRangesToStream(llvm::dbgs());
136
0
}
137
0
void MacroExpansionContext::dumpExpandedTexts() const {
138
0
  dumpExpandedTextsToStream(llvm::dbgs());
139
0
}
140
141
1
void MacroExpansionContext::dumpExpansionRangesToStream(raw_ostream &OS) const {
142
1
  std::vector<std::pair<SourceLocation, SourceLocation>> LocalExpansionRanges;
143
1
  LocalExpansionRanges.reserve(ExpansionRanges.size());
144
1
  for (const auto &Record : ExpansionRanges)
145
0
    LocalExpansionRanges.emplace_back(
146
0
        std::make_pair(Record.getFirst(), Record.getSecond()));
147
1
  llvm::sort(LocalExpansionRanges);
148
149
1
  OS << "\n=============== ExpansionRanges ===============\n";
150
1
  for (const auto &Record : LocalExpansionRanges) {
151
0
    OS << "> ";
152
0
    Record.first.print(OS, *SM);
153
0
    OS << ", ";
154
0
    Record.second.print(OS, *SM);
155
0
    OS << '\n';
156
0
  }
157
1
}
158
159
1
void MacroExpansionContext::dumpExpandedTextsToStream(raw_ostream &OS) const {
160
1
  std::vector<std::pair<SourceLocation, MacroExpansionText>>
161
1
      LocalExpandedTokens;
162
1
  LocalExpandedTokens.reserve(ExpandedTokens.size());
163
1
  for (const auto &Record : ExpandedTokens)
164
0
    LocalExpandedTokens.emplace_back(
165
0
        std::make_pair(Record.getFirst(), Record.getSecond()));
166
1
  llvm::sort(LocalExpandedTokens);
167
168
1
  OS << "\n=============== ExpandedTokens ===============\n";
169
1
  for (const auto &Record : LocalExpandedTokens) {
170
0
    OS << "> ";
171
0
    Record.first.print(OS, *SM);
172
0
    OS << " -> '" << Record.second << "'\n";
173
0
  }
174
1
}
175
176
525
static void dumpTokenInto(const Preprocessor &PP, raw_ostream &OS, Token Tok) {
177
525
  assert(Tok.isNot(tok::raw_identifier));
178
179
  // Ignore annotation tokens like: _Pragma("pack(push, 1)")
180
525
  if (Tok.isAnnotation())
181
0
    return;
182
183
525
  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
157
    OS << II->getName() << ' ';
189
368
  } else if (Tok.isLiteral() && 
!Tok.needsCleaning()64
&&
Tok.getLiteralData()64
) {
190
64
    OS << StringRef(Tok.getLiteralData(), Tok.getLength());
191
304
  } else {
192
304
    char Tmp[256];
193
304
    if (Tok.getLength() < sizeof(Tmp)) {
194
304
      const char *TokPtr = Tmp;
195
      // FIXME: Might use a different overload for cleaner callsite.
196
304
      unsigned Len = PP.getSpelling(Tok, TokPtr);
197
304
      OS.write(TokPtr, Len);
198
304
    } else {
199
0
      OS << "<too long token>";
200
0
    }
201
304
  }
202
525
}
203
204
1.46k
void MacroExpansionContext::onTokenLexed(const Token &Tok) {
205
1.46k
  SourceLocation SLoc = Tok.getLocation();
206
1.46k
  if (SLoc.isFileID())
207
944
    return;
208
209
525
  LLVM_DEBUG(llvm::dbgs() << "lexed macro expansion token '";
210
525
             dumpTokenInto(*PP, llvm::dbgs(), Tok); llvm::dbgs() << "' at ";
211
525
             SLoc.print(llvm::dbgs(), *SM); llvm::dbgs() << '\n';);
212
213
  // Remove spelling location.
214
525
  SourceLocation CurrExpansionLoc = SM->getExpansionLoc(SLoc);
215
216
525
  MacroExpansionText TokenAsString;
217
525
  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
525
  dumpTokenInto(*PP, OS, Tok);
223
224
525
  ExpansionMap::iterator It;
225
525
  bool Inserted;
226
525
  std::tie(It, Inserted) =
227
525
      ExpandedTokens.try_emplace(CurrExpansionLoc, std::move(TokenAsString));
228
525
  if (!Inserted)
229
463
    It->getSecond().append(TokenAsString);
230
525
}
231