Skip to content

Commit fc717f0

Browse files
committed
Add ASTComparator tests
1 parent 252f48e commit fc717f0

2 files changed

Lines changed: 360 additions & 1 deletion

File tree

test/CMakeLists.txt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,11 @@ set(yul_phaser_sources
209209
)
210210
detect_stray_source_files("${yul_phaser_sources}" "yulPhaser/")
211211

212+
set(yul_ast_comparator_sources
213+
yulASTComparator/ASTComparator.cpp
214+
)
215+
detect_stray_source_files("${yul_ast_comparator_sources}" "yulASTComparator/")
216+
212217
add_executable(soltest ${sources}
213218
${contracts_sources}
214219
${libsolutil_sources}
@@ -219,8 +224,9 @@ add_executable(soltest ${sources}
219224
${libsolidity_util_sources}
220225
${solcli_sources}
221226
${yul_phaser_sources}
227+
${yul_ast_comparator_sources}
222228
)
223-
target_link_libraries(soltest PRIVATE solcli libsolc yul solidity smtutil solutil phaser Boost::boost yulInterpreter evmasm Boost::filesystem Boost::program_options Boost::unit_test_framework evmc)
229+
target_link_libraries(soltest PRIVATE solcli libsolc yul solidity smtutil solutil phaser libYulASTComparator Boost::boost yulInterpreter evmasm Boost::filesystem Boost::program_options Boost::unit_test_framework evmc)
224230

225231

226232
# Special compilation flag for Visual Studio (version 2019 at least affected)
Lines changed: 353 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,353 @@
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+
#include <tools/yulASTComparator/ASTComparator.h>
20+
21+
#include <libyul/AST.h>
22+
#include <libyul/AsmAnalysis.h>
23+
#include <libyul/AsmAnalysisInfo.h>
24+
#include <libyul/Object.h>
25+
#include <libyul/ObjectParser.h>
26+
#include <libyul/backends/evm/EVMDialect.h>
27+
28+
#include <liblangutil/CharStream.h>
29+
#include <liblangutil/ErrorReporter.h>
30+
#include <liblangutil/EVMVersion.h>
31+
#include <liblangutil/Scanner.h>
32+
33+
#include <boost/test/unit_test.hpp>
34+
35+
using namespace solidity;
36+
using namespace solidity::yul;
37+
using namespace solidity::langutil;
38+
using namespace solidity::tools::cmpast;
39+
40+
namespace
41+
{
42+
43+
Dialect const& dialect()
44+
{
45+
static auto const& d = EVMDialect::strictAssemblyForEVMObjects(EVMVersion::current(), std::nullopt);
46+
return d;
47+
}
48+
49+
std::shared_ptr<Object> parse(std::string const& _source)
50+
{
51+
ErrorList errors;
52+
ErrorReporter errorReporter(errors);
53+
CharStream stream(_source, "test");
54+
std::shared_ptr<Scanner> const scanner = std::make_shared<Scanner>(stream);
55+
auto object = ObjectParser(errorReporter, dialect()).parse(scanner, false);
56+
BOOST_REQUIRE_MESSAGE(object && !errorReporter.hasErrors(), "Failed to parse: " + _source);
57+
AsmAnalyzer::analyzeStrictAssertCorrect(*object);
58+
return object;
59+
}
60+
61+
bool equivalent(std::string const& _a, std::string const& _b)
62+
{
63+
std::shared_ptr<Object> const objA = parse(_a);
64+
std::shared_ptr<Object> const objB = parse(_b);
65+
ASTComparator cmp(dialect());
66+
return static_cast<bool>(cmp.compareObjects(*objA, *objB));
67+
}
68+
69+
std::string mismatchReason(std::string const& _a, std::string const& _b)
70+
{
71+
std::shared_ptr<Object> const objA = parse(_a);
72+
std::shared_ptr<Object> const objB = parse(_b);
73+
ASTComparator cmp(dialect());
74+
auto const result = cmp.compareObjects(*objA, *objB);
75+
return result.mismatch().reason;
76+
}
77+
78+
}
79+
80+
BOOST_AUTO_TEST_SUITE(YulASTComparator, *boost::unit_test::label("nooptions"))
81+
BOOST_AUTO_TEST_SUITE(ASTComparatorTest)
82+
83+
BOOST_AUTO_TEST_SUITE(equivalence)
84+
BOOST_AUTO_TEST_CASE(empty_objects_are_equivalent)
85+
{
86+
BOOST_TEST(equivalent(
87+
"object \"A\" { code { } }",
88+
"object \"B\" { code { } }"
89+
));
90+
}
91+
92+
BOOST_AUTO_TEST_CASE(identical_code_is_equivalent)
93+
{
94+
std::string const src = "object \"X\" { code { let x := 1 let y := add(x, 2) } }";
95+
BOOST_TEST(equivalent(src, src));
96+
}
97+
98+
BOOST_AUTO_TEST_CASE(renamed_variables_are_equivalent)
99+
{
100+
BOOST_TEST(equivalent(
101+
"object \"A\" { code { let x := 1 let y := add(x, 2) } }",
102+
"object \"A\" { code { let a := 1 let b := add(a, 2) } }"
103+
));
104+
}
105+
106+
BOOST_AUTO_TEST_CASE(renamed_function_names_are_equivalent)
107+
{
108+
BOOST_TEST(equivalent(
109+
"object \"A\" { code { function foo() -> r { r := 1 } let x := foo() } }",
110+
"object \"A\" { code { function bar() -> s { s := 1 } let y := bar() } }"
111+
));
112+
}
113+
114+
BOOST_AUTO_TEST_CASE(renamed_parameters_are_equivalent)
115+
{
116+
BOOST_TEST(equivalent(
117+
"object \"A\" { code { function f(a, b) -> r { r := add(a, b) } } }",
118+
"object \"A\" { code { function g(x, y) -> z { z := add(x, y) } } }"
119+
));
120+
}
121+
122+
BOOST_AUTO_TEST_CASE(renamed_for_loop_variables_are_equivalent)
123+
{
124+
BOOST_TEST(equivalent(
125+
"object \"A\" { code { for { let i := 0 } lt(i, 10) { i := add(i, 1) } { } } }",
126+
"object \"A\" { code { for { let j := 0 } lt(j, 10) { j := add(j, 1) } { } } }"
127+
));
128+
}
129+
BOOST_AUTO_TEST_SUITE_END()
130+
131+
BOOST_AUTO_TEST_SUITE(inconsistent_renaming)
132+
BOOST_AUTO_TEST_CASE(inconsistent_variable_renaming_not_equivalent)
133+
{
134+
BOOST_TEST(!equivalent(
135+
"object \"A\" { code { let x := 1 let y := 2 let z := add(x, y) } }",
136+
"object \"A\" { code { let a := 1 let b := 2 let c := add(b, a) } }"
137+
));
138+
}
139+
140+
BOOST_AUTO_TEST_CASE(inconsistent_function_renaming_not_equivalent)
141+
{
142+
// Two different functions map to the same name
143+
BOOST_TEST(!equivalent(
144+
"object \"A\" { code { function f() { } function g() { } f() g() } }",
145+
"object \"A\" { code { function h() { } function k() { } h() h() } }"
146+
));
147+
}
148+
BOOST_AUTO_TEST_SUITE_END()
149+
150+
BOOST_AUTO_TEST_SUITE(mismatch)
151+
BOOST_AUTO_TEST_CASE(different_statement_types_mismatch)
152+
{
153+
BOOST_TEST(!equivalent(
154+
"object \"A\" { code { let x := 1 } }",
155+
"object \"A\" { code { pop(1) } }"
156+
));
157+
}
158+
159+
BOOST_AUTO_TEST_CASE(different_statement_count_in_block_mismatch)
160+
{
161+
auto reason = mismatchReason(
162+
"object \"A\" { code { let x := 1 } }",
163+
"object \"A\" { code { let x := 1 let y := 2 } }"
164+
);
165+
BOOST_TEST(reason.find("block statement count differs") != std::string::npos);
166+
}
167+
168+
BOOST_AUTO_TEST_CASE(assignment_value_mismatch)
169+
{
170+
BOOST_TEST(!equivalent(
171+
"object \"A\" { code { let x := 0 x := 42 } }",
172+
"object \"A\" { code { let x := 0 x := 99 } }"
173+
));
174+
}
175+
176+
BOOST_AUTO_TEST_CASE(function_parameter_count_mismatch)
177+
{
178+
auto reason = mismatchReason(
179+
"object \"A\" { code { function f(a) { } } }",
180+
"object \"A\" { code { function f(a, b) { } } }"
181+
);
182+
BOOST_TEST(reason.find("parameter count differs") != std::string::npos);
183+
}
184+
185+
BOOST_AUTO_TEST_CASE(function_return_variable_count_mismatch)
186+
{
187+
auto reason = mismatchReason(
188+
"object \"A\" { code { function f() -> a { a := 1 } } }",
189+
"object \"A\" { code { function f() -> a, b { a := 1 b := 2 } } }"
190+
);
191+
BOOST_TEST(reason.find("return variable count differs") != std::string::npos);
192+
}
193+
194+
BOOST_AUTO_TEST_CASE(function_body_mismatch)
195+
{
196+
BOOST_TEST(!equivalent(
197+
"object \"A\" { code { function f() -> r { r := 1 } } }",
198+
"object \"A\" { code { function f() -> r { r := 2 } } }"
199+
));
200+
}
201+
202+
BOOST_AUTO_TEST_CASE(if_condition_mismatch)
203+
{
204+
BOOST_TEST(!equivalent(
205+
"object \"A\" { code { if 1 { } } }",
206+
"object \"A\" { code { if 0 { } } }"
207+
));
208+
}
209+
210+
BOOST_AUTO_TEST_CASE(if_body_mismatch)
211+
{
212+
BOOST_TEST(!equivalent(
213+
"object \"A\" { code { if 1 { pop(1) } } }",
214+
"object \"A\" { code { if 1 { pop(2) } } }"
215+
));
216+
}
217+
218+
BOOST_AUTO_TEST_CASE(switch_case_count_mismatch)
219+
{
220+
auto reason = mismatchReason(
221+
"object \"A\" { code { switch 1 case 0 { } default { } } }",
222+
"object \"A\" { code { switch 1 case 0 { } case 1 { } default { } } }"
223+
);
224+
BOOST_TEST(reason.find("case count differs") != std::string::npos);
225+
}
226+
227+
BOOST_AUTO_TEST_CASE(switch_case_value_mismatch)
228+
{
229+
BOOST_TEST(!equivalent(
230+
"object \"A\" { code { switch 1 case 0 { } default { } } }",
231+
"object \"A\" { code { switch 1 case 1 { } default { } } }"
232+
));
233+
}
234+
235+
BOOST_AUTO_TEST_CASE(switch_case_body_mismatch)
236+
{
237+
BOOST_TEST(!equivalent(
238+
"object \"A\" { code { switch 1 case 0 { pop(1) } default { } } }",
239+
"object \"A\" { code { switch 1 case 0 { pop(2) } default { } } }"
240+
));
241+
}
242+
243+
BOOST_AUTO_TEST_CASE(switch_default_vs_nondefault_mismatch)
244+
{
245+
BOOST_TEST(!equivalent(
246+
"object \"A\" { code { switch 1 case 0 { } default { } } }",
247+
"object \"A\" { code { switch 1 case 0 { } case 1 { } } }"
248+
));
249+
}
250+
251+
BOOST_AUTO_TEST_CASE(switch_expression_mismatch)
252+
{
253+
BOOST_TEST(!equivalent(
254+
"object \"A\" { code { switch 1 case 0 { } default { } } }",
255+
"object \"A\" { code { switch 2 case 0 { } default { } } }"
256+
));
257+
}
258+
259+
BOOST_AUTO_TEST_CASE(for_loop_condition_mismatch)
260+
{
261+
BOOST_TEST(!equivalent(
262+
"object \"A\" { code { for { } lt(0, 10) { } { } } }",
263+
"object \"A\" { code { for { } lt(0, 20) { } { } } }"
264+
));
265+
}
266+
267+
BOOST_AUTO_TEST_CASE(for_loop_pre_mismatch)
268+
{
269+
BOOST_TEST(!equivalent(
270+
"object \"A\" { code { for { let i := 0 } lt(i, 10) { i := add(i, 1) } { } } }",
271+
"object \"A\" { code { for { let i := 1 } lt(i, 10) { i := add(i, 1) } { } } }"
272+
));
273+
}
274+
275+
BOOST_AUTO_TEST_CASE(for_loop_post_mismatch)
276+
{
277+
BOOST_TEST(!equivalent(
278+
"object \"A\" { code { for { let i := 0 } lt(i, 10) { i := add(i, 1) } { } } }",
279+
"object \"A\" { code { for { let i := 0 } lt(i, 10) { i := add(i, 2) } { } } }"
280+
));
281+
}
282+
283+
BOOST_AUTO_TEST_CASE(for_loop_body_mismatch)
284+
{
285+
BOOST_TEST(!equivalent(
286+
"object \"A\" { code { for { } 1 { } { pop(1) } } }",
287+
"object \"A\" { code { for { } 1 { } { pop(2) } } }"
288+
));
289+
}
290+
291+
BOOST_AUTO_TEST_CASE(literal_value_mismatch)
292+
{
293+
BOOST_TEST(!equivalent(
294+
"object \"A\" { code { let x := 42 } }",
295+
"object \"A\" { code { let x := 43 } }"
296+
));
297+
}
298+
299+
BOOST_AUTO_TEST_CASE(function_call_argument_count_mismatch)
300+
{
301+
BOOST_TEST(!equivalent(
302+
"object \"A\" { code { function f(a) { } function g(a, b) { } f(1) } }",
303+
"object \"A\" { code { function f(a) { } function g(a, b) { } g(1, 2) } }"
304+
));
305+
}
306+
307+
BOOST_AUTO_TEST_CASE(function_call_argument_value_mismatch)
308+
{
309+
BOOST_TEST(!equivalent(
310+
"object \"A\" { code { pop(1) } }",
311+
"object \"A\" { code { pop(2) } }"
312+
));
313+
}
314+
315+
BOOST_AUTO_TEST_CASE(builtin_function_mismatch)
316+
{
317+
BOOST_TEST(!equivalent(
318+
"object \"A\" { code { pop(add(1, 2)) } }",
319+
"object \"A\" { code { pop(sub(1, 2)) } }"
320+
));
321+
}
322+
323+
BOOST_AUTO_TEST_CASE(sub_object_count_mismatch)
324+
{
325+
auto reason = mismatchReason(
326+
"object \"A\" { code { } object \"B\" { code { } } }",
327+
"object \"A\" { code { } }"
328+
);
329+
BOOST_TEST(reason.find("different number of sub-objects") != std::string::npos);
330+
}
331+
332+
BOOST_AUTO_TEST_SUITE_END()
333+
334+
BOOST_AUTO_TEST_SUITE(scopes)
335+
BOOST_AUTO_TEST_CASE(nested_block_scopes_allow_name_reuse)
336+
{
337+
BOOST_TEST(equivalent(
338+
"object \"A\" { code { { let x := 1 } { let x := 2 x := add(x, 5) } } }",
339+
"object \"A\" { code { { let a := 1 } { let b := 2 b := add(b, 5) } } }"
340+
));
341+
}
342+
343+
BOOST_AUTO_TEST_CASE(function_scoping_independent_of_outer)
344+
{
345+
BOOST_TEST(equivalent(
346+
"object \"A\" { code { let x := 0 function f() -> r { let w := 1 r := w } } }",
347+
"object \"A\" { code { let a := 0 function g() -> s { let b := 1 s := b } } }"
348+
));
349+
}
350+
BOOST_AUTO_TEST_SUITE_END()
351+
352+
BOOST_AUTO_TEST_SUITE_END()
353+
BOOST_AUTO_TEST_SUITE_END()

0 commit comments

Comments
 (0)