forked from ethereum/solidity
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathAnalysisFramework.h
227 lines (191 loc) · 8.58 KB
/
AnalysisFramework.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
/*
This file is part of solidity.
solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
// SPDX-License-Identifier: GPL-3.0
/**
* Framework for testing features from the analysis phase of compiler.
*/
#pragma once
#include <test/libsolidity/ErrorCheck.h>
#include <libsolidity/interface/CompilerStack.h>
#include <functional>
#include <string>
#include <memory>
namespace solidity::frontend
{
class Type;
class FunctionType;
using FunctionTypePointer = FunctionType const*;
}
namespace solidity::frontend::test
{
enum class PipelineStage {
Parsing,
Analysis,
Compilation,
};
class AnalysisFramework
{
protected:
virtual ~AnalysisFramework() = default;
/// Runs analysis via runFramework() and returns either an AST or a filtered list of errors.
/// Uses Boost test macros to fail if errors do not occur specifically at the analysis stage.
///
/// @deprecated This is a legacy helper. Use runFramework() directly in new tests.
///
/// @param _includeWarningsAndInfos Do not remove warning and info messages from the error list.
/// @param _addPreamble Apply withPreamble() to @p _source.
/// @param _allowMultiple When false, use Boost test macros to fail when there's more
/// than one item on the error list.
std::pair<SourceUnit const*, langutil::ErrorList> runAnalysisAndExpectNoParsingErrors(
std::string const& _source,
bool _includeWarningsAndInfos = false,
bool _addPreamble = true,
bool _allowMultiple = false
);
/// Runs analysis via runAnalysisAndExpectNoParsingErrors() and returns the list of errors.
/// Uses Boost test macros to fail if there are no errors.
///
/// @deprecated This is a legacy helper. Use runFramework() directly in new tests.
///
/// @param _includeWarningsAndInfos Do not remove warning and info messages from the error list.
/// @param _allowMultiple When false, use Boost test macros to fail when there's more
/// than one item on the error list.
langutil::ErrorList runAnalysisAndExpectError(
std::string const& _source,
bool _includeWarningsAndInfos = false,
bool _allowMultiple = false
);
public:
/// Runs the full compiler pipeline on specified sources. This is the main function of the
/// framework. Resets the stack, configures it and runs either until the first failed stage or
/// until the @p _targetStage is reached.
/// Afterwards the caller can inspect the stack via @p compiler(). The framework provides a few
/// convenience helpers to check the state and error list, in general the caller can freely
/// access the stack, including generating outputs if the compilation succeeded.
bool runFramework(StringMap _sources, PipelineStage _targetStage = PipelineStage::Compilation);
bool runFramework(std::string _source, PipelineStage _targetStage = PipelineStage::Compilation)
{
return runFramework({{"", std::move(_source)}}, _targetStage);
}
void resetFramework();
PipelineStage targetStage() const { return m_targetStage; }
bool pipelineSuccessful() const { return stageSuccessful(m_targetStage); }
bool stageSuccessful(PipelineStage _stage) const;
std::string formatErrors(
langutil::ErrorList const& _errors,
bool _colored = false,
bool _withErrorIds = false
) const;
std::string formatError(
langutil::Error const& _error,
bool _colored = false,
bool _withErrorIds = false
) const;
static ContractDefinition const* retrieveContractByName(SourceUnit const& _source, std::string const& _name);
static FunctionTypePointer retrieveFunctionBySignature(
ContractDefinition const& _contract,
std::string const& _signature
);
/// filter out the warnings in m_warningsToFilter or all warnings and infos if _includeWarningsAndInfos is false
langutil::ErrorList filterErrors(langutil::ErrorList const& _errorList, bool _includeWarningsAndInfos = true) const;
langutil::ErrorList filteredErrors(bool _includeWarningsAndInfos = true) const
{
return filterErrors(compiler().errors(), _includeWarningsAndInfos);
}
/// @returns reference to lazy-instantiated CompilerStack.
solidity::frontend::CompilerStack& compiler()
{
if (!m_compiler)
m_compiler = createStack();
return *m_compiler;
}
/// @returns reference to lazy-instantiated CompilerStack.
solidity::frontend::CompilerStack const& compiler() const
{
if (!m_compiler)
m_compiler = createStack();
return *m_compiler;
}
protected:
/// Creates a new instance of @p CompilerStack. Override if your test case needs to pass in
/// custom constructor arguments.
virtual std::unique_ptr<CompilerStack> createStack() const;
/// Configures @p CompilerStack. The default implementation sets basic parameters based on
/// CLI options. Override if your test case needs extra configuration.
virtual void setupCompiler(CompilerStack& _compiler);
/// Executes the requested pipeline stages until @p m_targetStage is reached.
/// Stops at the first failed stage.
void executeCompilationPipeline();
std::vector<std::string> m_warningsToFilter = {"This is a pre-release compiler version"};
std::vector<std::string> m_messagesToCut = {"Source file requires different compiler version (current compiler is"};
private:
mutable std::unique_ptr<solidity::frontend::CompilerStack> m_compiler;
PipelineStage m_targetStage = PipelineStage::Compilation;
};
// Asserts that the compilation down to typechecking
// emits multiple errors of different types and messages, provided in the second argument.
#define CHECK_ALLOW_MULTI(text, expectations) \
do \
{ \
ErrorList errors = runAnalysisAndExpectError((text), true, true); \
auto message = searchErrors(errors, (expectations)); \
BOOST_CHECK_MESSAGE(message.empty(), message); \
} while(0)
#define CHECK_ERROR_OR_WARNING(text, typ, substrings, includeWarningsAndInfos, allowMulti) \
do \
{ \
ErrorList errors = runAnalysisAndExpectError((text), (includeWarningsAndInfos), (allowMulti)); \
std::vector<std::pair<Error::Type, std::string>> expectations; \
for (auto const& str: substrings) \
expectations.emplace_back((Error::Type::typ), str); \
auto message = searchErrors(errors, expectations); \
BOOST_CHECK_MESSAGE(message.empty(), message); \
} while(0)
// [checkError(text, type, substring)] asserts that the compilation down to typechecking
// emits an error of type [type] and with a message containing [substring].
#define CHECK_ERROR(text, type, substring) \
CHECK_ERROR_OR_WARNING(text, type, std::vector<std::string>{(substring)}, false, false)
// [checkError(text, type, substring)] asserts that the compilation down to typechecking
// emits multiple errors of the same type [type] and with a messages containing [substrings].
// Because of the limitations of the preprocessor, you cannot use {{T1, "abc"}, {T2, "def"}} as arguments,
// but have to replace them by (std::vector<std::pair<Error::Type, std::string>>{"abc", "def"})
// (note the parentheses)
#define CHECK_ERROR_ALLOW_MULTI(text, type, substrings) \
CHECK_ERROR_OR_WARNING(text, type, substrings, false, true)
// [checkWarning(text, substring)] asserts that the compilation down to typechecking
// emits a warning and with a message containing [substring].
#define CHECK_WARNING(text, substring) \
CHECK_ERROR_OR_WARNING(text, Warning, std::vector<std::string>{(substring)}, true, false)
// [checkWarningAllowMulti(text, substring)] asserts that the compilation down to typechecking
// emits a warning and with a message containing [substring].
// Because of the limitations of the preprocessor, you cannot use {"abc", "def"} as arguments,
// but have to replace them by (std::vector<std::string>{"abc", "def"}) (note the parentheses)
#define CHECK_WARNING_ALLOW_MULTI(text, substrings) \
CHECK_ERROR_OR_WARNING(text, Warning, substrings, true, true)
// [checkSuccess(text)] asserts that the compilation down to typechecking succeeds.
#define CHECK_SUCCESS(text) do { \
auto [ast, errors] = runAnalysisAndExpectNoParsingErrors((text)); \
BOOST_CHECK(errors.empty()); \
} while(0)
#define CHECK_SUCCESS_NO_WARNINGS(text) \
do \
{ \
auto [ast, errors] = runAnalysisAndExpectNoParsingErrors((text), true); \
std::string message; \
if (!errors.empty()) \
message = formatErrors(errors);\
BOOST_CHECK_MESSAGE(errors.empty(), message); \
} \
while(0)
}