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  * Framework for testing features from the analysis phase of compiler.
20  */
21 
22 #include <test/libsolidity/AnalysisFramework.h>
23 
24 #include <test/Common.h>
25 
26 #include <libsolidity/interface/CompilerStack.h>
27 #include <liblangutil/SourceReferenceFormatter.h>
28 
29 #include <libsolidity/ast/AST.h>
30 
31 #include <liblangutil/Scanner.h>
32 
33 #include <libsolutil/Keccak256.h>
34 
35 #include <boost/test/unit_test.hpp>
36 
37 using namespace std;
38 using namespace solidity;
39 using namespace solidity::util;
40 using namespace solidity::langutil;
41 using namespace solidity::frontend;
42 using namespace solidity::frontend::test;
43 
44 pair<SourceUnit const*, ErrorList>
parseAnalyseAndReturnError(string const & _source,bool _reportWarnings,bool _insertLicenseAndVersionPragma,bool _allowMultipleErrors,bool _allowRecoveryErrors)45 AnalysisFramework::parseAnalyseAndReturnError(
46 	string const& _source,
47 	bool _reportWarnings,
48 	bool _insertLicenseAndVersionPragma,
49 	bool _allowMultipleErrors,
50 	bool _allowRecoveryErrors
51 )
52 {
53 	compiler().reset();
54 	// Do not insert license if it is already present.
55 	bool insertLicense = _insertLicenseAndVersionPragma && _source.find("// SPDX-License-Identifier:") == string::npos;
56 	compiler().setSources({{"",
57 		string{_insertLicenseAndVersionPragma ? "pragma solidity >=0.0;\n" : ""} +
58 		string{insertLicense ? "// SPDX-License-Identifier: GPL-3.0\n" : ""} +
59 		_source
60 	}});
61 	compiler().setEVMVersion(solidity::test::CommonOptions::get().evmVersion());
62 	compiler().setParserErrorRecovery(_allowRecoveryErrors);
63 	_allowMultipleErrors = _allowMultipleErrors || _allowRecoveryErrors;
64 	if (!compiler().parse())
65 	{
66 		BOOST_FAIL("Parsing contract failed in analysis test suite:" + formatErrors());
67 	}
68 
69 	compiler().analyze();
70 
71 	ErrorList errors = filterErrors(compiler().errors(), _reportWarnings);
72 	if (errors.size() > 1 && !_allowMultipleErrors)
73 		BOOST_FAIL("Multiple errors found: " + formatErrors());
74 
75 	return make_pair(&compiler().ast(""), std::move(errors));
76 }
77 
filterErrors(ErrorList const & _errorList,bool _includeWarningsAndInfos) const78 ErrorList AnalysisFramework::filterErrors(ErrorList const& _errorList, bool _includeWarningsAndInfos) const
79 {
80 	ErrorList errors;
81 	for (auto const& currentError: _errorList)
82 	{
83 		solAssert(currentError->comment(), "");
84 		if (!Error::isError(currentError->type()))
85 		{
86 			if (!_includeWarningsAndInfos)
87 				continue;
88 			bool ignoreWarningsAndInfos = false;
89 			for (auto const& filter: m_warningsToFilter)
90 				if (currentError->comment()->find(filter) == 0)
91 				{
92 					ignoreWarningsAndInfos = true;
93 					break;
94 				}
95 			if (ignoreWarningsAndInfos)
96 				continue;
97 		}
98 
99 		std::shared_ptr<Error const> newError = currentError;
100 		for (auto const& messagePrefix: m_messagesToCut)
101 			if (currentError->comment()->find(messagePrefix) == 0)
102 			{
103 				SourceLocation const* location = currentError->sourceLocation();
104 				// sufficient for now, but in future we might clone the error completely, including the secondary location
105 				newError = make_shared<Error>(
106 					currentError->errorId(),
107 					currentError->type(),
108 					messagePrefix + " ....",
109 					location ? *location : SourceLocation()
110 				);
111 				break;
112 			}
113 
114 		errors.emplace_back(newError);
115 	}
116 
117 	return errors;
118 }
119 
parseAndAnalyse(string const & _source)120 SourceUnit const* AnalysisFramework::parseAndAnalyse(string const& _source)
121 {
122 	auto sourceAndError = parseAnalyseAndReturnError(_source);
123 	BOOST_REQUIRE(!!sourceAndError.first);
124 	string message;
125 	if (!sourceAndError.second.empty())
126 		message = "Unexpected error: " + formatErrors();
127 	BOOST_REQUIRE_MESSAGE(sourceAndError.second.empty(), message);
128 	return sourceAndError.first;
129 }
130 
success(string const & _source)131 bool AnalysisFramework::success(string const& _source)
132 {
133 	return parseAnalyseAndReturnError(_source).second.empty();
134 }
135 
expectError(std::string const & _source,bool _warning,bool _allowMultiple)136 ErrorList AnalysisFramework::expectError(std::string const& _source, bool _warning, bool _allowMultiple)
137 {
138 	auto sourceAndErrors = parseAnalyseAndReturnError(_source, _warning, true, _allowMultiple);
139 	BOOST_REQUIRE(!sourceAndErrors.second.empty());
140 	BOOST_REQUIRE_MESSAGE(!!sourceAndErrors.first, "Expected error, but no error happened.");
141 	return sourceAndErrors.second;
142 }
143 
formatErrors() const144 string AnalysisFramework::formatErrors() const
145 {
146 	string message;
147 	for (auto const& error: compiler().errors())
148 		message += formatError(*error);
149 	return message;
150 }
151 
formatError(Error const & _error) const152 string AnalysisFramework::formatError(Error const& _error) const
153 {
154 	return SourceReferenceFormatter::formatErrorInformation(_error, *m_compiler);
155 }
156 
retrieveContractByName(SourceUnit const & _source,string const & _name)157 ContractDefinition const* AnalysisFramework::retrieveContractByName(SourceUnit const& _source, string const& _name)
158 {
159 	ContractDefinition* contract = nullptr;
160 
161 	for (shared_ptr<ASTNode> const& node: _source.nodes())
162 		if ((contract = dynamic_cast<ContractDefinition*>(node.get())) && contract->name() == _name)
163 			return contract;
164 
165 	return nullptr;
166 }
167 
retrieveFunctionBySignature(ContractDefinition const & _contract,std::string const & _signature)168 FunctionTypePointer AnalysisFramework::retrieveFunctionBySignature(
169 	ContractDefinition const& _contract,
170 	std::string const& _signature
171 )
172 {
173 	FixedHash<4> hash(util::keccak256(_signature));
174 	return _contract.interfaceFunctions()[hash];
175 }
176