fix(transform_conformance): only print semantic mismatch errors when output is correct (#5589)

closes #5166

Had to rerun for mismatch errors :-/
This commit is contained in:
Boshen 2024-09-07 16:32:53 +00:00
parent 24d6a47f8b
commit 919d17fc5c
6 changed files with 2831 additions and 4732 deletions

View file

@ -102,7 +102,7 @@ impl BabelOptions {
} }
pub fn is_module(&self) -> bool { pub fn is_module(&self) -> bool {
self.source_type.as_ref().map_or(false, |s| matches!(s.as_str(), "module")) self.source_type.as_ref().map_or(false, |s| s.as_str() == "module")
} }
pub fn is_unambiguous(&self) -> bool { pub fn is_unambiguous(&self) -> bool {

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -10,6 +10,7 @@ use oxc::{
}; };
pub struct Driver { pub struct Driver {
check_semantic: bool,
options: TransformOptions, options: TransformOptions,
printed: String, printed: String,
errors: Vec<OxcDiagnostic>, errors: Vec<OxcDiagnostic>,
@ -41,6 +42,7 @@ impl CompilerInterface for Driver {
program: &mut Program<'_>, program: &mut Program<'_>,
transformer_return: &mut TransformerReturn, transformer_return: &mut TransformerReturn,
) -> ControlFlow<()> { ) -> ControlFlow<()> {
if self.check_semantic {
if let Some(errors) = check_semantic_after_transform( if let Some(errors) = check_semantic_after_transform(
&transformer_return.symbols, &transformer_return.symbols,
&transformer_return.scopes, &transformer_return.scopes,
@ -48,13 +50,14 @@ impl CompilerInterface for Driver {
) { ) {
self.errors.extend(errors); self.errors.extend(errors);
} }
}
ControlFlow::Continue(()) ControlFlow::Continue(())
} }
} }
impl Driver { impl Driver {
pub fn new(options: TransformOptions) -> Self { pub fn new(check_semantic: bool, options: TransformOptions) -> Self {
Self { options, printed: String::new(), errors: vec![] } Self { check_semantic, options, printed: String::new(), errors: vec![] }
} }
pub fn errors(&mut self) -> Vec<OxcDiagnostic> { pub fn errors(&mut self) -> Vec<OxcDiagnostic> {

View file

@ -175,13 +175,13 @@ impl TestRunner {
let errors = test_case.errors(); let errors = test_case.errors();
if !errors.is_empty() { if !errors.is_empty() {
snapshot.push('\n'); snapshot.push('\n');
for error in test_case.errors() { for error in errors {
snapshot.push_str(&error.message); snapshot.push_str(&error.message);
}
snapshot.push('\n'); snapshot.push('\n');
} }
snapshot.push('\n'); snapshot.push('\n');
} }
}
snapshot.push('\n'); snapshot.push('\n');
} }
} }

View file

