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