mirror of
https://github.com/ruby/ruby.git
synced 2025-08-24 21:44:30 +02:00

This should make it easier on the sync to determine what changed
and hopefully result in fewer merge conflicts that have to be
manually resolved.
17d82afbfc
1131 lines
28 KiB
Ruby
1131 lines
28 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require_relative "test_helper"
|
|
|
|
class ErrorsTest < Test::Unit::TestCase
|
|
include ::YARP::DSL
|
|
|
|
def test_constant_path_with_invalid_token_after
|
|
assert_error_messages "A::$b", [
|
|
"Expected identifier or constant after '::'",
|
|
"Expected a newline or semicolon after statement."
|
|
]
|
|
end
|
|
|
|
def test_module_name_recoverable
|
|
expected = ModuleNode(
|
|
[],
|
|
Location(),
|
|
ConstantReadNode(),
|
|
StatementsNode(
|
|
[ModuleNode([], Location(), MissingNode(), nil, Location(), "")]
|
|
),
|
|
Location(),
|
|
"Parent"
|
|
)
|
|
|
|
assert_errors expected, "module Parent module end", [
|
|
["Expected to find a module name after `module`.", 20..20]
|
|
]
|
|
end
|
|
|
|
def test_for_loops_index_missing
|
|
expected = ForNode(
|
|
MissingNode(),
|
|
expression("1..10"),
|
|
StatementsNode([expression("i")]),
|
|
Location(),
|
|
Location(),
|
|
nil,
|
|
Location()
|
|
)
|
|
|
|
assert_errors expected, "for in 1..10\ni\nend", [
|
|
["Expected index after for.", 0..0]
|
|
]
|
|
end
|
|
|
|
def test_for_loops_only_end
|
|
expected = ForNode(
|
|
MissingNode(),
|
|
MissingNode(),
|
|
nil,
|
|
Location(),
|
|
Location(),
|
|
nil,
|
|
Location()
|
|
)
|
|
|
|
assert_errors expected, "for end", [
|
|
["Expected index after for.", 0..0],
|
|
["Expected keyword in.", 3..3],
|
|
["Expected collection.", 3..3]
|
|
]
|
|
end
|
|
|
|
def test_pre_execution_missing_brace
|
|
expected = PreExecutionNode(
|
|
StatementsNode([expression("1")]),
|
|
Location(),
|
|
Location(),
|
|
Location()
|
|
)
|
|
|
|
assert_errors expected, "BEGIN 1 }", [
|
|
["Expected '{' after 'BEGIN'.", 5..5]
|
|
]
|
|
end
|
|
|
|
def test_pre_execution_context
|
|
expected = PreExecutionNode(
|
|
StatementsNode([
|
|
CallNode(
|
|
expression("1"),
|
|
nil,
|
|
Location(),
|
|
nil,
|
|
ArgumentsNode([MissingNode()]),
|
|
nil,
|
|
nil,
|
|
0,
|
|
"+"
|
|
)
|
|
]),
|
|
Location(),
|
|
Location(),
|
|
Location()
|
|
)
|
|
|
|
assert_errors expected, "BEGIN { 1 + }", [
|
|
["Expected a value after the operator.", 11..11]
|
|
]
|
|
end
|
|
|
|
def test_unterminated_embdoc
|
|
assert_errors expression("1"), "1\n=begin\n", [
|
|
["Unterminated embdoc", 2..9]
|
|
]
|
|
end
|
|
|
|
def test_unterminated_i_list
|
|
assert_errors expression("%i["), "%i[", [
|
|
["Expected a closing delimiter for a `%i` list.", 3..3]
|
|
]
|
|
end
|
|
|
|
def test_unterminated_w_list
|
|
assert_errors expression("%w["), "%w[", [
|
|
["Expected a closing delimiter for a `%w` list.", 3..3]
|
|
]
|
|
end
|
|
|
|
def test_unterminated_W_list
|
|
assert_errors expression("%W["), "%W[", [
|
|
["Expected a closing delimiter for a `%W` list.", 3..3]
|
|
]
|
|
end
|
|
|
|
def test_unterminated_regular_expression
|
|
assert_errors expression("/hello"), "/hello", [
|
|
["Expected a closing delimiter for a regular expression.", 1..1]
|
|
]
|
|
end
|
|
|
|
def test_unterminated_regular_expression_with_heredoc
|
|
source = "<<-END + /b\nEND\n"
|
|
|
|
assert_errors expression(source), source, [
|
|
["Expected a closing delimiter for a regular expression.", 10..10]
|
|
]
|
|
end
|
|
|
|
def test_unterminated_xstring
|
|
assert_errors expression("`hello"), "`hello", [
|
|
["Expected a closing delimiter for an xstring.", 1..1]
|
|
]
|
|
end
|
|
|
|
def test_unterminated_string
|
|
assert_errors expression('"hello'), '"hello', [
|
|
["Expected a closing delimiter for an interpolated string.", 1..1]
|
|
]
|
|
end
|
|
|
|
def test_unterminated_s_symbol
|
|
assert_errors expression("%s[abc"), "%s[abc", [
|
|
["Expected a closing delimiter for a dynamic symbol.", 3..3]
|
|
]
|
|
end
|
|
|
|
def test_unterminated_parenthesized_expression
|
|
assert_errors expression('(1 + 2'), '(1 + 2', [
|
|
["Expected to be able to parse an expression.", 6..6],
|
|
["Expected a closing parenthesis.", 6..6]
|
|
]
|
|
end
|
|
|
|
def test_unterminated_argument_expression
|
|
assert_errors expression('a %'), 'a %', [
|
|
["Unexpected end of input", 2..3],
|
|
["Expected a value after the operator.", 3..3],
|
|
]
|
|
end
|
|
|
|
def test_cr_without_lf_in_percent_expression
|
|
assert_errors expression("%\r"), "%\r", [
|
|
["Invalid %% token", 0..2],
|
|
]
|
|
end
|
|
|
|
def test_1_2_3
|
|
assert_errors expression("(1, 2, 3)"), "(1, 2, 3)", [
|
|
["Expected to be able to parse an expression.", 2..2],
|
|
["Expected a closing parenthesis.", 2..2],
|
|
["Expected a newline or semicolon after statement.", 2..2],
|
|
["Expected to be able to parse an expression.", 2..2],
|
|
["Expected a newline or semicolon after statement.", 5..5],
|
|
["Expected to be able to parse an expression.", 5..5],
|
|
["Expected a newline or semicolon after statement.", 8..8],
|
|
["Expected to be able to parse an expression.", 8..8],
|
|
]
|
|
end
|
|
|
|
def test_return_1_2_3
|
|
assert_error_messages "return(1, 2, 3)", [
|
|
"Expected to be able to parse an expression.",
|
|
"Expected a closing parenthesis.",
|
|
"Expected a newline or semicolon after statement.",
|
|
"Expected to be able to parse an expression."
|
|
]
|
|
end
|
|
|
|
def test_return_1
|
|
assert_errors expression("return 1,;"), "return 1,;", [
|
|
["Expected to be able to parse an argument.", 9..9]
|
|
]
|
|
end
|
|
|
|
def test_next_1_2_3
|
|
assert_errors expression("next(1, 2, 3)"), "next(1, 2, 3)", [
|
|
["Expected to be able to parse an expression.", 6..6],
|
|
["Expected a closing parenthesis.", 6..6],
|
|
["Expected a newline or semicolon after statement.", 12..12],
|
|
["Expected to be able to parse an expression.", 12..12]
|
|
]
|
|
end
|
|
|
|
def test_next_1
|
|
assert_errors expression("next 1,;"), "next 1,;", [
|
|
["Expected to be able to parse an argument.", 7..7]
|
|
]
|
|
end
|
|
|
|
def test_break_1_2_3
|
|
assert_errors expression("break(1, 2, 3)"), "break(1, 2, 3)", [
|
|
["Expected to be able to parse an expression.", 7..7],
|
|
["Expected a closing parenthesis.", 7..7],
|
|
["Expected a newline or semicolon after statement.", 13..13],
|
|
["Expected to be able to parse an expression.", 13..13],
|
|
]
|
|
end
|
|
|
|
def test_break_1
|
|
assert_errors expression("break 1,;"), "break 1,;", [
|
|
["Expected to be able to parse an argument.", 8..8]
|
|
]
|
|
end
|
|
|
|
def test_argument_forwarding_when_parent_is_not_forwarding
|
|
assert_errors expression('def a(x, y, z); b(...); end'), 'def a(x, y, z); b(...); end', [
|
|
["unexpected ... when parent method is not forwarding.", 18..21]
|
|
]
|
|
end
|
|
|
|
def test_argument_forwarding_only_effects_its_own_internals
|
|
assert_errors expression('def a(...); b(...); end; def c(x, y, z); b(...); end'),
|
|
'def a(...); b(...); end; def c(x, y, z); b(...); end', [
|
|
["unexpected ... when parent method is not forwarding.", 43..46]
|
|
]
|
|
end
|
|
|
|
def test_top_level_constant_with_downcased_identifier
|
|
assert_error_messages "::foo", [
|
|
"Expected a constant after ::.",
|
|
"Expected a newline or semicolon after statement."
|
|
]
|
|
end
|
|
|
|
def test_top_level_constant_starting_with_downcased_identifier
|
|
assert_error_messages "::foo::A", [
|
|
"Expected a constant after ::.",
|
|
"Expected a newline or semicolon after statement."
|
|
]
|
|
end
|
|
|
|
def test_aliasing_global_variable_with_non_global_variable
|
|
assert_errors expression("alias $a b"), "alias $a b", [
|
|
["Expected a global variable.", 9..10]
|
|
]
|
|
end
|
|
|
|
def test_aliasing_non_global_variable_with_global_variable
|
|
assert_errors expression("alias a $b"), "alias a $b", [
|
|
["Expected a bare word or symbol argument.", 8..10]
|
|
]
|
|
end
|
|
|
|
def test_aliasing_global_variable_with_global_number_variable
|
|
assert_errors expression("alias $a $1"), "alias $a $1", [
|
|
["Can't make alias for number variables.", 9..11]
|
|
]
|
|
end
|
|
|
|
def test_def_with_expression_receiver_and_no_identifier
|
|
assert_errors expression("def (a); end"), "def (a); end", [
|
|
["Expected '.' or '::' after receiver", 7..7]
|
|
]
|
|
end
|
|
|
|
def test_def_with_multiple_statements_receiver
|
|
assert_errors expression("def (\na\nb\n).c; end"), "def (\na\nb\n).c; end", [
|
|
["Expected closing ')' for receiver.", 7..7],
|
|
["Expected '.' or '::' after receiver", 7..7],
|
|
["Expected to be able to parse an expression.", 10..10],
|
|
["Expected to be able to parse an expression.", 11..11]
|
|
]
|
|
end
|
|
|
|
def test_def_with_empty_expression_receiver
|
|
assert_errors expression("def ().a; end"), "def ().a; end", [
|
|
["Expected to be able to parse receiver.", 5..5]
|
|
]
|
|
end
|
|
|
|
def test_block_beginning_with_brace_and_ending_with_end
|
|
assert_error_messages "x.each { x end", [
|
|
"Expected a newline or semicolon after statement.",
|
|
"Expected to be able to parse an expression.",
|
|
"Expected to be able to parse an expression.",
|
|
"Expected block beginning with '{' to end with '}'."
|
|
]
|
|
end
|
|
|
|
def test_double_splat_followed_by_splat_argument
|
|
expected = CallNode(
|
|
nil,
|
|
nil,
|
|
Location(),
|
|
Location(),
|
|
ArgumentsNode([
|
|
KeywordHashNode([AssocSplatNode(expression("kwargs"), Location())]),
|
|
SplatNode(Location(), expression("args"))
|
|
]),
|
|
Location(),
|
|
nil,
|
|
0,
|
|
"a"
|
|
)
|
|
|
|
assert_errors expected, "a(**kwargs, *args)", [
|
|
["Unexpected splat argument after double splat.", 12..17]
|
|
]
|
|
end
|
|
|
|
def test_arguments_after_block
|
|
expected = CallNode(
|
|
nil,
|
|
nil,
|
|
Location(),
|
|
Location(),
|
|
ArgumentsNode([
|
|
BlockArgumentNode(expression("block"), Location()),
|
|
expression("foo")
|
|
]),
|
|
Location(),
|
|
nil,
|
|
0,
|
|
"a"
|
|
)
|
|
|
|
assert_errors expected, "a(&block, foo)", [
|
|
["Unexpected argument after block argument.", 10..13]
|
|
]
|
|
end
|
|
|
|
def test_arguments_binding_power_for_and
|
|
assert_error_messages "foo(*bar and baz)", [
|
|
"Expected a ')' to close the argument list.",
|
|
"Expected a newline or semicolon after statement.",
|
|
"Expected to be able to parse an expression."
|
|
]
|
|
end
|
|
|
|
def test_splat_argument_after_keyword_argument
|
|
expected = CallNode(
|
|
nil,
|
|
nil,
|
|
Location(),
|
|
Location(),
|
|
ArgumentsNode([
|
|
KeywordHashNode(
|
|
[AssocNode(
|
|
SymbolNode(nil, Location(), Location(), "foo"),
|
|
expression("bar"),
|
|
nil
|
|
)]
|
|
),
|
|
SplatNode(Location(), expression("args"))
|
|
]),
|
|
Location(),
|
|
nil,
|
|
0,
|
|
"a"
|
|
)
|
|
|
|
assert_errors expected, "a(foo: bar, *args)", [
|
|
["Unexpected splat argument after double splat.", 12..17]
|
|
]
|
|
end
|
|
|
|
def test_module_definition_in_method_body
|
|
expected = DefNode(
|
|
Location(),
|
|
nil,
|
|
nil,
|
|
StatementsNode([ModuleNode([], Location(), ConstantReadNode(), nil, Location(), "A")]),
|
|
[],
|
|
Location(),
|
|
nil,
|
|
nil,
|
|
nil,
|
|
nil,
|
|
Location()
|
|
)
|
|
|
|
assert_errors expected, "def foo;module A;end;end", [
|
|
["Module definition in method body", 8..14]
|
|
]
|
|
end
|
|
|
|
def test_module_definition_in_method_body_within_block
|
|
expected = DefNode(
|
|
Location(),
|
|
nil,
|
|
nil,
|
|
StatementsNode(
|
|
[CallNode(
|
|
nil,
|
|
nil,
|
|
Location(),
|
|
nil,
|
|
nil,
|
|
nil,
|
|
BlockNode(
|
|
[],
|
|
nil,
|
|
StatementsNode([ModuleNode([], Location(), ConstantReadNode(), nil, Location(), "Foo")]),
|
|
Location(),
|
|
Location()
|
|
),
|
|
0,
|
|
"bar"
|
|
)]
|
|
),
|
|
[],
|
|
Location(),
|
|
nil,
|
|
nil,
|
|
nil,
|
|
nil,
|
|
Location()
|
|
)
|
|
|
|
assert_errors expected, "
|
|
def foo
|
|
bar do
|
|
module Foo;end
|
|
end
|
|
end
|
|
", [
|
|
["Module definition in method body", 40..46]
|
|
]
|
|
end
|
|
|
|
def test_class_definition_in_method_body
|
|
expected = DefNode(
|
|
Location(),
|
|
nil,
|
|
nil,
|
|
StatementsNode(
|
|
[ClassNode(
|
|
[],
|
|
Location(),
|
|
ConstantReadNode(),
|
|
nil,
|
|
nil,
|
|
nil,
|
|
Location(),
|
|
"A"
|
|
)]
|
|
),
|
|
[],
|
|
Location(),
|
|
nil,
|
|
nil,
|
|
nil,
|
|
nil,
|
|
Location()
|
|
)
|
|
|
|
assert_errors expected, "def foo;class A;end;end", [
|
|
["Class definition in method body", 8..13]
|
|
]
|
|
end
|
|
|
|
def test_bad_arguments
|
|
expected = DefNode(
|
|
Location(),
|
|
nil,
|
|
ParametersNode([
|
|
RequiredParameterNode(:A),
|
|
RequiredParameterNode(:@a),
|
|
RequiredParameterNode(:$A),
|
|
RequiredParameterNode(:@@a),
|
|
], [], [], nil, [], nil, nil),
|
|
nil,
|
|
[:A, :@a, :$A, :@@a],
|
|
Location(),
|
|
nil,
|
|
Location(),
|
|
Location(),
|
|
nil,
|
|
Location()
|
|
)
|
|
|
|
assert_errors expected, "def foo(A, @a, $A, @@a);end", [
|
|
["Formal argument cannot be a constant", 8..9],
|
|
["Formal argument cannot be an instance variable", 11..13],
|
|
["Formal argument cannot be a global variable", 15..17],
|
|
["Formal argument cannot be a class variable", 19..22],
|
|
]
|
|
end
|
|
|
|
def test_cannot_assign_to_a_reserved_numbered_parameter
|
|
expected = BeginNode(
|
|
Location(),
|
|
StatementsNode([
|
|
LocalVariableWriteNode(:_1, 0, SymbolNode(Location(), Location(), nil, "a"), Location(), Location()),
|
|
LocalVariableWriteNode(:_2, 0, SymbolNode(Location(), Location(), nil, "a"), Location(), Location()),
|
|
LocalVariableWriteNode(:_3, 0, SymbolNode(Location(), Location(), nil, "a"), Location(), Location()),
|
|
LocalVariableWriteNode(:_4, 0, SymbolNode(Location(), Location(), nil, "a"), Location(), Location()),
|
|
LocalVariableWriteNode(:_5, 0, SymbolNode(Location(), Location(), nil, "a"), Location(), Location()),
|
|
LocalVariableWriteNode(:_6, 0, SymbolNode(Location(), Location(), nil, "a"), Location(), Location()),
|
|
LocalVariableWriteNode(:_7, 0, SymbolNode(Location(), Location(), nil, "a"), Location(), Location()),
|
|
LocalVariableWriteNode(:_8, 0, SymbolNode(Location(), Location(), nil, "a"), Location(), Location()),
|
|
LocalVariableWriteNode(:_9, 0, SymbolNode(Location(), Location(), nil, "a"), Location(), Location()),
|
|
LocalVariableWriteNode(:_10, 0, SymbolNode(Location(), Location(), nil, "a"), Location(), Location())
|
|
]),
|
|
nil,
|
|
nil,
|
|
nil,
|
|
Location()
|
|
)
|
|
source = <<~RUBY
|
|
begin
|
|
_1=:a;_2=:a;_3=:a;_4=:a;_5=:a
|
|
_6=:a;_7=:a;_8=:a;_9=:a;_10=:a
|
|
end
|
|
RUBY
|
|
assert_errors expected, source, [
|
|
["reserved for numbered parameter", 8..10],
|
|
["reserved for numbered parameter", 14..16],
|
|
["reserved for numbered parameter", 20..22],
|
|
["reserved for numbered parameter", 26..28],
|
|
["reserved for numbered parameter", 32..34],
|
|
["reserved for numbered parameter", 40..42],
|
|
["reserved for numbered parameter", 46..48],
|
|
["reserved for numbered parameter", 52..54],
|
|
["reserved for numbered parameter", 58..60],
|
|
]
|
|
end
|
|
|
|
def test_do_not_allow_trailing_commas_in_method_parameters
|
|
expected = DefNode(
|
|
Location(),
|
|
nil,
|
|
ParametersNode(
|
|
[RequiredParameterNode(:a), RequiredParameterNode(:b), RequiredParameterNode(:c)],
|
|
[],
|
|
[],
|
|
nil,
|
|
[],
|
|
nil,
|
|
nil
|
|
),
|
|
nil,
|
|
[:a, :b, :c],
|
|
Location(),
|
|
nil,
|
|
Location(),
|
|
Location(),
|
|
nil,
|
|
Location()
|
|
)
|
|
|
|
assert_errors expected, "def foo(a,b,c,);end", [
|
|
["Unexpected ','.", 13..14]
|
|
]
|
|
end
|
|
|
|
def test_do_not_allow_trailing_commas_in_lambda_parameters
|
|
expected = LambdaNode(
|
|
[:a, :b],
|
|
Location(),
|
|
Location(),
|
|
Location(),
|
|
BlockParametersNode(
|
|
ParametersNode([RequiredParameterNode(:a), RequiredParameterNode(:b)], [], [], nil, [], nil, nil),
|
|
[],
|
|
Location(),
|
|
Location()
|
|
),
|
|
nil
|
|
)
|
|
assert_errors expected, "-> (a, b, ) {}", [
|
|
["Unexpected ','.", 8..9]
|
|
]
|
|
end
|
|
|
|
def test_do_not_allow_multiple_codepoints_in_a_single_character_literal
|
|
expected = StringNode(Location(), Location(), nil, "\u0001\u0002")
|
|
|
|
assert_errors expected, '?\u{0001 0002}', [
|
|
["Multiple codepoints at single character literal", 9..12]
|
|
]
|
|
end
|
|
|
|
def test_do_not_allow_more_than_6_hexadecimal_digits_in_u_Unicode_character_notation
|
|
expected = StringNode(Location(), Location(), Location(), "\u0001")
|
|
|
|
assert_errors expected, '"\u{0000001}"', [
|
|
["invalid Unicode escape.", 4..11],
|
|
["invalid Unicode escape.", 4..11]
|
|
]
|
|
end
|
|
|
|
def test_do_not_allow_characters_other_than_0_9_a_f_and_A_F_in_u_Unicode_character_notation
|
|
expected = StringNode(Location(), Location(), Location(), "\u0000z}")
|
|
|
|
assert_errors expected, '"\u{000z}"', [
|
|
["unterminated Unicode escape", 7..7],
|
|
["unterminated Unicode escape", 7..7]
|
|
]
|
|
end
|
|
|
|
def test_method_parameters_after_block
|
|
expected = DefNode(
|
|
Location(),
|
|
nil,
|
|
ParametersNode(
|
|
[],
|
|
[],
|
|
[RequiredParameterNode(:a)],
|
|
nil,
|
|
[],
|
|
nil,
|
|
BlockParameterNode(Location(), Location())
|
|
),
|
|
nil,
|
|
[:block, :a],
|
|
Location(),
|
|
nil,
|
|
Location(),
|
|
Location(),
|
|
nil,
|
|
Location()
|
|
)
|
|
assert_errors expected, "def foo(&block, a)\nend", [
|
|
["Unexpected parameter order", 16..17]
|
|
]
|
|
end
|
|
|
|
def test_method_with_arguments_after_anonymous_block
|
|
expected = DefNode(
|
|
Location(),
|
|
nil,
|
|
ParametersNode([], [], [RequiredParameterNode(:a)], nil, [], nil, BlockParameterNode(nil, Location())),
|
|
nil,
|
|
[:&, :a],
|
|
Location(),
|
|
nil,
|
|
Location(),
|
|
Location(),
|
|
nil,
|
|
Location()
|
|
)
|
|
|
|
assert_errors expected, "def foo(&, a)\nend", [
|
|
["Unexpected parameter order", 11..12]
|
|
]
|
|
end
|
|
|
|
def test_method_parameters_after_arguments_forwarding
|
|
expected = DefNode(
|
|
Location(),
|
|
nil,
|
|
ParametersNode(
|
|
[],
|
|
[],
|
|
[RequiredParameterNode(:a)],
|
|
nil,
|
|
[],
|
|
ForwardingParameterNode(),
|
|
nil
|
|
),
|
|
nil,
|
|
[:"...", :a],
|
|
Location(),
|
|
nil,
|
|
Location(),
|
|
Location(),
|
|
nil,
|
|
Location()
|
|
)
|
|
assert_errors expected, "def foo(..., a)\nend", [
|
|
["Unexpected parameter order", 13..14]
|
|
]
|
|
end
|
|
|
|
def test_keywords_parameters_before_required_parameters
|
|
expected = DefNode(
|
|
Location(),
|
|
nil,
|
|
ParametersNode(
|
|
[],
|
|
[],
|
|
[RequiredParameterNode(:a)],
|
|
nil,
|
|
[KeywordParameterNode(Location(), nil)],
|
|
nil,
|
|
nil
|
|
),
|
|
nil,
|
|
[:b, :a],
|
|
Location(),
|
|
nil,
|
|
Location(),
|
|
Location(),
|
|
nil,
|
|
Location()
|
|
)
|
|
assert_errors expected, "def foo(b:, a)\nend", [
|
|
["Unexpected parameter order", 12..13]
|
|
]
|
|
end
|
|
|
|
def test_rest_keywords_parameters_before_required_parameters
|
|
expected = DefNode(
|
|
Location(),
|
|
nil,
|
|
ParametersNode(
|
|
[],
|
|
[],
|
|
[],
|
|
nil,
|
|
[KeywordParameterNode(Location(), nil)],
|
|
KeywordRestParameterNode(Location(), Location()),
|
|
nil
|
|
),
|
|
nil,
|
|
[:rest, :b],
|
|
Location(),
|
|
nil,
|
|
Location(),
|
|
Location(),
|
|
nil,
|
|
Location()
|
|
)
|
|
assert_errors expected, "def foo(**rest, b:)\nend", [
|
|
["Unexpected parameter order", 16..18]
|
|
]
|
|
end
|
|
|
|
def test_double_arguments_forwarding
|
|
expected = DefNode(
|
|
Location(),
|
|
nil,
|
|
ParametersNode([], [], [], nil, [], ForwardingParameterNode(), nil),
|
|
nil,
|
|
[:"..."],
|
|
Location(),
|
|
nil,
|
|
Location(),
|
|
Location(),
|
|
nil,
|
|
Location()
|
|
)
|
|
|
|
assert_errors expected, "def foo(..., ...)\nend", [
|
|
["Unexpected parameter order", 13..16]
|
|
]
|
|
end
|
|
|
|
def test_multiple_error_in_parameters_order
|
|
expected = DefNode(
|
|
Location(),
|
|
nil,
|
|
ParametersNode(
|
|
[],
|
|
[],
|
|
[RequiredParameterNode(:a)],
|
|
nil,
|
|
[KeywordParameterNode(Location(), nil)],
|
|
KeywordRestParameterNode(Location(), Location()),
|
|
nil
|
|
),
|
|
nil,
|
|
[:args, :a, :b],
|
|
Location(),
|
|
nil,
|
|
Location(),
|
|
Location(),
|
|
nil,
|
|
Location()
|
|
)
|
|
|
|
assert_errors expected, "def foo(**args, a, b:)\nend", [
|
|
["Unexpected parameter order", 16..17],
|
|
["Unexpected parameter order", 19..21]
|
|
]
|
|
end
|
|
|
|
def test_switching_to_optional_arguments_twice
|
|
expected = DefNode(
|
|
Location(),
|
|
nil,
|
|
ParametersNode(
|
|
[],
|
|
[],
|
|
[RequiredParameterNode(:a)],
|
|
nil,
|
|
[KeywordParameterNode(Location(), nil)],
|
|
KeywordRestParameterNode(Location(), Location()),
|
|
nil
|
|
),
|
|
nil,
|
|
[:args, :a, :b],
|
|
Location(),
|
|
nil,
|
|
Location(),
|
|
Location(),
|
|
nil,
|
|
Location(),
|
|
)
|
|
|
|
assert_errors expected, "def foo(**args, a, b:)\nend", [
|
|
["Unexpected parameter order", 16..17],
|
|
["Unexpected parameter order", 19..21]
|
|
]
|
|
end
|
|
|
|
def test_switching_to_named_arguments_twice
|
|
expected = DefNode(
|
|
Location(),
|
|
nil,
|
|
ParametersNode(
|
|
[],
|
|
[],
|
|
[RequiredParameterNode(:a)],
|
|
nil,
|
|
[KeywordParameterNode(Location(), nil)],
|
|
KeywordRestParameterNode(Location(), Location()),
|
|
nil
|
|
),
|
|
nil,
|
|
[:args, :a, :b],
|
|
Location(),
|
|
nil,
|
|
Location(),
|
|
Location(),
|
|
nil,
|
|
Location(),
|
|
)
|
|
|
|
assert_errors expected, "def foo(**args, a, b:)\nend", [
|
|
["Unexpected parameter order", 16..17],
|
|
["Unexpected parameter order", 19..21]
|
|
]
|
|
end
|
|
|
|
def test_returning_to_optional_parameters_multiple_times
|
|
expected = DefNode(
|
|
Location(),
|
|
nil,
|
|
ParametersNode(
|
|
[RequiredParameterNode(:a)],
|
|
[
|
|
OptionalParameterNode(:b, Location(), Location(), IntegerNode()),
|
|
OptionalParameterNode(:d, Location(), Location(), IntegerNode())
|
|
],
|
|
[RequiredParameterNode(:c), RequiredParameterNode(:e)],
|
|
nil,
|
|
[],
|
|
nil,
|
|
nil
|
|
),
|
|
nil,
|
|
[:a, :b, :c, :d, :e],
|
|
Location(),
|
|
nil,
|
|
Location(),
|
|
Location(),
|
|
nil,
|
|
Location(),
|
|
)
|
|
|
|
assert_errors expected, "def foo(a, b = 1, c, d = 2, e)\nend", [
|
|
["Unexpected parameter order", 23..24]
|
|
]
|
|
end
|
|
|
|
def test_case_without_when_clauses_errors_on_else_clause
|
|
expected = CaseNode(
|
|
SymbolNode(Location(), Location(), nil, "a"),
|
|
[],
|
|
ElseNode(Location(), nil, Location()),
|
|
Location(),
|
|
Location()
|
|
)
|
|
|
|
assert_errors expected, "case :a\nelse\nend", [
|
|
["Unexpected else without no when clauses in case statement.", 8..12]
|
|
]
|
|
end
|
|
|
|
def test_setter_method_cannot_be_defined_in_an_endless_method_definition
|
|
expected = DefNode(
|
|
Location(),
|
|
nil,
|
|
nil,
|
|
StatementsNode([IntegerNode()]),
|
|
[],
|
|
Location(),
|
|
nil,
|
|
Location(),
|
|
Location(),
|
|
Location(),
|
|
nil
|
|
)
|
|
|
|
assert_errors expected, "def a=() = 42", [
|
|
["Setter method cannot be defined in an endless method definition", 4..6]
|
|
]
|
|
end
|
|
|
|
def test_do_not_allow_forward_arguments_in_lambda_literals
|
|
expected = LambdaNode(
|
|
[:"..."],
|
|
Location(),
|
|
Location(),
|
|
Location(),
|
|
BlockParametersNode(ParametersNode([], [], [], nil, [], ForwardingParameterNode(), nil), [], Location(), Location()),
|
|
nil
|
|
)
|
|
|
|
assert_errors expected, "->(...) {}", [
|
|
["Unexpected ...", 3..6]
|
|
]
|
|
end
|
|
|
|
def test_do_not_allow_forward_arguments_in_blocks
|
|
expected = CallNode(
|
|
nil,
|
|
nil,
|
|
Location(),
|
|
nil,
|
|
nil,
|
|
nil,
|
|
BlockNode(
|
|
[:"..."],
|
|
BlockParametersNode(ParametersNode([], [], [], nil, [], ForwardingParameterNode(), nil), [], Location(), Location()),
|
|
nil,
|
|
Location(),
|
|
Location()
|
|
),
|
|
0,
|
|
"a"
|
|
)
|
|
|
|
assert_errors expected, "a {|...|}", [
|
|
["Unexpected ...", 4..7]
|
|
]
|
|
end
|
|
|
|
def test_dont_allow_return_inside_class_body
|
|
expected = ClassNode(
|
|
[],
|
|
Location(),
|
|
ConstantReadNode(),
|
|
nil,
|
|
nil,
|
|
StatementsNode([ReturnNode(Location(), nil)]),
|
|
Location(),
|
|
"A"
|
|
)
|
|
|
|
assert_errors expected, "class A; return; end", [
|
|
["Invalid return in class/module body", 15..16]
|
|
]
|
|
end
|
|
|
|
def test_dont_allow_return_inside_module_body
|
|
expected = ModuleNode(
|
|
[],
|
|
Location(),
|
|
ConstantReadNode(),
|
|
StatementsNode([ReturnNode(Location(), nil)]),
|
|
Location(),
|
|
"A"
|
|
)
|
|
|
|
assert_errors expected, "module A; return; end", [
|
|
["Invalid return in class/module body", 16..17]
|
|
]
|
|
end
|
|
|
|
def test_dont_allow_setting_to_back_and_nth_reference
|
|
expected = BeginNode(
|
|
Location(),
|
|
StatementsNode([
|
|
GlobalVariableWriteNode(Location(), Location(), NilNode()),
|
|
GlobalVariableWriteNode(Location(), Location(), NilNode())
|
|
]),
|
|
nil,
|
|
nil,
|
|
nil,
|
|
Location()
|
|
)
|
|
|
|
assert_errors expected, "begin\n$+ = nil\n$1466 = nil\nend", [
|
|
["Can't set variable", 6..8],
|
|
["Can't set variable", 15..20]
|
|
]
|
|
end
|
|
|
|
def test_duplicated_parameter_names
|
|
# For some reason, Ripper reports no error for Ruby 3.0 when you have
|
|
# duplicated parameter names for positional parameters.
|
|
unless RUBY_VERSION < "3.1.0"
|
|
expected = DefNode(
|
|
Location(),
|
|
nil,
|
|
ParametersNode([RequiredParameterNode(:a), RequiredParameterNode(:b), RequiredParameterNode(:a)], [], [], nil, [], nil, nil),
|
|
nil,
|
|
[:a, :b],
|
|
Location(),
|
|
nil,
|
|
Location(),
|
|
Location(),
|
|
nil,
|
|
Location()
|
|
)
|
|
|
|
assert_errors expected, "def foo(a,b,a);end", [
|
|
["Duplicated parameter name.", 12..13]
|
|
]
|
|
end
|
|
|
|
expected = DefNode(
|
|
Location(),
|
|
nil,
|
|
ParametersNode([RequiredParameterNode(:a), RequiredParameterNode(:b)], [], [], RestParameterNode(Location(), Location()), [], nil, nil),
|
|
nil,
|
|
[:a, :b],
|
|
Location(),
|
|
nil,
|
|
Location(),
|
|
Location(),
|
|
nil,
|
|
Location()
|
|
)
|
|
|
|
assert_errors expected, "def foo(a,b,*a);end", [
|
|
["Duplicated parameter name.", 13..14]
|
|
]
|
|
|
|
expected = DefNode(
|
|
Location(),
|
|
nil,
|
|
ParametersNode([RequiredParameterNode(:a), RequiredParameterNode(:b)], [], [], nil, [], KeywordRestParameterNode(Location(), Location()), nil),
|
|
nil,
|
|
[:a, :b],
|
|
Location(),
|
|
nil,
|
|
Location(),
|
|
Location(),
|
|
nil,
|
|
Location()
|
|
)
|
|
|
|
assert_errors expected, "def foo(a,b,**a);end", [
|
|
["Duplicated parameter name.", 14..15]
|
|
]
|
|
|
|
expected = DefNode(
|
|
Location(),
|
|
nil,
|
|
ParametersNode([RequiredParameterNode(:a), RequiredParameterNode(:b)], [], [], nil, [], nil, BlockParameterNode(Location(), Location())),
|
|
nil,
|
|
[:a, :b],
|
|
Location(),
|
|
nil,
|
|
Location(),
|
|
Location(),
|
|
nil,
|
|
Location()
|
|
)
|
|
|
|
assert_errors expected, "def foo(a,b,&a);end", [
|
|
["Duplicated parameter name.", 13..14]
|
|
]
|
|
|
|
expected = DefNode(
|
|
Location(),
|
|
nil,
|
|
ParametersNode([], [OptionalParameterNode(:a, Location(), Location(), IntegerNode())], [RequiredParameterNode(:b)], RestParameterNode(Location(), Location()), [], nil, nil),
|
|
nil,
|
|
[:a, :b, :c],
|
|
Location(),
|
|
nil,
|
|
Location(),
|
|
Location(),
|
|
nil,
|
|
Location()
|
|
)
|
|
|
|
assert_errors expected, "def foo(a = 1,b,*c);end", [["Unexpected parameter *", 16..17]]
|
|
end
|
|
|
|
private
|
|
|
|
def assert_errors(expected, source, errors)
|
|
# Ripper behaves differently on JRuby/TruffleRuby, so only check this on CRuby
|
|
assert_nil Ripper.sexp_raw(source) if RUBY_ENGINE == "ruby"
|
|
|
|
result = YARP.parse(source)
|
|
node = result.value.statements.body.last
|
|
|
|
assert_equal_nodes(expected, node, compare_location: false)
|
|
assert_equal(errors, result.errors.map { |e| [e.message, e.location.start_offset..e.location.end_offset] })
|
|
end
|
|
|
|
def assert_error_messages(source, errors)
|
|
assert_nil Ripper.sexp_raw(source)
|
|
result = YARP.parse(source)
|
|
assert_equal(errors, result.errors.map(&:message))
|
|
end
|
|
|
|
def expression(source)
|
|
YARP.parse(source).value.statements.body.last
|
|
end
|
|
end
|