/src/solidity/libsolidity/analysis/ContractLevelChecker.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/ContractLevelChecker.h> |
24 | | |
25 | | #include <libsolidity/ast/AST.h> |
26 | | #include <libsolidity/ast/TypeProvider.h> |
27 | | #include <libsolidity/analysis/TypeChecker.h> |
28 | | #include <libsolutil/FunctionSelector.h> |
29 | | #include <liblangutil/ErrorReporter.h> |
30 | | |
31 | | #include <range/v3/view/reverse.hpp> |
32 | | |
33 | | using namespace std; |
34 | | using namespace solidity; |
35 | | using namespace solidity::langutil; |
36 | | using namespace solidity::frontend; |
37 | | |
38 | | namespace |
39 | | { |
40 | | |
41 | | template <class T, class B> |
42 | | bool hasEqualExternalCallableParameters(T const& _a, B const& _b) |
43 | 0 | { |
44 | 0 | return FunctionType(_a).asExternallyCallableFunction(false)->hasEqualParameterTypes( |
45 | 0 | *FunctionType(_b).asExternallyCallableFunction(false) |
46 | 0 | ); |
47 | 0 | } Unexecuted instantiation: ContractLevelChecker.cpp:bool (anonymous namespace)::hasEqualExternalCallableParameters<solidity::frontend::FunctionDefinition, solidity::frontend::FunctionDefinition>(solidity::frontend::FunctionDefinition const&, solidity::frontend::FunctionDefinition const&) Unexecuted instantiation: ContractLevelChecker.cpp:bool (anonymous namespace)::hasEqualExternalCallableParameters<solidity::frontend::EventDefinition, solidity::frontend::EventDefinition>(solidity::frontend::EventDefinition const&, solidity::frontend::EventDefinition const&) |
48 | | |
49 | | template<typename T> |
50 | | map<ASTString, vector<T const*>> filterDeclarations( |
51 | | map<ASTString, vector<Declaration const*>> const& _declarations) |
52 | 7.21k | { |
53 | 7.21k | map<ASTString, vector<T const*>> filteredDeclarations; |
54 | 7.21k | for (auto const& [name, overloads]: _declarations) |
55 | 7.21k | for (auto const* declaration: overloads) |
56 | 7.21k | if (auto typedDeclaration = dynamic_cast<T const*>(declaration)) |
57 | 0 | filteredDeclarations[name].push_back(typedDeclaration); |
58 | 7.21k | return filteredDeclarations; |
59 | 7.21k | } ContractLevelChecker.cpp:std::__1::map<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::vector<solidity::frontend::FunctionDefinition const*, std::__1::allocator<solidity::frontend::FunctionDefinition const*> >, std::__1::less<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::allocator<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const, std::__1::vector<solidity::frontend::FunctionDefinition const*, std::__1::allocator<solidity::frontend::FunctionDefinition const*> > > > > (anonymous namespace)::filterDeclarations<solidity::frontend::FunctionDefinition>(std::__1::map<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::vector<solidity::frontend::Declaration const*, std::__1::allocator<solidity::frontend::Declaration const*> >, std::__1::less<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::allocator<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const, std::__1::vector<solidity::frontend::Declaration const*, std::__1::allocator<solidity::frontend::Declaration const*> > > > > const&) Line | Count | Source | 52 | 3.60k | { | 53 | 3.60k | map<ASTString, vector<T const*>> filteredDeclarations; | 54 | 3.60k | for (auto const& [name, overloads]: _declarations) | 55 | 3.60k | for (auto const* declaration: overloads) | 56 | 3.60k | if (auto typedDeclaration = dynamic_cast<T const*>(declaration)) | 57 | 0 | filteredDeclarations[name].push_back(typedDeclaration); | 58 | 3.60k | return filteredDeclarations; | 59 | 3.60k | } |
ContractLevelChecker.cpp:std::__1::map<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::vector<solidity::frontend::EventDefinition const*, std::__1::allocator<solidity::frontend::EventDefinition const*> >, std::__1::less<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::allocator<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const, std::__1::vector<solidity::frontend::EventDefinition const*, std::__1::allocator<solidity::frontend::EventDefinition const*> > > > > (anonymous namespace)::filterDeclarations<solidity::frontend::EventDefinition>(std::__1::map<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::vector<solidity::frontend::Declaration const*, std::__1::allocator<solidity::frontend::Declaration const*> >, std::__1::less<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::allocator<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const, std::__1::vector<solidity::frontend::Declaration const*, std::__1::allocator<solidity::frontend::Declaration const*> > > > > const&) Line | Count | Source | 52 | 3.60k | { | 53 | 3.60k | map<ASTString, vector<T const*>> filteredDeclarations; | 54 | 3.60k | for (auto const& [name, overloads]: _declarations) | 55 | 3.60k | for (auto const* declaration: overloads) | 56 | 3.60k | if (auto typedDeclaration = dynamic_cast<T const*>(declaration)) | 57 | 0 | filteredDeclarations[name].push_back(typedDeclaration); | 58 | 3.60k | return filteredDeclarations; | 59 | 3.60k | } |
|
60 | | |
61 | | } |
62 | | |
63 | | bool ContractLevelChecker::check(SourceUnit const& _sourceUnit) |
64 | 3.60k | { |
65 | 3.60k | bool noErrors = true; |
66 | 3.60k | findDuplicateDefinitions( |
67 | 3.60k | filterDeclarations<FunctionDefinition>(*_sourceUnit.annotation().exportedSymbols) |
68 | 3.60k | ); |
69 | | // This check flags duplicate free events when free events become |
70 | | // a Solidity feature |
71 | 3.60k | findDuplicateDefinitions( |
72 | 3.60k | filterDeclarations<EventDefinition>(*_sourceUnit.annotation().exportedSymbols) |
73 | 3.60k | ); |
74 | 3.60k | if (Error::containsErrors(m_errorReporter.errors())) |
75 | 0 | noErrors = false; |
76 | 3.60k | for (ASTPointer<ASTNode> const& node: _sourceUnit.nodes()) |
77 | 10.8k | if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get())) |
78 | 3.60k | if (!check(*contract)) |
79 | 0 | noErrors = false; |
80 | 3.60k | return noErrors; |
81 | 3.60k | } |
82 | | |
83 | | bool ContractLevelChecker::check(ContractDefinition const& _contract) |
84 | 3.60k | { |
85 | 3.60k | _contract.annotation().unimplementedDeclarations = std::vector<Declaration const*>(); |
86 | | |
87 | 3.60k | checkDuplicateFunctions(_contract); |
88 | 3.60k | checkDuplicateEvents(_contract); |
89 | 3.60k | checkReceiveFunction(_contract); |
90 | 3.60k | m_overrideChecker.check(_contract); |
91 | 3.60k | checkBaseConstructorArguments(_contract); |
92 | 3.60k | checkAbstractDefinitions(_contract); |
93 | 3.60k | checkExternalTypeClashes(_contract); |
94 | 3.60k | checkHashCollisions(_contract); |
95 | 3.60k | checkLibraryRequirements(_contract); |
96 | 3.60k | checkBaseABICompatibility(_contract); |
97 | 3.60k | checkPayableFallbackWithoutReceive(_contract); |
98 | 3.60k | checkStorageSize(_contract); |
99 | | |
100 | 3.60k | return !Error::containsErrors(m_errorReporter.errors()); |
101 | 3.60k | } |
102 | | |
103 | | void ContractLevelChecker::checkDuplicateFunctions(ContractDefinition const& _contract) |
104 | 3.60k | { |
105 | | /// Checks that two functions with the same name defined in this contract have different |
106 | | /// argument types and that there is at most one constructor. |
107 | 3.60k | map<string, vector<FunctionDefinition const*>> functions; |
108 | 3.60k | FunctionDefinition const* constructor = nullptr; |
109 | 3.60k | FunctionDefinition const* fallback = nullptr; |
110 | 3.60k | FunctionDefinition const* receive = nullptr; |
111 | 3.60k | for (FunctionDefinition const* function: _contract.definedFunctions()) |
112 | 39.6k | if (function->isConstructor()) |
113 | 0 | { |
114 | 0 | if (constructor) |
115 | 0 | m_errorReporter.declarationError( |
116 | 0 | 7997_error, |
117 | 0 | function->location(), |
118 | 0 | SecondarySourceLocation().append("Another declaration is here:", constructor->location()), |
119 | 0 | "More than one constructor defined." |
120 | 0 | ); |
121 | 0 | constructor = function; |
122 | 0 | } |
123 | 39.6k | else if (function->isFallback()) |
124 | 0 | { |
125 | 0 | if (fallback) |
126 | 0 | m_errorReporter.declarationError( |
127 | 0 | 7301_error, |
128 | 0 | function->location(), |
129 | 0 | SecondarySourceLocation().append("Another declaration is here:", fallback->location()), |
130 | 0 | "Only one fallback function is allowed." |
131 | 0 | ); |
132 | 0 | fallback = function; |
133 | 0 | } |
134 | 39.6k | else if (function->isReceive()) |
135 | 0 | { |
136 | 0 | if (receive) |
137 | 0 | m_errorReporter.declarationError( |
138 | 0 | 4046_error, |
139 | 0 | function->location(), |
140 | 0 | SecondarySourceLocation().append("Another declaration is here:", receive->location()), |
141 | 0 | "Only one receive function is allowed." |
142 | 0 | ); |
143 | 0 | receive = function; |
144 | 0 | } |
145 | 39.6k | else |
146 | 39.6k | { |
147 | 39.6k | solAssert(!function->name().empty(), ""); |
148 | 39.6k | functions[function->name()].push_back(function); |
149 | 39.6k | } |
150 | | |
151 | 3.60k | findDuplicateDefinitions(functions); |
152 | 3.60k | } |
153 | | |
154 | | void ContractLevelChecker::checkDuplicateEvents(ContractDefinition const& _contract) |
155 | 3.60k | { |
156 | | /// Checks that two events with the same name defined in this contract have different |
157 | | /// argument types |
158 | 3.60k | map<string, vector<EventDefinition const*>> events; |
159 | 3.60k | for (auto const* contract: _contract.annotation().linearizedBaseContracts) |
160 | 3.60k | for (EventDefinition const* event: contract->events()) |
161 | 0 | events[event->name()].push_back(event); |
162 | | |
163 | 3.60k | findDuplicateDefinitions(events); |
164 | 3.60k | } |
165 | | |
166 | | void ContractLevelChecker::checkReceiveFunction(ContractDefinition const& _contract) |
167 | 3.60k | { |
168 | 3.60k | for (FunctionDefinition const* function: _contract.definedFunctions()) |
169 | 39.6k | { |
170 | 39.6k | solAssert(function, ""); |
171 | 39.6k | if (function->isReceive()) |
172 | 0 | { |
173 | 0 | if (function->libraryFunction()) |
174 | 0 | m_errorReporter.declarationError(4549_error, function->location(), "Libraries cannot have receive ether functions."); |
175 | |
|
176 | 0 | if (function->stateMutability() != StateMutability::Payable) |
177 | 0 | m_errorReporter.declarationError( |
178 | 0 | 7793_error, |
179 | 0 | function->location(), |
180 | 0 | "Receive ether function must be payable, but is \"" + |
181 | 0 | stateMutabilityToString(function->stateMutability()) + |
182 | 0 | "\"." |
183 | 0 | ); |
184 | 0 | if (function->visibility() != Visibility::External) |
185 | 0 | m_errorReporter.declarationError(4095_error, function->location(), "Receive ether function must be defined as \"external\"."); |
186 | |
|
187 | 0 | if (!function->returnParameters().empty()) |
188 | 0 | m_errorReporter.fatalDeclarationError(6899_error, function->returnParameterList()->location(), "Receive ether function cannot return values."); |
189 | 0 | if (!function->parameters().empty()) |
190 | 0 | m_errorReporter.fatalDeclarationError(6857_error, function->parameterList().location(), "Receive ether function cannot take parameters."); |
191 | 0 | } |
192 | 39.6k | } |
193 | 3.60k | } |
194 | | |
195 | | template <class T> |
196 | | void ContractLevelChecker::findDuplicateDefinitions(map<string, vector<T>> const& _definitions) |
197 | 14.4k | { |
198 | 14.4k | for (auto const& it: _definitions) |
199 | 39.6k | { |
200 | 39.6k | vector<T> const& overloads = it.second; |
201 | 39.6k | set<size_t> reported; |
202 | 79.3k | for (size_t i = 0; i < overloads.size() && !reported.count(i); ++i) |
203 | 39.6k | { |
204 | 39.6k | SecondarySourceLocation ssl; |
205 | | |
206 | 39.6k | for (size_t j = i + 1; j < overloads.size(); ++j) |
207 | 0 | if (hasEqualExternalCallableParameters(*overloads[i], *overloads[j])) |
208 | 0 | { |
209 | 0 | solAssert( |
210 | 0 | ( |
211 | 0 | dynamic_cast<ContractDefinition const*>(overloads[i]->scope()) && |
212 | 0 | dynamic_cast<ContractDefinition const*>(overloads[j]->scope()) && |
213 | 0 | overloads[i]->name() == overloads[j]->name() |
214 | 0 | ) || |
215 | 0 | ( |
216 | 0 | dynamic_cast<SourceUnit const*>(overloads[i]->scope()) && |
217 | 0 | dynamic_cast<SourceUnit const*>(overloads[j]->scope()) |
218 | 0 | ), |
219 | 0 | "Override is neither a namesake function/event in contract scope nor " |
220 | 0 | "a free function/event (alias)." |
221 | 0 | ); |
222 | 0 | ssl.append("Other declaration is here:", overloads[j]->location()); |
223 | 0 | reported.insert(j); |
224 | 0 | } |
225 | | |
226 | 39.6k | if (ssl.infos.size() > 0) |
227 | 0 | { |
228 | 0 | ErrorId error; |
229 | 0 | string message; |
230 | 0 | if constexpr (is_same_v<T, FunctionDefinition const*>) |
231 | 0 | { |
232 | 0 | error = 1686_error; |
233 | 0 | message = "Function with same name and parameter types defined twice."; |
234 | 0 | } |
235 | 0 | else |
236 | 0 | { |
237 | 0 | static_assert(is_same_v<T, EventDefinition const*>, "Expected \"FunctionDefinition const*\" or \"EventDefinition const*\""); |
238 | 0 | error = 5883_error; |
239 | 0 | message = "Event with same name and parameter types defined twice."; |
240 | 0 | } |
241 | |
|
242 | 0 | ssl.limitSize(message); |
243 | |
|
244 | 0 | m_errorReporter.declarationError( |
245 | 0 | error, |
246 | 0 | overloads[i]->location(), |
247 | 0 | ssl, |
248 | 0 | message |
249 | 0 | ); |
250 | 0 | } |
251 | 39.6k | } |
252 | 39.6k | } |
253 | 14.4k | } void solidity::frontend::ContractLevelChecker::findDuplicateDefinitions<solidity::frontend::FunctionDefinition const*>(std::__1::map<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::vector<solidity::frontend::FunctionDefinition const*, std::__1::allocator<solidity::frontend::FunctionDefinition const*> >, std::__1::less<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::allocator<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const, std::__1::vector<solidity::frontend::FunctionDefinition const*, std::__1::allocator<solidity::frontend::FunctionDefinition const*> > > > > const&) Line | Count | Source | 197 | 7.21k | { | 198 | 7.21k | for (auto const& it: _definitions) | 199 | 39.6k | { | 200 | 39.6k | vector<T> const& overloads = it.second; | 201 | 39.6k | set<size_t> reported; | 202 | 79.3k | for (size_t i = 0; i < overloads.size() && !reported.count(i); ++i) | 203 | 39.6k | { | 204 | 39.6k | SecondarySourceLocation ssl; | 205 | | | 206 | 39.6k | for (size_t j = i + 1; j < overloads.size(); ++j) | 207 | 0 | if (hasEqualExternalCallableParameters(*overloads[i], *overloads[j])) | 208 | 0 | { | 209 | 0 | solAssert( | 210 | 0 | ( | 211 | 0 | dynamic_cast<ContractDefinition const*>(overloads[i]->scope()) && | 212 | 0 | dynamic_cast<ContractDefinition const*>(overloads[j]->scope()) && | 213 | 0 | overloads[i]->name() == overloads[j]->name() | 214 | 0 | ) || | 215 | 0 | ( | 216 | 0 | dynamic_cast<SourceUnit const*>(overloads[i]->scope()) && | 217 | 0 | dynamic_cast<SourceUnit const*>(overloads[j]->scope()) | 218 | 0 | ), | 219 | 0 | "Override is neither a namesake function/event in contract scope nor " | 220 | 0 | "a free function/event (alias)." | 221 | 0 | ); | 222 | 0 | ssl.append("Other declaration is here:", overloads[j]->location()); | 223 | 0 | reported.insert(j); | 224 | 0 | } | 225 | | | 226 | 39.6k | if (ssl.infos.size() > 0) | 227 | 0 | { | 228 | 0 | ErrorId error; | 229 | 0 | string message; | 230 | 0 | if constexpr (is_same_v<T, FunctionDefinition const*>) | 231 | 0 | { | 232 | 0 | error = 1686_error; | 233 | 0 | message = "Function with same name and parameter types defined twice."; | 234 | 0 | } | 235 | 0 | else | 236 | 0 | { | 237 | 0 | static_assert(is_same_v<T, EventDefinition const*>, "Expected \"FunctionDefinition const*\" or \"EventDefinition const*\""); | 238 | 0 | error = 5883_error; | 239 | 0 | message = "Event with same name and parameter types defined twice."; | 240 | 0 | } | 241 | |
| 242 | 0 | ssl.limitSize(message); | 243 | |
| 244 | 0 | m_errorReporter.declarationError( | 245 | 0 | error, | 246 | 0 | overloads[i]->location(), | 247 | 0 | ssl, | 248 | 0 | message | 249 | 0 | ); | 250 | 0 | } | 251 | 39.6k | } | 252 | 39.6k | } | 253 | 7.21k | } |
void solidity::frontend::ContractLevelChecker::findDuplicateDefinitions<solidity::frontend::EventDefinition const*>(std::__1::map<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::vector<solidity::frontend::EventDefinition const*, std::__1::allocator<solidity::frontend::EventDefinition const*> >, std::__1::less<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::allocator<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const, std::__1::vector<solidity::frontend::EventDefinition const*, std::__1::allocator<solidity::frontend::EventDefinition const*> > > > > const&) Line | Count | Source | 197 | 7.21k | { | 198 | 7.21k | for (auto const& it: _definitions) | 199 | 0 | { | 200 | 0 | vector<T> const& overloads = it.second; | 201 | 0 | set<size_t> reported; | 202 | 0 | for (size_t i = 0; i < overloads.size() && !reported.count(i); ++i) | 203 | 0 | { | 204 | 0 | SecondarySourceLocation ssl; | 205 | |
| 206 | 0 | for (size_t j = i + 1; j < overloads.size(); ++j) | 207 | 0 | if (hasEqualExternalCallableParameters(*overloads[i], *overloads[j])) | 208 | 0 | { | 209 | 0 | solAssert( | 210 | 0 | ( | 211 | 0 | dynamic_cast<ContractDefinition const*>(overloads[i]->scope()) && | 212 | 0 | dynamic_cast<ContractDefinition const*>(overloads[j]->scope()) && | 213 | 0 | overloads[i]->name() == overloads[j]->name() | 214 | 0 | ) || | 215 | 0 | ( | 216 | 0 | dynamic_cast<SourceUnit const*>(overloads[i]->scope()) && | 217 | 0 | dynamic_cast<SourceUnit const*>(overloads[j]->scope()) | 218 | 0 | ), | 219 | 0 | "Override is neither a namesake function/event in contract scope nor " | 220 | 0 | "a free function/event (alias)." | 221 | 0 | ); | 222 | 0 | ssl.append("Other declaration is here:", overloads[j]->location()); | 223 | 0 | reported.insert(j); | 224 | 0 | } | 225 | | | 226 | 0 | if (ssl.infos.size() > 0) | 227 | 0 | { | 228 | 0 | ErrorId error; | 229 | 0 | string message; | 230 | 0 | if constexpr (is_same_v<T, FunctionDefinition const*>) | 231 | 0 | { | 232 | 0 | error = 1686_error; | 233 | 0 | message = "Function with same name and parameter types defined twice."; | 234 | 0 | } | 235 | 0 | else | 236 | 0 | { | 237 | 0 | static_assert(is_same_v<T, EventDefinition const*>, "Expected \"FunctionDefinition const*\" or \"EventDefinition const*\""); | 238 | 0 | error = 5883_error; | 239 | 0 | message = "Event with same name and parameter types defined twice."; | 240 | 0 | } | 241 | |
| 242 | 0 | ssl.limitSize(message); | 243 | |
| 244 | 0 | m_errorReporter.declarationError( | 245 | 0 | error, | 246 | 0 | overloads[i]->location(), | 247 | 0 | ssl, | 248 | 0 | message | 249 | 0 | ); | 250 | 0 | } | 251 | 0 | } | 252 | 0 | } | 253 | 7.21k | } |
|
254 | | |
255 | | void ContractLevelChecker::checkAbstractDefinitions(ContractDefinition const& _contract) |
256 | 3.60k | { |
257 | | // Collects functions, static variable getters and modifiers. If they |
258 | | // override (unimplemented) base class ones, they are replaced. |
259 | 3.60k | set<OverrideProxy, OverrideProxy::CompareBySignature> proxies; |
260 | | |
261 | 3.60k | auto registerProxy = [&proxies](OverrideProxy const& _overrideProxy) |
262 | 39.6k | { |
263 | | // Overwrite an existing proxy, if it exists. |
264 | 39.6k | if (!_overrideProxy.unimplemented()) |
265 | 39.6k | proxies.erase(_overrideProxy); |
266 | | |
267 | 39.6k | proxies.insert(_overrideProxy); |
268 | 39.6k | }; |
269 | | |
270 | | // Search from base to derived, collect all functions and modifiers and |
271 | | // update proxies. |
272 | 3.60k | for (ContractDefinition const* contract: _contract.annotation().linearizedBaseContracts | ranges::views::reverse) |
273 | 3.60k | { |
274 | 3.60k | for (VariableDeclaration const* v: contract->stateVariables()) |
275 | 2.94k | if (v->isPartOfExternalInterface()) |
276 | 0 | registerProxy(OverrideProxy(v)); |
277 | | |
278 | 3.60k | for (FunctionDefinition const* function: contract->definedFunctions()) |
279 | 39.6k | if (!function->isConstructor()) |
280 | 39.6k | registerProxy(OverrideProxy(function)); |
281 | | |
282 | 3.60k | for (ModifierDefinition const* modifier: contract->functionModifiers()) |
283 | 0 | registerProxy(OverrideProxy(modifier)); |
284 | 3.60k | } |
285 | | |
286 | | // Set to not fully implemented if at least one flag is false. |
287 | | // Note that `_contract.annotation().unimplementedDeclarations` has already been |
288 | | // pre-filled by `checkBaseConstructorArguments`. |
289 | | // |
290 | 3.60k | for (auto const& proxy: proxies) |
291 | 39.6k | if (proxy.unimplemented()) |
292 | 0 | _contract.annotation().unimplementedDeclarations->push_back(proxy.declaration()); |
293 | | |
294 | 3.60k | if (_contract.abstract()) |
295 | 0 | { |
296 | 0 | if (_contract.contractKind() == ContractKind::Interface) |
297 | 0 | m_errorReporter.typeError(9348_error, _contract.location(), "Interfaces do not need the \"abstract\" keyword, they are abstract implicitly."); |
298 | 0 | else if (_contract.contractKind() == ContractKind::Library) |
299 | 0 | m_errorReporter.typeError(9571_error, _contract.location(), "Libraries cannot be abstract."); |
300 | 0 | else |
301 | 0 | solAssert(_contract.contractKind() == ContractKind::Contract, ""); |
302 | 0 | } |
303 | | |
304 | | // For libraries, we emit errors on function-level, so this is fine as long as we do |
305 | | // not have inheritance for libraries. |
306 | 3.60k | if ( |
307 | 3.60k | _contract.contractKind() == ContractKind::Contract && |
308 | 3.60k | !_contract.abstract() && |
309 | 3.60k | !_contract.annotation().unimplementedDeclarations->empty() |
310 | 3.60k | ) |
311 | 0 | { |
312 | 0 | SecondarySourceLocation ssl; |
313 | 0 | for (auto declaration: *_contract.annotation().unimplementedDeclarations) |
314 | 0 | ssl.append("Missing implementation: ", declaration->location()); |
315 | 0 | m_errorReporter.typeError( |
316 | 0 | 3656_error, |
317 | 0 | _contract.location(), |
318 | 0 | ssl, |
319 | 0 | "Contract \"" + *_contract.annotation().canonicalName + "\" should be marked as abstract." |
320 | 0 | ); |
321 | 0 | } |
322 | 3.60k | } |
323 | | |
324 | | |
325 | | void ContractLevelChecker::checkBaseConstructorArguments(ContractDefinition const& _contract) |
326 | 3.60k | { |
327 | 3.60k | vector<ContractDefinition const*> const& bases = _contract.annotation().linearizedBaseContracts; |
328 | | |
329 | | // Determine the arguments that are used for the base constructors. |
330 | 3.60k | for (ContractDefinition const* contract: bases) |
331 | 3.60k | { |
332 | 3.60k | if (FunctionDefinition const* constructor = contract->constructor()) |
333 | 0 | for (auto const& modifier: constructor->modifiers()) |
334 | 0 | if (auto baseContract = dynamic_cast<ContractDefinition const*>( |
335 | 0 | modifier->name().annotation().referencedDeclaration |
336 | 0 | )) |
337 | 0 | { |
338 | 0 | if (modifier->arguments()) |
339 | 0 | { |
340 | 0 | if (baseContract->constructor()) |
341 | 0 | annotateBaseConstructorArguments(_contract, baseContract->constructor(), modifier.get()); |
342 | 0 | } |
343 | 0 | else |
344 | 0 | m_errorReporter.declarationError( |
345 | 0 | 1563_error, |
346 | 0 | modifier->location(), |
347 | 0 | "Modifier-style base constructor call without arguments." |
348 | 0 | ); |
349 | 0 | } |
350 | | |
351 | 3.60k | for (ASTPointer<InheritanceSpecifier> const& base: contract->baseContracts()) |
352 | 0 | { |
353 | 0 | ContractDefinition const* baseContract = dynamic_cast<ContractDefinition const*>( |
354 | 0 | base->name().annotation().referencedDeclaration |
355 | 0 | ); |
356 | 0 | solAssert(baseContract, ""); |
357 | | |
358 | 0 | if (baseContract->constructor() && base->arguments() && !base->arguments()->empty()) |
359 | 0 | annotateBaseConstructorArguments(_contract, baseContract->constructor(), base.get()); |
360 | 0 | } |
361 | 3.60k | } |
362 | | |
363 | | // check that we get arguments for all base constructors that need it. |
364 | | // If not mark the contract as abstract (not fully implemented) |
365 | 3.60k | for (ContractDefinition const* contract: bases) |
366 | 3.60k | if (FunctionDefinition const* constructor = contract->constructor()) |
367 | 0 | if (contract != &_contract && !constructor->parameters().empty()) |
368 | 0 | if (!_contract.annotation().baseConstructorArguments.count(constructor)) |
369 | 0 | _contract.annotation().unimplementedDeclarations->push_back(constructor); |
370 | 3.60k | } |
371 | | |
372 | | void ContractLevelChecker::annotateBaseConstructorArguments( |
373 | | ContractDefinition const& _currentContract, |
374 | | FunctionDefinition const* _baseConstructor, |
375 | | ASTNode const* _argumentNode |
376 | | ) |
377 | 0 | { |
378 | 0 | solAssert(_baseConstructor, ""); |
379 | 0 | solAssert(_argumentNode, ""); |
380 | | |
381 | 0 | auto insertionResult = _currentContract.annotation().baseConstructorArguments.insert( |
382 | 0 | std::make_pair(_baseConstructor, _argumentNode) |
383 | 0 | ); |
384 | 0 | if (!insertionResult.second) |
385 | 0 | { |
386 | 0 | ASTNode const* previousNode = insertionResult.first->second; |
387 | |
|
388 | 0 | SourceLocation const* mainLocation = nullptr; |
389 | 0 | SecondarySourceLocation ssl; |
390 | |
|
391 | 0 | if ( |
392 | 0 | _currentContract.location().contains(previousNode->location()) || |
393 | 0 | _currentContract.location().contains(_argumentNode->location()) |
394 | 0 | ) |
395 | 0 | { |
396 | 0 | mainLocation = &previousNode->location(); |
397 | 0 | ssl.append("Second constructor call is here:", _argumentNode->location()); |
398 | 0 | } |
399 | 0 | else |
400 | 0 | { |
401 | 0 | mainLocation = &_currentContract.location(); |
402 | 0 | ssl.append("First constructor call is here:", _argumentNode->location()); |
403 | 0 | ssl.append("Second constructor call is here:", previousNode->location()); |
404 | 0 | } |
405 | |
|
406 | 0 | m_errorReporter.declarationError( |
407 | 0 | 3364_error, |
408 | 0 | *mainLocation, |
409 | 0 | ssl, |
410 | 0 | "Base constructor arguments given twice." |
411 | 0 | ); |
412 | 0 | } |
413 | |
|
414 | 0 | } |
415 | | |
416 | | void ContractLevelChecker::checkExternalTypeClashes(ContractDefinition const& _contract) |
417 | 3.60k | { |
418 | 3.60k | map<string, vector<pair<Declaration const*, FunctionTypePointer>>> externalDeclarations; |
419 | 3.60k | for (ContractDefinition const* contract: _contract.annotation().linearizedBaseContracts) |
420 | 3.60k | { |
421 | 3.60k | for (FunctionDefinition const* f: contract->definedFunctions()) |
422 | 39.6k | if (f->isPartOfExternalInterface()) |
423 | 28.8k | { |
424 | 28.8k | auto functionType = TypeProvider::function(*f); |
425 | | // under non error circumstances this should be true |
426 | 28.8k | if (functionType->interfaceFunctionType()) |
427 | 28.8k | externalDeclarations[functionType->externalSignature()].emplace_back( |
428 | 28.8k | f, functionType->asExternallyCallableFunction(false) |
429 | 28.8k | ); |
430 | 28.8k | } |
431 | 3.60k | for (VariableDeclaration const* v: contract->stateVariables()) |
432 | 2.94k | if (v->isPartOfExternalInterface()) |
433 | 0 | { |
434 | 0 | auto functionType = TypeProvider::function(*v); |
435 | | // under non error circumstances this should be true |
436 | 0 | if (functionType->interfaceFunctionType()) |
437 | 0 | externalDeclarations[functionType->externalSignature()].emplace_back( |
438 | 0 | v, functionType->asExternallyCallableFunction(false) |
439 | 0 | ); |
440 | 0 | } |
441 | 3.60k | } |
442 | 3.60k | for (auto const& it: externalDeclarations) |
443 | 57.7k | for (size_t i = 0; i < it.second.size(); ++i) |
444 | 28.8k | for (size_t j = i + 1; j < it.second.size(); ++j) |
445 | 0 | if (!it.second[i].second->hasEqualParameterTypes(*it.second[j].second)) |
446 | 0 | m_errorReporter.typeError( |
447 | 0 | 9914_error, |
448 | 0 | it.second[j].first->location(), |
449 | 0 | "Function overload clash during conversion to external types for arguments." |
450 | 0 | ); |
451 | 3.60k | } |
452 | | |
453 | | void ContractLevelChecker::checkHashCollisions(ContractDefinition const& _contract) |
454 | 3.60k | { |
455 | 3.60k | set<util::FixedHash<4>> hashes; |
456 | 3.60k | for (auto const& it: _contract.interfaceFunctionList()) |
457 | 28.8k | { |
458 | 28.8k | util::FixedHash<4> const& hash = it.first; |
459 | 28.8k | if (hashes.count(hash)) |
460 | 0 | m_errorReporter.fatalTypeError( |
461 | 0 | 1860_error, |
462 | 0 | _contract.location(), |
463 | 0 | string("Function signature hash collision for ") + it.second->externalSignature() |
464 | 0 | ); |
465 | 28.8k | hashes.insert(hash); |
466 | 28.8k | } |
467 | 3.60k | } |
468 | | |
469 | | void ContractLevelChecker::checkLibraryRequirements(ContractDefinition const& _contract) |
470 | 3.60k | { |
471 | 3.60k | if (!_contract.isLibrary()) |
472 | 3.60k | return; |
473 | | |
474 | 0 | if (!_contract.baseContracts().empty()) |
475 | 0 | m_errorReporter.typeError(9469_error, _contract.location(), "Library is not allowed to inherit."); |
476 | |
|
477 | 0 | for (auto const& var: _contract.stateVariables()) |
478 | 0 | if (!var->isConstant()) |
479 | 0 | m_errorReporter.typeError(9957_error, var->location(), "Library cannot have non-constant state variables"); |
480 | 0 | } |
481 | | |
482 | | void ContractLevelChecker::checkBaseABICompatibility(ContractDefinition const& _contract) |
483 | 3.60k | { |
484 | 3.60k | if (*_contract.sourceUnit().annotation().useABICoderV2) |
485 | 3.60k | return; |
486 | | |
487 | 0 | if (_contract.isLibrary()) |
488 | 0 | { |
489 | 0 | solAssert( |
490 | 0 | _contract.baseContracts().empty() || m_errorReporter.hasErrors(), |
491 | 0 | "Library is not allowed to inherit" |
492 | 0 | ); |
493 | 0 | return; |
494 | 0 | } |
495 | | |
496 | 0 | SecondarySourceLocation errors; |
497 | | |
498 | | // interfaceFunctionList contains all inherited functions as well |
499 | 0 | for (auto const& func: _contract.interfaceFunctionList()) |
500 | 0 | { |
501 | 0 | solAssert(func.second->hasDeclaration(), "Function has no declaration?!"); |
502 | | |
503 | 0 | if (!*func.second->declaration().sourceUnit().annotation().useABICoderV2) |
504 | 0 | continue; |
505 | | |
506 | 0 | auto const& currentLoc = func.second->declaration().location(); |
507 | |
|
508 | 0 | for (Type const* paramType: func.second->parameterTypes() + func.second->returnParameterTypes()) |
509 | 0 | if (!TypeChecker::typeSupportedByOldABIEncoder(*paramType, false)) |
510 | 0 | { |
511 | 0 | errors.append("Type only supported by ABIEncoderV2", currentLoc); |
512 | 0 | break; |
513 | 0 | } |
514 | 0 | } |
515 | | |
516 | 0 | if (!errors.infos.empty()) |
517 | 0 | m_errorReporter.fatalTypeError( |
518 | 0 | 6594_error, |
519 | 0 | _contract.location(), |
520 | 0 | errors, |
521 | 0 | std::string("Contract \"") + |
522 | 0 | _contract.name() + |
523 | 0 | "\" does not use ABI coder v2 but wants to inherit from a contract " + |
524 | 0 | "which uses types that require it. " + |
525 | 0 | "Use \"pragma abicoder v2;\" for the inheriting contract as well to enable the feature." |
526 | 0 | ); |
527 | |
|
528 | 0 | } |
529 | | |
530 | | void ContractLevelChecker::checkPayableFallbackWithoutReceive(ContractDefinition const& _contract) |
531 | 3.60k | { |
532 | 3.60k | if (auto const* fallback = _contract.fallbackFunction()) |
533 | 0 | if (fallback->isPayable() && !_contract.interfaceFunctionList().empty() && !_contract.receiveFunction()) |
534 | 0 | m_errorReporter.warning( |
535 | 0 | 3628_error, |
536 | 0 | _contract.location(), |
537 | 0 | "This contract has a payable fallback function, but no receive ether function. Consider adding a receive ether function.", |
538 | 0 | SecondarySourceLocation{}.append("The payable fallback function is defined here.", fallback->location()) |
539 | 0 | ); |
540 | 3.60k | } |
541 | | |
542 | | void ContractLevelChecker::checkStorageSize(ContractDefinition const& _contract) |
543 | 3.60k | { |
544 | 3.60k | bigint size = 0; |
545 | 3.60k | for (ContractDefinition const* contract: _contract.annotation().linearizedBaseContracts | ranges::views::reverse) |
546 | 3.60k | for (VariableDeclaration const* variable: contract->stateVariables()) |
547 | 2.94k | if (!(variable->isConstant() || variable->immutable())) |
548 | 2.94k | { |
549 | 2.94k | size += variable->annotation().type->storageSizeUpperBound(); |
550 | 2.94k | if (size >= bigint(1) << 256) |
551 | 0 | { |
552 | 0 | m_errorReporter.typeError(7676_error, _contract.location(), "Contract requires too much storage."); |
553 | 0 | break; |
554 | 0 | } |
555 | 2.94k | } |
556 | 3.60k | } |