use super::*;
#[test]
fn reports_missing_ternary_colon_fixture() {
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/expressions/missing-ternary-colon.mel"
));
assert!(!parse.errors.is_empty());
assert_eq!(
parse.errors[0].message,
"expected ':' in ternary expression"
);
}
#[test]
fn reports_missing_prefix_update_operand_fixture() {
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/expressions/missing-prefix-update-operand.mel"
));
assert!(!parse.errors.is_empty());
assert_eq!(
parse.errors[0].message,
"expected expression after prefix update"
);
}
#[test]
fn reports_missing_do_while_semi_fixture() {
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/statements/missing-do-while-semi.mel"
));
assert!(!parse.errors.is_empty());
assert_eq!(
parse.errors[0].message,
"expected ';' after do-while statement"
);
}
#[test]
fn reports_missing_for_clause_expr_after_comma_fixture() {
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/statements/missing-for-clause-expr-after-comma.mel"
));
assert!(!parse.errors.is_empty());
assert_eq!(
parse.errors[0].message,
"expected expression after ',' in for clause"
);
}
#[test]
fn reports_missing_var_declarator_fixture() {
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/statements/missing-var-declarator.mel"
));
assert!(!parse.errors.is_empty());
assert_eq!(parse.errors[0].message, "expected variable declarator");
}
#[test]
fn reports_missing_while_condition_fixture() {
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/statements/missing-while-condition.mel"
));
assert!(!parse.errors.is_empty());
assert_eq!(parse.errors[0].message, "expected while condition");
}
#[test]
fn reports_missing_while_body_fixture() {
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/statements/missing-while-body.mel"
));
assert!(!parse.errors.is_empty());
assert_eq!(parse.errors[0].message, "expected while body");
}
#[test]
fn reports_missing_switch_case_value_fixture() {
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/statements/missing-switch-case-value.mel"
));
assert!(!parse.errors.is_empty());
assert_eq!(parse.errors[0].message, "expected case value");
}
#[test]
fn reports_missing_switch_colon_fixture() {
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/statements/missing-switch-colon.mel"
));
assert!(!parse.errors.is_empty());
assert_eq!(parse.errors[0].message, "expected ':' after switch label");
}
#[test]
fn reports_missing_unary_negate_operand_fixture() {
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/expressions/missing-unary-negate-operand.mel"
));
assert!(!parse.errors.is_empty());
assert_eq!(
parse.errors[0].message,
"expected expression after unary operator"
);
}
#[test]
fn reports_missing_caret_rhs_fixture() {
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/expressions/missing-caret-rhs.mel"
));
assert!(!parse.errors.is_empty());
assert_eq!(
parse.errors[0].message,
"expected expression after operator"
);
}
#[test]
fn reports_missing_brace_list_close_fixture() {
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/expressions/missing-brace-list-close.mel"
));
assert!(!parse.errors.is_empty());
assert_eq!(parse.errors[0].message, "expected '}' to close brace list");
}
#[test]
fn reports_missing_cast_operand_fixture() {
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/expressions/missing-cast-operand.mel"
));
assert!(!parse.errors.is_empty());
assert_eq!(parse.errors[0].message, "expected expression after cast");
}
#[test]
fn reports_malformed_path_like_bareword_missing_segment_fixture() {
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/expressions/malformed-path-like-bareword-missing-segment.mel"
));
assert!(!parse.errors.is_empty());
assert_eq!(parse.errors[0].message, "expected expression inside index");
}
#[test]
fn reports_missing_vector_close_fixture() {
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/expressions/missing-vector-close.mel"
));
assert!(!parse.errors.is_empty());
assert_eq!(
parse.errors[0].message,
"expected '>>' to close vector literal"
);
}
#[test]
fn reports_missing_member_name_fixture() {
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/expressions/missing-member-name.mel"
));
assert!(!parse.errors.is_empty());
assert_eq!(parse.errors[0].message, "expected member name after '.'");
}
#[test]
fn reports_trailing_dot_float_double_dot_fixture() {
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/expressions/trailing-dot-float-double-dot.mel"
));
assert!(!parse.errors.is_empty());
assert_eq!(parse.errors[0].message, "expected member name after '.'");
}
#[test]
fn reports_malformed_command_word_fixture() {
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/statements/malformed-command-word.mel"
));
assert!(!parse.errors.is_empty());
assert_eq!(
parse.errors[0].message,
"unexpected token in command invocation"
);
}
#[test]
fn reports_missing_closing_backquote_without_command_cascade_fixture() {
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/statements/malformed-command-missing-closing-backquote.mel"
));
assert_eq!(parse.errors.len(), 1);
assert_eq!(parse.errors[0].message, "expected closing backquote");
assert_eq!(parse.syntax.items.len(), 2);
match &parse.syntax.items[1] {
Item::Stmt(stmt) => match &**stmt {
Stmt::Expr {
expr: Expr::Invoke(invoke),
..
} => match &invoke.surface {
InvokeSurface::ShellLike {
head_range, words, ..
} => {
assert_eq!(parse.source_slice(*head_range), "optionVar");
assert!(matches!(words[0], ShellWord::Flag { .. }));
assert!(matches!(
words[1],
ShellWord::GroupedExpr {
ref expr,
..
} if matches!(&**expr, Expr::Invoke(_))
));
assert!(matches!(
words[2],
ShellWord::GroupedExpr {
ref expr,
..
} if matches!(&**expr, Expr::Invoke(_))
));
}
_ => panic!("expected shell-like invoke"),
},
_ => panic!("expected command expression"),
},
_ => panic!("expected statement"),
}
}
#[test]
fn reports_malformed_command_signed_number_fixture() {
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/statements/malformed-command-signed-number.mel"
));
assert!(!parse.errors.is_empty());
assert_eq!(
parse.errors[0].message,
"unexpected token in command invocation"
);
}
#[test]
fn reports_malformed_command_spaced_flag_fixture() {
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/statements/malformed-command-spaced-flag.mel"
));
assert!(!parse.errors.is_empty());
assert_eq!(
parse.errors[0].message,
"unexpected token in command invocation"
);
}
#[test]
fn reports_malformed_command_single_dot_fixture() {
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/statements/malformed-command-single-dot.mel"
));
assert!(!parse.errors.is_empty());
assert_eq!(
parse.errors[0].message,
"unexpected token in command invocation"
);
}
#[test]
fn reports_malformed_command_leading_dot_no_digit_fixture() {
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/statements/malformed-command-leading-dot-no-digit.mel"
));
assert!(!parse.errors.is_empty());
assert_eq!(
parse.errors[0].message,
"unexpected token in command invocation"
);
}
#[test]
fn reports_malformed_command_spaced_dotted_bareword_fixture() {
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/statements/malformed-command-spaced-dotted-bareword.mel"
));
assert!(!parse.errors.is_empty());
assert_eq!(
parse.errors[0].message,
"unexpected token in command invocation"
);
}
#[test]
fn reports_malformed_command_spaced_pipe_bareword_fixture() {
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/statements/malformed-command-spaced-pipe-bareword.mel"
));
assert!(!parse.errors.is_empty());
assert_eq!(
parse.errors[0].message,
"unexpected token in command invocation"
);
}
#[test]
fn reports_malformed_command_empty_pipe_bareword_fixture() {
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/statements/malformed-command-empty-pipe-bareword.mel"
));
assert!(!parse.errors.is_empty());
assert_eq!(
parse.errors[0].message,
"unexpected token in command invocation"
);
}
#[test]
fn reports_malformed_command_pipe_followed_by_whitespace() {
let parse = parse_source("select -r | spine_00;");
assert!(!parse.errors.is_empty());
assert_eq!(
parse.errors[0].message,
"unexpected token in command invocation"
);
}
#[test]
fn reports_malformed_command_trailing_pipe_before_semicolon() {
let parse = parse_source("select -r y_ang| ;");
assert!(!parse.errors.is_empty());
assert_eq!(
parse.errors[0].message,
"unexpected token in command invocation"
);
}
#[test]
fn reports_malformed_command_spaced_namespace_bareword_fixture() {
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/statements/malformed-command-spaced-namespace-bareword.mel"
));
assert!(!parse.errors.is_empty());
assert_eq!(
parse.errors[0].message,
"unexpected token in command invocation"
);
}
#[test]
fn reports_malformed_command_leading_colon_bareword_fixture() {
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/statements/malformed-command-leading-colon-bareword.mel"
));
assert!(!parse.errors.is_empty());
assert_eq!(
parse.errors[0].message,
"unexpected token in command invocation"
);
}
#[test]
fn reports_malformed_command_dotted_variable_indexed_bareword_fixture() {
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/statements/malformed-command-dotted-variable-indexed-bareword.mel"
));
assert!(!parse.errors.is_empty());
assert_eq!(
parse.errors[0].message,
"unexpected token in command invocation"
);
}
#[test]
fn reports_malformed_command_brace_list_fixture() {
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/statements/malformed-command-brace-list.mel"
));
assert!(!parse.errors.is_empty());
assert_eq!(parse.errors[0].message, "expected '}' to close brace list");
}
#[test]
fn reports_malformed_command_vector_literal_fixture() {
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/statements/malformed-command-vector-literal.mel"
));
assert!(!parse.errors.is_empty());
assert_eq!(
parse.errors[0].message,
"expected '>>' to close vector literal"
);
}
#[test]
fn recovers_missing_statement_parens_fixture() {
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/statements/missing-statement-parens.mel"
));
assert_eq!(parse.errors.len(), 2);
assert_eq!(
parse.errors[0].message,
"expected ')' to close grouped expression"
);
assert_eq!(
parse.errors[1].message,
"expected ')' to close function invocation"
);
assert_eq!(parse.syntax.items.len(), 3);
assert!(matches!(
parse.syntax.items[2],
Item::Stmt(ref stmt) if matches!(
&**stmt,
Stmt::Expr {
expr: Expr::Assign { .. },
..
}
)
));
}
#[test]
fn recovers_missing_statement_semi_fixture() {
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/statements/missing-statement-semi-recovery.mel"
));
assert!(!parse.errors.is_empty());
assert_eq!(parse.errors[0].message, "expected ';' after statement");
assert_eq!(parse.syntax.items.len(), 2);
assert!(matches!(
parse.syntax.items[1],
Item::Stmt(ref stmt) if matches!(
&**stmt,
Stmt::Expr {
expr: Expr::Assign { .. },
..
}
)
));
}
#[test]
fn allows_trailing_top_level_statement_without_semicolon_in_lenient_mode() {
let parse = parse_source_with_options(
include_str!(
"../../../../../tests/corpus/parser/statements/trailing-statement-no-semi.mel"
),
ParseOptions {
mode: ParseMode::AllowTrailingStmtWithoutSemi,
..ParseOptions::default()
},
);
assert!(parse.errors.is_empty());
assert_eq!(parse.syntax.items.len(), 1);
assert!(matches!(
parse.syntax.items[0],
Item::Stmt(ref stmt) if matches!(
&**stmt,
Stmt::Expr {
expr: Expr::Invoke(_),
..
}
)
));
}
#[test]
fn still_requires_semicolon_between_top_level_statements_in_lenient_mode() {
let parse = parse_source_with_options(
"$x = 1\n$y = 2;",
ParseOptions {
mode: ParseMode::AllowTrailingStmtWithoutSemi,
..ParseOptions::default()
},
);
assert!(!parse.errors.is_empty());
assert_eq!(parse.errors[0].message, "expected ';' after statement");
}
#[test]
fn still_requires_semicolon_for_nested_statement_in_lenient_mode() {
let parse = parse_source_with_options(
"if ($ready) print(\"hello\")",
ParseOptions {
mode: ParseMode::AllowTrailingStmtWithoutSemi,
..ParseOptions::default()
},
);
assert!(!parse.errors.is_empty());
assert_eq!(parse.errors[0].message, "expected ';' after statement");
}
#[test]
fn reports_max_bytes_budget_before_full_parse_starts() {
let parse = parse_source_with_options(
"print \"hello\";\n",
ParseOptions {
budgets: ParseBudgets {
max_bytes: 4,
..ParseBudgets::default()
},
..ParseOptions::default()
},
);
assert!(parse.syntax.items.is_empty());
assert_eq!(parse.errors.len(), 1);
assert_eq!(
parse.errors[0].message,
"source exceeds parse budget: max_bytes"
);
}
#[test]
fn reports_max_statements_budget_and_stops_after_first_item() {
let parse = parse_source_with_options(
"$a = 1;\n$b = 2;\n",
ParseOptions {
budgets: ParseBudgets {
max_statements: 1,
..ParseBudgets::default()
},
..ParseOptions::default()
},
);
assert_eq!(parse.syntax.items.len(), 1);
assert_eq!(parse.errors.len(), 1);
assert_eq!(
parse.errors[0].message,
"source exceeds parse budget: max_statements"
);
}
#[test]
fn reports_max_literal_bytes_budget_for_string_literal() {
let parse = parse_source_with_options(
"$name = \"abcdef\";\n",
ParseOptions {
budgets: ParseBudgets {
max_literal_bytes: 4,
..ParseBudgets::default()
},
..ParseOptions::default()
},
);
assert_eq!(parse.errors.len(), 1);
assert_eq!(
parse.errors[0].message,
"source exceeds parse budget: max_literal_bytes"
);
}
#[test]
fn reports_max_nesting_depth_budget_for_grouped_expr() {
let parse = parse_source_with_options(
"$x = ((((1))));\n",
ParseOptions {
budgets: ParseBudgets {
max_nesting_depth: 2,
..ParseBudgets::default()
},
..ParseOptions::default()
},
);
assert_eq!(parse.errors.len(), 1);
assert_eq!(
parse.errors[0].message,
"source exceeds parse budget: max_nesting_depth"
);
}
#[test]
fn reports_max_tokens_budget_for_long_expression() {
let parse = parse_source_with_options(
"$x = 1 + 2 + 3 + 4;\n",
ParseOptions {
budgets: ParseBudgets {
max_tokens: 5,
..ParseBudgets::default()
},
..ParseOptions::default()
},
);
assert_eq!(parse.errors.len(), 1);
assert_eq!(
parse.errors[0].message,
"source exceeds parse budget: max_tokens"
);
}
#[test]
fn auto_detects_cp932_and_maps_ranges_to_original_bytes() {
let source = r#"print "設定";"#;
let (bytes, _, had_errors) = SHIFT_JIS.encode(source);
assert!(!had_errors);
let parse = parse_bytes(bytes.as_ref());
assert_eq!(parse.source_encoding, SourceEncoding::Cp932);
assert!(parse.errors.is_empty());
match &parse.syntax.items[0] {
Item::Stmt(stmt) => match &**stmt {
Stmt::Expr {
expr: Expr::Invoke(invoke),
..
} => match &invoke.surface {
InvokeSurface::ShellLike { words, .. } => match &words[0] {
ShellWord::QuotedString { range, .. } => {
assert_eq!(*range, text_range(6, bytes.len() as u32 - 1));
}
_ => panic!("expected quoted string"),
},
_ => panic!("expected shell-like invoke"),
},
_ => panic!("expected command expression"),
},
_ => panic!("expected statement"),
}
}
#[test]
fn auto_detects_sparse_cp932_bytes_near_end_of_large_file() {
let prefix = "setAttr \".tx\" 1;\n".repeat(8192);
let source = format!("{prefix}print \"設定\";");
let (bytes, _, had_errors) = SHIFT_JIS.encode(&source);
assert!(!had_errors);
let parse = parse_bytes(bytes.as_ref());
assert_eq!(parse.source_encoding, SourceEncoding::Cp932);
assert!(parse.errors.is_empty());
match parse.syntax.items.last() {
Some(Item::Stmt(stmt)) => match &**stmt {
Stmt::Expr {
expr: Expr::Invoke(invoke),
..
} => match &invoke.surface {
InvokeSurface::ShellLike { words, .. } => match words.last() {
Some(ShellWord::QuotedString { range, .. }) => {
assert_eq!(parse.string_literal_contents(*range), Some("設定"));
}
_ => panic!("expected quoted string"),
},
_ => panic!("expected shell-like invoke"),
},
_ => panic!("expected command expression"),
},
_ => panic!("expected trailing statement"),
}
}
#[test]
fn explicit_gbk_decode_preserves_command_surface() {
let source = r#"print "按钮";"#;
let (bytes, _, had_errors) = GBK.encode(source);
assert!(!had_errors);
let parse = parse_bytes_with_encoding(bytes.as_ref(), SourceEncoding::Gbk);
assert_eq!(parse.source_encoding, SourceEncoding::Gbk);
assert!(parse.errors.is_empty());
match &parse.syntax.items[0] {
Item::Stmt(stmt) => match &**stmt {
Stmt::Expr {
expr: Expr::Invoke(invoke),
..
} => match &invoke.surface {
InvokeSurface::ShellLike {
head_range, words, ..
} => match &words[0] {
ShellWord::QuotedString { range, .. } => {
assert_eq!(*range, text_range(6, bytes.len() as u32 - 1));
assert_eq!(parse.source_slice(*head_range), "print");
assert_eq!(parse.source_slice(*range), "\"按钮\"");
assert_eq!(parse.string_literal_contents(*range), Some("按钮"));
}
_ => panic!("expected quoted string"),
},
_ => panic!("expected shell-like invoke"),
},
_ => panic!("expected command expression"),
},
_ => panic!("expected statement"),
}
}
#[test]
fn auto_detect_prefers_less_suspicious_gbk_candidate() {
let parse = parse_bytes(b"print \"\xA0\xA1\";");
assert_eq!(parse.source_encoding, SourceEncoding::Gbk);
assert!(parse.errors.is_empty());
match &parse.syntax.items[0] {
Item::Stmt(stmt) => match &**stmt {
Stmt::Expr {
expr: Expr::Invoke(invoke),
..
} => match &invoke.surface {
InvokeSurface::ShellLike { words, .. } => match &words[0] {
ShellWord::QuotedString { range, .. } => {
assert_eq!(parse.string_literal_contents(*range), Some("牎"));
}
_ => panic!("expected quoted string"),
},
_ => panic!("expected shell-like invoke"),
},
_ => panic!("expected command expression"),
},
_ => panic!("expected statement"),
}
}
#[test]
fn lossy_utf8_single_invalid_byte_keeps_display_ranges_aligned() {
let parse = parse_bytes(b"print \"A\xffB\";\nprint(;\n");
assert_eq!(parse.source_encoding, SourceEncoding::Utf8);
assert_eq!(parse.decode_errors.len(), 1);
assert_eq!(
parse.decode_errors[0].message,
"source is not valid UTF-8; decoded lossily"
);
assert_eq!(parse.decode_errors[0].range, text_range(8, 9));
let decode_span = parse.source_map.display_range(parse.decode_errors[0].range);
assert_eq!(&parse.source_text[decode_span], "\u{FFFD}");
assert!(!parse.errors.is_empty());
let parse_error_span = parse.source_map.display_range(parse.errors[0].range);
assert_eq!(&parse.source_text[parse_error_span], ";");
}
#[test]
fn utf8_unknown_codepoint_produces_single_lex_error_and_safe_display_slice() {
let parse = parse_source("😀;\n");
assert_eq!(parse.source_encoding, SourceEncoding::Utf8);
assert_eq!(parse.decode_errors.len(), 0);
assert_eq!(parse.lex_errors.len(), 1);
assert_eq!(parse.lex_errors[0].message, "unknown character");
assert_eq!(parse.lex_errors[0].range, text_range(0, 4));
assert_eq!(parse.display_slice(parse.lex_errors[0].range), "😀");
}
#[test]
fn cp932_unknown_codepoint_remaps_to_original_source_bytes() {
let (bytes, _, had_errors) = SHIFT_JIS.encode("設;\n");
assert!(!had_errors);
let parse = parse_bytes_with_encoding(bytes.as_ref(), SourceEncoding::Cp932);
assert_eq!(parse.source_encoding, SourceEncoding::Cp932);
assert!(parse.decode_errors.is_empty());
assert_eq!(parse.lex_errors.len(), 1);
assert_eq!(parse.lex_errors[0].message, "unknown character");
assert_eq!(parse.lex_errors[0].range, text_range(0, 2));
assert_eq!(parse.display_slice(parse.lex_errors[0].range), "設");
}
#[test]
fn lossy_utf8_unknown_codepoint_maps_full_replacement_character() {
let parse = parse_bytes_with_encoding(b"\xff;\n", SourceEncoding::Utf8);
assert_eq!(parse.source_encoding, SourceEncoding::Utf8);
assert_eq!(parse.decode_errors.len(), 1);
assert_eq!(parse.lex_errors.len(), 1);
assert_eq!(parse.lex_errors[0].message, "unknown character");
assert_eq!(parse.lex_errors[0].range, text_range(0, 1));
assert_eq!(parse.display_slice(parse.lex_errors[0].range), "\u{FFFD}");
}
#[test]
fn lossy_utf8_truncated_sequence_maps_full_invalid_span_to_replacement() {
let parse = parse_bytes_with_encoding(b"print \"A\xe3\x81\";\nprint(;\n", SourceEncoding::Utf8);
assert_eq!(parse.source_encoding, SourceEncoding::Utf8);
assert_eq!(parse.decode_errors.len(), 1);
assert_eq!(parse.decode_errors[0].range, text_range(8, 10));
let decode_span = parse.source_map.display_range(parse.decode_errors[0].range);
assert_eq!(&parse.source_text[decode_span], "\u{FFFD}");
assert!(!parse.errors.is_empty());
let parse_error_span = parse.source_map.display_range(parse.errors[0].range);
assert_eq!(&parse.source_text[parse_error_span], ";");
}
#[test]
fn explicit_utf8_override_does_not_fall_back_to_cp932_auto_detection() {
let source = r#"print "設定";"#;
let (bytes, _, had_errors) = SHIFT_JIS.encode(source);
assert!(!had_errors);
let parse = parse_bytes_with_encoding(bytes.as_ref(), SourceEncoding::Utf8);
assert_eq!(parse.source_encoding, SourceEncoding::Utf8);
assert_eq!(parse.decode_errors.len(), 1);
assert_eq!(
parse.decode_errors[0].message,
"source is not valid UTF-8; decoded lossily"
);
}
#[test]
fn reports_decimal_integer_literal_overflow() {
let parse = parse_source("int $value = 9223372036854775808;");
assert!(!parse.errors.is_empty());
assert_eq!(parse.errors[0].message, "integer literal out of range");
assert_eq!(parse.errors[0].range, text_range(13, 32));
match &parse.syntax.items[0] {
Item::Stmt(stmt) => match &**stmt {
Stmt::VarDecl { decl, .. } => match decl.declarators[0].initializer.as_ref() {
Some(Expr::Int { value, .. }) => assert_eq!(*value, 0),
_ => panic!("expected integer initializer"),
},
_ => panic!("expected variable declaration"),
},
_ => panic!("expected statement"),
}
}
#[test]
fn reports_hex_integer_literal_overflow() {
let parse = parse_source("int $value = 0x8000000000000000;");
assert!(!parse.errors.is_empty());
assert_eq!(parse.errors[0].message, "integer literal out of range");
assert_eq!(parse.errors[0].range, text_range(13, 31));
match &parse.syntax.items[0] {
Item::Stmt(stmt) => match &**stmt {
Stmt::VarDecl { decl, .. } => match decl.declarators[0].initializer.as_ref() {
Some(Expr::Int { value, .. }) => assert_eq!(*value, 0),
_ => panic!("expected integer initializer"),
},
_ => panic!("expected variable declaration"),
},
_ => panic!("expected statement"),
}
}
#[test]
fn parse_helpers_slice_and_unquote_string_literals() {
let parse = parse_source("print \"hello\";");
assert!(parse.errors.is_empty());
match &parse.syntax.items[0] {
Item::Stmt(stmt) => match &**stmt {
Stmt::Expr {
expr: Expr::Invoke(invoke),
..
} => match &invoke.surface {
InvokeSurface::ShellLike { words, .. } => match &words[0] {
ShellWord::QuotedString { range, .. } => {
assert_eq!(parse.source_slice(*range), "\"hello\"");
assert_eq!(parse.display_slice(*range), "\"hello\"");
assert_eq!(parse.string_literal_contents(*range), Some("hello"));
}
_ => panic!("expected quoted string shell word"),
},
_ => panic!("expected shell-like invoke"),
},
_ => panic!("expected command expression"),
},
_ => panic!("expected statement"),
}
}