use super::*;
use perl_tdd_support::must;
fn parse_ok(code: &str) -> String {
let mut parser = Parser::new(code);
let result = parser.parse();
let ast = must(result);
let sexp = ast.to_sexp();
assert!(!sexp.contains("ERROR"), "Parse produced ERROR node(s) for: {}\nSexp: {}", code, sexp);
sexp
}
fn assert_has_modifier(code: &str, modifier_keyword: &str) {
let sexp = parse_ok(code);
let expected_fragment = format!("statement_modifier_{}", modifier_keyword);
assert!(
sexp.contains(&expected_fragment),
"Expected statement modifier '{}' in parsed output of: {}\nSexp: {}",
modifier_keyword,
code,
sexp
);
}
#[test]
fn test_modifier_after_hash_assignment() -> Result<(), Box<dyn std::error::Error>> {
assert_has_modifier(r#"$hash{$k} ||= '' if $cond;"#, "if");
Ok(())
}
#[test]
fn test_modifier_after_push_with_deref() -> Result<(), Box<dyn std::error::Error>> {
assert_has_modifier(r#"push @{$ref}, $v unless $skip;"#, "unless");
Ok(())
}
#[test]
fn test_modifier_with_method_call() -> Result<(), Box<dyn std::error::Error>> {
assert_has_modifier(r#"$obj->method for @list;"#, "for");
Ok(())
}
#[test]
fn test_modifier_after_delete() -> Result<(), Box<dyn std::error::Error>> {
assert_has_modifier(r#"delete $hash{$key} if exists $hash{$key};"#, "if");
Ok(())
}
#[test]
fn test_modifier_after_print_with_filehandle() -> Result<(), Box<dyn std::error::Error>> {
assert_has_modifier(r#"print $fh "hello\n" unless $quiet;"#, "unless");
Ok(())
}
#[test]
fn test_modifier_after_push_nested_deref() -> Result<(), Box<dyn std::error::Error>> {
assert_has_modifier(r#"push @{$hash{$k}}, $v if $cond;"#, "if");
Ok(())
}
#[test]
fn test_modifier_while_after_expression() -> Result<(), Box<dyn std::error::Error>> {
assert_has_modifier(r#"$count++ while $count < 10;"#, "while");
Ok(())
}
#[test]
fn test_modifier_until_after_expression() -> Result<(), Box<dyn std::error::Error>> {
assert_has_modifier(r#"$x *= 2 until $x > 100;"#, "until");
Ok(())
}
#[test]
fn test_modifier_foreach_after_expression() -> Result<(), Box<dyn std::error::Error>> {
assert_has_modifier(r#"print $_ foreach @items;"#, "foreach");
Ok(())
}
#[test]
fn test_simple_modifier_if() -> Result<(), Box<dyn std::error::Error>> {
assert_has_modifier(r#"$x = 1 if $cond;"#, "if");
Ok(())
}
#[test]
fn test_modifier_after_nested_hash_deref() -> Result<(), Box<dyn std::error::Error>> {
assert_has_modifier(r#"push @{$hash{$k}{$j}}, $v if $cond;"#, "if");
Ok(())
}
#[test]
fn test_modifier_after_chained_deref() -> Result<(), Box<dyn std::error::Error>> {
assert_has_modifier(r#"push @{$self->{data}}, $item for @items;"#, "for");
Ok(())
}
#[test]
fn test_modifier_after_defined_or_assign() -> Result<(), Box<dyn std::error::Error>> {
assert_has_modifier(r#"$hash{key} //= [] if $init;"#, "if");
Ok(())
}
#[test]
fn test_modifier_after_array_deref() -> Result<(), Box<dyn std::error::Error>> {
assert_has_modifier(r#"@{$ref} = () unless @{$ref};"#, "unless");
Ok(())
}
#[test]
fn test_modifier_after_mixed_deref() -> Result<(), Box<dyn std::error::Error>> {
assert_has_modifier(r#"$ref->{$k}[$i] = $v unless $skip;"#, "unless");
Ok(())
}
#[test]
fn test_modifier_if_after_hash_key_if() -> Result<(), Box<dyn std::error::Error>> {
assert_has_modifier(r#"$x = $hash{if} if $y;"#, "if");
Ok(())
}
#[test]
fn test_modifier_if_after_hash_key_for() -> Result<(), Box<dyn std::error::Error>> {
assert_has_modifier(r#"$x->{for} = 1 if $y;"#, "if");
Ok(())
}
#[test]
fn test_modifier_after_grep_block() -> Result<(), Box<dyn std::error::Error>> {
assert_has_modifier(r#"@a = grep { $_ > 0 } @b if @b;"#, "if");
Ok(())
}
#[test]
fn test_modifier_after_join_call() -> Result<(), Box<dyn std::error::Error>> {
assert_has_modifier(r#"$r = join(',', @a) if @a;"#, "if");
Ok(())
}
#[test]
fn test_modifier_after_unshift_deref() -> Result<(), Box<dyn std::error::Error>> {
assert_has_modifier(r#"unshift @{$data->{items}}, $new if $new;"#, "if");
Ok(())
}
#[test]
fn test_modifier_after_splice_deref() -> Result<(), Box<dyn std::error::Error>> {
assert_has_modifier(r#"splice @{$arr}, $i, 1 if $remove;"#, "if");
Ok(())
}
#[test]
fn test_modifier_after_my_declaration() -> Result<(), Box<dyn std::error::Error>> {
assert_has_modifier(r#"my $x = 1 if $cond;"#, "if");
Ok(())
}
#[test]
fn test_modifier_after_chomp_with_parens() -> Result<(), Box<dyn std::error::Error>> {
assert_has_modifier(r#"chomp(my $line = <STDIN>) if $flag;"#, "if");
Ok(())
}
#[test]
fn test_modifier_after_user_function_with_hash_arg() -> Result<(), Box<dyn std::error::Error>> {
assert_has_modifier(r#"foo($hash{$k}) if $cond;"#, "if");
Ok(())
}
#[test]
fn test_modifier_after_complex_assignment_chain() -> Result<(), Box<dyn std::error::Error>> {
assert_has_modifier(r#"$a = $b = $c if $d;"#, "if");
Ok(())
}
#[test]
fn test_modifier_after_ternary() -> Result<(), Box<dyn std::error::Error>> {
assert_has_modifier(r#"$x = $a ? $b : $c if $d;"#, "if");
Ok(())
}
#[test]
fn test_modifier_after_regex_match() -> Result<(), Box<dyn std::error::Error>> {
assert_has_modifier(r#"$text =~ s/foo/bar/g if $text;"#, "if");
Ok(())
}
#[test]
fn test_modifier_after_string_concat() -> Result<(), Box<dyn std::error::Error>> {
assert_has_modifier(r#"$str .= "suffix" unless $done;"#, "unless");
Ok(())
}
#[test]
fn test_modifier_after_complex_deref_chain() -> Result<(), Box<dyn std::error::Error>> {
assert_has_modifier(r#"$self->{cache}{$key} = $val if defined $val;"#, "if");
Ok(())
}
#[test]
fn test_modifier_after_array_slice() -> Result<(), Box<dyn std::error::Error>> {
assert_has_modifier(r#"@result = @array[0..2] if @array;"#, "if");
Ok(())
}
#[test]
fn test_modifier_after_die() -> Result<(), Box<dyn std::error::Error>> {
assert_has_modifier(r#"die "error" if $bad;"#, "if");
Ok(())
}
#[test]
fn test_modifier_after_warn() -> Result<(), Box<dyn std::error::Error>> {
assert_has_modifier(r#"warn "oops" unless $quiet;"#, "unless");
Ok(())
}
#[test]
fn test_modifier_after_return() -> Result<(), Box<dyn std::error::Error>> {
assert_has_modifier(r#"return $val if defined $val;"#, "if");
Ok(())
}
#[test]
fn test_modifier_after_next_in_loop() -> Result<(), Box<dyn std::error::Error>> {
assert_has_modifier(r#"next if $skip;"#, "if");
Ok(())
}
#[test]
fn test_modifier_after_last_in_loop() -> Result<(), Box<dyn std::error::Error>> {
assert_has_modifier(r#"last unless $continue;"#, "unless");
Ok(())
}
#[test]
fn test_modifier_after_chained_method_calls() -> Result<(), Box<dyn std::error::Error>> {
assert_has_modifier(r#"$obj->foo->bar if $cond;"#, "if");
Ok(())
}
#[test]
fn test_modifier_after_deep_arrow_deref() -> Result<(), Box<dyn std::error::Error>> {
assert_has_modifier(r#"$self->{data}->[$i] = $v for @items;"#, "for");
Ok(())
}
#[test]
fn test_modifier_after_scalar_deref() -> Result<(), Box<dyn std::error::Error>> {
assert_has_modifier(r#"${$ref} = 1 if $flag;"#, "if");
Ok(())
}
#[test]
fn test_modifier_after_nested_hash_access() -> Result<(), Box<dyn std::error::Error>> {
assert_has_modifier(r#"$hash{a}{b}{c} = 1 unless $done;"#, "unless");
Ok(())
}
#[test]
fn test_modifier_after_local_sig_handler() -> Result<(), Box<dyn std::error::Error>> {
assert_has_modifier(r#"local $SIG{__WARN__} = sub {} if $quiet;"#, "if");
Ok(())
}
#[test]
fn test_modifier_after_postfix_deref_push() -> Result<(), Box<dyn std::error::Error>> {
assert_has_modifier(r#"push @{$data->{list}}, map { $_ * 2 } @vals if @vals;"#, "if");
Ok(())
}
#[test]
fn test_modifier_after_complex_lhs_or_assign() -> Result<(), Box<dyn std::error::Error>> {
assert_has_modifier(r#"$cache->{$key} ||= compute($key) if $use_cache;"#, "if");
Ok(())
}
#[test]
fn test_modifier_after_substr_assign() -> Result<(), Box<dyn std::error::Error>> {
assert_has_modifier(r#"substr($str, 0, 3) = "abc" if length($str) >= 3;"#, "if");
Ok(())
}
#[test]
fn test_modifier_after_comma_expression() -> Result<(), Box<dyn std::error::Error>> {
assert_has_modifier(r#"($a, $b) = (1, 2) if $cond;"#, "if");
Ok(())
}
#[test]
fn test_modifier_after_open_with_three_args() -> Result<(), Box<dyn std::error::Error>> {
assert_has_modifier(r#"open my $fh, '<', $file or die if -e $file;"#, "if");
Ok(())
}
#[test]
fn test_modifier_after_push_with_map() -> Result<(), Box<dyn std::error::Error>> {
assert_has_modifier(r#"push @result, map { $_->{name} } @items unless $skip;"#, "unless");
Ok(())
}