/src/solidity/libsolidity/analysis/OverrideChecker.cpp
Line | Count | Source (jump to first uncovered line) |
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 | 299 | { |
51 | 299 | return _item.name() == m_name; |
52 | 299 | } |
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 | 150 | { |
65 | 150 | for (auto const& baseFunction: _baseCallables) |
66 | 305 | addEdge(0, visit(baseFunction)); |
67 | 150 | } |
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 | 649 | { |
74 | 649 | edges[_a].insert(_b); |
75 | 649 | edges[_b].insert(_a); |
76 | 649 | } |
77 | | private: |
78 | | /// Completes the graph starting from @a _function and |
79 | | /// @returns the node ID. |
80 | | int visit(OverrideProxy const& _function) |
81 | 424 | { |
82 | 424 | auto it = nodes.find(_function); |
83 | 424 | if (it != nodes.end()) |
84 | 92 | return it->second; |
85 | 332 | int currentNode = static_cast<int>(numNodes++); |
86 | 332 | nodes[_function] = currentNode; |
87 | 332 | nodeInv[currentNode] = _function; |
88 | | |
89 | 332 | if (!_function.baseFunctions().empty()) |
90 | 107 | for (auto const& baseFunction: _function.baseFunctions()) |
91 | 119 | addEdge(currentNode, visit(baseFunction)); |
92 | 225 | else |
93 | 225 | addEdge(currentNode, 1); |
94 | | |
95 | 332 | return currentNode; |
96 | 424 | } |
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 | | CutVertexFinder(OverrideGraph const& _graph): m_graph(_graph) |
106 | 150 | { |
107 | 150 | run(); |
108 | 150 | } |
109 | 150 | 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 | 632 | { |
122 | 632 | m_visited.at(_u) = true; |
123 | 632 | m_depths.at(_u) = m_low.at(_u) = static_cast<int>(_depth); |
124 | 632 | for (int const v: m_graph.edges.at(static_cast<int>(_u))) |
125 | 1.29k | { |
126 | 1.29k | auto const vInd = static_cast<size_t>(v); |
127 | 1.29k | if (!m_visited.at(vInd)) |
128 | 482 | { |
129 | 482 | m_parent[vInd] = static_cast<int>(_u); |
130 | 482 | run(vInd, _depth + 1); |
131 | 482 | if (m_low[vInd] >= m_depths[_u] && m_parent[_u] != -1) |
132 | 86 | m_cutVertices.insert(m_graph.nodeInv.at(static_cast<int>(_u))); |
133 | 482 | m_low[_u] = std::min(m_low[_u], m_low[vInd]); |
134 | 482 | } |
135 | 816 | else if (v != m_parent[_u]) |
136 | 334 | m_low[_u] = std::min(m_low[_u], m_depths[vInd]); |
137 | 1.29k | } |
138 | 632 | } |
139 | | }; |
140 | | |
141 | | std::vector<ContractDefinition const*> resolveDirectBaseContracts(ContractDefinition const& _contract) |
142 | 53.0k | { |
143 | 53.0k | std::vector<ContractDefinition const*> resolvedContracts; |
144 | | |
145 | 53.0k | for (ASTPointer<InheritanceSpecifier> const& specifier: _contract.baseContracts()) |
146 | 9.13k | { |
147 | 9.13k | Declaration const* baseDecl = |
148 | 9.13k | specifier->name().annotation().referencedDeclaration; |
149 | 9.13k | auto contract = dynamic_cast<ContractDefinition const*>(baseDecl); |
150 | 9.13k | if (contract) |
151 | 9.13k | resolvedContracts.emplace_back(contract); |
152 | 9.13k | } |
153 | | |
154 | 53.0k | return resolvedContracts; |
155 | 53.0k | } |
156 | | |
157 | | std::vector<ASTPointer<IdentifierPath>> sortByContract(std::vector<ASTPointer<IdentifierPath>> const& _list) |
158 | 106 | { |
159 | 106 | auto sorted = _list; |
160 | | |
161 | 106 | stable_sort(sorted.begin(), sorted.end(), |
162 | 2.39k | [] (ASTPointer<IdentifierPath> _a, ASTPointer<IdentifierPath> _b) { |
163 | 2.39k | if (!_a || !_b) |
164 | 0 | return _a < _b; |
165 | | |
166 | 2.39k | Declaration const* aDecl = _a->annotation().referencedDeclaration; |
167 | 2.39k | Declaration const* bDecl = _b->annotation().referencedDeclaration; |
168 | | |
169 | 2.39k | if (!aDecl || !bDecl) |
170 | 0 | return aDecl < bDecl; |
171 | | |
172 | 2.39k | return aDecl->id() < bDecl->id(); |
173 | 2.39k | } |
174 | 106 | ); |
175 | | |
176 | 106 | return sorted; |
177 | 106 | } |
178 | | |
179 | | OverrideProxy makeOverrideProxy(CallableDeclaration const& _callable) |
180 | 253 | { |
181 | 253 | if (auto const* fun = dynamic_cast<FunctionDefinition const*>(&_callable)) |
182 | 222 | return OverrideProxy{fun}; |
183 | 31 | else if (auto const* mod = dynamic_cast<ModifierDefinition const*>(&_callable)) |
184 | 31 | return OverrideProxy{mod}; |
185 | 0 | else |
186 | 31 | solAssert(false, "Invalid call to makeOverrideProxy."); |
187 | 0 | return {}; |
188 | 253 | } |
189 | | |
190 | | } |
191 | | |
192 | | bool OverrideProxy::operator<(OverrideProxy const& _other) const |
193 | 1.75k | { |
194 | 1.75k | return id() < _other.id(); |
195 | 1.75k | } |
196 | | |
197 | | bool OverrideProxy::isVariable() const |
198 | 4.23k | { |
199 | 4.23k | return std::holds_alternative<VariableDeclaration const*>(m_item); |
200 | 4.23k | } |
201 | | |
202 | | bool OverrideProxy::isFunction() const |
203 | 6.86k | { |
204 | 6.86k | return std::holds_alternative<FunctionDefinition const*>(m_item); |
205 | 6.86k | } |
206 | | |
207 | | bool OverrideProxy::isModifier() const |
208 | 6.63k | { |
209 | 6.63k | return std::holds_alternative<ModifierDefinition const*>(m_item); |
210 | 6.63k | } |
211 | | |
212 | | bool OverrideProxy::CompareBySignature::operator()(OverrideProxy const& _a, OverrideProxy const& _b) const |
213 | 151k | { |
214 | 151k | return _a.overrideComparator() < _b.overrideComparator(); |
215 | 151k | } |
216 | | |
217 | | size_t OverrideProxy::id() const |
218 | 3.50k | { |
219 | 3.50k | return std::visit(GenericVisitor{ |
220 | 3.50k | [&](auto const* _item) -> size_t { return static_cast<size_t>(_item->id()); } OverrideChecker.cpp:unsigned long solidity::frontend::OverrideProxy::id() const::$_1::operator()<solidity::frontend::FunctionDefinition>(solidity::frontend::FunctionDefinition const*) const Line | Count | Source | 220 | 2.78k | [&](auto const* _item) -> size_t { return static_cast<size_t>(_item->id()); } |
OverrideChecker.cpp:unsigned long solidity::frontend::OverrideProxy::id() const::$_1::operator()<solidity::frontend::ModifierDefinition>(solidity::frontend::ModifierDefinition const*) const Line | Count | Source | 220 | 528 | [&](auto const* _item) -> size_t { return static_cast<size_t>(_item->id()); } |
OverrideChecker.cpp:unsigned long solidity::frontend::OverrideProxy::id() const::$_1::operator()<solidity::frontend::VariableDeclaration>(solidity::frontend::VariableDeclaration const*) const Line | Count | Source | 220 | 194 | [&](auto const* _item) -> size_t { return static_cast<size_t>(_item->id()); } |
|
221 | 3.50k | }, m_item); |
222 | 3.50k | } |
223 | | |
224 | | std::shared_ptr<OverrideSpecifier> OverrideProxy::overrides() const |
225 | 94.7k | { |
226 | 94.7k | return std::visit(GenericVisitor{ |
227 | 94.7k | [&](auto const* _item) { return _item->overrides(); } OverrideChecker.cpp:auto solidity::frontend::OverrideProxy::overrides() const::$_2::operator()<solidity::frontend::FunctionDefinition>(solidity::frontend::FunctionDefinition const*) const Line | Count | Source | 227 | 70.6k | [&](auto const* _item) { return _item->overrides(); } |
OverrideChecker.cpp:auto solidity::frontend::OverrideProxy::overrides() const::$_2::operator()<solidity::frontend::ModifierDefinition>(solidity::frontend::ModifierDefinition const*) const Line | Count | Source | 227 | 10.4k | [&](auto const* _item) { return _item->overrides(); } |
OverrideChecker.cpp:auto solidity::frontend::OverrideProxy::overrides() const::$_2::operator()<solidity::frontend::VariableDeclaration>(solidity::frontend::VariableDeclaration const*) const Line | Count | Source | 227 | 13.6k | [&](auto const* _item) { return _item->overrides(); } |
|
228 | 94.7k | }, m_item); |
229 | 94.7k | } |
230 | | |
231 | | std::set<OverrideProxy> OverrideProxy::baseFunctions() const |
232 | 540 | { |
233 | 540 | return std::visit(GenericVisitor{ |
234 | 540 | [&](auto const* _item) -> std::set<OverrideProxy> { |
235 | 540 | std::set<OverrideProxy> ret; |
236 | 540 | for (auto const* f: _item->annotation().baseFunctions) |
237 | 253 | ret.insert(makeOverrideProxy(*f)); |
238 | 540 | return ret; |
239 | 540 | } 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::$_3::operator()<solidity::frontend::FunctionDefinition>(solidity::frontend::FunctionDefinition const*) const Line | Count | Source | 234 | 452 | [&](auto const* _item) -> std::set<OverrideProxy> { | 235 | 452 | std::set<OverrideProxy> ret; | 236 | 452 | for (auto const* f: _item->annotation().baseFunctions) | 237 | 216 | ret.insert(makeOverrideProxy(*f)); | 238 | 452 | return ret; | 239 | 452 | } |
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::$_3::operator()<solidity::frontend::ModifierDefinition>(solidity::frontend::ModifierDefinition const*) const Line | Count | Source | 234 | 65 | [&](auto const* _item) -> std::set<OverrideProxy> { | 235 | 65 | std::set<OverrideProxy> ret; | 236 | 65 | for (auto const* f: _item->annotation().baseFunctions) | 237 | 31 | ret.insert(makeOverrideProxy(*f)); | 238 | 65 | return ret; | 239 | 65 | } |
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::$_3::operator()<solidity::frontend::VariableDeclaration>(solidity::frontend::VariableDeclaration const*) const Line | Count | Source | 234 | 23 | [&](auto const* _item) -> std::set<OverrideProxy> { | 235 | 23 | std::set<OverrideProxy> ret; | 236 | 23 | for (auto const* f: _item->annotation().baseFunctions) | 237 | 6 | ret.insert(makeOverrideProxy(*f)); | 238 | 23 | return ret; | 239 | 23 | } |
|
240 | 540 | }, m_item); |
241 | 540 | } |
242 | | |
243 | | void OverrideProxy::storeBaseFunction(OverrideProxy const& _base) const |
244 | 1.83k | { |
245 | 1.83k | std::visit(GenericVisitor{ |
246 | 1.83k | [&](FunctionDefinition const* _item) { |
247 | 826 | _item->annotation().baseFunctions.emplace(std::get<FunctionDefinition const*>(_base.m_item)); |
248 | 826 | }, |
249 | 1.83k | [&](ModifierDefinition const* _item) { |
250 | 856 | _item->annotation().baseFunctions.emplace(std::get<ModifierDefinition const*>(_base.m_item)); |
251 | 856 | }, |
252 | 1.83k | [&](VariableDeclaration const* _item) { |
253 | 156 | _item->annotation().baseFunctions.emplace(std::get<FunctionDefinition const*>(_base.m_item)); |
254 | 156 | } |
255 | 1.83k | }, m_item); |
256 | 1.83k | } |
257 | | |
258 | | std::string const& OverrideProxy::name() const |
259 | 1.04k | { |
260 | 1.04k | return std::visit(GenericVisitor{ |
261 | 1.04k | [&](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::$_7::operator()<solidity::frontend::FunctionDefinition>(solidity::frontend::FunctionDefinition const*) const Line | Count | Source | 261 | 87 | [&](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::$_7::operator()<solidity::frontend::ModifierDefinition>(solidity::frontend::ModifierDefinition const*) const Line | Count | Source | 261 | 944 | [&](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::$_7::operator()<solidity::frontend::VariableDeclaration>(solidity::frontend::VariableDeclaration const*) const Line | Count | Source | 261 | 13 | [&](auto const* _item) -> std::string const& { return _item->name(); } |
|
262 | 1.04k | }, m_item); |
263 | 1.04k | } |
264 | | |
265 | | ContractDefinition const& OverrideProxy::contract() const |
266 | 2.37k | { |
267 | 2.37k | return std::visit(GenericVisitor{ |
268 | 2.37k | [&](auto const* _item) -> ContractDefinition const& { |
269 | 2.37k | return dynamic_cast<ContractDefinition const&>(*_item->scope()); |
270 | 2.37k | } OverrideChecker.cpp:solidity::frontend::ContractDefinition const& solidity::frontend::OverrideProxy::contract() const::$_8::operator()<solidity::frontend::FunctionDefinition>(solidity::frontend::FunctionDefinition const*) const Line | Count | Source | 268 | 1.39k | [&](auto const* _item) -> ContractDefinition const& { | 269 | 1.39k | return dynamic_cast<ContractDefinition const&>(*_item->scope()); | 270 | 1.39k | } |
OverrideChecker.cpp:solidity::frontend::ContractDefinition const& solidity::frontend::OverrideProxy::contract() const::$_8::operator()<solidity::frontend::ModifierDefinition>(solidity::frontend::ModifierDefinition const*) const Line | Count | Source | 268 | 892 | [&](auto const* _item) -> ContractDefinition const& { | 269 | 892 | return dynamic_cast<ContractDefinition const&>(*_item->scope()); | 270 | 892 | } |
OverrideChecker.cpp:solidity::frontend::ContractDefinition const& solidity::frontend::OverrideProxy::contract() const::$_8::operator()<solidity::frontend::VariableDeclaration>(solidity::frontend::VariableDeclaration const*) const Line | Count | Source | 268 | 85 | [&](auto const* _item) -> ContractDefinition const& { | 269 | 85 | return dynamic_cast<ContractDefinition const&>(*_item->scope()); | 270 | 85 | } |
|
271 | 2.37k | }, m_item); |
272 | 2.37k | } |
273 | | |
274 | | std::string const& OverrideProxy::contractName() const |
275 | 165 | { |
276 | 165 | return contract().name(); |
277 | 165 | } |
278 | | |
279 | | Visibility OverrideProxy::visibility() const |
280 | 4.89k | { |
281 | 4.89k | return std::visit(GenericVisitor{ |
282 | 4.89k | [&](FunctionDefinition const* _item) { return _item->visibility(); }, |
283 | 4.89k | [&](ModifierDefinition const* _item) { return _item->visibility(); }, |
284 | 4.89k | [&](VariableDeclaration const*) { return Visibility::External; } |
285 | 4.89k | }, m_item); |
286 | 4.89k | } |
287 | | |
288 | | StateMutability OverrideProxy::stateMutability() const |
289 | 3.03k | { |
290 | 3.03k | return std::visit(GenericVisitor{ |
291 | 3.03k | [&](FunctionDefinition const* _item) { return _item->stateMutability(); }, |
292 | 3.03k | [&](ModifierDefinition const*) { solAssert(false, "Requested state mutability from modifier."); return StateMutability{}; }, |
293 | 3.03k | [&](VariableDeclaration const* _var) { return _var->isConstant() ? StateMutability::Pure : StateMutability::View; } |
294 | 3.03k | }, m_item); |
295 | 3.03k | } |
296 | | |
297 | | bool OverrideProxy::virtualSemantics() const |
298 | 1.83k | { |
299 | 1.83k | return std::visit(GenericVisitor{ |
300 | 1.83k | [&](FunctionDefinition const* _item) { return _item->virtualSemantics(); }, |
301 | 1.83k | [&](ModifierDefinition const* _item) { return _item->virtualSemantics(); }, |
302 | 1.83k | [&](VariableDeclaration const*) { return false; } |
303 | 1.83k | }, m_item); |
304 | 1.83k | } |
305 | | |
306 | | Token OverrideProxy::functionKind() const |
307 | 1.34k | { |
308 | 1.34k | return std::visit(GenericVisitor{ |
309 | 1.34k | [&](FunctionDefinition const* _item) { return _item->kind(); }, |
310 | 1.34k | [&](ModifierDefinition const*) { return Token::Function; }, |
311 | 1.34k | [&](VariableDeclaration const*) { return Token::Function; } |
312 | 1.34k | }, m_item); |
313 | 1.34k | } |
314 | | |
315 | | FunctionType const* OverrideProxy::externalFunctionType() const |
316 | 26.9k | { |
317 | 26.9k | return std::visit(GenericVisitor{ |
318 | 26.9k | [&](FunctionDefinition const* _item) { return FunctionType(*_item).asExternallyCallableFunction(false); }, |
319 | 26.9k | [&](VariableDeclaration const* _item) { return FunctionType(*_item).asExternallyCallableFunction(false); }, |
320 | 26.9k | [&](ModifierDefinition const*) -> FunctionType const* { solAssert(false, "Requested function type of modifier."); return nullptr; } |
321 | 26.9k | }, m_item); |
322 | 26.9k | } |
323 | | |
324 | | FunctionType const* OverrideProxy::originalFunctionType() const |
325 | 1.43k | { |
326 | 1.43k | return std::visit(GenericVisitor{ |
327 | 1.43k | [&](FunctionDefinition const* _item) { return TypeProvider::function(*_item); }, |
328 | 1.43k | [&](VariableDeclaration const*) -> FunctionType const* { solAssert(false, "Requested specific function type of variable."); return nullptr; }, |
329 | 1.43k | [&](ModifierDefinition const*) -> FunctionType const* { solAssert(false, "Requested specific function type of modifier."); return nullptr; } |
330 | 1.43k | }, m_item); |
331 | 1.43k | } |
332 | | |
333 | | ModifierType const* OverrideProxy::modifierType() const |
334 | 1.71k | { |
335 | 1.71k | return std::visit(GenericVisitor{ |
336 | 1.71k | [&](FunctionDefinition const*) -> ModifierType const* { solAssert(false, "Requested modifier type of function."); return nullptr; }, |
337 | 1.71k | [&](VariableDeclaration const*) -> ModifierType const* { solAssert(false, "Requested modifier type of variable."); return nullptr; }, |
338 | 1.71k | [&](ModifierDefinition const* _modifier) -> ModifierType const* { return TypeProvider::modifier(*_modifier); } |
339 | 1.71k | }, m_item); |
340 | 1.71k | } |
341 | | |
342 | | |
343 | | Declaration const* OverrideProxy::declaration() const |
344 | 6.15k | { |
345 | 6.15k | return std::visit(GenericVisitor{ |
346 | 6.15k | [&](FunctionDefinition const* _function) -> Declaration const* { return _function; }, |
347 | 6.15k | [&](VariableDeclaration const* _variable) -> Declaration const* { return _variable; }, |
348 | 6.15k | [&](ModifierDefinition const* _modifier) -> Declaration const* { return _modifier; } |
349 | 6.15k | }, m_item); |
350 | 6.15k | } |
351 | | |
352 | | SourceLocation const& OverrideProxy::location() const |
353 | 3.82k | { |
354 | 3.82k | return std::visit(GenericVisitor{ |
355 | 3.82k | [&](auto const* _item) -> SourceLocation const& { return _item->location(); } OverrideChecker.cpp:solidity::langutil::SourceLocation const& solidity::frontend::OverrideProxy::location() const::$_33::operator()<solidity::frontend::FunctionDefinition>(solidity::frontend::FunctionDefinition const*) const Line | Count | Source | 355 | 456 | [&](auto const* _item) -> SourceLocation const& { return _item->location(); } |
OverrideChecker.cpp:solidity::langutil::SourceLocation const& solidity::frontend::OverrideProxy::location() const::$_33::operator()<solidity::frontend::ModifierDefinition>(solidity::frontend::ModifierDefinition const*) const Line | Count | Source | 355 | 3.09k | [&](auto const* _item) -> SourceLocation const& { return _item->location(); } |
OverrideChecker.cpp:solidity::langutil::SourceLocation const& solidity::frontend::OverrideProxy::location() const::$_33::operator()<solidity::frontend::VariableDeclaration>(solidity::frontend::VariableDeclaration const*) const Line | Count | Source | 355 | 275 | [&](auto const* _item) -> SourceLocation const& { return _item->location(); } |
|
356 | 3.82k | }, m_item); |
357 | 3.82k | } |
358 | | |
359 | | std::string OverrideProxy::astNodeName() const |
360 | 5.30k | { |
361 | 5.30k | return std::visit(GenericVisitor{ |
362 | 5.30k | [&](FunctionDefinition const*) { return "function"; }, |
363 | 5.30k | [&](ModifierDefinition const*) { return "modifier"; }, |
364 | 5.30k | [&](VariableDeclaration const*) { return "public state variable"; }, |
365 | 5.30k | }, m_item); |
366 | 5.30k | } |
367 | | |
368 | | std::string OverrideProxy::astNodeNameCapitalized() const |
369 | 331 | { |
370 | 331 | return std::visit(GenericVisitor{ |
371 | 331 | [&](FunctionDefinition const*) { return "Function"; }, |
372 | 331 | [&](ModifierDefinition const*) { return "Modifier"; }, |
373 | 331 | [&](VariableDeclaration const*) { return "Public state variable"; }, |
374 | 331 | }, m_item); |
375 | 331 | } |
376 | | |
377 | | std::string OverrideProxy::distinguishingProperty() const |
378 | 80 | { |
379 | 80 | return std::visit(GenericVisitor{ |
380 | 80 | [&](FunctionDefinition const*) { return "name and parameter types"; }, |
381 | 80 | [&](ModifierDefinition const*) { return "name"; }, |
382 | 80 | [&](VariableDeclaration const*) { return "name and parameter types"; }, |
383 | 80 | }, m_item); |
384 | 80 | } |
385 | | |
386 | | bool OverrideProxy::unimplemented() const |
387 | 74.8k | { |
388 | 74.8k | return std::visit(GenericVisitor{ |
389 | 74.8k | [&](FunctionDefinition const* _item) { return !_item->isImplemented(); }, |
390 | 74.8k | [&](ModifierDefinition const* _item) { return !_item->isImplemented(); }, |
391 | 74.8k | [&](VariableDeclaration const*) { return false; } |
392 | 74.8k | }, m_item); |
393 | 74.8k | } |
394 | | |
395 | | bool OverrideProxy::OverrideComparator::operator<(OverrideComparator const& _other) const |
396 | 151k | { |
397 | 151k | if (name != _other.name) |
398 | 122k | return name < _other.name; |
399 | | |
400 | 29.5k | if (!functionKind || !_other.functionKind) |
401 | 11.5k | return false; |
402 | | |
403 | 17.9k | if (functionKind != _other.functionKind) |
404 | 77 | return *functionKind < *_other.functionKind; |
405 | | |
406 | | // Parameters do not matter for non-regular functions. |
407 | 17.8k | if (functionKind != Token::Function) |
408 | 637 | return false; |
409 | | |
410 | 17.2k | if (!parameterTypes || !_other.parameterTypes) |
411 | 0 | return false; |
412 | | |
413 | 17.2k | return boost::lexicographical_compare(*parameterTypes, *_other.parameterTypes); |
414 | 17.2k | } |
415 | | |
416 | | OverrideProxy::OverrideComparator const& OverrideProxy::overrideComparator() const |
417 | 303k | { |
418 | 303k | if (!m_comparator) |
419 | 38.5k | { |
420 | 38.5k | m_comparator = std::make_shared<OverrideComparator>(std::visit(GenericVisitor{ |
421 | 38.5k | [&](FunctionDefinition const* _function) |
422 | 38.5k | { |
423 | 19.1k | std::vector<std::string> paramTypes; |
424 | 19.1k | for (Type const* t: externalFunctionType()->parameterTypes()) |
425 | 10.5k | paramTypes.emplace_back(t->richIdentifier()); |
426 | 19.1k | return OverrideComparator{ |
427 | 19.1k | _function->name(), |
428 | 19.1k | _function->kind(), |
429 | 19.1k | std::move(paramTypes) |
430 | 19.1k | }; |
431 | 19.1k | }, |
432 | 38.5k | [&](VariableDeclaration const* _var) |
433 | 38.5k | { |
434 | 5.89k | std::vector<std::string> paramTypes; |
435 | 5.89k | for (Type const* t: externalFunctionType()->parameterTypes()) |
436 | 970 | paramTypes.emplace_back(t->richIdentifier()); |
437 | 5.89k | return OverrideComparator{ |
438 | 5.89k | _var->name(), |
439 | 5.89k | Token::Function, |
440 | 5.89k | std::move(paramTypes) |
441 | 5.89k | }; |
442 | 5.89k | }, |
443 | 38.5k | [&](ModifierDefinition const* _mod) |
444 | 38.5k | { |
445 | 13.4k | return OverrideComparator{ |
446 | 13.4k | _mod->name(), |
447 | 13.4k | {}, |
448 | 13.4k | {} |
449 | 13.4k | }; |
450 | 13.4k | } |
451 | 38.5k | }, m_item)); |
452 | 38.5k | } |
453 | | |
454 | 303k | return *m_comparator; |
455 | 303k | } |
456 | | |
457 | | bool OverrideChecker::CompareByID::operator()(ContractDefinition const* _a, ContractDefinition const* _b) const |
458 | 5.79k | { |
459 | 5.79k | if (!_a || !_b) |
460 | 0 | return _a < _b; |
461 | | |
462 | 5.79k | return _a->id() < _b->id(); |
463 | 5.79k | } |
464 | | |
465 | | void OverrideChecker::check(ContractDefinition const& _contract) |
466 | 26.5k | { |
467 | 26.5k | checkIllegalOverrides(_contract); |
468 | 26.5k | checkAmbiguousOverrides(_contract); |
469 | 26.5k | } |
470 | | |
471 | | void OverrideChecker::checkIllegalOverrides(ContractDefinition const& _contract) |
472 | 26.5k | { |
473 | 26.5k | OverrideProxyBySignatureMultiSet const& inheritedFuncs = inheritedFunctions(_contract); |
474 | 26.5k | OverrideProxyBySignatureMultiSet const& inheritedMods = inheritedModifiers(_contract); |
475 | | |
476 | 26.5k | for (ModifierDefinition const* modifier: _contract.functionModifiers()) |
477 | 2.88k | { |
478 | 2.88k | 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 | 2.88k | checkOverrideList(OverrideProxy{modifier}, inheritedMods); |
486 | 2.88k | } |
487 | | |
488 | 26.5k | for (FunctionDefinition const* function: _contract.definedFunctions()) |
489 | 25.1k | { |
490 | 25.1k | if (function->isConstructor()) |
491 | 2.26k | continue; |
492 | | |
493 | 22.8k | if (contains_if(inheritedMods, MatchByName{function->name()})) |
494 | 2 | m_errorReporter.typeError(1469_error, function->location(), "Override changes modifier to function."); |
495 | | |
496 | 22.8k | checkOverrideList(OverrideProxy{function}, inheritedFuncs); |
497 | 22.8k | } |
498 | 26.5k | for (auto const* stateVar: _contract.stateVariables()) |
499 | 12.3k | { |
500 | 12.3k | if (!stateVar->isPublic()) |
501 | 8.07k | { |
502 | 8.07k | if (stateVar->overrides()) |
503 | 2 | m_errorReporter.typeError(8022_error, stateVar->location(), "Override can only be used with public state variables."); |
504 | | |
505 | 8.07k | continue; |
506 | 8.07k | } |
507 | | |
508 | 4.24k | 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 | 4.24k | checkOverrideList(OverrideProxy{stateVar}, inheritedFuncs); |
512 | 4.24k | } |
513 | | |
514 | 26.5k | } |
515 | | |
516 | | void OverrideChecker::checkOverride(OverrideProxy const& _overriding, OverrideProxy const& _super) |
517 | 1.90k | { |
518 | 1.90k | solAssert(_super.isModifier() == _overriding.isModifier(), ""); |
519 | | |
520 | 1.90k | if (_super.isFunction() || _super.isModifier()) |
521 | 1.83k | _overriding.storeBaseFunction(_super); |
522 | | |
523 | 1.90k | if (_overriding.isModifier() && *_overriding.modifierType() != *_super.modifierType()) |
524 | 47 | m_errorReporter.typeError( |
525 | 47 | 1078_error, |
526 | 47 | _overriding.location(), |
527 | 47 | "Override changes modifier signature." |
528 | 47 | ); |
529 | | |
530 | 1.90k | if (!_overriding.overrides() && !(_super.isFunction() && _super.contract().isInterface())) |
531 | 853 | overrideError( |
532 | 853 | _overriding, |
533 | 853 | _super, |
534 | 853 | 9456_error, |
535 | 853 | "Overriding " + _overriding.astNodeName() + " is missing \"override\" specifier.", |
536 | 853 | "Overridden " + _overriding.astNodeName() + " is here:" |
537 | 853 | ); |
538 | | |
539 | 1.90k | if (_super.isVariable()) |
540 | 67 | overrideError( |
541 | 67 | _super, |
542 | 67 | _overriding, |
543 | 67 | 1452_error, |
544 | 67 | "Cannot override public state variable.", |
545 | 67 | "Overriding " + _overriding.astNodeName() + " is here:" |
546 | 67 | ); |
547 | 1.83k | else if (!_super.virtualSemantics()) |
548 | 779 | overrideError( |
549 | 779 | _super, |
550 | 779 | _overriding, |
551 | 779 | 4334_error, |
552 | 779 | "Trying to override non-virtual " + _super.astNodeName() + ". Did you forget to add \"virtual\"?", |
553 | 779 | "Overriding " + _overriding.astNodeName() + " is here:" |
554 | 779 | ); |
555 | | |
556 | 1.90k | if (_overriding.isVariable()) |
557 | 211 | { |
558 | 211 | if (_super.visibility() != Visibility::External) |
559 | 16 | overrideError( |
560 | 16 | _overriding, |
561 | 16 | _super, |
562 | 16 | 5225_error, |
563 | 16 | "Public state variables can only override functions with external visibility.", |
564 | 16 | "Overridden function is here:" |
565 | 16 | ); |
566 | 211 | solAssert(_overriding.visibility() == Visibility::External, ""); |
567 | 211 | } |
568 | 1.69k | else if (_overriding.visibility() != _super.visibility()) |
569 | 138 | { |
570 | | // Visibility change from external to public is fine. |
571 | | // Any other change is disallowed. |
572 | 138 | if (!( |
573 | 138 | _super.visibility() == Visibility::External && |
574 | 138 | _overriding.visibility() == Visibility::Public |
575 | 138 | )) |
576 | 15 | overrideError( |
577 | 15 | _overriding, |
578 | 15 | _super, |
579 | 15 | 9098_error, |
580 | 15 | "Overriding " + _overriding.astNodeName() + " visibility differs.", |
581 | 15 | "Overridden " + _overriding.astNodeName() + " is here:" |
582 | 15 | ); |
583 | 138 | } |
584 | | |
585 | 1.90k | if (_overriding.unimplemented() && !_super.unimplemented()) |
586 | 19 | { |
587 | 19 | solAssert(!_overriding.isVariable() || !_overriding.unimplemented(), ""); |
588 | 19 | overrideError( |
589 | 19 | _overriding, |
590 | 19 | _super, |
591 | 19 | 4593_error, |
592 | 19 | "Overriding an implemented " + _super.astNodeName() + |
593 | 19 | " with an unimplemented " + _overriding.astNodeName() + |
594 | 19 | " is not allowed." |
595 | 19 | ); |
596 | 19 | } |
597 | | |
598 | 1.90k | if (_super.isFunction()) |
599 | 982 | { |
600 | 982 | FunctionType const* functionType = _overriding.externalFunctionType(); |
601 | 982 | FunctionType const* superType = _super.externalFunctionType(); |
602 | | |
603 | 982 | bool returnTypesDifferAlready = false; |
604 | 982 | if (_overriding.functionKind() != Token::Fallback) |
605 | 948 | { |
606 | 948 | solAssert(functionType->hasEqualParameterTypes(*superType), "Override doesn't have equal parameters!"); |
607 | | |
608 | 948 | if (!functionType->hasEqualReturnTypes(*superType)) |
609 | 14 | { |
610 | 14 | returnTypesDifferAlready = true; |
611 | 14 | overrideError( |
612 | 14 | _overriding, |
613 | 14 | _super, |
614 | 14 | 4822_error, |
615 | 14 | "Overriding " + _overriding.astNodeName() + " return types differ.", |
616 | 14 | "Overridden " + _overriding.astNodeName() + " is here:" |
617 | 14 | ); |
618 | 14 | } |
619 | 948 | } |
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 | 982 | if ( |
626 | 982 | _overriding.isFunction() && |
627 | 982 | !returnTypesDifferAlready && |
628 | 982 | _super.visibility() != Visibility::External && |
629 | 982 | _overriding.functionKind() != Token::Fallback |
630 | 982 | ) |
631 | 358 | { |
632 | 358 | 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 | 358 | if (!_overriding.originalFunctionType()->hasEqualReturnTypes(*_super.originalFunctionType())) |
641 | 2 | overrideError( |
642 | 2 | _overriding, |
643 | 2 | _super, |
644 | 2 | 1443_error, |
645 | 2 | "Data locations of return variables have to be the same when overriding non-external functions, but they differ.", |
646 | 2 | "Overridden " + _overriding.astNodeName() + " is here:" |
647 | 2 | ); |
648 | 358 | } |
649 | | |
650 | | // Stricter mutability is always okay except when super is Payable |
651 | 982 | if ( |
652 | 982 | (_overriding.isFunction() || _overriding.isVariable()) && |
653 | 982 | ( |
654 | 982 | _overriding.stateMutability() > _super.stateMutability() || |
655 | 982 | _super.stateMutability() == StateMutability::Payable |
656 | 982 | ) && |
657 | 982 | _overriding.stateMutability() != _super.stateMutability() |
658 | 982 | ) |
659 | 12 | overrideError( |
660 | 12 | _overriding, |
661 | 12 | _super, |
662 | 12 | 6959_error, |
663 | 12 | "Overriding " + |
664 | 12 | _overriding.astNodeName() + |
665 | 12 | " changes state mutability from \"" + |
666 | 12 | stateMutabilityToString(_super.stateMutability()) + |
667 | 12 | "\" to \"" + |
668 | 12 | stateMutabilityToString(_overriding.stateMutability()) + |
669 | 12 | "\"." |
670 | 12 | ); |
671 | 982 | } |
672 | 1.90k | } |
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 | 320 | { |
682 | | // Using a set rather than a vector so the order is always the same |
683 | 320 | std::set<std::string> names; |
684 | 320 | SecondarySourceLocation ssl; |
685 | 320 | for (Declaration const* c: _secondary) |
686 | 665 | { |
687 | 665 | ssl.append("This contract: ", c->location()); |
688 | 665 | names.insert("\"" + c->name() + "\""); |
689 | 665 | } |
690 | 320 | std::string contractSingularPlural = "contract "; |
691 | 320 | if (_secondary.size() > 1) |
692 | 213 | contractSingularPlural = "contracts "; |
693 | | |
694 | 320 | m_errorReporter.typeError( |
695 | 320 | _error, |
696 | 320 | _item.overrides() ? _item.overrides()->location() : _item.location(), |
697 | 320 | ssl, |
698 | 320 | _message1 + |
699 | 320 | contractSingularPlural + |
700 | 320 | _message2 + |
701 | 320 | joinHumanReadable(names, ", ", " and ") + |
702 | 320 | "." |
703 | 320 | ); |
704 | 320 | } |
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 | 1.77k | { |
714 | 1.77k | m_errorReporter.typeError( |
715 | 1.77k | _error, |
716 | 1.77k | _overriding.location(), |
717 | 1.77k | SecondarySourceLocation().append( |
718 | 1.77k | _secondaryMsg.value_or("Overridden " + _super.astNodeName() + " is here:"), |
719 | 1.77k | _super.location() |
720 | 1.77k | ), |
721 | 1.77k | _message |
722 | 1.77k | ); |
723 | 1.77k | } |
724 | | |
725 | | void OverrideChecker::checkAmbiguousOverrides(ContractDefinition const& _contract) const |
726 | 26.5k | { |
727 | 26.5k | { |
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 | 26.5k | OverrideProxyBySignatureMultiSet nonOverriddenFunctions = inheritedFunctions(_contract); |
732 | | |
733 | | // Remove all functions that match the signature of a function in the current contract. |
734 | 26.5k | for (FunctionDefinition const* f: _contract.definedFunctions()) |
735 | 25.1k | nonOverriddenFunctions.erase(OverrideProxy{f}); |
736 | 26.5k | for (VariableDeclaration const* v: _contract.stateVariables()) |
737 | 12.3k | if (v->isPublic()) |
738 | 4.24k | nonOverriddenFunctions.erase(OverrideProxy{v}); |
739 | | |
740 | | // Walk through the set of functions signature by signature. |
741 | 28.6k | for (auto it = nonOverriddenFunctions.cbegin(); it != nonOverriddenFunctions.cend();) |
742 | 2.08k | { |
743 | 2.08k | std::set<OverrideProxy> baseFunctions; |
744 | 4.45k | for (auto nextSignature = nonOverriddenFunctions.upper_bound(*it); it != nextSignature; ++it) |
745 | 2.36k | baseFunctions.insert(*it); |
746 | | |
747 | 2.08k | checkAmbiguousOverridesInternal(std::move(baseFunctions), _contract.location()); |
748 | 2.08k | } |
749 | 26.5k | } |
750 | | |
751 | 26.5k | { |
752 | 26.5k | OverrideProxyBySignatureMultiSet modifiers = inheritedModifiers(_contract); |
753 | 26.5k | for (ModifierDefinition const* mod: _contract.functionModifiers()) |
754 | 2.88k | modifiers.erase(OverrideProxy{mod}); |
755 | | |
756 | 29.7k | for (auto it = modifiers.cbegin(); it != modifiers.cend();) |
757 | 3.25k | { |
758 | 3.25k | std::set<OverrideProxy> baseModifiers; |
759 | 6.56k | for (auto next = modifiers.upper_bound(*it); it != next; ++it) |
760 | 3.31k | baseModifiers.insert(*it); |
761 | | |
762 | 3.25k | checkAmbiguousOverridesInternal(std::move(baseModifiers), _contract.location()); |
763 | 3.25k | } |
764 | | |
765 | 26.5k | } |
766 | 26.5k | } |
767 | | |
768 | | void OverrideChecker::checkAmbiguousOverridesInternal(std::set<OverrideProxy> _baseCallables, SourceLocation const& _location) const |
769 | 5.33k | { |
770 | 5.33k | if (_baseCallables.size() <= 1) |
771 | 5.18k | return; |
772 | | |
773 | 150 | OverrideGraph overrideGraph(_baseCallables); |
774 | 150 | CutVertexFinder cutVertexFinder{overrideGraph}; |
775 | | |
776 | | // Remove all base functions overridden by cut vertices (they don't need to be overridden). |
777 | 150 | for (OverrideProxy const& function: cutVertexFinder.cutVertices()) |
778 | 86 | { |
779 | 86 | std::set<OverrideProxy> toTraverse = function.baseFunctions(); |
780 | 101 | while (!toTraverse.empty()) |
781 | 15 | { |
782 | 15 | OverrideProxy base = *toTraverse.begin(); |
783 | 15 | toTraverse.erase(toTraverse.begin()); |
784 | 15 | _baseCallables.erase(base); |
785 | 15 | for (OverrideProxy const& f: base.baseFunctions()) |
786 | 0 | toTraverse.insert(f); |
787 | 15 | } |
788 | | // Remove unimplemented base functions at the cut vertices itself as well. |
789 | 86 | if (function.unimplemented()) |
790 | 78 | _baseCallables.erase(function); |
791 | 86 | } |
792 | | |
793 | | // If more than one function is left, they have to be overridden. |
794 | 150 | if (_baseCallables.size() <= 1) |
795 | 70 | return; |
796 | | |
797 | 80 | SecondarySourceLocation ssl; |
798 | 80 | for (OverrideProxy const& baseFunction: _baseCallables) |
799 | 165 | ssl.append("Definition in \"" + baseFunction.contractName() + "\": ", baseFunction.location()); |
800 | | |
801 | 80 | std::string callableName = _baseCallables.begin()->astNodeName(); |
802 | 80 | if (_baseCallables.begin()->isVariable()) |
803 | 8 | callableName = "function"; |
804 | 80 | std::string distinguishingProperty = _baseCallables.begin()->distinguishingProperty(); |
805 | | |
806 | 80 | bool foundVariable = false; |
807 | 80 | for (auto const& base: _baseCallables) |
808 | 165 | if (base.isVariable()) |
809 | 18 | foundVariable = true; |
810 | | |
811 | 80 | std::string message = |
812 | 80 | "Derived contract must override " + callableName + " \"" + |
813 | 80 | _baseCallables.begin()->name() + |
814 | 80 | "\". Two or more base classes define " + callableName + " with same " + distinguishingProperty + "."; |
815 | | |
816 | 80 | if (foundVariable) |
817 | 13 | message += |
818 | 13 | " Since one of the bases defines a public state variable which cannot be overridden, " |
819 | 13 | "you have to change the inheritance layout or the names of the functions."; |
820 | | |
821 | 80 | m_errorReporter.typeError(6480_error, _location, ssl, message); |
822 | 80 | } |
823 | | |
824 | | std::set<ContractDefinition const*, OverrideChecker::CompareByID> OverrideChecker::resolveOverrideList(OverrideSpecifier const& _overrides) const |
825 | 913 | { |
826 | 913 | std::set<ContractDefinition const*, CompareByID> resolved; |
827 | | |
828 | 913 | for (ASTPointer<IdentifierPath> const& override: _overrides.overrides()) |
829 | 1.55k | { |
830 | 1.55k | Declaration const* decl = override->annotation().referencedDeclaration; |
831 | 1.55k | 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.55k | if (ContractDefinition const* contract = dynamic_cast<decltype(contract)>(decl)) |
836 | 1.55k | resolved.insert(contract); |
837 | 1.55k | } |
838 | | |
839 | 913 | return resolved; |
840 | 913 | } |
841 | | |
842 | | void OverrideChecker::checkOverrideList(OverrideProxy _item, OverrideProxyBySignatureMultiSet const& _inherited) |
843 | 30.0k | { |
844 | 30.0k | std::set<ContractDefinition const*, CompareByID> specifiedContracts = |
845 | 30.0k | _item.overrides() ? |
846 | 913 | resolveOverrideList(*_item.overrides()) : |
847 | 30.0k | decltype(specifiedContracts){}; |
848 | | |
849 | | // Check for duplicates in override list |
850 | 30.0k | if (_item.overrides() && specifiedContracts.size() != _item.overrides()->overrides().size()) |
851 | 106 | { |
852 | | // Sort by contract id to find duplicate for error reporting |
853 | 106 | std::vector<ASTPointer<IdentifierPath>> list = |
854 | 106 | sortByContract(_item.overrides()->overrides()); |
855 | | |
856 | | // Find duplicates and output error |
857 | 1.00k | for (size_t i = 1; i < list.size(); i++) |
858 | 901 | { |
859 | 901 | Declaration const* aDecl = list[i]->annotation().referencedDeclaration; |
860 | 901 | Declaration const* bDecl = list[i-1]->annotation().referencedDeclaration; |
861 | 901 | if (!aDecl || !bDecl) |
862 | 0 | continue; |
863 | | |
864 | 901 | if (aDecl->id() == bDecl->id()) |
865 | 665 | { |
866 | 665 | SecondarySourceLocation ssl; |
867 | 665 | ssl.append("First occurrence here: ", list[i-1]->location()); |
868 | 665 | m_errorReporter.typeError( |
869 | 665 | 4520_error, |
870 | 665 | list[i]->location(), |
871 | 665 | ssl, |
872 | 665 | "Duplicate contract \"" + |
873 | 665 | joinHumanReadable(list[i]->path(), ".") + |
874 | 665 | "\" found in override list of \"" + |
875 | 665 | _item.name() + |
876 | 665 | "\"." |
877 | 665 | ); |
878 | 665 | } |
879 | 901 | } |
880 | 106 | } |
881 | | |
882 | 30.0k | std::set<ContractDefinition const*, CompareByID> expectedContracts; |
883 | | |
884 | | // Build list of expected contracts |
885 | 31.9k | for (auto [begin, end] = _inherited.equal_range(_item); begin != end; begin++) |
886 | 1.90k | { |
887 | | // Validate the override |
888 | 1.90k | checkOverride(_item, *begin); |
889 | | |
890 | 1.90k | expectedContracts.insert(&begin->contract()); |
891 | 1.90k | } |
892 | | |
893 | 30.0k | if (_item.overrides() && expectedContracts.empty()) |
894 | 266 | m_errorReporter.typeError( |
895 | 266 | 7792_error, |
896 | 266 | _item.overrides()->location(), |
897 | 266 | _item.astNodeNameCapitalized() + " has override specified but does not override anything." |
898 | 266 | ); |
899 | | |
900 | 30.0k | std::set<ContractDefinition const*, CompareByID> missingContracts; |
901 | | // If we expect only one contract, no contract needs to be specified |
902 | 30.0k | if (expectedContracts.size() > 1) |
903 | 185 | missingContracts = expectedContracts - specifiedContracts; |
904 | | |
905 | 30.0k | if (!missingContracts.empty()) |
906 | 65 | overrideListError( |
907 | 65 | _item, |
908 | 65 | missingContracts, |
909 | 65 | 4327_error, |
910 | 65 | _item.astNodeNameCapitalized() + " needs to specify overridden ", |
911 | 65 | "" |
912 | 65 | ); |
913 | | |
914 | 30.0k | auto surplusContracts = specifiedContracts - expectedContracts; |
915 | 30.0k | if (!surplusContracts.empty()) |
916 | 255 | overrideListError( |
917 | 255 | _item, |
918 | 255 | surplusContracts, |
919 | 255 | 2353_error, |
920 | 255 | "Invalid ", |
921 | 255 | "specified in override list: " |
922 | 255 | ); |
923 | 30.0k | } |
924 | | |
925 | | OverrideChecker::OverrideProxyBySignatureMultiSet const& OverrideChecker::inheritedFunctions(ContractDefinition const& _contract) const |
926 | 57.6k | { |
927 | 57.6k | if (!m_inheritedFunctions.count(&_contract)) |
928 | 26.5k | { |
929 | 26.5k | OverrideProxyBySignatureMultiSet result; |
930 | | |
931 | 26.5k | for (auto const* base: resolveDirectBaseContracts(_contract)) |
932 | 4.56k | { |
933 | 4.56k | std::set<OverrideProxy, OverrideProxy::CompareBySignature> functionsInBase; |
934 | 4.56k | for (FunctionDefinition const* fun: base->definedFunctions()) |
935 | 3.05k | if (!fun->isConstructor()) |
936 | 2.35k | functionsInBase.emplace(OverrideProxy{fun}); |
937 | 4.56k | for (VariableDeclaration const* var: base->stateVariables()) |
938 | 1.16k | if (var->isPublic()) |
939 | 865 | functionsInBase.emplace(OverrideProxy{var}); |
940 | | |
941 | 4.56k | result += functionsInBase; |
942 | | |
943 | 4.56k | for (OverrideProxy const& func: inheritedFunctions(*base)) |
944 | 840 | if (!functionsInBase.count(func)) |
945 | 563 | result.insert(func); |
946 | 4.56k | } |
947 | | |
948 | 26.5k | m_inheritedFunctions[&_contract] = result; |
949 | 26.5k | } |
950 | | |
951 | 57.6k | return m_inheritedFunctions[&_contract]; |
952 | 57.6k | } |
953 | | |
954 | | OverrideChecker::OverrideProxyBySignatureMultiSet const& OverrideChecker::inheritedModifiers(ContractDefinition const& _contract) const |
955 | 57.6k | { |
956 | 57.6k | if (!m_inheritedModifiers.count(&_contract)) |
957 | 26.5k | { |
958 | 26.5k | OverrideProxyBySignatureMultiSet result; |
959 | | |
960 | 26.5k | for (auto const* base: resolveDirectBaseContracts(_contract)) |
961 | 4.56k | { |
962 | 4.56k | std::set<OverrideProxy, OverrideProxy::CompareBySignature> modifiersInBase; |
963 | 4.56k | for (ModifierDefinition const* mod: base->functionModifiers()) |
964 | 4.03k | modifiersInBase.emplace(OverrideProxy{mod}); |
965 | | |
966 | 4.56k | for (OverrideProxy const& mod: inheritedModifiers(*base)) |
967 | 682 | modifiersInBase.insert(mod); |
968 | | |
969 | 4.56k | result += modifiersInBase; |
970 | 4.56k | } |
971 | | |
972 | 26.5k | m_inheritedModifiers[&_contract] = result; |
973 | 26.5k | } |
974 | | |
975 | 57.6k | return m_inheritedModifiers[&_contract]; |
976 | 57.6k | } |