Coverage Report

Created: 2022-07-16 07:03

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