Coverage Report

Created: 2026-08-14 07:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/solidity/libsolidity/analysis/OverrideChecker.cpp
Line
Count
Source
1
/*
2
  This file is part of solidity.
3
4
  solidity is free software: you can redistribute it and/or modify
5
  it under the terms of the GNU General Public License as published by
6
  the Free Software Foundation, either version 3 of the License, or
7
  (at your option) any later version.
8
9
  solidity is distributed in the hope that it will be useful,
10
  but WITHOUT ANY WARRANTY; without even the implied warranty of
11
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
  GNU General Public License for more details.
13
14
  You should have received a copy of the GNU General Public License
15
  along with solidity.  If not, see <http://www.gnu.org/licenses/>.
16
*/
17
// SPDX-License-Identifier: GPL-3.0
18
/**
19
 * Component that verifies overloads, abstract contracts, function clashes and others
20
 * checks at contract or function level.
21
 */
22
23
#include <libsolidity/analysis/OverrideChecker.h>
24
25
#include <libsolidity/ast/AST.h>
26
#include <libsolidity/ast/TypeProvider.h>
27
#include <libsolidity/analysis/TypeChecker.h>
28
#include <liblangutil/ErrorReporter.h>
29
#include <libsolutil/Visitor.h>
30
31
#include <boost/algorithm/string/predicate.hpp>
32
33
34
using namespace solidity;
35
using namespace solidity::frontend;
36
using namespace solidity::langutil;
37
38
using solidity::util::GenericVisitor;
39
using solidity::util::contains_if;
40
using solidity::util::joinHumanReadable;
41
42
namespace
43
{
44
45
// Helper struct to do a search by name
46
struct MatchByName
47
{
48
  std::string const& m_name;
49
  bool operator()(OverrideProxy const& _item)
50
305
  {
51
305
    return _item.name() == m_name;
52
305
  }
53
};
54
55
/**
56
 * Construct the override graph for this signature.
57
 * Reserve node 0 for the current contract and node
58
 * 1 for an artificial top node to which all override paths
59
 * connect at the end.
60
 */
61
struct OverrideGraph
62
{
63
  OverrideGraph(std::set<OverrideProxy> const& _baseCallables)
64
173
  {
65
173
    for (auto const& baseFunction: _baseCallables)
66
346
      addEdge(0, visit(baseFunction));
67
173
  }
68
  std::map<OverrideProxy, int> nodes;
69
  std::map<int, OverrideProxy> nodeInv;
70
  std::map<int, std::set<int>> edges;
71
  size_t numNodes = 2;
72
  void addEdge(int _a, int _b)
73
739
  {
74
739
    edges[_a].insert(_b);
75
739
    edges[_b].insert(_a);
76
739
  }
77
private:
78
  /// Completes the graph starting from @a _function and
79
  /// @returns the node ID.
80
  int visit(OverrideProxy const& _function)
81
472
  {
82
472
    auto it = nodes.find(_function);
83
472
    if (it != nodes.end())
84
93
      return it->second;
85
379
    int currentNode = static_cast<int>(numNodes++);
86
379
    nodes[_function] = currentNode;
87
379
    nodeInv[currentNode] = _function;
88
89
379
    if (!_function.baseFunctions().empty())
90
112
      for (auto const& baseFunction: _function.baseFunctions())
91
126
        addEdge(currentNode, visit(baseFunction));
92
267
    else
93
267
      addEdge(currentNode, 1);
94
95
379
    return currentNode;
96
472
  }
97
};
98
99
/**
100
 * Detect cut vertices following https://en.wikipedia.org/wiki/Biconnected_component#Pseudocode
101
 * Can ignore the root node, since it is never a cut vertex in our case.
102
 */
103
struct CutVertexFinder
104
{
105
173
  CutVertexFinder(OverrideGraph const& _graph): m_graph(_graph)
106
173
  {
107
173
    run();
108
173
  }
109
173
  std::set<OverrideProxy> const& cutVertices() const { return m_cutVertices; }
110
111
private:
112
  OverrideGraph const& m_graph;
113
114
  std::vector<bool> m_visited = std::vector<bool>(m_graph.numNodes, false);
115
  std::vector<int> m_depths = std::vector<int>(m_graph.numNodes, -1);
116
  std::vector<int> m_low = std::vector<int>(m_graph.numNodes, -1);
117
  std::vector<int> m_parent = std::vector<int>(m_graph.numNodes, -1);
118
  std::set<OverrideProxy> m_cutVertices{};
119
120
  void run(size_t _u = 0, size_t _depth = 0)
121
725
  {
122
725
    m_visited.at(_u) = true;
123
725
    m_depths.at(_u) = m_low.at(_u) = static_cast<int>(_depth);
124
725
    for (int const v: m_graph.edges.at(static_cast<int>(_u)))
125
1.47k
    {
126
1.47k
      auto const vInd = static_cast<size_t>(v);
127
1.47k
      if (!m_visited.at(vInd))
128
552
      {
129
552
        m_parent[vInd] = static_cast<int>(_u);
130
552
        run(vInd, _depth + 1);
131
552
        if (m_low[vInd] >= m_depths[_u] && m_parent[_u] != -1)
132
89
          m_cutVertices.insert(m_graph.nodeInv.at(static_cast<int>(_u)));
133
552
        m_low[_u] = std::min(m_low[_u], m_low[vInd]);
134
552
      }
135
926
      else if (v != m_parent[_u])
136
374
        m_low[_u] = std::min(m_low[_u], m_depths[vInd]);
137
1.47k
    }
138
725
  }
139
};
140
141
std::vector<ContractDefinition const*> resolveDirectBaseContracts(ContractDefinition const& _contract)
142
57.3k
{
143
57.3k
  std::vector<ContractDefinition const*> resolvedContracts;
144
145
57.3k
  for (ASTPointer<InheritanceSpecifier> const& specifier: _contract.baseContracts())
146
9.91k
  {
147
9.91k
    Declaration const* baseDecl =
148
9.91k
      specifier->name().annotation().referencedDeclaration;
149
9.91k
    auto contract = dynamic_cast<ContractDefinition const*>(baseDecl);
150
9.91k
    if (contract)
151
9.91k
      resolvedContracts.emplace_back(contract);
152
9.91k
  }
153
154
57.3k
  return resolvedContracts;
155
57.3k
}
156
157
std::vector<ASTPointer<IdentifierPath>> sortByContract(std::vector<ASTPointer<IdentifierPath>> const& _list)
158
108
{
159
108
  auto sorted = _list;
160
161
108
  stable_sort(sorted.begin(), sorted.end(),
162
1.97k
    [] (ASTPointer<IdentifierPath> _a, ASTPointer<IdentifierPath> _b) {
163
1.97k
      if (!_a || !_b)
164
0
        return _a < _b;
165
166
1.97k
      Declaration const* aDecl = _a->annotation().referencedDeclaration;
167
1.97k
      Declaration const* bDecl = _b->annotation().referencedDeclaration;
168
169
1.97k
      if (!aDecl || !bDecl)
170
0
        return aDecl < bDecl;
171
172
1.97k
      return aDecl->id() < bDecl->id();
173
1.97k
    }
174
108
  );
175
176
108
  return sorted;
177
108
}
178
179
OverrideProxy makeOverrideProxy(CallableDeclaration const& _callable)
180
272
{
181
272
  if (auto const* fun = dynamic_cast<FunctionDefinition const*>(&_callable))
182
236
    return OverrideProxy{fun};
183
36
  else if (auto const* mod = dynamic_cast<ModifierDefinition const*>(&_callable))
184
36
    return OverrideProxy{mod};
185
0
  else
186
36
    solAssert(false, "Invalid call to makeOverrideProxy.");
187
0
  return {};
188
272
}
189
190
}
191
192
bool OverrideProxy::operator<(OverrideProxy const& _other) const
193
1.91k
{
194
1.91k
  return id() < _other.id();
195
1.91k
}
196
197
bool OverrideProxy::isVariable() const
198
5.02k
{
199
5.02k
  return std::holds_alternative<VariableDeclaration const*>(m_item);
200
5.02k
}
201
202
bool OverrideProxy::isFunction() const
203
8.11k
{
204
8.11k
  return std::holds_alternative<FunctionDefinition const*>(m_item);
205
8.11k
}
206
207
bool OverrideProxy::isModifier() const
208
7.95k
{
209
7.95k
  return std::holds_alternative<ModifierDefinition const*>(m_item);
210
7.95k
}
211
212
bool OverrideProxy::CompareBySignature::operator()(OverrideProxy const& _a, OverrideProxy const& _b) const
213
158k
{
214
158k
  return _a.overrideComparator() < _b.overrideComparator();
215
158k
}
216
217
size_t OverrideProxy::id() const
218
3.82k
{
219
3.82k
  return std::visit(GenericVisitor{
220
3.82k
    [&](auto const* _item) -> size_t { return static_cast<size_t>(_item->id()); }
OverrideChecker.cpp:unsigned long solidity::frontend::OverrideProxy::id() const::$_0::operator()<solidity::frontend::FunctionDefinition>(solidity::frontend::FunctionDefinition const*) const
Line
Count
Source
220
3.04k
    [&](auto const* _item) -> size_t { return static_cast<size_t>(_item->id()); }
OverrideChecker.cpp:unsigned long solidity::frontend::OverrideProxy::id() const::$_0::operator()<solidity::frontend::ModifierDefinition>(solidity::frontend::ModifierDefinition const*) const
Line
Count
Source
220
614
    [&](auto const* _item) -> size_t { return static_cast<size_t>(_item->id()); }
OverrideChecker.cpp:unsigned long solidity::frontend::OverrideProxy::id() const::$_0::operator()<solidity::frontend::VariableDeclaration>(solidity::frontend::VariableDeclaration const*) const
Line
Count
Source
220
169
    [&](auto const* _item) -> size_t { return static_cast<size_t>(_item->id()); }
221
3.82k
  }, m_item);
222
3.82k
}
223
224
std::shared_ptr<OverrideSpecifier> OverrideProxy::overrides() const
225
103k
{
226
103k
  return std::visit(GenericVisitor{
227
103k
    [&](auto const* _item) { return _item->overrides(); }
OverrideChecker.cpp:auto solidity::frontend::OverrideProxy::overrides() const::$_0::operator()<solidity::frontend::FunctionDefinition>(solidity::frontend::FunctionDefinition const*) const
Line
Count
Source
227
74.8k
    [&](auto const* _item) { return _item->overrides(); }
OverrideChecker.cpp:auto solidity::frontend::OverrideProxy::overrides() const::$_0::operator()<solidity::frontend::ModifierDefinition>(solidity::frontend::ModifierDefinition const*) const
Line
Count
Source
227
11.7k
    [&](auto const* _item) { return _item->overrides(); }
OverrideChecker.cpp:auto solidity::frontend::OverrideProxy::overrides() const::$_0::operator()<solidity::frontend::VariableDeclaration>(solidity::frontend::VariableDeclaration const*) const
Line
Count
Source
227
16.7k
    [&](auto const* _item) { return _item->overrides(); }
228
103k
  }, m_item);
229
103k
}
230
231
std::set<OverrideProxy> OverrideProxy::baseFunctions() const
232
600
{
233
600
  return std::visit(GenericVisitor{
234
600
    [&](auto const* _item) -> std::set<OverrideProxy> {
235
600
      std::set<OverrideProxy> ret;
236
600
      for (auto const* f: _item->annotation().baseFunctions)
237
272
        ret.insert(makeOverrideProxy(*f));
238
600
      return ret;
239
600
    }
OverrideChecker.cpp:std::__1::set<solidity::frontend::OverrideProxy, std::__1::less<solidity::frontend::OverrideProxy>, std::__1::allocator<solidity::frontend::OverrideProxy> > solidity::frontend::OverrideProxy::baseFunctions() const::$_0::operator()<solidity::frontend::FunctionDefinition>(solidity::frontend::FunctionDefinition const*) const
Line
Count
Source
234
508
    [&](auto const* _item) -> std::set<OverrideProxy> {
235
508
      std::set<OverrideProxy> ret;
236
508
      for (auto const* f: _item->annotation().baseFunctions)
237
230
        ret.insert(makeOverrideProxy(*f));
238
508
      return ret;
239
508
    }
OverrideChecker.cpp:std::__1::set<solidity::frontend::OverrideProxy, std::__1::less<solidity::frontend::OverrideProxy>, std::__1::allocator<solidity::frontend::OverrideProxy> > solidity::frontend::OverrideProxy::baseFunctions() const::$_0::operator()<solidity::frontend::ModifierDefinition>(solidity::frontend::ModifierDefinition const*) const
Line
Count
Source
234
72
    [&](auto const* _item) -> std::set<OverrideProxy> {
235
72
      std::set<OverrideProxy> ret;
236
72
      for (auto const* f: _item->annotation().baseFunctions)
237
36
        ret.insert(makeOverrideProxy(*f));
238
72
      return ret;
239
72
    }
OverrideChecker.cpp:std::__1::set<solidity::frontend::OverrideProxy, std::__1::less<solidity::frontend::OverrideProxy>, std::__1::allocator<solidity::frontend::OverrideProxy> > solidity::frontend::OverrideProxy::baseFunctions() const::$_0::operator()<solidity::frontend::VariableDeclaration>(solidity::frontend::VariableDeclaration const*) const
Line
Count
Source
234
20
    [&](auto const* _item) -> std::set<OverrideProxy> {
235
20
      std::set<OverrideProxy> ret;
236
20
      for (auto const* f: _item->annotation().baseFunctions)
237
6
        ret.insert(makeOverrideProxy(*f));
238
20
      return ret;
239
20
    }
240
600
  }, m_item);
241
600
}
242
243
void OverrideProxy::storeBaseFunction(OverrideProxy const& _base) const
244
2.18k
{
245
2.18k
  std::visit(GenericVisitor{
246
2.18k
    [&](FunctionDefinition const* _item) {
247
977
      _item->annotation().baseFunctions.emplace(std::get<FunctionDefinition const*>(_base.m_item));
248
977
    },
249
2.18k
    [&](ModifierDefinition const* _item) {
250
1.05k
      _item->annotation().baseFunctions.emplace(std::get<ModifierDefinition const*>(_base.m_item));
251
1.05k
    },
252
2.18k
    [&](VariableDeclaration const* _item) {
253
154
      _item->annotation().baseFunctions.emplace(std::get<FunctionDefinition const*>(_base.m_item));
254
154
    }
255
2.18k
  }, m_item);
256
2.18k
}
257
258
std::string const& OverrideProxy::name() const
259
995
{
260
995
  return std::visit(GenericVisitor{
261
995
    [&](auto const* _item) -> std::string const& { return _item->name(); }
OverrideChecker.cpp:std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const& solidity::frontend::OverrideProxy::name() const::$_0::operator()<solidity::frontend::FunctionDefinition>(solidity::frontend::FunctionDefinition const*) const
Line
Count
Source
261
118
    [&](auto const* _item) -> std::string const& { return _item->name(); }
OverrideChecker.cpp:std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const& solidity::frontend::OverrideProxy::name() const::$_0::operator()<solidity::frontend::ModifierDefinition>(solidity::frontend::ModifierDefinition const*) const
Line
Count
Source
261
857
    [&](auto const* _item) -> std::string const& { return _item->name(); }
OverrideChecker.cpp:std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const& solidity::frontend::OverrideProxy::name() const::$_0::operator()<solidity::frontend::VariableDeclaration>(solidity::frontend::VariableDeclaration const*) const
Line
Count
Source
261
20
    [&](auto const* _item) -> std::string const& { return _item->name(); }
262
995
  }, m_item);
263
995
}
264
265
ContractDefinition const& OverrideProxy::contract() const
266
2.80k
{
267
2.80k
  return std::visit(GenericVisitor{
268
2.80k
    [&](auto const* _item) -> ContractDefinition const& {
269
2.80k
      return dynamic_cast<ContractDefinition const&>(*_item->scope());
270
2.80k
    }
OverrideChecker.cpp:solidity::frontend::ContractDefinition const& solidity::frontend::OverrideProxy::contract() const::$_0::operator()<solidity::frontend::FunctionDefinition>(solidity::frontend::FunctionDefinition const*) const
Line
Count
Source
268
1.60k
    [&](auto const* _item) -> ContractDefinition const& {
269
1.60k
      return dynamic_cast<ContractDefinition const&>(*_item->scope());
270
1.60k
    }
OverrideChecker.cpp:solidity::frontend::ContractDefinition const& solidity::frontend::OverrideProxy::contract() const::$_0::operator()<solidity::frontend::ModifierDefinition>(solidity::frontend::ModifierDefinition const*) const
Line
Count
Source
268
1.09k
    [&](auto const* _item) -> ContractDefinition const& {
269
1.09k
      return dynamic_cast<ContractDefinition const&>(*_item->scope());
270
1.09k
    }
OverrideChecker.cpp:solidity::frontend::ContractDefinition const& solidity::frontend::OverrideProxy::contract() const::$_0::operator()<solidity::frontend::VariableDeclaration>(solidity::frontend::VariableDeclaration const*) const
Line
Count
Source
268
106
    [&](auto const* _item) -> ContractDefinition const& {
269
106
      return dynamic_cast<ContractDefinition const&>(*_item->scope());
270
106
    }
271
2.80k
  }, m_item);
272
2.80k
}
273
274
std::string const& OverrideProxy::contractName() const
275
210
{
276
210
  return contract().name();
277
210
}
278
279
Visibility OverrideProxy::visibility() const
280
5.84k
{
281
5.84k
  return std::visit(GenericVisitor{
282
5.84k
    [&](FunctionDefinition const* _item) { return _item->visibility(); },
283
5.84k
    [&](ModifierDefinition const* _item) { return _item->visibility(); },
284
5.84k
    [&](VariableDeclaration const*) { return Visibility::External; }
285
5.84k
  }, m_item);
286
5.84k
}
287
288
StateMutability OverrideProxy::stateMutability() const
289
3.51k
{
290
3.51k
  return std::visit(GenericVisitor{
291
3.51k
    [&](FunctionDefinition const* _item) { return _item->stateMutability(); },
292
3.51k
    [&](ModifierDefinition const*) { solAssert(false, "Requested state mutability from modifier."); return StateMutability{}; },
293
3.51k
    [&](VariableDeclaration const* _var) { return _var->isConstant() ? StateMutability::Pure : StateMutability::View; }
294
3.51k
  }, m_item);
295
3.51k
}
296
297
bool OverrideProxy::virtualSemantics() const
298
2.18k
{
299
2.18k
  return std::visit(GenericVisitor{
300
2.18k
    [&](FunctionDefinition const* _item) { return _item->virtualSemantics(); },
301
2.18k
    [&](ModifierDefinition const* _item) { return _item->virtualSemantics(); },
302
2.18k
    [&](VariableDeclaration const*) { return false; }
303
2.18k
  }, m_item);
304
2.18k
}
305
306
Token OverrideProxy::functionKind() const
307
1.55k
{
308
1.55k
  return std::visit(GenericVisitor{
309
1.55k
    [&](FunctionDefinition const* _item) { return _item->kind(); },
310
1.55k
    [&](ModifierDefinition const*) { return Token::Function; },
311
1.55k
    [&](VariableDeclaration const*) { return Token::Function; }
312
1.55k
  }, m_item);
313
1.55k
}
314
315
FunctionType const* OverrideProxy::externalFunctionType() const
316
30.9k
{
317
30.9k
  return std::visit(GenericVisitor{
318
30.9k
    [&](FunctionDefinition const* _item) { return FunctionType(*_item).asExternallyCallableFunction(false); },
319
30.9k
    [&](VariableDeclaration const* _item) { return FunctionType(*_item).asExternallyCallableFunction(false); },
320
30.9k
    [&](ModifierDefinition const*) -> FunctionType const* { solAssert(false, "Requested function type of modifier."); return nullptr; }
321
30.9k
  }, m_item);
322
30.9k
}
323
324
FunctionType const* OverrideProxy::originalFunctionType() const
325
1.62k
{
326
1.62k
  return std::visit(GenericVisitor{
327
1.62k
    [&](FunctionDefinition const* _item) { return TypeProvider::function(*_item); },
328
1.62k
    [&](VariableDeclaration const*) -> FunctionType const* { solAssert(false, "Requested specific function type of variable."); return nullptr; },
329
1.62k
    [&](ModifierDefinition const*) -> FunctionType const* { solAssert(false, "Requested specific function type of modifier."); return nullptr; }
330
1.62k
  }, m_item);
331
1.62k
}
332
333
ModifierType const* OverrideProxy::modifierType() const
334
2.10k
{
335
2.10k
  return std::visit(GenericVisitor{
336
2.10k
    [&](FunctionDefinition const*) -> ModifierType const* { solAssert(false, "Requested modifier type of function."); return nullptr; },
337
2.10k
    [&](VariableDeclaration const*) -> ModifierType const* { solAssert(false, "Requested modifier type of variable."); return nullptr; },
338
2.10k
    [&](ModifierDefinition const* _modifier) -> ModifierType const* { return TypeProvider::modifier(*_modifier); }
339
2.10k
  }, m_item);
340
2.10k
}
341
342
343
Declaration const* OverrideProxy::declaration() const
344
7.31k
{
345
7.31k
  return std::visit(GenericVisitor{
346
7.31k
    [&](FunctionDefinition const* _function) -> Declaration const* { return _function; },
347
7.31k
    [&](VariableDeclaration const* _variable) -> Declaration const* { return _variable; },
348
7.31k
    [&](ModifierDefinition const* _modifier) -> Declaration const* { return _modifier; }
349
7.31k
  }, m_item);
350
7.31k
}
351
352
SourceLocation const& OverrideProxy::location() const
353
4.74k
{
354
4.74k
  return std::visit(GenericVisitor{
355
4.74k
    [&](auto const* _item) -> SourceLocation const& { return _item->location(); }
OverrideChecker.cpp:solidity::langutil::SourceLocation const& solidity::frontend::OverrideProxy::location() const::$_0::operator()<solidity::frontend::FunctionDefinition>(solidity::frontend::FunctionDefinition const*) const
Line
Count
Source
355
590
    [&](auto const* _item) -> SourceLocation const& { return _item->location(); }
OverrideChecker.cpp:solidity::langutil::SourceLocation const& solidity::frontend::OverrideProxy::location() const::$_0::operator()<solidity::frontend::ModifierDefinition>(solidity::frontend::ModifierDefinition const*) const
Line
Count
Source
355
3.79k
    [&](auto const* _item) -> SourceLocation const& { return _item->location(); }
OverrideChecker.cpp:solidity::langutil::SourceLocation const& solidity::frontend::OverrideProxy::location() const::$_0::operator()<solidity::frontend::VariableDeclaration>(solidity::frontend::VariableDeclaration const*) const
Line
Count
Source
355
366
    [&](auto const* _item) -> SourceLocation const& { return _item->location(); }
356
4.74k
  }, m_item);
357
4.74k
}
358
359
std::string OverrideProxy::astNodeName() const
360
6.62k
{
361
6.62k
  return std::visit(GenericVisitor{
362
6.62k
    [&](FunctionDefinition const*) { return "function"; },
363
6.62k
    [&](ModifierDefinition const*) { return "modifier"; },
364
6.62k
    [&](VariableDeclaration const*) { return "public state variable"; },
365
6.62k
  }, m_item);
366
6.62k
}
367
368
std::string OverrideProxy::astNodeNameCapitalized() const
369
356
{
370
356
  return std::visit(GenericVisitor{
371
356
    [&](FunctionDefinition const*) { return "Function"; },
372
356
    [&](ModifierDefinition const*) { return "Modifier"; },
373
356
    [&](VariableDeclaration const*) { return "Public state variable"; },
374
356
  }, m_item);
375
356
}
376
377
std::string OverrideProxy::distinguishingProperty() const
378
105
{
379
105
  return std::visit(GenericVisitor{
380
105
    [&](FunctionDefinition const*) { return "name and parameter types"; },
381
105
    [&](ModifierDefinition const*) { return "name"; },
382
105
    [&](VariableDeclaration const*) { return "name and parameter types"; },
383
105
  }, m_item);
384
105
}
385
386
bool OverrideProxy::unimplemented() const
387
82.7k
{
388
82.7k
  return std::visit(GenericVisitor{
389
82.7k
    [&](FunctionDefinition const* _item) { return !_item->isImplemented(); },
390
82.7k
    [&](ModifierDefinition const* _item) { return !_item->isImplemented(); },
391
82.7k
    [&](VariableDeclaration const*) { return false; }
392
82.7k
  }, m_item);
393
82.7k
}
394
395
bool OverrideProxy::OverrideComparator::operator<(OverrideComparator const& _other) const
396
158k
{
397
158k
  if (name != _other.name)
398
124k
    return name < _other.name;
399
400
33.9k
  if (!functionKind || !_other.functionKind)
401
13.9k
    return false;
402
403
20.0k
  if (functionKind != _other.functionKind)
404
50
    return *functionKind < *_other.functionKind;
405
406
  // Parameters do not matter for non-regular functions.
407
20.0k
  if (functionKind != Token::Function)
408
647
    return false;
409
410
19.3k
  if (!parameterTypes || !_other.parameterTypes)
411
0
    return false;
412
413
19.3k
  return boost::lexicographical_compare(*parameterTypes, *_other.parameterTypes);
414
19.3k
}
415
416
OverrideProxy::OverrideComparator const& OverrideProxy::overrideComparator() const
417
317k
{
418
317k
  if (!m_comparator)
419
44.5k
  {
420
44.5k
    m_comparator = std::make_shared<OverrideComparator>(std::visit(GenericVisitor{
421
44.5k
      [&](FunctionDefinition const* _function)
422
44.5k
      {
423
21.3k
        std::vector<std::string> paramTypes;
424
21.3k
        for (Type const* t: externalFunctionType()->parameterTypes())
425
10.7k
          paramTypes.emplace_back(t->richIdentifier());
426
21.3k
        return OverrideComparator{
427
21.3k
          _function->name(),
428
21.3k
          _function->kind(),
429
21.3k
          std::move(paramTypes)
430
21.3k
        };
431
21.3k
      },
432
44.5k
      [&](VariableDeclaration const* _var)
433
44.5k
      {
434
7.34k
        std::vector<std::string> paramTypes;
435
7.34k
        for (Type const* t: externalFunctionType()->parameterTypes())
436
1.03k
          paramTypes.emplace_back(t->richIdentifier());
437
7.34k
        return OverrideComparator{
438
7.34k
          _var->name(),
439
7.34k
          Token::Function,
440
7.34k
          std::move(paramTypes)
441
7.34k
        };
442
7.34k
      },
443
44.5k
      [&](ModifierDefinition const* _mod)
444
44.5k
      {
445
15.7k
        return OverrideComparator{
446
15.7k
          _mod->name(),
447
15.7k
          {},
448
15.7k
          {}
449
15.7k
        };
450
15.7k
      }
451
44.5k
    }, m_item));
452
44.5k
  }
453
454
317k
  return *m_comparator;
455
317k
}
456
457
bool OverrideChecker::CompareByID::operator()(ContractDefinition const* _a, ContractDefinition const* _b) const
458
4.74k
{
459
4.74k
  if (!_a || !_b)
460
0
    return _a < _b;
461
462
4.74k
  return _a->id() < _b->id();
463
4.74k
}
464
465
void OverrideChecker::check(ContractDefinition const& _contract)
466
28.6k
{
467
28.6k
  checkIllegalOverrides(_contract);
468
28.6k
  checkAmbiguousOverrides(_contract);
469
28.6k
}
470
471
void OverrideChecker::checkIllegalOverrides(ContractDefinition const& _contract)
472
28.6k
{
473
28.6k
  OverrideProxyBySignatureMultiSet const& inheritedFuncs = inheritedFunctions(_contract);
474
28.6k
  OverrideProxyBySignatureMultiSet const& inheritedMods = inheritedModifiers(_contract);
475
476
28.6k
  for (ModifierDefinition const* modifier: _contract.functionModifiers())
477
3.24k
  {
478
3.24k
    if (contains_if(inheritedFuncs, MatchByName{modifier->name()}))
479
2
      m_errorReporter.typeError(
480
2
        5631_error,
481
2
        modifier->location(),
482
2
        "Override changes function or public state variable to modifier."
483
2
      );
484
485
3.24k
    checkOverrideList(OverrideProxy{modifier}, inheritedMods);
486
3.24k
  }
487
488
28.6k
  for (FunctionDefinition const* function: _contract.definedFunctions())
489
26.7k
  {
490
26.7k
    if (function->isConstructor())
491
2.62k
      continue;
492
493
24.1k
    if (contains_if(inheritedMods, MatchByName{function->name()}))
494
2
      m_errorReporter.typeError(1469_error, function->location(), "Override changes modifier to function.");
495
496
24.1k
    checkOverrideList(OverrideProxy{function}, inheritedFuncs);
497
24.1k
  }
498
28.6k
  for (auto const* stateVar: _contract.stateVariables())
499
14.0k
  {
500
14.0k
    if (!stateVar->isPublic())
501
8.84k
    {
502
8.84k
      if (stateVar->overrides())
503
2
        m_errorReporter.typeError(8022_error, stateVar->location(), "Override can only be used with public state variables.");
504
505
8.84k
      continue;
506
8.84k
    }
507
508
5.25k
    if (contains_if(inheritedMods, MatchByName{stateVar->name()}))
509
2
      m_errorReporter.typeError(1456_error, stateVar->location(), "Override changes modifier to public state variable.");
510
511
5.25k
    checkOverrideList(OverrideProxy{stateVar}, inheritedFuncs);
512
5.25k
  }
513
514
28.6k
}
515
516
void OverrideChecker::checkOverride(OverrideProxy const& _overriding, OverrideProxy const& _super)
517
2.27k
{
518
2.27k
  solAssert(_super.isModifier() == _overriding.isModifier(), "");
519
520
2.27k
  if (_super.isFunction() || _super.isModifier())
521
2.18k
    _overriding.storeBaseFunction(_super);
522
523
2.27k
  if (_overriding.isModifier() && *_overriding.modifierType() != *_super.modifierType())
524
37
    m_errorReporter.typeError(
525
37
      1078_error,
526
37
      _overriding.location(),
527
37
      "Override changes modifier signature."
528
37
    );
529
530
2.27k
  if (!_overriding.overrides() && !(_super.isFunction() && _super.contract().isInterface()))
531
1.06k
    overrideError(
532
1.06k
      _overriding,
533
1.06k
      _super,
534
1.06k
      9456_error,
535
1.06k
      "Overriding " + _overriding.astNodeName() + " is missing \"override\" specifier.",
536
1.06k
      "Overridden " + _overriding.astNodeName() + " is here:"
537
1.06k
    );
538
539
2.27k
  if (_super.isVariable())
540
90
    overrideError(
541
90
      _super,
542
90
      _overriding,
543
90
      1452_error,
544
90
      "Cannot override public state variable.",
545
90
      "Overriding " + _overriding.astNodeName() + " is here:"
546
90
    );
547
2.18k
  else if (!_super.virtualSemantics())
548
963
    overrideError(
549
963
      _super,
550
963
      _overriding,
551
963
      4334_error,
552
963
      "Trying to override non-virtual " + _super.astNodeName() + ". Did you forget to add \"virtual\"?",
553
963
      "Overriding " + _overriding.astNodeName() + " is here:"
554
963
    );
555
556
2.27k
  if (_overriding.isVariable())
557
238
  {
558
238
    if (_super.visibility() != Visibility::External)
559
19
      overrideError(
560
19
        _overriding,
561
19
        _super,
562
19
        5225_error,
563
19
        "Public state variables can only override functions with external visibility.",
564
19
        "Overridden function is here:"
565
19
      );
566
238
    solAssert(_overriding.visibility() == Visibility::External, "");
567
238
  }
568
2.03k
  else if (_overriding.visibility() != _super.visibility())
569
177
  {
570
    // Visibility change from external to public is fine.
571
    // Any other change is disallowed.
572
177
    if (!(
573
177
      _super.visibility() == Visibility::External &&
574
160
      _overriding.visibility() == Visibility::Public
575
177
    ))
576
17
      overrideError(
577
17
        _overriding,
578
17
        _super,
579
17
        9098_error,
580
17
        "Overriding " + _overriding.astNodeName() + " visibility differs.",
581
17
        "Overridden " + _overriding.astNodeName() + " is here:"
582
17
      );
583
177
  }
584
585
2.27k
  if (_overriding.unimplemented() && !_super.unimplemented())
586
12
  {
587
12
    solAssert(!_overriding.isVariable() || !_overriding.unimplemented(), "");
588
12
    overrideError(
589
12
      _overriding,
590
12
      _super,
591
12
      4593_error,
592
12
      "Overriding an implemented " + _super.astNodeName() +
593
12
      " with an unimplemented " + _overriding.astNodeName() +
594
12
      " is not allowed."
595
12
    );
596
12
  }
597
598
2.27k
  if (_super.isFunction())
599
1.13k
  {
600
1.13k
    FunctionType const* functionType = _overriding.externalFunctionType();
601
1.13k
    FunctionType const* superType = _super.externalFunctionType();
602
603
1.13k
    bool returnTypesDifferAlready = false;
604
1.13k
    if (_overriding.functionKind() != Token::Fallback)
605
1.08k
    {
606
1.08k
      solAssert(functionType->hasEqualParameterTypes(*superType), "Override doesn't have equal parameters!");
607
608
1.08k
      if (!functionType->hasEqualReturnTypes(*superType))
609
34
      {
610
34
        returnTypesDifferAlready = true;
611
34
        overrideError(
612
34
          _overriding,
613
34
          _super,
614
34
          4822_error,
615
34
          "Overriding " + _overriding.astNodeName() + " return types differ.",
616
34
          "Overridden " + _overriding.astNodeName() + " is here:"
617
34
        );
618
34
      }
619
1.08k
    }
620
621
    // The override proxy considers calldata and memory the same data location.
622
    // Here we do a more specific check:
623
    // Data locations of parameters and return variables have to match
624
    // unless we have a public function overriding an external one.
625
1.13k
    if (
626
1.13k
      _overriding.isFunction() &&
627
977
      !returnTypesDifferAlready &&
628
959
      _super.visibility() != Visibility::External &&
629
425
      _overriding.functionKind() != Token::Fallback
630
1.13k
    )
631
407
    {
632
407
      if (!_overriding.originalFunctionType()->hasEqualParameterTypes(*_super.originalFunctionType()))
633
2
        overrideError(
634
2
          _overriding,
635
2
          _super,
636
2
          7723_error,
637
2
          "Data locations of parameters have to be the same when overriding non-external functions, but they differ.",
638
2
          "Overridden " + _overriding.astNodeName() + " is here:"
639
2
        );
640
407
      if (!_overriding.originalFunctionType()->hasEqualReturnTypes(*_super.originalFunctionType()))
641
3
        overrideError(
642
3
          _overriding,
643
3
          _super,
644
3
          1443_error,
645
3
          "Data locations of return variables have to be the same when overriding non-external functions, but they differ.",
646
3
          "Overridden " + _overriding.astNodeName() + " is here:"
647
3
        );
648
407
    }
649
650
    // Stricter mutability is always okay except when super is Payable
651
1.13k
    if (
652
1.13k
      (_overriding.isFunction() || _overriding.isVariable()) &&
653
1.13k
      (
654
1.13k
        _overriding.stateMutability() > _super.stateMutability() ||
655
1.11k
        _super.stateMutability() == StateMutability::Payable
656
1.13k
      ) &&
657
52
      _overriding.stateMutability() != _super.stateMutability()
658
1.13k
    )
659
17
      overrideError(
660
17
        _overriding,
661
17
        _super,
662
17
        6959_error,
663
17
        "Overriding " +
664
17
        _overriding.astNodeName() +
665
17
        " changes state mutability from \"" +
666
17
        stateMutabilityToString(_super.stateMutability()) +
667
17
        "\" to \"" +
668
17
        stateMutabilityToString(_overriding.stateMutability()) +
669
17
        "\"."
670
17
      );
671
1.13k
  }
672
2.27k
}
673
674
void OverrideChecker::overrideListError(
675
  OverrideProxy const& _item,
676
  std::set<ContractDefinition const*, CompareByID> _secondary,
677
  ErrorId _error,
678
  std::string const& _message1,
679
  std::string const& _message2
680
)
681
333
{
682
  // Using a set rather than a vector so the order is always the same
683
333
  std::set<std::string> names;
684
333
  SecondarySourceLocation ssl;
685
333
  for (Declaration const* c: _secondary)
686
656
  {
687
656
    ssl.append("This contract: ", c->location());
688
656
    names.insert("\"" + c->name() + "\"");
689
656
  }
690
333
  std::string contractSingularPlural = "contract ";
691
333
  if (_secondary.size() > 1)
692
217
    contractSingularPlural = "contracts ";
693
694
333
  m_errorReporter.typeError(
695
333
    _error,
696
333
    _item.overrides() ? _item.overrides()->location() : _item.location(),
697
333
    ssl,
698
333
    _message1 +
699
333
    contractSingularPlural +
700
333
    _message2 +
701
333
    joinHumanReadable(names, ", ", " and ") +
702
333
    "."
703
333
  );
704
333
}
705
706
void OverrideChecker::overrideError(
707
  OverrideProxy const& _overriding,
708
  OverrideProxy const& _super,
709
  ErrorId _error,
710
  std::string const& _message,
711
  std::optional<std::string> const& _secondaryMsg
712
)
713
2.22k
{
714
2.22k
  m_errorReporter.typeError(
715
2.22k
    _error,
716
2.22k
    _overriding.location(),
717
2.22k
    SecondarySourceLocation().append(
718
2.22k
      _secondaryMsg.value_or("Overridden " + _super.astNodeName() + " is here:"),
719
2.22k
      _super.location()
720
2.22k
    ),
721
2.22k
    _message
722
2.22k
  );
723
2.22k
}
724
725
void OverrideChecker::checkAmbiguousOverrides(ContractDefinition const& _contract) const
726
28.6k
{
727
28.6k
  {
728
    // Fetch inherited functions and sort them by signature.
729
    // We get at least one function per signature and direct base contract, which is
730
    // enough because we re-construct the inheritance graph later.
731
28.6k
    OverrideProxyBySignatureMultiSet nonOverriddenFunctions = inheritedFunctions(_contract);
732
733
    // Remove all functions that match the signature of a function in the current contract.
734
28.6k
    for (FunctionDefinition const* f: _contract.definedFunctions())
735
26.7k
      nonOverriddenFunctions.erase(OverrideProxy{f});
736
28.6k
    for (VariableDeclaration const* v: _contract.stateVariables())
737
14.0k
      if (v->isPublic())
738
5.25k
        nonOverriddenFunctions.erase(OverrideProxy{v});
739
740
    // Walk through the set of functions signature by signature.
741
31.2k
    for (auto it = nonOverriddenFunctions.cbegin(); it != nonOverriddenFunctions.cend();)
742
2.54k
    {
743
2.54k
      std::set<OverrideProxy> baseFunctions;
744
5.38k
      for (auto nextSignature = nonOverriddenFunctions.upper_bound(*it); it != nextSignature; ++it)
745
2.84k
        baseFunctions.insert(*it);
746
747
2.54k
      checkAmbiguousOverridesInternal(std::move(baseFunctions), _contract.location());
748
2.54k
    }
749
28.6k
  }
750
751
28.6k
  {
752
28.6k
    OverrideProxyBySignatureMultiSet modifiers = inheritedModifiers(_contract);
753
28.6k
    for (ModifierDefinition const* mod: _contract.functionModifiers())
754
3.24k
      modifiers.erase(OverrideProxy{mod});
755
756
32.2k
    for (auto it = modifiers.cbegin(); it != modifiers.cend();)
757
3.54k
    {
758
3.54k
      std::set<OverrideProxy> baseModifiers;
759
7.15k
      for (auto next = modifiers.upper_bound(*it); it != next; ++it)
760
3.61k
        baseModifiers.insert(*it);
761
762
3.54k
      checkAmbiguousOverridesInternal(std::move(baseModifiers), _contract.location());
763
3.54k
    }
764
765
28.6k
  }
766
28.6k
}
767
768
void OverrideChecker::checkAmbiguousOverridesInternal(std::set<OverrideProxy> _baseCallables, SourceLocation const& _location) const
769
6.08k
{
770
6.08k
  if (_baseCallables.size() <= 1)
771
5.90k
    return;
772
773
173
  OverrideGraph overrideGraph(_baseCallables);
774
173
  CutVertexFinder cutVertexFinder{overrideGraph};
775
776
  // Remove all base functions overridden by cut vertices (they don't need to be overridden).
777
173
  for (OverrideProxy const& function: cutVertexFinder.cutVertices())
778
89
  {
779
89
    std::set<OverrideProxy> toTraverse = function.baseFunctions();
780
109
    while (!toTraverse.empty())
781
20
    {
782
20
      OverrideProxy base = *toTraverse.begin();
783
20
      toTraverse.erase(toTraverse.begin());
784
20
      _baseCallables.erase(base);
785
20
      for (OverrideProxy const& f: base.baseFunctions())
786
0
        toTraverse.insert(f);
787
20
    }
788
    // Remove unimplemented base functions at the cut vertices itself as well.
789
89
    if (function.unimplemented())
790
77
      _baseCallables.erase(function);
791
89
  }
792
793
  // If more than one function is left, they have to be overridden.
794
173
  if (_baseCallables.size() <= 1)
795
68
    return;
796
797
105
  SecondarySourceLocation ssl;
798
105
  for (OverrideProxy const& baseFunction: _baseCallables)
799
210
    ssl.append("Definition in \"" + baseFunction.contractName() + "\": ", baseFunction.location());
800
801
105
  std::string callableName = _baseCallables.begin()->astNodeName();
802
105
  if (_baseCallables.begin()->isVariable())
803
5
    callableName = "function";
804
105
  std::string distinguishingProperty = _baseCallables.begin()->distinguishingProperty();
805
806
105
  bool foundVariable = false;
807
105
  for (auto const& base: _baseCallables)
808
210
    if (base.isVariable())
809
16
      foundVariable = true;
810
811
105
  std::string message =
812
105
    "Derived contract must override " + callableName + " \"" +
813
105
    _baseCallables.begin()->name() +
814
105
    "\". Two or more base classes define " + callableName + " with same " + distinguishingProperty + ".";
815
816
105
  if (foundVariable)
817
14
    message +=
818
14
      " Since one of the bases defines a public state variable which cannot be overridden, "
819
14
      "you have to change the inheritance layout or the names of the functions.";
820
821
105
  m_errorReporter.typeError(6480_error, _location, ssl, message);
822
105
}
823
824
std::set<ContractDefinition const*, OverrideChecker::CompareByID> OverrideChecker::resolveOverrideList(OverrideSpecifier const& _overrides) const
825
1.05k
{
826
1.05k
  std::set<ContractDefinition const*, CompareByID> resolved;
827
828
1.05k
  for (ASTPointer<IdentifierPath> const& override: _overrides.overrides())
829
1.49k
  {
830
1.49k
    Declaration const* decl  = override->annotation().referencedDeclaration;
831
1.49k
    solAssert(decl, "Expected declaration to be resolved.");
832
833
    // If it's not a contract it will be caught
834
    // in the reference resolver
835
1.49k
    if (ContractDefinition const* contract = dynamic_cast<decltype(contract)>(decl))
836
1.49k
      resolved.insert(contract);
837
1.49k
  }
838
839
1.05k
  return resolved;
840
1.05k
}
841
842
void OverrideChecker::checkOverrideList(OverrideProxy _item, OverrideProxyBySignatureMultiSet const& _inherited)
843
32.6k
{
844
32.6k
  std::set<ContractDefinition const*, CompareByID> specifiedContracts =
845
32.6k
    _item.overrides() ?
846
1.05k
    resolveOverrideList(*_item.overrides()) :
847
32.6k
    decltype(specifiedContracts){};
848
849
  // Check for duplicates in override list
850
32.6k
  if (_item.overrides() && specifiedContracts.size() != _item.overrides()->overrides().size())
851
108
  {
852
    // Sort by contract id to find duplicate for error reporting
853
108
    std::vector<ASTPointer<IdentifierPath>> list =
854
108
      sortByContract(_item.overrides()->overrides());
855
856
    // Find duplicates and output error
857
914
    for (size_t i = 1; i < list.size(); i++)
858
806
    {
859
806
      Declaration const* aDecl = list[i]->annotation().referencedDeclaration;
860
806
      Declaration const* bDecl = list[i-1]->annotation().referencedDeclaration;
861
806
      if (!aDecl || !bDecl)
862
0
        continue;
863
864
806
      if (aDecl->id() == bDecl->id())
865
585
      {
866
585
        SecondarySourceLocation ssl;
867
585
        ssl.append("First occurrence here: ", list[i-1]->location());
868
585
        m_errorReporter.typeError(
869
585
          4520_error,
870
585
          list[i]->location(),
871
585
          ssl,
872
585
          "Duplicate contract \"" +
873
585
          joinHumanReadable(list[i]->path(), ".") +
874
585
          "\" found in override list of \"" +
875
585
          _item.name() +
876
585
          "\"."
877
585
        );
878
585
      }
879
806
    }
880
108
  }
881
882
32.6k
  std::set<ContractDefinition const*, CompareByID> expectedContracts;
883
884
  // Build list of expected contracts
885
34.9k
  for (auto [begin, end] = _inherited.equal_range(_item); begin != end; begin++)
886
2.27k
  {
887
    // Validate the override
888
2.27k
    checkOverride(_item, *begin);
889
890
2.27k
    expectedContracts.insert(&begin->contract());
891
2.27k
  }
892
893
32.6k
  if (_item.overrides() && expectedContracts.empty())
894
285
    m_errorReporter.typeError(
895
285
      7792_error,
896
285
      _item.overrides()->location(),
897
285
      _item.astNodeNameCapitalized() + " has override specified but does not override anything."
898
285
    );
899
900
32.6k
  std::set<ContractDefinition const*, CompareByID> missingContracts;
901
  // If we expect only one contract, no contract needs to be specified
902
32.6k
  if (expectedContracts.size() > 1)
903
209
    missingContracts = expectedContracts - specifiedContracts;
904
905
32.6k
  if (!missingContracts.empty())
906
71
    overrideListError(
907
71
      _item,
908
71
      missingContracts,
909
71
      4327_error,
910
71
      _item.astNodeNameCapitalized() + " needs to specify overridden ",
911
71
      ""
912
71
    );
913
914
32.6k
  auto surplusContracts = specifiedContracts - expectedContracts;
915
32.6k
  if (!surplusContracts.empty())
916
262
    overrideListError(
917
262
      _item,
918
262
      surplusContracts,
919
262
      2353_error,
920
262
      "Invalid ",
921
262
      "specified in override list: "
922
262
    );
923
32.6k
}
924
925
OverrideChecker::OverrideProxyBySignatureMultiSet const& OverrideChecker::inheritedFunctions(ContractDefinition const& _contract) const
926
62.3k
{
927
62.3k
  if (!m_inheritedFunctions.count(&_contract))
928
28.6k
  {
929
28.6k
    OverrideProxyBySignatureMultiSet result;
930
931
28.6k
    for (auto const* base: resolveDirectBaseContracts(_contract))
932
4.95k
    {
933
4.95k
      std::set<OverrideProxy, OverrideProxy::CompareBySignature> functionsInBase;
934
4.95k
      for (FunctionDefinition const* fun: base->definedFunctions())
935
3.61k
        if (!fun->isConstructor())
936
2.87k
          functionsInBase.emplace(OverrideProxy{fun});
937
4.95k
      for (VariableDeclaration const* var: base->stateVariables())
938
1.51k
        if (var->isPublic())
939
1.16k
          functionsInBase.emplace(OverrideProxy{var});
940
941
4.95k
      result += functionsInBase;
942
943
4.95k
      for (OverrideProxy const& func: inheritedFunctions(*base))
944
886
        if (!functionsInBase.count(func))
945
602
          result.insert(func);
946
4.95k
    }
947
948
28.6k
    m_inheritedFunctions[&_contract] = result;
949
28.6k
  }
950
951
62.3k
  return m_inheritedFunctions[&_contract];
952
62.3k
}
953
954
OverrideChecker::OverrideProxyBySignatureMultiSet const& OverrideChecker::inheritedModifiers(ContractDefinition const& _contract) const
955
62.3k
{
956
62.3k
  if (!m_inheritedModifiers.count(&_contract))
957
28.6k
  {
958
28.6k
    OverrideProxyBySignatureMultiSet result;
959
960
28.6k
    for (auto const* base: resolveDirectBaseContracts(_contract))
961
4.95k
    {
962
4.95k
      std::set<OverrideProxy, OverrideProxy::CompareBySignature> modifiersInBase;
963
4.95k
      for (ModifierDefinition const* mod: base->functionModifiers())
964
4.66k
        modifiersInBase.emplace(OverrideProxy{mod});
965
966
4.95k
      for (OverrideProxy const& mod: inheritedModifiers(*base))
967
759
        modifiersInBase.insert(mod);
968
969
4.95k
      result += modifiersInBase;
970
4.95k
    }
971
972
28.6k
    m_inheritedModifiers[&_contract] = result;
973
28.6k
  }
974
975
62.3k
  return m_inheritedModifiers[&_contract];
976
62.3k
}