Skip to content

Commit 77c89ff

Browse files
committed
Some more comments and gated testing infra
1 parent 7899ddc commit 77c89ff

File tree

6 files changed

+272
-247
lines changed

6 files changed

+272
-247
lines changed

crates/solidity-v2/outputs/cargo/parser/Cargo.toml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,15 @@ license = "MIT"
1818
keywords = ["parser"]
1919
categories = ["compilers", "parsing", "parser-implementations"]
2020

21+
[features]
22+
__private_testing_utils = ["anyhow", "infra_utils"]
23+
2124
[build-dependencies]
2225
lalrpop = { workspace = true }
2326

2427
[dependencies]
25-
anyhow = { workspace = true }
26-
infra_utils = { workspace = true }
28+
anyhow = { workspace = true, optional = true }
29+
infra_utils = { workspace = true, optional = true }
2730
lalrpop-util = { workspace = true }
2831
logos = { workspace = true }
2932
semver = { workspace = true }

crates/solidity-v2/outputs/cargo/parser/src/parser/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ pub trait Parser {
2828
// TODO(v2): Errors should be something other than `String`
2929
fn parse(input: &str, version: LanguageVersion) -> Result<Self::NonTerminal, String>;
3030

31+
// TODO(v2): This is temporary, once the language definition is restricted to only supported versions
32+
// it won't be needed
3133
fn check_version(version: LanguageVersion) -> Result<(), String> {
3234
if version == LanguageVersion::V0_8_30 {
3335
Ok(())

crates/solidity-v2/outputs/cargo/parser/src/parser/nodes.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ use slang_solidity_v2_cst::structured_cst::nodes::{
99
new_expression_index_access_expression, new_expression_member_access_expression,
1010
new_function_type, new_function_type_attributes, new_index_access_expression,
1111
new_member_access_expression, new_type_name_array_type_name, new_type_name_elementary_type,
12-
new_type_name_identifier_path, CloseBracket, ConstantKeyword, ElementaryType, Expression,
13-
FunctionType, FunctionTypeAttribute, FunctionTypeStruct, Identifier, IdentifierPath,
14-
IdentifierPathElement, IndexAccessEnd, OpenBracket, Period, StateVariableAttribute, TypeName,
12+
new_type_name_identifier_path, CloseBracket, ElementaryType, Expression, FunctionType,
13+
FunctionTypeAttribute, FunctionTypeStruct, Identifier, IdentifierPath, IdentifierPathElement,
14+
IndexAccessEnd, OpenBracket, Period, StateVariableAttribute, TypeName,
1515
};
1616

1717
/// An `IndexAccessPath` represents a path or elementary type followed by

crates/solidity-v2/outputs/cargo/parser/src/parser/temp_testing/mod.rs

Lines changed: 7 additions & 241 deletions
Original file line numberDiff line numberDiff line change
@@ -1,249 +1,15 @@
11
#[path = "node_checker.generated.rs"]
2-
pub mod node_checker;
2+
mod node_checker;
33

4-
use std::fmt::{Debug, Write};
5-
use std::path::Path;
6-
use std::rc::Rc;
7-
8-
use anyhow::Result;
9-
use infra_utils::codegen::CodegenFileSystem;
10-
use semver::Version;
11-
use slang_solidity::cst::{Cursor, Node, TextRange};
12-
use slang_solidity::parser::ParseOutput;
4+
#[cfg(feature = "__private_testing_utils")]
5+
pub mod testing;
6+
use slang_solidity::cst::{Cursor, TextRange};
137
use slang_solidity_v2_common::versions::LanguageVersion;
14-
use slang_solidity_v2_cst::structured_cst::nodes::{ContractDefinition, Expression, SourceUnit};
8+
#[cfg(feature = "__private_testing_utils")]
9+
pub use testing::V2TesterConstructor;
1510

16-
use crate::parser::{ContractDefinitionParser, ExpressionParser, SourceUnitParser};
11+
use crate::parser::{Parser, SourceUnitParser};
1712
use crate::temp_testing::node_checker::{NodeChecker, NodeCheckerError};
18-
use crate::Parser as ParserV2;
19-
20-
/// A Tester for V2 parser that compares against V1 outputs.
21-
///
22-
/// It generates snapshots for V2 outputs and diffs against V1 outputs.
23-
pub trait V2Tester {
24-
fn test_next(
25-
&mut self,
26-
test_dir: &Path,
27-
fs: &mut CodegenFileSystem,
28-
source_id: &str,
29-
source: &str,
30-
version: &Version,
31-
v1_output: &ParseOutput,
32-
) -> Result<()>;
33-
}
34-
35-
pub struct V2TesterConstructor;
36-
37-
impl V2TesterConstructor {
38-
/// Return a new `V2Tester` instance for the given parser name.
39-
///
40-
/// Only `SourceUnit`, `Expression`, and `ContractDefinition` are supported for now.
41-
pub fn new_tester(parser_name: &str) -> Box<dyn V2Tester> {
42-
match parser_name {
43-
"SourceUnit" => Box::new(V2TesterImpl::<SourceUnit, SourceUnitParser>::new()),
44-
"Expression" => Box::new(V2TesterImpl::<Expression, ExpressionParser>::new()),
45-
"ContractDefinition" => {
46-
Box::new(V2TesterImpl::<ContractDefinition, ContractDefinitionParser>::new())
47-
}
48-
// TODO(v2): We should consolidate all tests to the supported non terminals
49-
_ => Box::new(NonSupportedParserTester),
50-
}
51-
}
52-
}
53-
54-
/// A dummy tester that does nothing, used for unsupported parsers.
55-
struct NonSupportedParserTester;
56-
57-
impl V2Tester for NonSupportedParserTester {
58-
fn test_next(
59-
&mut self,
60-
_test_dir: &Path,
61-
_fs: &mut CodegenFileSystem,
62-
_source_id: &str,
63-
_source: &str,
64-
_version: &Version,
65-
_v1_output: &ParseOutput,
66-
) -> Result<()> {
67-
Ok(())
68-
}
69-
}
70-
71-
struct V2TesterImpl<NT, T: ParserV2<NonTerminal = NT>> {
72-
last_output: Option<Result<NT, String>>,
73-
last_diff: Option<(bool, Option<String>, String)>,
74-
phantom: std::marker::PhantomData<T>,
75-
}
76-
77-
impl<NT, T: ParserV2<NonTerminal = NT>> V2TesterImpl<NT, T> {
78-
pub fn new() -> Self {
79-
Self {
80-
last_output: None,
81-
last_diff: None,
82-
phantom: std::marker::PhantomData,
83-
}
84-
}
85-
}
86-
87-
impl<NT: NodeChecker + Debug + PartialEq, T: ParserV2<NonTerminal = NT>> V2Tester
88-
for V2TesterImpl<NT, T>
89-
{
90-
fn test_next(
91-
&mut self,
92-
test_dir: &Path,
93-
fs: &mut CodegenFileSystem,
94-
source_id: &str,
95-
source: &str,
96-
version: &Version,
97-
v1_output: &ParseOutput,
98-
) -> Result<()> {
99-
// We check version 0.8.30
100-
// TODO(v2) check all versions
101-
// _SLANG_V2_PARSER_VERSION_ (keep in sync)
102-
if *version != Version::new(0, 8, 30) {
103-
return Ok(());
104-
}
105-
106-
// Get the output for v2
107-
let lang_version = LanguageVersion::try_from(version.clone())
108-
.unwrap_or_else(|_| panic!("Unsupported version: {version}"));
109-
110-
let v2_output: Result<NT, _> = T::parse(source, lang_version);
111-
112-
let v2_output = match self.last_output {
113-
// Skip this version if it produces the same output.
114-
// Note: comparing objects cheaply before expensive serialization.
115-
Some(ref last) if last == &v2_output => last,
116-
_ => {
117-
let status = if v2_output.is_ok() {
118-
"success"
119-
} else {
120-
"failure"
121-
};
122-
123-
let snapshot_path = test_dir
124-
.join("v2/generated")
125-
.join(format!("{version}-{status}.txt"));
126-
127-
let mut s = String::new();
128-
129-
match &v2_output {
130-
Ok(parsed_checker) => {
131-
// Print AST
132-
writeln!(s, "{parsed_checker:#?}")?;
133-
}
134-
Err(err) => {
135-
// We don't care about the errors for now, we just write them
136-
writeln!(s, "{err:#?}")?;
137-
}
138-
}
139-
140-
fs.write_file_raw(&snapshot_path, s)?;
141-
self.last_output.insert(v2_output)
142-
}
143-
};
144-
145-
// Now check the diff between V1 and V2
146-
147-
let diff = Self::diff_report(v1_output, v2_output, source_id, source);
148-
149-
match &self.last_diff {
150-
// Skip if the diff is the same as last time
151-
Some(ref last) if last == &diff => (),
152-
_ => {
153-
let (is_same, should_panic, diff_report) = &diff;
154-
155-
let diff_path = test_dir.join("diff/generated").join(format!(
156-
"{version}-{diff}.txt",
157-
diff = if *is_same { "same" } else { "diff" }
158-
));
159-
160-
fs.write_file_raw(&diff_path, diff_report)?;
161-
162-
if let Some(panic_message) = should_panic {
163-
panic!("{panic_message}");
164-
}
165-
self.last_diff = Some(diff);
166-
}
167-
}
168-
169-
Ok(())
170-
}
171-
}
172-
173-
impl<NT: NodeChecker + Debug + PartialEq, T: ParserV2<NonTerminal = NT>> V2TesterImpl<NT, T> {
174-
fn diff_report(
175-
v1_output: &ParseOutput,
176-
v2_output: &Result<NT, String>,
177-
source_id: &str,
178-
source: &str,
179-
) -> (bool, Option<String>, String) {
180-
match v2_output {
181-
Ok(parsed_checker) => {
182-
// check V1 validity
183-
if v1_output.is_valid() {
184-
// Check for errors
185-
let checked =
186-
parsed_checker.check_node(&Node::Nonterminal(Rc::clone(v1_output.tree())));
187-
188-
if checked.is_empty() {
189-
(
190-
true,
191-
None,
192-
"V2 parser produced the same output as V1 output.".to_string(),
193-
)
194-
} else {
195-
let errors = write_errors(&checked, source_id, source);
196-
197-
// TODO(v2): This is forced not to panic since some tests in V1 produce different outputs,
198-
// in particular `state_variable_function`
199-
(false, None, errors)
200-
}
201-
} else {
202-
// TODO(v2): This is forced not to panic, since V2 has no validation yet, but we
203-
// do want to be aware of these cases, so we write them in the diff report and we can review them later.
204-
(
205-
false,
206-
None,
207-
"V1 Parser: Invalid\nV2 Parser: Valid\n".to_string(),
208-
)
209-
}
210-
}
211-
// TODO(v2): We force this not to panic, since we need lexical context switching to work for some
212-
// tests to pass
213-
Err(_) if v1_output.is_valid() => (
214-
false,
215-
None,
216-
"V1 Parser: Valid\nV2 Parser: Invalid\n".to_string(),
217-
),
218-
Err(_) => {
219-
// TODO(v2): Both are invalid, compare the errors
220-
(
221-
true,
222-
None,
223-
"Both V1 and V2 produced invalid output.\n".to_string(),
224-
)
225-
}
226-
}
227-
}
228-
}
229-
230-
fn write_errors(errors: &Vec<NodeCheckerError>, source_id: &str, source: &str) -> String {
231-
if errors.is_empty() {
232-
return String::new();
233-
}
234-
235-
let mut s = String::new();
236-
writeln!(s, "Errors: # {count} total", count = errors.len()).unwrap();
237-
238-
for error in errors {
239-
writeln!(s, " - >").unwrap();
240-
for line in slang_solidity::diagnostic::render(error, source_id, source, false).lines() {
241-
writeln!(s, " {line}").unwrap();
242-
}
243-
}
244-
245-
s
246-
}
24713

24814
pub fn compare_with_v1_cursor(source: &str, root_cursor: &Cursor) -> Vec<NodeCheckerError> {
24915
let v2_output = SourceUnitParser::parse(source, LanguageVersion::V0_8_30);

0 commit comments

Comments
 (0)