/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 std; |
43 | | using namespace solidity; |
44 | | using namespace solidity::langutil; |
45 | | using namespace solidity::frontend; |
46 | | |
47 | | |
48 | | bool ReferencesResolver::resolve(ASTNode const& _root) |
49 | 70.3k | { |
50 | 70.3k | auto errorWatcher = m_errorReporter.errorWatcher(); |
51 | 70.3k | _root.accept(*this); |
52 | 70.3k | return errorWatcher.ok(); |
53 | 70.3k | } |
54 | | |
55 | | bool ReferencesResolver::visit(Block const& _block) |
56 | 30.1k | { |
57 | 30.1k | if (!m_resolveInsideCode) |
58 | 12.7k | return false; |
59 | 17.4k | m_resolver.setScope(&_block); |
60 | 17.4k | return true; |
61 | 30.1k | } |
62 | | |
63 | | void ReferencesResolver::endVisit(Block const& _block) |
64 | 30.1k | { |
65 | 30.1k | if (!m_resolveInsideCode) |
66 | 12.7k | return; |
67 | | |
68 | 17.4k | m_resolver.setScope(_block.scope()); |
69 | 17.4k | } |
70 | | |
71 | | bool ReferencesResolver::visit(TryCatchClause const& _tryCatchClause) |
72 | 417 | { |
73 | 417 | if (!m_resolveInsideCode) |
74 | 0 | return false; |
75 | 417 | m_resolver.setScope(&_tryCatchClause); |
76 | 417 | return true; |
77 | 417 | } |
78 | | |
79 | | void ReferencesResolver::endVisit(TryCatchClause const& _tryCatchClause) |
80 | 414 | { |
81 | 414 | if (!m_resolveInsideCode) |
82 | 0 | return; |
83 | | |
84 | 414 | m_resolver.setScope(_tryCatchClause.scope()); |
85 | 414 | } |
86 | | |
87 | | bool ReferencesResolver::visit(ForStatement const& _for) |
88 | 192 | { |
89 | 192 | if (!m_resolveInsideCode) |
90 | 0 | return false; |
91 | 192 | m_resolver.setScope(&_for); |
92 | 192 | return true; |
93 | 192 | } |
94 | | |
95 | | void ReferencesResolver::endVisit(ForStatement const& _for) |
96 | 190 | { |
97 | 190 | if (!m_resolveInsideCode) |
98 | 0 | return; |
99 | 190 | m_resolver.setScope(_for.scope()); |
100 | 190 | } |
101 | | |
102 | | void ReferencesResolver::endVisit(VariableDeclarationStatement const& _varDeclStatement) |
103 | 5.56k | { |
104 | 5.56k | if (!m_resolveInsideCode) |
105 | 0 | return; |
106 | 5.56k | for (auto const& var: _varDeclStatement.declarations()) |
107 | 6.85k | if (var) |
108 | 6.19k | m_resolver.activateVariable(var->name()); |
109 | 5.56k | } |
110 | | |
111 | | bool ReferencesResolver::visit(VariableDeclaration const& _varDecl) |
112 | 65.5k | { |
113 | 65.5k | if (_varDecl.documentation()) |
114 | 181 | resolveInheritDoc(*_varDecl.documentation(), _varDecl.annotation()); |
115 | | |
116 | 65.5k | return true; |
117 | 65.5k | } |
118 | | |
119 | | bool ReferencesResolver::visit(Identifier const& _identifier) |
120 | 42.4k | { |
121 | 42.4k | auto declarations = m_resolver.nameFromCurrentScope(_identifier.name()); |
122 | 42.4k | if (declarations.empty()) |
123 | 1.90k | { |
124 | 1.90k | string suggestions = m_resolver.similarNameSuggestions(_identifier.name()); |
125 | 1.90k | string errorMessage = "Undeclared identifier."; |
126 | 1.90k | if (!suggestions.empty()) |
127 | 496 | { |
128 | 496 | if ("\"" + _identifier.name() + "\"" == suggestions) |
129 | 26 | errorMessage += " " + std::move(suggestions) + " is not (or not yet) visible at this point."; |
130 | 470 | else |
131 | 470 | errorMessage += " Did you mean " + std::move(suggestions) + "?"; |
132 | 496 | } |
133 | 1.90k | m_errorReporter.declarationError(7576_error, _identifier.location(), errorMessage); |
134 | 1.90k | } |
135 | 40.5k | else if (declarations.size() == 1) |
136 | 40.0k | _identifier.annotation().referencedDeclaration = declarations.front(); |
137 | 471 | else |
138 | 471 | _identifier.annotation().candidateDeclarations = declarations; |
139 | 42.4k | return false; |
140 | 42.4k | } |
141 | | |
142 | | bool ReferencesResolver::visit(FunctionDefinition const& _functionDefinition) |
143 | 27.0k | { |
144 | 27.0k | m_returnParameters.push_back(_functionDefinition.returnParameterList().get()); |
145 | | |
146 | 27.0k | if (_functionDefinition.documentation()) |
147 | 345 | resolveInheritDoc(*_functionDefinition.documentation(), _functionDefinition.annotation()); |
148 | | |
149 | 27.0k | return true; |
150 | 27.0k | } |
151 | | |
152 | | void ReferencesResolver::endVisit(FunctionDefinition const&) |
153 | 26.8k | { |
154 | 26.8k | solAssert(!m_returnParameters.empty(), ""); |
155 | 26.8k | m_returnParameters.pop_back(); |
156 | 26.8k | } |
157 | | |
158 | | bool ReferencesResolver::visit(ModifierDefinition const& _modifierDefinition) |
159 | 920 | { |
160 | 920 | m_returnParameters.push_back(nullptr); |
161 | | |
162 | 920 | if (_modifierDefinition.documentation()) |
163 | 17 | resolveInheritDoc(*_modifierDefinition.documentation(), _modifierDefinition.annotation()); |
164 | | |
165 | 920 | return true; |
166 | 920 | } |
167 | | |
168 | | void ReferencesResolver::endVisit(ModifierDefinition const&) |
169 | 913 | { |
170 | 913 | solAssert(!m_returnParameters.empty(), ""); |
171 | 913 | m_returnParameters.pop_back(); |
172 | 913 | } |
173 | | |
174 | | void ReferencesResolver::endVisit(IdentifierPath const& _path) |
175 | 11.6k | { |
176 | 11.6k | std::vector<Declaration const*> declarations = m_resolver.pathFromCurrentScopeWithAllDeclarations(_path.path()); |
177 | 11.6k | if (declarations.empty()) |
178 | 395 | { |
179 | 395 | m_errorReporter.fatalDeclarationError(7920_error, _path.location(), "Identifier not found or not unique."); |
180 | 395 | return; |
181 | 395 | } |
182 | | |
183 | 11.2k | _path.annotation().referencedDeclaration = declarations.back(); |
184 | 11.2k | _path.annotation().pathDeclarations = std::move(declarations); |
185 | 11.2k | } |
186 | | |
187 | | bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly) |
188 | 1.02k | { |
189 | 1.02k | m_yulAnnotation = &_inlineAssembly.annotation(); |
190 | 1.02k | (*this)(_inlineAssembly.operations()); |
191 | 1.02k | m_yulAnnotation = nullptr; |
192 | | |
193 | 1.02k | return false; |
194 | 1.02k | } |
195 | | |
196 | | bool ReferencesResolver::visit(Return const& _return) |
197 | 2.64k | { |
198 | 2.64k | solAssert(!m_returnParameters.empty(), ""); |
199 | 2.64k | _return.annotation().functionReturnParameters = m_returnParameters.back(); |
200 | 2.64k | return true; |
201 | 2.64k | } |
202 | | |
203 | | void ReferencesResolver::operator()(yul::FunctionDefinition const& _function) |
204 | 56 | { |
205 | 56 | solAssert(nativeLocationOf(_function) == originLocationOf(_function), ""); |
206 | 56 | validateYulIdentifierName(_function.name, nativeLocationOf(_function)); |
207 | 56 | for (yul::TypedName const& varName: _function.parameters + _function.returnVariables) |
208 | 74 | { |
209 | 74 | solAssert(nativeLocationOf(varName) == originLocationOf(varName), ""); |
210 | 74 | validateYulIdentifierName(varName.name, nativeLocationOf(varName)); |
211 | 74 | } |
212 | | |
213 | 56 | bool wasInsideFunction = m_yulInsideFunction; |
214 | 56 | m_yulInsideFunction = true; |
215 | 56 | this->operator()(_function.body); |
216 | 56 | m_yulInsideFunction = wasInsideFunction; |
217 | 56 | } |
218 | | |
219 | | void ReferencesResolver::operator()(yul::Identifier const& _identifier) |
220 | 1.47k | { |
221 | 1.47k | solAssert(nativeLocationOf(_identifier) == originLocationOf(_identifier), ""); |
222 | | |
223 | 1.47k | static set<string> suffixes{"slot", "offset", "length", "address", "selector"}; |
224 | 1.47k | string suffix; |
225 | 1.47k | for (string const& s: suffixes) |
226 | 7.36k | if (boost::algorithm::ends_with(_identifier.name.str(), "." + s)) |
227 | 142 | suffix = s; |
228 | | |
229 | | // Could also use `pathFromCurrentScope`, split by '.'. |
230 | | // If we do that, suffix should only be set for when it has a special |
231 | | // meaning, not for normal identifierPaths. |
232 | 1.47k | auto declarations = m_resolver.nameFromCurrentScope(_identifier.name.str()); |
233 | 1.47k | if (!suffix.empty()) |
234 | 142 | { |
235 | | // special mode to access storage variables |
236 | 142 | if (!declarations.empty()) |
237 | | // the special identifier exists itself, we should not allow that. |
238 | 0 | return; |
239 | 142 | string realName = _identifier.name.str().substr(0, _identifier.name.str().size() - suffix.size() - 1); |
240 | 142 | solAssert(!realName.empty(), "Empty name."); |
241 | 142 | declarations = m_resolver.nameFromCurrentScope(realName); |
242 | 142 | if (!declarations.empty()) |
243 | | // To support proper path resolution, we have to use pathFromCurrentScope. |
244 | 142 | solAssert(!util::contains(realName, '.'), ""); |
245 | 142 | } |
246 | 1.47k | if (declarations.size() > 1) |
247 | 1 | { |
248 | 1 | m_errorReporter.declarationError( |
249 | 1 | 4718_error, |
250 | 1 | nativeLocationOf(_identifier), |
251 | 1 | "Multiple matching identifiers. Resolving overloaded identifiers is not supported." |
252 | 1 | ); |
253 | 1 | return; |
254 | 1 | } |
255 | 1.47k | else if (declarations.size() == 0) |
256 | 704 | { |
257 | 704 | if ( |
258 | 704 | boost::algorithm::ends_with(_identifier.name.str(), "_slot") || |
259 | 704 | boost::algorithm::ends_with(_identifier.name.str(), "_offset") |
260 | 704 | ) |
261 | 3 | m_errorReporter.declarationError( |
262 | 3 | 9467_error, |
263 | 3 | nativeLocationOf(_identifier), |
264 | 3 | "Identifier not found. Use \".slot\" and \".offset\" to access storage variables." |
265 | 3 | ); |
266 | 704 | return; |
267 | 704 | } |
268 | 767 | if (auto var = dynamic_cast<VariableDeclaration const*>(declarations.front())) |
269 | 682 | if (var->isLocalVariable() && m_yulInsideFunction) |
270 | 2 | { |
271 | 2 | m_errorReporter.declarationError( |
272 | 2 | 6578_error, |
273 | 2 | nativeLocationOf(_identifier), |
274 | 2 | "Cannot access local Solidity variables from inside an inline assembly function." |
275 | 2 | ); |
276 | 2 | return; |
277 | 2 | } |
278 | | |
279 | 765 | m_yulAnnotation->externalReferences[&_identifier].suffix = move(suffix); |
280 | 765 | m_yulAnnotation->externalReferences[&_identifier].declaration = declarations.front(); |
281 | 765 | } |
282 | | |
283 | | void ReferencesResolver::operator()(yul::VariableDeclaration const& _varDecl) |
284 | 185 | { |
285 | 185 | for (auto const& identifier: _varDecl.variables) |
286 | 337 | { |
287 | 337 | solAssert(nativeLocationOf(identifier) == originLocationOf(identifier), ""); |
288 | 337 | validateYulIdentifierName(identifier.name, nativeLocationOf(identifier)); |
289 | | |
290 | 337 | if ( |
291 | 337 | auto declarations = m_resolver.nameFromCurrentScope(identifier.name.str()); |
292 | 337 | !declarations.empty() |
293 | 337 | ) |
294 | 4 | { |
295 | 4 | SecondarySourceLocation ssl; |
296 | 4 | for (auto const* decl: declarations) |
297 | 4 | ssl.append("The shadowed declaration is here:", decl->location()); |
298 | 4 | if (!ssl.infos.empty()) |
299 | 4 | m_errorReporter.declarationError( |
300 | 4 | 3859_error, |
301 | 4 | nativeLocationOf(identifier), |
302 | 4 | ssl, |
303 | 4 | "This declaration shadows a declaration outside the inline assembly block." |
304 | 4 | ); |
305 | 4 | } |
306 | 337 | } |
307 | | |
308 | 185 | if (_varDecl.value) |
309 | 90 | visit(*_varDecl.value); |
310 | 185 | } |
311 | | |
312 | | void ReferencesResolver::resolveInheritDoc(StructuredDocumentation const& _documentation, StructurallyDocumentedAnnotation& _annotation) |
313 | 543 | { |
314 | 543 | switch (_annotation.docTags.count("inheritdoc")) |
315 | 543 | { |
316 | 487 | case 0: |
317 | 487 | break; |
318 | 47 | case 1: |
319 | 47 | { |
320 | 47 | string const& name = _annotation.docTags.find("inheritdoc")->second.content; |
321 | 47 | if (name.empty()) |
322 | 3 | { |
323 | 3 | m_errorReporter.docstringParsingError( |
324 | 3 | 1933_error, |
325 | 3 | _documentation.location(), |
326 | 3 | "Expected contract name following documentation tag @inheritdoc." |
327 | 3 | ); |
328 | 3 | return; |
329 | 3 | } |
330 | | |
331 | 44 | vector<string> path; |
332 | 44 | boost::split(path, name, boost::is_any_of(".")); |
333 | 68 | if (any_of(path.begin(), path.end(), [](auto& _str) { return _str.empty(); })) |
334 | 3 | { |
335 | 3 | m_errorReporter.docstringParsingError( |
336 | 3 | 5967_error, |
337 | 3 | _documentation.location(), |
338 | 3 | "Documentation tag @inheritdoc reference \"" + |
339 | 3 | name + |
340 | 3 | "\" is malformed." |
341 | 3 | ); |
342 | 3 | return; |
343 | 3 | } |
344 | 41 | Declaration const* result = m_resolver.pathFromCurrentScope(path); |
345 | | |
346 | 41 | if (result == nullptr) |
347 | 12 | { |
348 | 12 | m_errorReporter.docstringParsingError( |
349 | 12 | 9397_error, |
350 | 12 | _documentation.location(), |
351 | 12 | "Documentation tag @inheritdoc references inexistent contract \"" + |
352 | 12 | name + |
353 | 12 | "\"." |
354 | 12 | ); |
355 | 12 | return; |
356 | 12 | } |
357 | 29 | else |
358 | 29 | { |
359 | 29 | _annotation.inheritdocReference = dynamic_cast<ContractDefinition const*>(result); |
360 | | |
361 | 29 | if (!_annotation.inheritdocReference) |
362 | 1 | m_errorReporter.docstringParsingError( |
363 | 1 | 1430_error, |
364 | 1 | _documentation.location(), |
365 | 1 | "Documentation tag @inheritdoc reference \"" + |
366 | 1 | name + |
367 | 1 | "\" is not a contract." |
368 | 1 | ); |
369 | 29 | } |
370 | 29 | break; |
371 | 41 | } |
372 | 29 | default: |
373 | 9 | m_errorReporter.docstringParsingError( |
374 | 9 | 5142_error, |
375 | 9 | _documentation.location(), |
376 | 9 | "Documentation tag @inheritdoc can only be given once." |
377 | 9 | ); |
378 | 9 | break; |
379 | 543 | } |
380 | 543 | } |
381 | | |
382 | | void ReferencesResolver::validateYulIdentifierName(yul::YulString _name, SourceLocation const& _location) |
383 | 467 | { |
384 | 467 | if (util::contains(_name.str(), '.')) |
385 | 14 | m_errorReporter.declarationError( |
386 | 14 | 3927_error, |
387 | 14 | _location, |
388 | 14 | "User-defined identifiers in inline assembly cannot contain '.'." |
389 | 14 | ); |
390 | | |
391 | 467 | if (set<string>{"this", "super", "_"}.count(_name.str())) |
392 | 7 | m_errorReporter.declarationError( |
393 | 7 | 4113_error, |
394 | 7 | _location, |
395 | 7 | "The identifier name \"" + _name.str() + "\" is reserved." |
396 | 7 | ); |
397 | 467 | } |