Skip to content

Commit 309a8de

Browse files
committed
Refactor AnalysisFramework to share more features between test cases that depend on it
1 parent dc44f8a commit 309a8de

11 files changed

+172
-59
lines changed

test/libsolidity/AnalysisFramework.cpp

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include <test/libsolidity/AnalysisFramework.h>
2323

2424
#include <test/libsolidity/util/Common.h>
25+
#include <test/libsolidity/util/SoltestErrors.h>
2526
#include <test/Common.h>
2627

2728
#include <libsolidity/interface/CompilerStack.h>
@@ -66,11 +67,60 @@ AnalysisFramework::parseAnalyseAndReturnError(
6667
return make_pair(&compiler().ast(""), std::move(errors));
6768
}
6869

70+
bool AnalysisFramework::runFramework(StringMap _sources, PipelineStage _targetStage)
71+
{
72+
resetFramework();
73+
m_targetStage = _targetStage;
74+
soltestAssert(m_compiler);
75+
76+
m_compiler->setSources(std::move(_sources));
77+
setupCompiler(*m_compiler);
78+
executeCompilationPipeline();
79+
return pipelineSuccessful();
80+
}
81+
82+
void AnalysisFramework::resetFramework()
83+
{
84+
compiler().reset();
85+
m_targetStage = PipelineStage::Compilation;
86+
}
87+
6988
std::unique_ptr<CompilerStack> AnalysisFramework::createStack() const
7089
{
7190
return std::make_unique<CompilerStack>();
7291
}
7392

