/src/solidity/libsolidity/analysis/ReferencesResolver.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 | | * @author Christian <c@ethdev.com> |
20 | | * @date 2015 |
21 | | * Component that resolves type names to types and annotates the AST accordingly. |
22 | | */ |
23 | | |
24 | | #include <libsolidity/analysis/ReferencesResolver.h> |
25 | | #include <libsolidity/analysis/NameAndTypeResolver.h> |
26 | | #include <libsolidity/ast/AST.h> |
27 | | |
28 | | #include <libyul/AsmAnalysis.h> |
29 | | #include <libyul/AsmAnalysisInfo.h> |
30 | | #include <libyul/AST.h> |
31 | | #include <libyul/backends/evm/EVMDialect.h> |
32 | | |
33 | | #include <liblangutil/ErrorReporter.h> |
34 | | #include <liblangutil/Exceptions.h> |
35 | | |
36 | | #include <libsolutil/StringUtils.h> |
37 | | #include <libsolutil/CommonData.h> |
38 | | |
39 | | #include <boost/algorithm/string.hpp> |
40 | | #include <boost/algorithm/string/split.hpp> |
41 | | |
42 | | using namespace solidity; |
43 | | using namespace solidity::langutil; |
44 | | using namespace solidity::frontend; |
45 | | |
46 | | |
47 | | bool ReferencesResolver::resolve(ASTNode const& _root) |
48 | 160k | { |
49 | 160k | auto errorWatcher = m_errorReporter.errorWatcher(); |
50 | 160k | _root.accept(*this); |
51 | 160k | return errorWatcher.ok(); |
52 | 160k | } |
53 | | |
54 | | bool ReferencesResolver::visit(Block const& _block) |
55 | 54.1k | { |
56 | 54.1k | if (!m_resolveInsideCode) |
57 | 25.1k | return false; |
58 | 29.0k | m_resolver.setScope(&_block); |
59 | 29.0k | return true; |
60 | 54.1k | } |
61 | | |
62 | | void ReferencesResolver::endVisit(Block const& _block) |
63 | 54.1k | { |
64 | 54.1k | if (!m_resolveInsideCode) |
65 | 25.1k | return; |
66 | | |
67 | 28.9k | m_resolver.setScope(_block.scope()); |
68 | 28.9k | } |
69 | | |
70 | | bool ReferencesResolver::visit(TryCatchClause const& _tryCatchClause) |
71 | 672 | { |
72 | 672 | if (!m_resolveInsideCode) |
73 | 0 | return false; |
74 | 672 | m_resolver.setScope(&_tryCatchClause); |
75 | 672 | return true; |
76 | 672 | } |
77 | | |
78 | | void ReferencesResolver::endVisit(TryCatchClause const& _tryCatchClause) |
79 | 669 | { |
80 | 669 | if (!m_resolveInsideCode) |
81 | 0 | return; |
82 | | |
83 | 669 | m_resolver.setScope(_tryCatchClause.scope()); |
84 | 669 | } |
85 | | |
86 | | bool ReferencesResolver::visit(ForStatement const& _for) |
87 | 2.77k | { |
88 | 2.77k | if (!m_resolveInsideCode) |
89 | 0 | return false; |
90 | 2.77k | m_resolver.setScope(&_for); |
91 | 2.77k | return true; |
92 | 2.77k | } |
93 | | |
94 | | void ReferencesResolver::endVisit(ForStatement const& _for) |
95 | 2.77k | { |
96 | 2.77k | if (!m_resolveInsideCode) |
97 | 0 | return; |
98 | 2.77k | m_resolver.setScope(_for.scope()); |
99 | 2.77k | } |
100 | | |
101 | | void ReferencesResolver::endVisit(VariableDeclarationStatement const& _varDeclStatement) |
102 | 11.1k | { |
103 | 11.1k | if (!m_resolveInsideCode) |
104 | 0 | return; |
105 | 11.1k | for (auto const& var: _varDeclStatement.declarations()) |
106 | 12.2k | if (var) |
107 | 11.9k | m_resolver.activateVariable(var->name()); |
108 | 11.1k | } |
109 | | |
110 | | bool ReferencesResolver::visit(VariableDeclaration const& _varDecl) |
111 | 161k | { |
112 | 161k | if (_varDecl.documentation()) |
113 | 806 | resolveInheritDoc(*_varDecl.documentation(), _varDecl.annotation()); |
114 | | |
115 | 161k | if (m_resolver.experimentalSolidity()) |
116 | 306 | { |
117 | 306 | solAssert(!_varDecl.hasTypeName()); |
118 | 306 | if (_varDecl.typeExpression()) |
119 | 169 | { |
120 | 169 | ScopedSaveAndRestore typeContext{m_typeContext, true}; |
121 | 169 | _varDecl.typeExpression()->accept(*this); |
122 | 169 | } |
123 | 306 | if (_varDecl.overrides()) |
124 | 0 | _varDecl.overrides()->accept(*this); |
125 | 306 | if (_varDecl.value()) |
126 | 0 | _varDecl.value()->accept(*this); |
127 | 306 | return false; |
128 | 306 | } |
129 | | |
130 | 161k | return true; |
131 | 161k | } |
132 | | |
133 | | bool ReferencesResolver::visit(Identifier const& _identifier) |
134 | 162k | { |
135 | 162k | auto declarations = m_resolver.nameFromCurrentScope(_identifier.name()); |
136 | 162k | if (declarations.empty()) |
137 | 5.86k | { |
138 | 5.86k | if (m_resolver.experimentalSolidity() && m_typeContext) |
139 | 87 | return false; |
140 | 5.77k | std::string suggestions = m_resolver.similarNameSuggestions(_identifier.name()); |
141 | 5.77k | std::string errorMessage = "Undeclared identifier."; |
142 | 5.77k | if (!suggestions.empty()) |
143 | 763 | { |
144 | 763 | if ("\"" + _identifier.name() + "\"" == suggestions) |
145 | 77 | errorMessage += " " + std::move(suggestions) + " is not (or not yet) visible at this point."; |
146 | 686 | else |
147 | 686 | errorMessage += " Did you mean " + std::move(suggestions) + "?"; |
148 | 763 | } |
149 | 5.77k | m_errorReporter.declarationError(7576_error, _identifier.location(), errorMessage); |
150 | 5.77k | } |
151 | 157k | else if (declarations.size() == 1) |
152 | 155k | _identifier.annotation().referencedDeclaration = declarations.front(); |
153 | 1.09k | else |
154 | 1.09k | _identifier.annotation().candidateDeclarations = declarations; |
155 | 162k | return false; |
156 | 162k | } |
157 | | |
158 | | bool ReferencesResolver::visit(FunctionDefinition const& _functionDefinition) |
159 | 53.8k | { |
160 | 53.8k | m_functionDefinitions.push_back(&_functionDefinition); |
161 | | |
162 | 53.8k | if (_functionDefinition.documentation()) |
163 | 2.09k | resolveInheritDoc(*_functionDefinition.documentation(), _functionDefinition.annotation()); |
164 | | |
165 | 53.8k | return true; |
166 | 53.8k | } |
167 | | |
168 | | void ReferencesResolver::endVisit(FunctionDefinition const&) |
169 | 53.4k | { |
170 | 53.4k | solAssert(!m_functionDefinitions.empty(), ""); |
171 | 53.4k | m_functionDefinitions.pop_back(); |
172 | 53.4k | } |
173 | | |
174 | | bool ReferencesResolver::visit(ModifierDefinition const& _modifierDefinition) |
175 | 4.78k | { |
176 | 4.78k | m_functionDefinitions.push_back(nullptr); |
177 | | |
178 | 4.78k | if (_modifierDefinition.documentation()) |
179 | 13 | resolveInheritDoc(*_modifierDefinition.documentation(), _modifierDefinition.annotation()); |
180 | | |
181 | 4.78k | return true; |
182 | 4.78k | } |
183 | | |
184 | | void ReferencesResolver::endVisit(ModifierDefinition const&) |
185 | 4.76k | { |
186 | 4.76k | solAssert(!m_functionDefinitions.empty(), ""); |
187 | 4.76k | m_functionDefinitions.pop_back(); |
188 | 4.76k | } |
189 | | |
190 | | void ReferencesResolver::endVisit(IdentifierPath const& _path) |
191 | 46.5k | { |
192 | | // Note that library/functions names in "using {} for" directive are resolved separately in visit(UsingForDirective) |
193 | 46.5k | std::vector<Declaration const*> declarations = m_resolver.pathFromCurrentScopeWithAllDeclarations(_path.path()); |
194 | 46.5k | if (declarations.empty()) |
195 | 1.46k | { |
196 | 1.46k | m_errorReporter.fatalDeclarationError(7920_error, _path.location(), "Identifier not found or not unique."); |
197 | 1.46k | return; |
198 | 1.46k | } |
199 | | |
200 | 45.1k | _path.annotation().referencedDeclaration = declarations.back(); |
201 | 45.1k | _path.annotation().pathDeclarations = std::move(declarations); |
202 | 45.1k | } |
203 | | |
204 | | bool ReferencesResolver::visit(UsingForDirective const& _usingFor) |
205 | 915 | { |
206 | 915 | for (ASTPointer<IdentifierPath> const& path: _usingFor.functionsOrLibrary()) |
207 | 985 | { |
208 | | // _includeInvisibles is enabled here because external library functions are marked invisible. |
209 | | // As unintended side-effects other invisible names (eg.: super, this) may be returned as well. |
210 | | // DeclarationTypeChecker should detect and report such situations. |
211 | 985 | std::vector<Declaration const*> declarations = m_resolver.pathFromCurrentScopeWithAllDeclarations(path->path(), true /* _includeInvisibles */); |
212 | 985 | if (declarations.empty()) |
213 | 13 | { |
214 | 13 | std::string libraryOrFunctionNameErrorMessage = |
215 | 13 | _usingFor.usesBraces() ? |
216 | 8 | "Identifier is not a function name or not unique." : |
217 | 13 | "Identifier is not a library name."; |
218 | 13 | m_errorReporter.fatalDeclarationError( |
219 | 13 | 9589_error, |
220 | 13 | path->location(), |
221 | 13 | libraryOrFunctionNameErrorMessage |
222 | 13 | ); |
223 | 13 | break; |
224 | 13 | } |
225 | | |
226 | 972 | path->annotation().referencedDeclaration = declarations.back(); |
227 | 972 | path->annotation().pathDeclarations = std::move(declarations); |
228 | 972 | } |
229 | | |
230 | 915 | if (_usingFor.typeName()) |
231 | 667 | _usingFor.typeName()->accept(*this); |
232 | | |
233 | 915 | return false; |
234 | 915 | } |
235 | | |
236 | | bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly) |
237 | 2.73k | { |
238 | 2.73k | m_yulAnnotation = &_inlineAssembly.annotation(); |
239 | 2.73k | (*this)(_inlineAssembly.operations().root()); |
240 | 2.73k | m_yulAnnotation = nullptr; |
241 | | |
242 | 2.73k | return false; |
243 | 2.73k | } |
244 | | |
245 | | bool ReferencesResolver::visit(Return const& _return) |
246 | 46.4k | { |
247 | 46.4k | solAssert(!m_functionDefinitions.empty(), ""); |
248 | 46.4k | _return.annotation().function = m_functionDefinitions.back(); |
249 | 46.4k | _return.annotation().functionReturnParameters = m_functionDefinitions.back() ? m_functionDefinitions.back()->returnParameterList().get() : nullptr; |
250 | 46.4k | return true; |
251 | 46.4k | } |
252 | | |
253 | | bool ReferencesResolver::visit(BinaryOperation const& _binaryOperation) |
254 | 111k | { |
255 | 111k | if (m_resolver.experimentalSolidity()) |
256 | 16 | { |
257 | 16 | _binaryOperation.leftExpression().accept(*this); |
258 | 16 | if (_binaryOperation.getOperator() == Token::Colon) |
259 | 3 | { |
260 | 3 | ScopedSaveAndRestore typeContext(m_typeContext, !m_typeContext); |
261 | 3 | _binaryOperation.rightExpression().accept(*this); |
262 | 3 | } |
263 | 13 | else |
264 | 13 | _binaryOperation.rightExpression().accept(*this); |
265 | 16 | return false; |
266 | 16 | } |
267 | 111k | else |
268 | 111k | return ASTConstVisitor::visit(_binaryOperation); |
269 | 111k | } |
270 | | |
271 | | void ReferencesResolver::operator()(yul::FunctionDefinition const& _function) |
272 | 259 | { |
273 | 259 | solAssert(nativeLocationOf(_function) == originLocationOf(_function), ""); |
274 | 259 | validateYulIdentifierName(_function.name, nativeLocationOf(_function)); |
275 | 259 | for (yul::NameWithDebugData const& varName: _function.parameters + _function.returnVariables) |
276 | 274 | { |
277 | 274 | solAssert(nativeLocationOf(varName) == originLocationOf(varName), ""); |
278 | 274 | validateYulIdentifierName(varName.name, nativeLocationOf(varName)); |
279 | 274 | } |
280 | | |
281 | 259 | bool wasInsideFunction = m_yulInsideFunction; |
282 | 259 | m_yulInsideFunction = true; |
283 | 259 | this->operator()(_function.body); |
284 | 259 | m_yulInsideFunction = wasInsideFunction; |
285 | 259 | } |
286 | | |
287 | | void ReferencesResolver::operator()(yul::Identifier const& _identifier) |
288 | 2.55k | { |
289 | 2.55k | solAssert(nativeLocationOf(_identifier) == originLocationOf(_identifier), ""); |
290 | | |
291 | 2.55k | if (m_resolver.experimentalSolidity()) |
292 | 0 | { |
293 | 0 | std::vector<std::string> splitName; |
294 | 0 | boost::split(splitName, _identifier.name.str(), boost::is_any_of(".")); |
295 | 0 | solAssert(!splitName.empty()); |
296 | 0 | if (splitName.size() > 2) |
297 | 0 | { |
298 | 0 | m_errorReporter.declarationError( |
299 | 0 | 4955_error, |
300 | 0 | nativeLocationOf(_identifier), |
301 | 0 | "Unsupported identifier in inline assembly." |
302 | 0 | ); |
303 | 0 | return; |
304 | 0 | } |
305 | 0 | std::string name = splitName.front(); |
306 | 0 | auto declarations = m_resolver.nameFromCurrentScope(name); |
307 | 0 | switch (declarations.size()) |
308 | 0 | { |
309 | 0 | case 0: |
310 | 0 | if (splitName.size() > 1) |
311 | 0 | m_errorReporter.declarationError( |
312 | 0 | 7531_error, |
313 | 0 | nativeLocationOf(_identifier), |
314 | 0 | "Unsupported identifier in inline assembly." |
315 | 0 | ); |
316 | 0 | break; |
317 | 0 | case 1: |
318 | 0 | m_yulAnnotation->externalReferences[&_identifier].declaration = declarations.front(); |
319 | 0 | m_yulAnnotation->externalReferences[&_identifier].suffix = splitName.size() > 1 ? splitName.back() : ""; |
320 | 0 | break; |
321 | 0 | default: |
322 | 0 | m_errorReporter.declarationError( |
323 | 0 | 5387_error, |
324 | 0 | nativeLocationOf(_identifier), |
325 | 0 | "Multiple matching identifiers. Resolving overloaded identifiers is not supported." |
326 | 0 | ); |
327 | 0 | break; |
328 | 0 | } |
329 | 0 | return; |
330 | 0 | } |
331 | | |
332 | 2.55k | static std::set<std::string> suffixes{"slot", "offset", "length", "address", "selector"}; |
333 | 2.55k | std::string suffix; |
334 | 2.55k | for (std::string const& s: suffixes) |
335 | 12.7k | if (boost::algorithm::ends_with(_identifier.name.str(), "." + s)) |
336 | 329 | suffix = s; |
337 | | |
338 | | // Could also use `pathFromCurrentScope`, split by '.'. |
339 | | // If we do that, suffix should only be set for when it has a special |
340 | | // meaning, not for normal identifierPaths. |
341 | 2.55k | auto declarations = m_resolver.nameFromCurrentScope(_identifier.name.str()); |
342 | 2.55k | if (!suffix.empty()) |
343 | 329 | { |
344 | | // special mode to access storage variables |
345 | 329 | if (!declarations.empty()) |
346 | | // the special identifier exists itself, we should not allow that. |
347 | 0 | return; |
348 | 329 | std::string realName = _identifier.name.str().substr(0, _identifier.name.str().size() - suffix.size() - 1); |
349 | 329 | solAssert(!realName.empty(), "Empty name."); |
350 | 329 | declarations = m_resolver.nameFromCurrentScope(realName); |
351 | 329 | if (!declarations.empty()) |
352 | | // To support proper path resolution, we have to use pathFromCurrentScope. |
353 | 329 | solAssert(!util::contains(realName, '.'), ""); |
354 | 329 | } |
355 | 2.55k | if (declarations.size() > 1) |
356 | 2 | { |
357 | 2 | m_errorReporter.declarationError( |
358 | 2 | 4718_error, |
359 | 2 | nativeLocationOf(_identifier), |
360 | 2 | "Multiple matching identifiers. Resolving overloaded identifiers is not supported." |
361 | 2 | ); |
362 | 2 | return; |
363 | 2 | } |
364 | 2.55k | else if (declarations.size() == 0) |
365 | 887 | { |
366 | 887 | if ( |
367 | 887 | boost::algorithm::ends_with(_identifier.name.str(), "_slot") || |
368 | 887 | boost::algorithm::ends_with(_identifier.name.str(), "_offset") |
369 | 887 | ) |
370 | 11 | m_errorReporter.declarationError( |
371 | 11 | 9467_error, |
372 | 11 | nativeLocationOf(_identifier), |
373 | 11 | "Identifier not found. Use \".slot\" and \".offset\" to access storage or transient storage variables." |
374 | 11 | ); |
375 | 887 | return; |
376 | 887 | } |
377 | 1.66k | if (auto var = dynamic_cast<VariableDeclaration const*>(declarations.front())) |
378 | 1.64k | if (var->isLocalVariable() && m_yulInsideFunction) |
379 | 2 | { |
380 | 2 | m_errorReporter.declarationError( |
381 | 2 | 6578_error, |
382 | 2 | nativeLocationOf(_identifier), |
383 | 2 | "Cannot access local Solidity variables from inside an inline assembly function." |
384 | 2 | ); |
385 | 2 | return; |
386 | 2 | } |
387 | | |
388 | 1.66k | m_yulAnnotation->externalReferences[&_identifier].suffix = std::move(suffix); |
389 | 1.66k | m_yulAnnotation->externalReferences[&_identifier].declaration = declarations.front(); |
390 | 1.66k | } |
391 | | |
392 | | void ReferencesResolver::operator()(yul::VariableDeclaration const& _varDecl) |
393 | 268 | { |
394 | 268 | for (auto const& identifier: _varDecl.variables) |
395 | 318 | { |
396 | 318 | solAssert(nativeLocationOf(identifier) == originLocationOf(identifier), ""); |
397 | 318 | validateYulIdentifierName(identifier.name, nativeLocationOf(identifier)); |
398 | | |
399 | 318 | if ( |
400 | 318 | auto declarations = m_resolver.nameFromCurrentScope(identifier.name.str()); |
401 | 318 | !declarations.empty() |
402 | 318 | ) |
403 | 4 | { |
404 | 4 | SecondarySourceLocation ssl; |
405 | 4 | for (auto const* decl: declarations) |
406 | 4 | ssl.append("The shadowed declaration is here:", decl->location()); |
407 | 4 | if (!ssl.infos.empty()) |
408 | 4 | m_errorReporter.declarationError( |
409 | 4 | 3859_error, |
410 | 4 | nativeLocationOf(identifier), |
411 | 4 | ssl, |
412 | 4 | "This declaration shadows a declaration outside the inline assembly block." |
413 | 4 | ); |
414 | 4 | } |
415 | 318 | } |
416 | | |
417 | 268 | if (_varDecl.value) |
418 | 209 | visit(*_varDecl.value); |
419 | 268 | } |
420 | | |
421 | | void ReferencesResolver::resolveInheritDoc(StructuredDocumentation const& _documentation, StructurallyDocumentedAnnotation& _annotation) |
422 | 2.91k | { |
423 | 2.91k | switch (_annotation.docTags.count("inheritdoc")) |
424 | 2.91k | { |
425 | 2.77k | case 0: |
426 | 2.77k | break; |
427 | 111 | case 1: |
428 | 111 | { |
429 | 111 | std::string const& name = _annotation.docTags.find("inheritdoc")->second.content; |
430 | 111 | if (name.empty()) |
431 | 1 | { |
432 | 1 | m_errorReporter.docstringParsingError( |
433 | 1 | 1933_error, |
434 | 1 | _documentation.location(), |
435 | 1 | "Expected contract name following documentation tag @inheritdoc." |
436 | 1 | ); |
437 | 1 | return; |
438 | 1 | } |
439 | | |
440 | 110 | std::vector<std::string> path; |
441 | 110 | boost::split(path, name, boost::is_any_of(".")); |
442 | 146 | if (any_of(path.begin(), path.end(), [](auto& _str) { return _str.empty(); })) |
443 | 1 | { |
444 | 1 | m_errorReporter.docstringParsingError( |
445 | 1 | 5967_error, |
446 | 1 | _documentation.location(), |
447 | 1 | "Documentation tag @inheritdoc reference \"" + |
448 | 1 | name + |
449 | 1 | "\" is malformed." |
450 | 1 | ); |
451 | 1 | return; |
452 | 1 | } |
453 | 109 | Declaration const* result = m_resolver.pathFromCurrentScope(path); |
454 | | |
455 | 109 | if (result == nullptr) |
456 | 91 | { |
457 | 91 | m_errorReporter.docstringParsingError( |
458 | 91 | 9397_error, |
459 | 91 | _documentation.location(), |
460 | 91 | "Documentation tag @inheritdoc references inexistent contract \"" + |
461 | 91 | name + |
462 | 91 | "\"." |
463 | 91 | ); |
464 | 91 | return; |
465 | 91 | } |
466 | 18 | else |
467 | 18 | { |
468 | 18 | _annotation.inheritdocReference = dynamic_cast<ContractDefinition const*>(result); |
469 | | |
470 | 18 | if (!_annotation.inheritdocReference) |
471 | 2 | m_errorReporter.docstringParsingError( |
472 | 2 | 1430_error, |
473 | 2 | _documentation.location(), |
474 | 2 | "Documentation tag @inheritdoc reference \"" + |
475 | 2 | name + |
476 | 2 | "\" is not a contract." |
477 | 2 | ); |
478 | 18 | } |
479 | 18 | break; |
480 | 109 | } |
481 | 31 | default: |
482 | 31 | m_errorReporter.docstringParsingError( |
483 | 31 | 5142_error, |
484 | 31 | _documentation.location(), |
485 | 31 | "Documentation tag @inheritdoc can only be given once." |
486 | 31 | ); |
487 | 31 | break; |
488 | 2.91k | } |
489 | 2.91k | } |
490 | | |
491 | | void ReferencesResolver::validateYulIdentifierName(yul::YulName _name, SourceLocation const& _location) |
492 | 851 | { |
493 | 851 | if (util::contains(_name.str(), '.')) |
494 | 21 | m_errorReporter.declarationError( |
495 | 21 | 3927_error, |
496 | 21 | _location, |
497 | 21 | "User-defined identifiers in inline assembly cannot contain '.'." |
498 | 21 | ); |
499 | | |
500 | 851 | if (std::set<std::string>{"this", "super", "_"}.count(_name.str())) |
501 | 9 | m_errorReporter.declarationError( |
502 | 9 | 4113_error, |
503 | 9 | _location, |
504 | 9 | "The identifier name \"" + _name.str() + "\" is reserved." |
505 | 9 | ); |
506 | 851 | } |