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