93+
void AnalysisFramework::setupCompiler(CompilerStack& _compiler)
94+
{
95+
_compiler.setEVMVersion(solidity::test::CommonOptions::get().evmVersion());
96+
}
97+
98+
void AnalysisFramework::executeCompilationPipeline()
99+
{
100+
soltestAssert(m_compiler);
101+
102+
// If you add a new stage, remember to handle it below.
103+
soltestAssert(
104+
m_targetStage == PipelineStage::Parsing ||
105+
m_targetStage == PipelineStage::Analysis ||
106+
m_targetStage == PipelineStage::Compilation
107+
);
108+
109+
bool parsingSuccessful = m_compiler->parse();
110+
soltestAssert(parsingSuccessful || !filteredErrors(false /* _includeWarningsAndInfos */).empty());
111+
if (!parsingSuccessful || stageSuccessful(m_targetStage))
112+
return;
113+
114+
bool analysisSuccessful = m_compiler->analyze();
115+
soltestAssert(analysisSuccessful || !filteredErrors(false /* _includeWarningsAndInfos */).empty());
116+
if (!analysisSuccessful || stageSuccessful(m_targetStage))
117+
return;
118+
119+
bool compilationSuccessful = m_compiler->compile();
120+
soltestAssert(compilationSuccessful || !filteredErrors(false /* _includeWarningsAndInfos */).empty());
121+
soltestAssert(stageSuccessful(m_targetStage) == compilationSuccessful);
122+
}
123+
74124
ErrorList AnalysisFramework::filterErrors(ErrorList const& _errorList, bool _includeWarningsAndInfos) const
75125
{
76126
ErrorList errors;
@@ -113,6 +163,16 @@ ErrorList AnalysisFramework::filterErrors(ErrorList const& _errorList, bool _inc
113163
return errors;
114164
}
115165

166+
bool AnalysisFramework::stageSuccessful(PipelineStage _stage) const
167+
{
168+
switch (_stage) {
169+
case PipelineStage::Parsing: return compiler().state() >= CompilerStack::Parsed;
170+
case PipelineStage::Analysis: return compiler().state() >= CompilerStack::AnalysisSuccessful;
171+
case PipelineStage::Compilation: return compiler().state() >= CompilerStack::CompilationSuccessful;
172+
}
173+
unreachable();
174+
}
175+
116176
SourceUnit const* AnalysisFramework::parseAndAnalyse(std::string const& _source)
117177
{
118178
auto sourceAndError = parseAnalyseAndReturnError(_source);

test/libsolidity/AnalysisFramework.h

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@ using FunctionTypePointer = FunctionType const*;
3939
namespace solidity::frontend::test
4040
{
4141

42+
enum class PipelineStage {
43+
Parsing,
44+
Analysis,
45+
Compilation,
46+
};
47+
4248
class AnalysisFramework
4349
{
4450

@@ -56,6 +62,25 @@ class AnalysisFramework
5662
bool success(std::string const& _source);
5763
langutil::ErrorList expectError(std::string const& _source, bool _warning = false, bool _allowMultiple = false);
5864

65+
public:
66+
/// Runs the full compiler pipeline on specified sources. This is the main function of the
67+
/// framework. Resets the stack, configures it and runs either until the first failed stage or
68+
/// until the @p _targetStage is reached.
69+
/// Afterwards the caller can inspect the stack via @p compiler(). The framework provides a few
70+
/// convenience helpers to check the state and error list, in general the caller can freely
71+
/// access the stack, including generating outputs if the compilation succeeded.
72+
bool runFramework(StringMap _sources, PipelineStage _targetStage = PipelineStage::Compilation);
73+
bool runFramework(std::string _source, PipelineStage _targetStage = PipelineStage::Compilation)
74+
{
75+
return runFramework({{"", std::move(_source)}}, _targetStage);
76+
}
77+
78+
void resetFramework();
79+
80+
PipelineStage targetStage() const { return m_targetStage; }
81+
bool pipelineSuccessful() const { return stageSuccessful(m_targetStage); }
82+
bool stageSuccessful(PipelineStage _stage) const;
83+
5984
std::string formatErrors(
6085
langutil::ErrorList const& _errors,
6186
bool _colored = false,
@@ -80,9 +105,6 @@ class AnalysisFramework
80105
return filterErrors(compiler().errors(), _includeWarningsAndInfos);
81106
}
82107

83-
std::vector<std::string> m_warningsToFilter = {"This is a pre-release compiler version"};
84-
std::vector<std::string> m_messagesToCut = {"Source file requires different compiler version (current compiler is"};
85-
86108
/// @returns reference to lazy-instantiated CompilerStack.
87109
solidity::frontend::CompilerStack& compiler()
88110
{
@@ -99,12 +121,25 @@ class AnalysisFramework
99121
return *m_compiler;
100122
}
101123

124+
protected:
102125
/// Creates a new instance of @p CompilerStack. Override if your test case needs to pass in
103126
/// custom constructor arguments.
104127
virtual std::unique_ptr<CompilerStack> createStack() const;
105128

129+
/// Configures @p CompilerStack. The default implementation sets basic parameters based on
130+
/// CLI options. Override if your test case needs extra configuration.
131+
virtual void setupCompiler(CompilerStack& _compiler);
132+
133+
/// Executes the requested pipeline stages until @p m_targetStage is reached.
134+
/// Stops at the first failed stage.
135+
void executeCompilationPipeline();
136+
137+
std::vector<std::string> m_warningsToFilter = {"This is a pre-release compiler version"};
138+
std::vector<std::string> m_messagesToCut = {"Source file requires different compiler version (current compiler is"};
139+
106140
private:
107141
mutable std::unique_ptr<solidity::frontend::CompilerStack> m_compiler;
142+
PipelineStage m_targetStage = PipelineStage::Compilation;
108143
};
109144

110145
// Asserts that the compilation down to typechecking

test/libsolidity/GasTest.cpp

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -97,24 +97,26 @@ void GasTest::printUpdatedExpectations(std::ostream& _stream, std::string const&
9797
}
9898
}
9999

100-
TestCase::TestResult GasTest::run(std::ostream& _stream, std::string const& _linePrefix, bool _formatted)
100+
void GasTest::setupCompiler(CompilerStack& _compiler)
101101
{
102-
compiler().reset();
102+
103103
// Prerelease CBOR metadata varies in size due to changing version numbers and build dates.
104104
// This leads to volatile creation cost estimates. Therefore we force the compiler to
105105
// release mode for testing gas estimates.
106-
compiler().setMetadataFormat(CompilerStack::MetadataFormat::NoMetadata);
106+
_compiler.setMetadataFormat(CompilerStack::MetadataFormat::NoMetadata);
107107
OptimiserSettings settings = m_optimise ? OptimiserSettings::standard() : OptimiserSettings::minimal();
108108
if (m_optimiseYul)
109109
{
110110
settings.runYulOptimiser = m_optimise;
111111
settings.optimizeStackAllocation = m_optimise;
112112
}
113113
settings.expectedExecutionsPerDeployment = m_optimiseRuns;
114-
compiler().setOptimiserSettings(settings);
115-
compiler().setSources({{"", withPreamble(m_source)}});
114+
_compiler.setOptimiserSettings(settings);
115+
}
116116

117-
if (!compiler().parseAndAnalyze() || !compiler().compile())
117+
TestCase::TestResult GasTest::run(std::ostream& _stream, std::string const& _linePrefix, bool _formatted)
118+
{
119+
if (!runFramework(withPreamble(m_source), PipelineStage::Compilation))
118120
{
119121
_stream << formatErrors(filteredErrors(), _formatted);
120122
return TestResult::FatalError;

test/libsolidity/GasTest.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ class GasTest: AnalysisFramework, public TestCase
4444
void printSource(std::ostream &_stream, std::string const &_linePrefix = "", bool _formatted = false) const override;
4545
void printUpdatedExpectations(std::ostream& _stream, std::string const& _linePrefix) const override;
4646

47+
protected:
48+
void setupCompiler(CompilerStack& _compiler) override;
49+
4750
private:
4851
void parseExpectations(std::istream& _stream);
4952

test/libsolidity/MemoryGuardTest.cpp

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,15 @@ using namespace solidity::frontend;
3636
using namespace solidity::frontend::test;
3737
using namespace yul;
3838

39+
void MemoryGuardTest::setupCompiler(CompilerStack& _compiler)
40+
{
41+
_compiler.setViaIR(true);
42+
_compiler.setOptimiserSettings(OptimiserSettings::none());
43+
}
44+
3945
TestCase::TestResult MemoryGuardTest::run(std::ostream& _stream, std::string const& _linePrefix, bool _formatted)
4046
{
41-
compiler().reset();
42-
compiler().setSources(StringMap{{"", m_source}});
43-
compiler().setViaIR(true);
44-
compiler().setOptimiserSettings(OptimiserSettings::none());
45-
if (!compiler().compile())
47+
if (!runFramework(m_source, PipelineStage::Compilation))
4648
{
4749
_stream << formatErrors(filteredErrors(), _formatted);
4850
return TestResult::FatalError;

test/libsolidity/MemoryGuardTest.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ class MemoryGuardTest: public AnalysisFramework, public TestCase
4848
}
4949

5050
TestResult run(std::ostream& _stream, std::string const& _linePrefix = "", bool _formatted = false) override;
51+
52+
protected:
53+
void setupCompiler(CompilerStack& _compiler) override;
5154
};
5255

5356
}

test/libsolidity/SMTCheckerTest.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,11 +126,11 @@ SMTCheckerTest::SMTCheckerTest(std::string const& _filename): SyntaxTest(_filena
126126
m_modelCheckerSettings.bmcLoopIterations = std::optional<unsigned>{bmcLoopIterations};
127127
}
128128

129-
void SMTCheckerTest::setupCompiler()
129+
void SMTCheckerTest::setupCompiler(CompilerStack& _compiler)
130130
{
131-
SyntaxTest::setupCompiler();
131+
SyntaxTest::setupCompiler(_compiler);
132132

133-
compiler().setModelCheckerSettings(m_modelCheckerSettings);
133+
_compiler.setModelCheckerSettings(m_modelCheckerSettings);
134134
}
135135

136136
void SMTCheckerTest::filterObtainedErrors()

test/libsolidity/SMTCheckerTest.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ class SMTCheckerTest: public SyntaxTest
3838
}
3939
SMTCheckerTest(std::string const& _filename);
4040

41-
void setupCompiler() override;
41+
void setupCompiler(CompilerStack& _compiler) override;
4242
void filterObtainedErrors() override;
4343

4444
void printUpdatedExpectations(std::ostream& _stream, std::string const& _linePrefix) const override;

test/libsolidity/SolidityCompiler.cpp

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,17 @@
2828
namespace solidity::frontend::test
2929
{
3030

31-
BOOST_FIXTURE_TEST_SUITE(SolidityCompiler, AnalysisFramework)
31+
class SolidityCompilerFixture: protected AnalysisFramework
32+
{
33+
void setupCompiler(CompilerStack& _compiler) override
34+
{
35+
// FIXME: This test was probably supposed to respect CommonOptions::get().optimize but
36+
// due to a bug it was always running with optimizer disabled and it does not pass with it.
37+
_compiler.setOptimiserSettings(false);
38+
}
39+
};
40+
41+
BOOST_FIXTURE_TEST_SUITE(SolidityCompiler, SolidityCompilerFixture)
3242

3343
BOOST_AUTO_TEST_CASE(does_not_include_creation_time_only_internal_functions)
3444
{
@@ -39,12 +49,13 @@ BOOST_AUTO_TEST_CASE(does_not_include_creation_time_only_internal_functions)
3949
function f() internal { unchecked { for (uint i = 0; i < 10; ++i) x += 3 + i; } }
4050
}
4151
)";
42-
compiler().setOptimiserSettings(solidity::test::CommonOptions::get().optimize);
43-
BOOST_REQUIRE(success(sourceCode));
52+
53+
runFramework(sourceCode, PipelineStage::Compilation);
4454
BOOST_REQUIRE_MESSAGE(
45-
compiler().compile(),
55+
pipelineSuccessful(),
4656
"Contract compilation failed:\n" + formatErrors(filteredErrors(), true /* _colored */)
4757
);
58+
4859
bytes const& creationBytecode = solidity::test::bytecodeSansMetadata(compiler().object("C").bytecode);
4960
bytes const& runtimeBytecode = solidity::test::bytecodeSansMetadata(compiler().runtimeObject("C").bytecode);
5061
BOOST_CHECK(creationBytecode.size() >= 90);

test/libsolidity/SyntaxTest.cpp

Lines changed: 33 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -49,53 +49,51 @@ SyntaxTest::SyntaxTest(
4949
m_optimiseYul = m_reader.boolSetting("optimize-yul", true);
5050
}
5151

52-
void SyntaxTest::setupCompiler()
52+
void SyntaxTest::setupCompiler(CompilerStack& _compiler)
5353
{
54-
compiler().reset();
55-
compiler().setSources(withPreamble(m_sources.sources));
56-
compiler().setEVMVersion(m_evmVersion);
57-
compiler().setOptimiserSettings(
54+
AnalysisFramework::setupCompiler(_compiler);
55+
56+
_compiler.setEVMVersion(m_evmVersion);
57+
_compiler.setOptimiserSettings(
5858
m_optimiseYul ?
5959
OptimiserSettings::full() :
6060
OptimiserSettings::minimal()
6161
);
62-
compiler().setMetadataFormat(CompilerStack::MetadataFormat::NoMetadata);
63-
compiler().setMetadataHash(CompilerStack::MetadataHash::None);
62+
_compiler.setMetadataFormat(CompilerStack::MetadataFormat::NoMetadata);
63+
_compiler.setMetadataHash(CompilerStack::MetadataHash::None);
6464
}
6565

6666
void SyntaxTest::parseAndAnalyze()
6767
{
68-
setupCompiler();
69-
70-
if (compiler().parse() && compiler().analyze())
71-
try
72-
{
73-
if (!compiler().compile())
74-
{
75-
ErrorList const& errors = compiler().errors();
76-
auto codeGeneretionErrorCount = count_if(errors.cbegin(), errors.cend(), [](auto const& error) {
77-
return error->type() == Error::Type::CodeGenerationError;
78-
});
79-
auto errorCount = count_if(errors.cbegin(), errors.cend(), [](auto const& error) {
80-
return Error::isError(error->type());
81-
});
82-
// failing compilation after successful analysis is a rare case,
83-
// it assumes that errors contain exactly one error, and the error is of type Error::Type::CodeGenerationError
84-
if (codeGeneretionErrorCount != 1 || errorCount != 1)
85-
BOOST_THROW_EXCEPTION(runtime_error("Compilation failed even though analysis was successful."));
86-
}
87-
}
88-
catch (UnimplementedFeatureError const& _e)
68+
try
69+
{
70+
runFramework(withPreamble(m_sources.sources), PipelineStage::Compilation);
71+
if (!pipelineSuccessful() && stageSuccessful(PipelineStage::Analysis))
8972
{
90-
m_errorList.emplace_back(SyntaxTestError{
91-
"UnimplementedFeatureError",
92-
nullopt,
93-
errorMessage(_e),
94-
"",
95-
-1,
96-
-1
73+
ErrorList const& errors = compiler().errors();
74+
auto codeGeneretionErrorCount = count_if(errors.cbegin(), errors.cend(), [](auto const& error) {
75+
return error->type() == Error::Type::CodeGenerationError;
9776
});
77+
auto errorCount = count_if(errors.cbegin(), errors.cend(), [](auto const& error) {
78+
return Error::isError(error->type());
79+
});
80+
// failing compilation after successful analysis is a rare case,
81+
// it assumes that errors contain exactly one error, and the error is of type Error::Type::CodeGenerationError
82+
if (codeGeneretionErrorCount != 1 || errorCount != 1)
83+
BOOST_THROW_EXCEPTION(runtime_error("Compilation failed even though analysis was successful."));
9884
}
85+
}
86+
catch (UnimplementedFeatureError const& _e)
87+
{
88+
m_errorList.emplace_back(SyntaxTestError{
89+
"UnimplementedFeatureError",
90+
nullopt,
91+
errorMessage(_e),
92+
"",
93+
-1,
94+
-1
95+
});
96+
}
9997

10098
filterObtainedErrors();
10199
}
@@ -146,4 +144,3 @@ void SyntaxTest::filterObtainedErrors()
146144
});
147145
}
148146
}
149-

0 commit comments

Comments
 (0)