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