Coverage Report

Created: 2020-12-01 06:44

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/Format/MacroExpander.cpp
Line
Count
Source (jump to first uncovered line)
1
//===--- MacroExpander.cpp - Format C++ code --------------------*- C++ -*-===//
2
//
3
//                     The LLVM Compiler Infrastructure
4
//
5
// This file is distributed under the University of Illinois Open Source
6
// License. See LICENSE.TXT for details.
7
//
8
//===----------------------------------------------------------------------===//
9
///
10
/// \file
11
/// This file contains the implementation of MacroExpander, which handles macro
12
/// configuration and expansion while formatting.
13
///
14
//===----------------------------------------------------------------------===//
15
16
#include "Macros.h"
17
18
#include "Encoding.h"
19
#include "FormatToken.h"
20
#include "FormatTokenLexer.h"
21
#include "clang/Basic/TokenKinds.h"
22
#include "clang/Format/Format.h"
23
#include "clang/Lex/HeaderSearch.h"
24
#include "clang/Lex/HeaderSearchOptions.h"
25
#include "clang/Lex/Lexer.h"
26
#include "clang/Lex/ModuleLoader.h"
27
#include "clang/Lex/Preprocessor.h"
28
#include "clang/Lex/PreprocessorOptions.h"
29
#include "llvm/ADT/StringSet.h"
30
#include "llvm/Support/ErrorHandling.h"
31
32
namespace clang {
33
namespace format {
34
35
struct MacroExpander::Definition {
36
  StringRef Name;
37
  SmallVector<FormatToken *, 8> Params;
38
  SmallVector<FormatToken *, 8> Body;
39
40
  // Map from each argument's name to its position in the argument list.
41
  // With "M(x, y) x + y":
42
  //   x -> 0
43
  //   y -> 1
44
  llvm::StringMap<size_t> ArgMap;
45
46
  bool ObjectLike = true;
47
};
48
49
class MacroExpander::DefinitionParser {
50
public:
51
20
  DefinitionParser(ArrayRef<FormatToken *> Tokens) : Tokens(Tokens) {
52
20
    assert(!Tokens.empty());
53
20
    Current = Tokens[0];
54
20
  }
55
56
  // Parse the token stream and return the corresonding Definition object.
57
  // Returns an empty definition object with a null-Name on error.
58
20
  MacroExpander::Definition parse() {
59
20
    if (!Current->is(tok::identifier))
60
0
      return {};
61
20
    Def.Name = Current->TokenText;
62
20
    nextToken();
63
20
    if (Current->is(tok::l_paren)) {
64
17
      Def.ObjectLike = false;
65
17
      if (!parseParams())
66
7
        return {};
67
13
    }
68
13
    if (!parseExpansion())
69
0
      return {};
70
71
13
    return Def;
72
13
  }
73
74
private:
75
17
  bool parseParams() {
76
17
    assert(Current->is(tok::l_paren));
77
17
    nextToken();
78
23
    while (Current->is(tok::identifier)) {
79
18
      Def.Params.push_back(Current);
80
18
      Def.ArgMap[Def.Params.back()->TokenText] = Def.Params.size() - 1;
81
18
      nextToken();
82
18
      if (Current->isNot(tok::comma))
83
12
        break;
84
6
      nextToken();
85
6
    }
86
17
    if (Current->isNot(tok::r_paren))
87
7
      return false;
88
10
    nextToken();
89
10
    return true;
90
10
  }
91
92
13
  bool parseExpansion() {
93
13
    if (!Current->isOneOf(tok::equal, tok::eof))
94
0
      return false;
95
13
    if (Current->is(tok::equal))
96
10
      nextToken();
97
13
    parseTail();
98
13
    return true;
99
13
  }
100
101
13
  void parseTail() {
102
42
    while (Current->isNot(tok::eof)) {
103
29
      Def.Body.push_back(Current);
104
29
      nextToken();
105
29
    }
106
13
    Def.Body.push_back(Current);
107
13
  }
108
109
110
  void nextToken() {
110
110
    if (Pos + 1 < Tokens.size())
111
110
      ++Pos;
112
110
    Current = Tokens[Pos];
113
110
    Current->Finalized = true;
114
110
  }
115
116
  size_t Pos = 0;
117
  FormatToken *Current = nullptr;
118
  Definition Def;
119
  ArrayRef<FormatToken *> Tokens;
120
};
121
122
MacroExpander::MacroExpander(
123
    const std::vector<std::string> &Macros, clang::SourceManager &SourceMgr,
124
    const FormatStyle &Style,
125
    llvm::SpecificBumpPtrAllocator<FormatToken> &Allocator,
126
    IdentifierTable &IdentTable)
127
    : SourceMgr(SourceMgr), Style(Style), Allocator(Allocator),
128
7
      IdentTable(IdentTable) {
129
20
  for (const std::string &Macro : Macros) {
130
20
    parseDefinition(Macro);
131
20
  }
132
7
}
133
134
7
MacroExpander::~MacroExpander() = default;
135
136
20
void MacroExpander::parseDefinition(const std::string &Macro) {
137
20
  Buffers.push_back(
138
20
      llvm::MemoryBuffer::getMemBufferCopy(Macro, "<scratch space>"));
139
20
  clang::FileID FID = SourceMgr.createFileID(Buffers.back()->getMemBufferRef());
140
20
  FormatTokenLexer Lex(SourceMgr, FID, 0, Style, encoding::Encoding_UTF8,
141
20
                       Allocator, IdentTable);
142
20
  const auto Tokens = Lex.lex();
143
20
  if (!Tokens.empty()) {
144
20
    DefinitionParser Parser(Tokens);
145
20
    auto Definition = Parser.parse();
146
20
    Definitions[Definition.Name] = std::move(Definition);
147
20
  }
148
20
}
149
150
28
bool MacroExpander::defined(llvm::StringRef Name) const {
151
28
  return Definitions.find(Name) != Definitions.end();
152
28
}
153
154
4
bool MacroExpander::objectLike(llvm::StringRef Name) const {
155
4
  return Definitions.find(Name)->second.ObjectLike;
156
4
}
157
158
llvm::SmallVector<FormatToken *, 8> MacroExpander::expand(FormatToken *ID,
159
14
                                                          ArgsList Args) const {
160
14
  assert(defined(ID->TokenText));
161
14
  SmallVector<FormatToken *, 8> Result;
162
14
  const Definition &Def = Definitions.find(ID->TokenText)->second;
163
164
  // Expand each argument at most once.
165
14
  llvm::StringSet<> ExpandedArgs;
166
167
  // Adds the given token to Result.
168
52
  auto pushToken = [&](FormatToken *Tok) {
169
52
    Tok->MacroCtx->ExpandedFrom.push_back(ID);
170
52
    Result.push_back(Tok);
171
52
  };
172
173
  // If Tok references a parameter, adds the corresponding argument to Result.
174
  // Returns false if Tok does not reference a parameter.
175
46
  auto expandArgument = [&](FormatToken *Tok) -> bool {
176
    // If the current token references a parameter, expand the corresponding
177
    // argument.
178
46
    if (!Tok->is(tok::identifier) || 
ExpandedArgs.contains(Tok->TokenText)18
)
179
30
      return false;
180
16
    ExpandedArgs.insert(Tok->TokenText);
181
16
    auto I = Def.ArgMap.find(Tok->TokenText);
182
16
    if (I == Def.ArgMap.end())
183
2
      return false;
184
    // If there are fewer arguments than referenced parameters, treat the
185
    // parameter as empty.
186
    // FIXME: Potentially fully abort the expansion instead.
187
14
    if (I->getValue() >= Args.size())
188
1
      return true;
189
20
    
for (FormatToken *Arg : Args[I->getValue()])13
{
190
      // A token can be part of a macro argument at multiple levels.
191
      // For example, with "ID(x) x":
192
      // in ID(ID(x)), 'x' is expanded first as argument to the inner
193
      // ID, then again as argument to the outer ID. We keep the macro
194
      // role the token had from the inner expansion.
195
20
      if (!Arg->MacroCtx)
196
18
        Arg->MacroCtx = MacroExpansion(MR_ExpandedArg);
197
20
      pushToken(Arg);
198
20
    }
199
13
    return true;
200
13
  };
201
202
  // Expand the definition into Result.
203
46
  for (FormatToken *Tok : Def.Body) {
204
46
    if (expandArgument(Tok))
205
14
      continue;
206
    // Create a copy of the tokens from the macro body, i.e. were not provided
207
    // by user code.
208
32
    FormatToken *New = new (Allocator.Allocate()) FormatToken;
209
32
    New->copyFrom(*Tok);
210
32
    assert(!New->MacroCtx);
211
    // Tokens that are not part of the user code are not formatted.
212
32
    New->MacroCtx = MacroExpansion(MR_Hidden);
213
32
    pushToken(New);
214
32
  }
215
14
  assert(Result.size() >= 1 && Result.back()->is(tok::eof));
216
14
  if (Result.size() > 1) {
217
11
    ++Result[0]->MacroCtx->StartOfExpansion;
218
11
    ++Result[Result.size() - 2]->MacroCtx->EndOfExpansion;
219
11
  }
220
14
  return Result;
221
14
}
222
223
} // namespace format
224
} // namespace clang