use vb6parse::parsers::cst::ConcreteSyntaxTree;
fn assert_failure_count_matches_snapshot(snapshot_dir: &str, snapshot_name: &str, actual: usize) {
let primary_path = format!("snapshots/tests/invalid_syntax/{snapshot_dir}/{snapshot_name}.snap");
let prefixed_path = format!(
"snapshots/tests/invalid_syntax/{snapshot_dir}/invalid_syntax__{snapshot_dir}__{snapshot_name}.snap"
);
let path = if std::path::Path::new(&primary_path).exists() {
primary_path
} else {
prefixed_path
};
let content = std::fs::read_to_string(&path)
.unwrap_or_else(|err| panic!("Failed to read snapshot file {path}: {err}"));
let expected = content
.lines()
.filter(|line| line.trim_start().starts_with("- "))
.count();
assert_eq!(
actual,
expected,
"Unexpected failure count for snapshot {snapshot_name}"
);
}
#[test]
fn missing_comma_between_parameters() {
let source = r"
Sub Test(x As Integer y As String)
Debug.Print x
End Sub
";
let (cst_opt, failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
eprintln!("=== Failures for missing_comma_between_parameters ===");
eprintln!("Number of failures: {}", failures.len());
for failure in &failures {
failure.eprint();
}
eprintln!("=== End Failures ===");
let cst = cst_opt.expect("CST should be present even with syntax errors");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../snapshots/tests/invalid_syntax/invalid_parameter_list");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!("missing_comma_between_parameters_cst", tree);
let failure_messages: Vec<String> = failures.iter().map(|f| format!("{f:?}")).collect();
assert_failure_count_matches_snapshot("invalid_parameter_list", "missing_comma_between_parameters_failures", failures.len());
insta::assert_yaml_snapshot!("missing_comma_between_parameters_failures", failure_messages);
}
#[test]
fn trailing_comma_in_parameters() {
let source = r"
Function Calculate(x As Integer, y As String,) As Integer
Calculate = 0
End Function
";
let (cst_opt, failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
eprintln!("=== Failures for trailing_comma_in_parameters ===");
eprintln!("Number of failures: {}", failures.len());
for failure in &failures {
failure.eprint();
}
eprintln!("=== End Failures ===");
let cst = cst_opt.expect("CST should be present even with syntax errors");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../snapshots/tests/invalid_syntax/invalid_parameter_list");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!("trailing_comma_in_parameters_cst", tree);
let failure_messages: Vec<String> = failures.iter().map(|f| format!("{f:?}")).collect();
assert_failure_count_matches_snapshot("invalid_parameter_list", "trailing_comma_in_parameters_failures", failures.len());
insta::assert_yaml_snapshot!("trailing_comma_in_parameters_failures", failure_messages);
}
#[test]
fn missing_parameter_after_comma() {
let source = r"
Sub Test(x As Integer, )
Dim result As Integer
End Sub
";
let (cst_opt, failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
eprintln!("=== Failures for missing_parameter_after_comma ===");
eprintln!("Number of failures: {}", failures.len());
for failure in &failures {
failure.eprint();
}
eprintln!("=== End Failures ===");
let cst = cst_opt.expect("CST should be present even with syntax errors");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../snapshots/tests/invalid_syntax/invalid_parameter_list");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!("missing_parameter_after_comma_cst", tree);
let failure_messages: Vec<String> = failures.iter().map(|f| format!("{f:?}")).collect();
assert_failure_count_matches_snapshot("invalid_parameter_list", "missing_parameter_after_comma_failures", failures.len());
insta::assert_yaml_snapshot!("missing_parameter_after_comma_failures", failure_messages);
}
#[test]
fn duplicate_byval_modifier() {
let source = r"
Sub Test(ByVal ByVal x As Integer)
Debug.Print x
End Sub
";
let (cst_opt, failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
eprintln!("=== Failures for duplicate_byval_modifier ===");
eprintln!("Number of failures: {}", failures.len());
for failure in &failures {
failure.eprint();
}
eprintln!("=== End Failures ===");
let cst = cst_opt.expect("CST should be present even with syntax errors");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../snapshots/tests/invalid_syntax/invalid_parameter_list");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!("duplicate_byval_modifier_cst", tree);
let failure_messages: Vec<String> = failures.iter().map(|f| format!("{f:?}")).collect();
assert_failure_count_matches_snapshot("invalid_parameter_list", "duplicate_byval_modifier_failures", failures.len());
insta::assert_yaml_snapshot!("duplicate_byval_modifier_failures", failure_messages);
}
#[test]
fn conflicting_byval_byref() {
let source = r"
Function Calculate(ByVal ByRef x As Integer) As Integer
Calculate = x * 2
End Function
";
let (cst_opt, failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
eprintln!("=== Failures for conflicting_byval_byref ===");
eprintln!("Number of failures: {}", failures.len());
for failure in &failures {
failure.eprint();
}
eprintln!("=== End Failures ===");
let cst = cst_opt.expect("CST should be present even with syntax errors");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../snapshots/tests/invalid_syntax/invalid_parameter_list");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!("conflicting_byval_byref_cst", tree);
let failure_messages: Vec<String> = failures.iter().map(|f| format!("{f:?}")).collect();
assert_failure_count_matches_snapshot("invalid_parameter_list", "conflicting_byval_byref_failures", failures.len());
insta::assert_yaml_snapshot!("conflicting_byval_byref_failures", failure_messages);
}
#[test]
fn optional_before_required() {
let source = r"
Sub Test(Optional x As Integer, y As String)
Debug.Print y
End Sub
";
let (cst_opt, failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
eprintln!("=== Failures for optional_before_required ===");
eprintln!("Number of failures: {}", failures.len());
for failure in &failures {
failure.eprint();
}
eprintln!("=== End Failures ===");
let cst = cst_opt.expect("CST should be present even with syntax errors");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../snapshots/tests/invalid_syntax/invalid_parameter_list");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!("optional_before_required_cst", tree);
let failure_messages: Vec<String> = failures.iter().map(|f| format!("{f:?}")).collect();
assert_failure_count_matches_snapshot("invalid_parameter_list", "optional_before_required_failures", failures.len());
insta::assert_yaml_snapshot!("optional_before_required_failures", failure_messages);
}
#[test]
fn paramarray_not_last() {
let source = r"
Sub Test(ParamArray args() As Variant, x As Integer)
Debug.Print x
End Sub
";
let (cst_opt, failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
eprintln!("=== Failures for paramarray_not_last ===");
eprintln!("Number of failures: {}", failures.len());
for failure in &failures {
failure.eprint();
}
eprintln!("=== End Failures ===");
let cst = cst_opt.expect("CST should be present even with syntax errors");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../snapshots/tests/invalid_syntax/invalid_parameter_list");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!("paramarray_not_last_cst", tree);
let failure_messages: Vec<String> = failures.iter().map(|f| format!("{f:?}")).collect();
assert_failure_count_matches_snapshot("invalid_parameter_list", "paramarray_not_last_failures", failures.len());
insta::assert_yaml_snapshot!("paramarray_not_last_failures", failure_messages);
}
#[test]
fn paramarray_with_byval() {
let source = r"
Sub Test(ByVal ParamArray args() As Variant)
Dim i As Integer
End Sub
";
let (cst_opt, failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
eprintln!("=== Failures for paramarray_with_byval ===");
eprintln!("Number of failures: {}", failures.len());
for failure in &failures {
failure.eprint();
}
eprintln!("=== End Failures ===");
let cst = cst_opt.expect("CST should be present even with syntax errors");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../snapshots/tests/invalid_syntax/invalid_parameter_list");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!("paramarray_with_byval_cst", tree);
let failure_messages: Vec<String> = failures.iter().map(|f| format!("{f:?}")).collect();
assert_failure_count_matches_snapshot("invalid_parameter_list", "paramarray_with_byval_failures", failures.len());
insta::assert_yaml_snapshot!("paramarray_with_byval_failures", failure_messages);
}
#[test]
fn multiple_consecutive_commas() {
let source = r"
Function Calculate(x As Integer,, y As String) As Integer
Calculate = 0
End Function
";
let (cst_opt, failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
eprintln!("=== Failures for multiple_consecutive_commas ===");
eprintln!("Number of failures: {}", failures.len());
for failure in &failures {
failure.eprint();
}
eprintln!("=== End Failures ===");
let cst = cst_opt.expect("CST should be present even with syntax errors");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../snapshots/tests/invalid_syntax/invalid_parameter_list");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!("multiple_consecutive_commas_cst", tree);
let failure_messages: Vec<String> = failures.iter().map(|f| format!("{f:?}")).collect();
assert_failure_count_matches_snapshot("invalid_parameter_list", "multiple_consecutive_commas_failures", failures.len());
insta::assert_yaml_snapshot!("multiple_consecutive_commas_failures", failure_messages);
}
#[test]
fn parameter_missing_as_keyword() {
let source = r"
Sub Test(x Integer, y As String)
Debug.Print x
End Sub
";
let (cst_opt, failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
eprintln!("=== Failures for parameter_missing_as_keyword ===");
eprintln!("Number of failures: {}", failures.len());
for failure in &failures {
failure.eprint();
}
eprintln!("=== End Failures ===");
let cst = cst_opt.expect("CST should be present even with syntax errors");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../snapshots/tests/invalid_syntax/invalid_parameter_list");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!("parameter_missing_as_keyword_cst", tree);
let failure_messages: Vec<String> = failures.iter().map(|f| format!("{f:?}")).collect();
assert_failure_count_matches_snapshot("invalid_parameter_list", "parameter_missing_as_keyword_failures", failures.len());
insta::assert_yaml_snapshot!("parameter_missing_as_keyword_failures", failure_messages);
}
#[test]
fn optional_byval_with_default() {
let source = r"
Sub Test(Optional ByVal x As Integer = )
Debug.Print x
End Sub
";
let (cst_opt, failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
eprintln!("=== Failures for optional_byval_with_default ===");
eprintln!("Number of failures: {}", failures.len());
for failure in &failures {
failure.eprint();
}
eprintln!("=== End Failures ===");
let cst = cst_opt.expect("CST should be present even with syntax errors");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../snapshots/tests/invalid_syntax/invalid_parameter_list");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!("optional_byval_with_default_cst", tree);
let failure_messages: Vec<String> = failures.iter().map(|f| format!("{f:?}")).collect();
assert_failure_count_matches_snapshot("invalid_parameter_list", "optional_byval_with_default_failures", failures.len());
insta::assert_yaml_snapshot!("optional_byval_with_default_failures", failure_messages);
}
#[test]
fn duplicate_optional_modifier() {
let source = r"
Function Calculate(Optional Optional x As Integer) As Integer
Calculate = x
End Function
";
let (cst_opt, failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
eprintln!("=== Failures for duplicate_optional_modifier ===");
eprintln!("Number of failures: {}", failures.len());
for failure in &failures {
failure.eprint();
}
eprintln!("=== End Failures ===");
let cst = cst_opt.expect("CST should be present even with syntax errors");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../snapshots/tests/invalid_syntax/invalid_parameter_list");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!("duplicate_optional_modifier_cst", tree);
let failure_messages: Vec<String> = failures.iter().map(|f| format!("{f:?}")).collect();
assert_failure_count_matches_snapshot("invalid_parameter_list", "duplicate_optional_modifier_failures", failures.len());
insta::assert_yaml_snapshot!("duplicate_optional_modifier_failures", failure_messages);
}
#[test]
fn paramarray_without_parentheses() {
let source = r"
Sub Test(ParamArray args As Variant)
Dim i As Integer
End Sub
";
let (cst_opt, failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
eprintln!("=== Failures for paramarray_without_parentheses ===");
eprintln!("Number of failures: {}", failures.len());
for failure in &failures {
failure.eprint();
}
eprintln!("=== End Failures ===");
let cst = cst_opt.expect("CST should be present even with syntax errors");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../snapshots/tests/invalid_syntax/invalid_parameter_list");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!("paramarray_without_parentheses_cst", tree);
let failure_messages: Vec<String> = failures.iter().map(|f| format!("{f:?}")).collect();
assert_failure_count_matches_snapshot("invalid_parameter_list", "paramarray_without_parentheses_failures", failures.len());
insta::assert_yaml_snapshot!("paramarray_without_parentheses_failures", failure_messages);
}
#[test]
fn parameter_type_character_with_as() {
let source = r"
Sub Test(x% As Integer)
Debug.Print x
End Sub
";
let (cst_opt, failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
eprintln!("=== Failures for parameter_type_character_with_as ===");
eprintln!("Number of failures: {}", failures.len());
for failure in &failures {
failure.eprint();
}
eprintln!("=== End Failures ===");
let cst = cst_opt.expect("CST should be present even with syntax errors");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../snapshots/tests/invalid_syntax/invalid_parameter_list");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!("parameter_type_character_with_as_cst", tree);
let failure_messages: Vec<String> = failures.iter().map(|f| format!("{f:?}")).collect();
assert_failure_count_matches_snapshot("invalid_parameter_list", "parameter_type_character_with_as_failures", failures.len());
insta::assert_yaml_snapshot!("parameter_type_character_with_as_failures", failure_messages);
}