@ -172,16 +172,13 @@ pub trait TestCase {
// Some babel test cases have a js extension, but contain typescript code. // Some babel test cases have a js extension, but contain typescript code.
// Therefore, if the typescript plugin exists, enable typescript. // Therefore, if the typescript plugin exists, enable typescript.
let mut source_type = SourceType::from_path(path).unwrap(); let source_type = SourceType::from_path(path).unwrap().with_typescript(
if !source_type.is_typescript() self.options().get_plugin("transform-typescript").is_some()
&& (self.options().get_plugin("transform-typescript").is_some() || self.options().get_plugin("syntax-typescript").is_some(),
|| self.options().get_plugin("syntax-typescript").is_some()) );
{
source_type = source_type.with_typescript(true);
}
let driver = let driver =
Driver::new(transform_options.clone()).execute(&source_text, source_type, path); Driver::new(false, transform_options.clone()).execute(&source_text, source_type, path);
Ok(driver) Ok(driver)
} }
} }
@ -228,25 +225,27 @@ impl TestCase for ConformanceTestCase {
let allocator = Allocator::default(); let allocator = Allocator::default();
let input = fs::read_to_string(&self.path).unwrap(); let input = fs::read_to_string(&self.path).unwrap();
let input_is_js = self.path.extension().and_then(std::ffi::OsStr::to_str) == Some("js");
let output_is_js = output_path
.as_ref()
.is_some_and(|path| path.extension().and_then(std::ffi::OsStr::to_str) == Some("js"));
let source_type = {
let mut source_type = SourceType::from_path(&self.path) let mut source_type = SourceType::from_path(&self.path)
.unwrap() .unwrap()
.with_script(if self.options.source_type.is_some() {
!self.options.is_module()
} else {
input_is_js && output_is_js
})
.with_jsx(self.options.get_plugin("syntax-jsx").is_some()); .with_jsx(self.options.get_plugin("syntax-jsx").is_some());
if !source_type.is_typescript()
&& (self.options.get_plugin("transform-typescript").is_some() source_type = match self.options.source_type.as_deref() {
|| self.options.get_plugin("syntax-typescript").is_some()) Some("unambiguous") => source_type.with_unambiguous(true),
{ Some("script") => source_type.with_script(true),
source_type = source_type.with_typescript(true); Some("module") => source_type.with_module(true),
} Some(s) => panic!("Unexpected source type {s}"),
None => source_type,
};
source_type = source_type.with_typescript(
self.options.get_plugin("transform-typescript").is_some()
|| self.options.get_plugin("syntax-typescript").is_some(),
);
source_type
};
if filtered { if filtered {
println!("input_path: {:?}", &self.path); println!("input_path: {:?}", &self.path);
@ -255,14 +254,18 @@ impl TestCase for ConformanceTestCase {
let project_root = project_root(); let project_root = project_root();
let mut transformed_code = String::new(); let mut transformed_code = String::new();
let mut actual_errors = String::new(); let mut actual_errors = None;
let mut transform_options = None; let mut transform_options = None;
match self.transform_options() { match self.transform_options() {
Err(json_err) => {
let error = json_err.iter().map(ToString::to_string).collect::<Vec<_>>().join("\n");
actual_errors.replace(get_babel_error(&error));
}
Ok(options) => { Ok(options) => {
transform_options.replace(options.clone()); transform_options.replace(options.clone());
let mut driver = let mut driver =
Driver::new(options.clone()).execute(&input, source_type, &self.path); Driver::new(false, options.clone()).execute(&input, source_type, &self.path);
transformed_code = driver.printed(); transformed_code = driver.printed();
let errors = driver.errors(); let errors = driver.errors();
if !errors.is_empty() { if !errors.is_empty() {
@ -275,13 +278,9 @@ impl TestCase for ConformanceTestCase {
.map(|err| format!("{:?}", err.with_source_code(source.clone()))) .map(|err| format!("{:?}", err.with_source_code(source.clone())))
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join("\n"); .join("\n");
actual_errors = get_babel_error(&error); actual_errors.replace(get_babel_error(&error));
} }
} }
Err(json_err) => {
let error = json_err.iter().map(ToString::to_string).collect::<Vec<_>>().join("\n");
actual_errors = get_babel_error(&error);
}
} }
let babel_options = self.options(); let babel_options = self.options();
@ -289,7 +288,8 @@ impl TestCase for ConformanceTestCase {
let output; let output;
let passed = if let Some(throws) = &babel_options.throws { let passed = if let Some(throws) = &babel_options.throws {
output = throws.to_string().replace(" (1:6)", ""); output = throws.to_string().replace(" (1:6)", "");
!output.is_empty() && actual_errors.contains(&output) !output.is_empty()
&& actual_errors.as_ref().is_some_and(|errors| errors.contains(&output))
} else { } else {
// Get output.js by using our code gen so code comparison can match. // Get output.js by using our code gen so code comparison can match.
output = output_path.and_then(|path| fs::read_to_string(path).ok()).map_or_else( output = output_path.and_then(|path| fs::read_to_string(path).ok()).map_or_else(
@ -302,10 +302,10 @@ impl TestCase for ConformanceTestCase {
); );
if transformed_code == output { if transformed_code == output {
actual_errors.is_empty() actual_errors.is_none()
} else { } else {
if !actual_errors.is_empty() && !transformed_code.is_empty() { if actual_errors.is_none() {
actual_errors.insert_str(0, " x Output mismatch\n"); actual_errors.replace("x Output mismatch".to_string());
} }
false false
} }
@ -320,10 +320,12 @@ impl TestCase for ConformanceTestCase {
println!("Expected Errors:\n"); println!("Expected Errors:\n");
println!("{output}\n"); println!("{output}\n");
println!("Actual Errors:\n"); println!("Actual Errors:\n");
if let Some(actual_errors) = &actual_errors {
println!("{actual_errors}\n"); println!("{actual_errors}\n");
if !passed { if !passed {
println!("Diff:\n"); println!("Diff:\n");
print_diff_in_terminal(&output, &actual_errors); print_diff_in_terminal(&output, actual_errors);
}
} }
} else { } else {
println!("Expected:\n"); println!("Expected:\n");
@ -331,7 +333,9 @@ impl TestCase for ConformanceTestCase {
println!("Transformed:\n"); println!("Transformed:\n");
println!("{transformed_code}"); println!("{transformed_code}");
println!("Errors:\n"); println!("Errors:\n");
if let Some(actual_errors) = &actual_errors {
println!("{actual_errors}\n"); println!("{actual_errors}\n");
}
if !passed { if !passed {
println!("Diff:\n"); println!("Diff:\n");
print_diff_in_terminal(&output, &transformed_code); print_diff_in_terminal(&output, &transformed_code);
@ -341,7 +345,15 @@ impl TestCase for ConformanceTestCase {
println!("Passed: {passed}"); println!("Passed: {passed}");
} }
if !passed { if passed {
if let Some(options) = transform_options {
let mismatch_errors =
Driver::new(/* check transform mismatch */ true, options)
.execute(&input, source_type, &self.path)
.errors();
self.errors.extend(mismatch_errors);
}
} else if let Some(actual_errors) = actual_errors {
self.errors.push(OxcDiagnostic::error(actual_errors)); self.errors.push(OxcDiagnostic::error(actual_errors));
} }
} }