diff options
Diffstat (limited to 'test/prism/result')
| -rw-r--r-- | test/prism/result/breadth_first_search_test.rb | 11 | ||||
| -rw-r--r-- | test/prism/result/continuable_test.rb | 124 | ||||
| -rw-r--r-- | test/prism/result/error_recovery_test.rb | 237 | ||||
| -rw-r--r-- | test/prism/result/named_capture_test.rb | 29 | ||||
| -rw-r--r-- | test/prism/result/numeric_value_test.rb | 11 | ||||
| -rw-r--r-- | test/prism/result/overlap_test.rb | 9 | ||||
| -rw-r--r-- | test/prism/result/source_location_test.rb | 16 | ||||
| -rw-r--r-- | test/prism/result/string_test.rb | 32 | ||||
| -rw-r--r-- | test/prism/result/warnings_test.rb | 120 |
9 files changed, 543 insertions, 46 deletions
diff --git a/test/prism/result/breadth_first_search_test.rb b/test/prism/result/breadth_first_search_test.rb index e2e043a902..7e7962f172 100644 --- a/test/prism/result/breadth_first_search_test.rb +++ b/test/prism/result/breadth_first_search_test.rb @@ -14,5 +14,16 @@ module Prism refute_nil found assert_equal 8, found.start_offset end + + def test_breadth_first_search_all + result = Prism.parse("[1 + 2, 2]") + found_nodes = + result.value.breadth_first_search_all do |node| + node.is_a?(IntegerNode) + end + + assert_equal 3, found_nodes.size + assert_equal 8, found_nodes[0].start_offset + end end end diff --git a/test/prism/result/continuable_test.rb b/test/prism/result/continuable_test.rb new file mode 100644 index 0000000000..3533552167 --- /dev/null +++ b/test/prism/result/continuable_test.rb @@ -0,0 +1,124 @@ +# frozen_string_literal: true + +require_relative "../test_helper" + +module Prism + class ContinuableTest < TestCase + def test_valid_input + # Valid input is not continuable (nothing to continue). + refute_predicate Prism.parse("1 + 1"), :continuable? + refute_predicate Prism.parse(""), :continuable? + end + + def test_stray_closing_tokens + # Stray closing tokens make input non-continuable regardless of what + # follows (matches the feature-request examples exactly). + refute_predicate Prism.parse("1 + ]"), :continuable? + refute_predicate Prism.parse("end.tap do"), :continuable? + + # A mix: stray end plus an unclosed block is not continuable because the + # stray end cannot be fixed by appending more input. + refute_predicate Prism.parse("end\ntap do"), :continuable? + end + + def test_unclosed_constructs + # Unclosed constructs are continuable. + assert_predicate Prism.parse("1 + ["), :continuable? + assert_predicate Prism.parse("tap do"), :continuable? + end + + def test_unclosed_keywords + assert_predicate Prism.parse("def foo"), :continuable? + assert_predicate Prism.parse("class Foo"), :continuable? + assert_predicate Prism.parse("module Foo"), :continuable? + assert_predicate Prism.parse("if true"), :continuable? + assert_predicate Prism.parse("while true"), :continuable? + assert_predicate Prism.parse("begin"), :continuable? + assert_predicate Prism.parse("for x in [1]"), :continuable? + end + + def test_unclosed_delimiters + assert_predicate Prism.parse("{"), :continuable? + assert_predicate Prism.parse("foo("), :continuable? + assert_predicate Prism.parse('"hello'), :continuable? + assert_predicate Prism.parse("'hello"), :continuable? + assert_predicate Prism.parse("<<~HEREDOC\nhello"), :continuable? + end + + def test_trailing_whitespace + # Trailing whitespace or newlines should not affect continuability. + assert_predicate Prism.parse("class A\n"), :continuable? + assert_predicate Prism.parse("def f "), :continuable? + assert_predicate Prism.parse("def f\n"), :continuable? + assert_predicate Prism.parse("def f\n "), :continuable? + assert_predicate Prism.parse("( "), :continuable? + assert_predicate Prism.parse("(\n"), :continuable? + assert_predicate Prism.parse("1 +\n"), :continuable? + end + + def test_incomplete_expressions + assert_predicate Prism.parse("-"), :continuable? + assert_predicate Prism.parse("[1,"), :continuable? + assert_predicate Prism.parse("f arg1,"), :continuable? + assert_predicate Prism.parse("def f ="), :continuable? + assert_predicate Prism.parse("def $a"), :continuable? + assert_predicate Prism.parse("a ="), :continuable? + assert_predicate Prism.parse("a,b"), :continuable? + end + + def test_modifier_keywords + assert_predicate Prism.parse("return if"), :continuable? + assert_predicate Prism.parse("return unless"), :continuable? + assert_predicate Prism.parse("while"), :continuable? + assert_predicate Prism.parse("until"), :continuable? + end + + def test_ternary_operator + assert_predicate Prism.parse("x ?"), :continuable? + assert_predicate Prism.parse("x ? y :"), :continuable? + end + + def test_class_with_superclass + assert_predicate Prism.parse("class Foo <"), :continuable? + end + + def test_keyword_expressions + assert_predicate Prism.parse("not"), :continuable? + assert_predicate Prism.parse("defined?"), :continuable? + assert_predicate Prism.parse("module"), :continuable? + end + + def test_for_loops + assert_predicate Prism.parse("for"), :continuable? + assert_predicate Prism.parse("for x in"), :continuable? + end + + def test_pattern_matching + assert_predicate Prism.parse("foo => ["), :continuable? + assert_predicate Prism.parse("case foo; when"), :continuable? + end + + def test_splat_and_block_pass + assert_predicate Prism.parse("[*"), :continuable? + assert_predicate Prism.parse("f(**"), :continuable? + assert_predicate Prism.parse("f(&"), :continuable? + end + + def test_default_parameter_value + assert_predicate Prism.parse("def f(x ="), :continuable? + end + + def test_line_continuation + assert_predicate Prism.parse("1 +\\"), :continuable? + assert_predicate Prism.parse("\"foo\" \\"), :continuable? + end + + def test_embedded_document + # Embedded document (=begin) truncated at various points. + assert_predicate Prism.parse("=b"), :continuable? + assert_predicate Prism.parse("=beg"), :continuable? + assert_predicate Prism.parse("=begin"), :continuable? + assert_predicate Prism.parse("foo\n=b"), :continuable? + end + end +end diff --git a/test/prism/result/error_recovery_test.rb b/test/prism/result/error_recovery_test.rb new file mode 100644 index 0000000000..d07c858d1b --- /dev/null +++ b/test/prism/result/error_recovery_test.rb @@ -0,0 +1,237 @@ +# frozen_string_literal: true + +require_relative "../test_helper" + +module Prism + class ErrorRecoveryTest < TestCase + def test_alias_global_variable_node_old_name_symbol + result = Prism.parse("alias $a b") + refute result.success? + + node = result.value.statements.body.first + assert_kind_of ErrorRecoveryNode, node.old_name + assert_kind_of SymbolNode, node.old_name.unexpected + end + + def test_alias_global_variable_node_old_name_missing + result = Prism.parse("alias $a 42") + refute result.success? + + node = result.value.statements.body.first + assert_kind_of ErrorRecoveryNode, node.old_name + assert_nil node.old_name.unexpected + end + + def test_alias_method_node_old_name_global_variable + result = Prism.parse("alias a $b") + refute result.success? + + node = result.value.statements.body.first + assert_kind_of ErrorRecoveryNode, node.old_name + assert_kind_of GlobalVariableReadNode, node.old_name.unexpected + end + + def test_alias_method_node_old_name_missing + result = Prism.parse("alias a 42") + refute result.success? + + node = result.value.statements.body.first + assert_kind_of ErrorRecoveryNode, node.old_name + assert_nil node.old_name.unexpected + end + + def test_class_node_constant_path_call + result = Prism.parse("class 0.X; end") + refute result.success? + + node = result.value.statements.body.first + assert_kind_of ErrorRecoveryNode, node.constant_path + assert_kind_of CallNode, node.constant_path.unexpected + end + + def test_for_node_index_back_reference + result = Prism.parse("for $& in a; end") + refute result.success? + + node = result.value.statements.body.first + assert_kind_of ErrorRecoveryNode, node.index + assert_kind_of BackReferenceReadNode, node.index.unexpected + end + + def test_for_node_index_numbered_reference + result = Prism.parse("for $1 in a; end") + refute result.success? + + node = result.value.statements.body.first + assert_kind_of ErrorRecoveryNode, node.index + assert_kind_of NumberedReferenceReadNode, node.index.unexpected + end + + def test_for_node_index_missing + result = Prism.parse("for in 1..10; end") + refute result.success? + + node = result.value.statements.body.first + assert_kind_of ErrorRecoveryNode, node.index + assert_nil node.index.unexpected + end + + def test_interpolated_string_node_parts_xstring + result = Prism.parse("<<~`FOO` \"bar\"\nls\nFOO\n") + refute result.success? + + node = result.value.statements.body.first + assert node.parts.any? { |part| part.is_a?(ErrorRecoveryNode) && part.unexpected.is_a?(XStringNode) } + end + + def test_interpolated_string_node_parts_interpolated_xstring + result = Prism.parse("<<~`FOO` \"bar\"\n\#{ls}\nFOO\n") + refute result.success? + + node = result.value.statements.body.first + assert node.parts.any? { |part| part.is_a?(ErrorRecoveryNode) && part.unexpected.is_a?(InterpolatedXStringNode) } + end + + def test_module_node_constant_path_def + result = Prism.parse("module def foo; end") + refute result.success? + + node = result.value.statements.body.first + assert_kind_of ErrorRecoveryNode, node.constant_path + assert_kind_of DefNode, node.constant_path.unexpected + end + + def test_module_node_constant_path_missing + result = Prism.parse("module Parent module end") + refute result.success? + + node = result.value.statements.body.first.body.body.first + assert_kind_of ErrorRecoveryNode, node.constant_path + assert_nil node.constant_path.unexpected + end + + def test_multi_target_node_lefts_back_reference + result = Prism.parse("a, (b, $&) = z") + refute result.success? + + node = result.value.statements.body.first.lefts.last + assert node.lefts.any? { |left| left.is_a?(ErrorRecoveryNode) && left.unexpected.is_a?(BackReferenceReadNode) } + end + + def test_multi_target_node_lefts_numbered_reference + result = Prism.parse("a, (b, $1) = z") + refute result.success? + + node = result.value.statements.body.first.lefts.last + assert node.lefts.any? { |left| left.is_a?(ErrorRecoveryNode) && left.unexpected.is_a?(NumberedReferenceReadNode) } + end + + def test_multi_target_node_rights_back_reference + result = Prism.parse("a, (*, $&) = z") + refute result.success? + + node = result.value.statements.body.first.lefts.last + assert node.rights.any? { |right| right.is_a?(ErrorRecoveryNode) && right.unexpected.is_a?(BackReferenceReadNode) } + end + + def test_multi_target_node_rights_numbered_reference + result = Prism.parse("a, (*, $1) = z") + refute result.success? + + node = result.value.statements.body.first.lefts.last + assert node.rights.any? { |right| right.is_a?(ErrorRecoveryNode) && right.unexpected.is_a?(NumberedReferenceReadNode) } + end + + def test_multi_write_node_lefts_back_reference + result = Prism.parse("$&, = z") + refute result.success? + + node = result.value.statements.body.first + assert node.lefts.any? { |left| left.is_a?(ErrorRecoveryNode) && left.unexpected.is_a?(BackReferenceReadNode) } + end + + def test_multi_write_node_lefts_numbered_reference + result = Prism.parse("$1, = z") + refute result.success? + + node = result.value.statements.body.first + assert node.lefts.any? { |left| left.is_a?(ErrorRecoveryNode) && left.unexpected.is_a?(NumberedReferenceReadNode) } + end + + def test_multi_write_node_rights_back_reference + result = Prism.parse("*, $& = z") + refute result.success? + + node = result.value.statements.body.first + assert node.rights.any? { |right| right.is_a?(ErrorRecoveryNode) && right.unexpected.is_a?(BackReferenceReadNode) } + end + + def test_multi_write_node_rights_numbered_reference + result = Prism.parse("*, $1 = z") + refute result.success? + + node = result.value.statements.body.first + assert node.rights.any? { |right| right.is_a?(ErrorRecoveryNode) && right.unexpected.is_a?(NumberedReferenceReadNode) } + end + + def test_parameters_node_posts_keyword_rest + result = Prism.parse("def f(**kwargs, ...); end") + refute result.success? + + node = result.value.statements.body.first.parameters + assert node.posts.any? { |post| post.is_a?(ErrorRecoveryNode) && post.unexpected.is_a?(KeywordRestParameterNode) } + end + + def test_parameters_node_posts_no_keywords + result = Prism.parse("def f(**nil, ...); end") + refute result.success? + + node = result.value.statements.body.first.parameters + assert node.posts.any? { |post| post.is_a?(ErrorRecoveryNode) && post.unexpected.is_a?(NoKeywordsParameterNode) } + end + + def test_parameters_node_posts_forwarding + result = Prism.parse("def f(..., ...); end") + refute result.success? + + node = result.value.statements.body.first.parameters + assert node.posts.any? { |post| post.is_a?(ErrorRecoveryNode) && post.unexpected.is_a?(ForwardingParameterNode) } + end + + def test_pinned_variable_node_variable_missing + result = Prism.parse("foo in ^Bar") + refute result.success? + + node = result.value.statements.body.first.pattern + assert_kind_of ErrorRecoveryNode, node.variable + assert_nil node.variable.unexpected + end + + def test_rescue_node_reference_back_reference + result = Prism.parse("begin; rescue => $&; end") + refute result.success? + + node = result.value.statements.body.first.rescue_clause + assert_kind_of ErrorRecoveryNode, node.reference + assert_kind_of BackReferenceReadNode, node.reference.unexpected + end + + def test_rescue_node_reference_numbered_reference + result = Prism.parse("begin; rescue => $1; end") + refute result.success? + + node = result.value.statements.body.first.rescue_clause + assert_kind_of ErrorRecoveryNode, node.reference + assert_kind_of NumberedReferenceReadNode, node.reference.unexpected + end + + def test_rescue_node_reference_missing + result = Prism.parse("begin; rescue =>; end") + refute result.success? + + node = result.value.statements.body.first.rescue_clause + assert_kind_of ErrorRecoveryNode, node.reference + assert_nil node.reference.unexpected + end + end +end diff --git a/test/prism/result/named_capture_test.rb b/test/prism/result/named_capture_test.rb new file mode 100644 index 0000000000..36cb910899 --- /dev/null +++ b/test/prism/result/named_capture_test.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +require_relative "../test_helper" + +module Prism + class NamedCaptureTest < TestCase + def test_hex_escapes + assert_equal :😀, parse_name("\\xf0\\x9f\\x98\\x80") + end + + def test_unicode_escape + assert_equal :し, parse_name("\\u3057") + end + + def test_unicode_escapes_bracess + assert_equal :😀, parse_name("\\u{1f600}") + end + + def test_octal_escapes + assert_equal :😀, parse_name("\\xf0\\x9f\\x98\\200") + end + + private + + def parse_name(content) + Prism.parse_statement("/(?<#{content}>)/ =~ ''").targets.first.name + end + end +end diff --git a/test/prism/result/numeric_value_test.rb b/test/prism/result/numeric_value_test.rb index 5c89230a1f..0207fa6a86 100644 --- a/test/prism/result/numeric_value_test.rb +++ b/test/prism/result/numeric_value_test.rb @@ -6,16 +6,27 @@ module Prism class NumericValueTest < TestCase def test_numeric_value assert_equal 123, Prism.parse_statement("123").value + assert_equal 123, Prism.parse_statement("1_23").value assert_equal 3.14, Prism.parse_statement("3.14").value + assert_equal 3.14, Prism.parse_statement("3.1_4").value assert_equal 42i, Prism.parse_statement("42i").value + assert_equal 42i, Prism.parse_statement("4_2i").value assert_equal 42.1ri, Prism.parse_statement("42.1ri").value + assert_equal 42.1ri, Prism.parse_statement("42.1_0ri").value assert_equal 3.14i, Prism.parse_statement("3.14i").value + assert_equal 3.14i, Prism.parse_statement("3.1_4i").value assert_equal 42r, Prism.parse_statement("42r").value + assert_equal 42r, Prism.parse_statement("4_2r").value assert_equal 0.5r, Prism.parse_statement("0.5r").value + assert_equal 0.5r, Prism.parse_statement("0.5_0r").value assert_equal 42ri, Prism.parse_statement("42ri").value + assert_equal 42ri, Prism.parse_statement("4_2ri").value assert_equal 0.5ri, Prism.parse_statement("0.5ri").value + assert_equal 0.5ri, Prism.parse_statement("0.5_0ri").value assert_equal 0xFFr, Prism.parse_statement("0xFFr").value + assert_equal 0xFFr, Prism.parse_statement("0xF_Fr").value assert_equal 0xFFri, Prism.parse_statement("0xFFri").value + assert_equal 0xFFri, Prism.parse_statement("0xF_Fri").value end end end diff --git a/test/prism/result/overlap_test.rb b/test/prism/result/overlap_test.rb index 155bc870d3..d605eeca44 100644 --- a/test/prism/result/overlap_test.rb +++ b/test/prism/result/overlap_test.rb @@ -33,8 +33,13 @@ module Prism queue << child if compare - assert_operator current.location.start_offset, :<=, child.location.start_offset - assert_operator current.location.end_offset, :>=, child.location.end_offset + assert_operator current.location.start_offset, :<=, child.location.start_offset, -> { + "[#{fixture.full_path}] Parent node #{current.class} at #{current.location} does not start before child node #{child.class} at #{child.location}" + } + + assert_operator current.location.end_offset, :>=, child.location.end_offset, -> { + "[#{fixture.full_path}] Parent node #{current.class} at #{current.location} does not end after child node #{child.class} at #{child.location}" + } end end end diff --git a/test/prism/result/source_location_test.rb b/test/prism/result/source_location_test.rb index 7bdc707658..a8d27b95a8 100644 --- a/test/prism/result/source_location_test.rb +++ b/test/prism/result/source_location_test.rb @@ -13,7 +13,7 @@ module Prism end def test_AlternationPatternNode - assert_location(AlternationPatternNode, "foo => bar | baz", 7...16, &:pattern) + assert_location(AlternationPatternNode, "foo => 0 | 1", 7...12, &:pattern) end def test_AndNode @@ -650,6 +650,10 @@ module Prism assert_location(NilNode, "nil") end + def test_NoBlockParameterNode + assert_location(NoBlockParameterNode, "def foo(&nil); end", 8...12) { |node| node.parameters.block } + end + def test_NoKeywordsParameterNode assert_location(NoKeywordsParameterNode, "def foo(**nil); end", 8...13) { |node| node.parameters.keyword_rest } end @@ -920,7 +924,7 @@ module Prism end def test_all_tested - expected = Prism.constants.grep(/.Node$/).sort - %i[MissingNode ProgramNode] + expected = Prism.constants.grep(/.Node$/).sort - %i[ErrorRecoveryNode ProgramNode] actual = SourceLocationTest.instance_methods(false).grep(/.Node$/).map { |name| name[5..].to_sym }.sort assert_equal expected, actual end @@ -935,16 +939,16 @@ module Prism node = yield node if block_given? if expected.begin == 0 - assert_equal 0, node.location.start_column + assert_equal 0, node.location.start_column, "#{kind} start_column" end if expected.end == source.length - assert_equal source.split("\n").last.length, node.location.end_column + assert_equal source.split("\n").last.length, node.location.end_column, "#{kind} end_column" end assert_kind_of kind, node - assert_equal expected.begin, node.location.start_offset - assert_equal expected.end, node.location.end_offset + assert_equal expected.begin, node.location.start_offset, "#{kind} start_offset" + assert_equal expected.end, node.location.end_offset, "#{kind} end_offset" end end end diff --git a/test/prism/result/string_test.rb b/test/prism/result/string_test.rb new file mode 100644 index 0000000000..48c7f592eb --- /dev/null +++ b/test/prism/result/string_test.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +require_relative "../test_helper" + +module Prism + class StringTest < TestCase + def test_regular_expression_node_unescaped_frozen + node = Prism.parse_statement("/foo/") + assert_predicate node.unescaped, :frozen? + end + + def test_source_file_node_filepath_frozen + node = Prism.parse_statement("__FILE__") + assert_predicate node.filepath, :frozen? + end + + def test_string_node_unescaped_frozen + node = Prism.parse_statement('"foo"') + assert_predicate node.unescaped, :frozen? + end + + def test_symbol_node_unescaped_frozen + node = Prism.parse_statement(":foo") + assert_predicate node.unescaped, :frozen? + end + + def test_xstring_node_unescaped_frozen + node = Prism.parse_statement("`foo`") + assert_predicate node.unescaped, :frozen? + end + end +end diff --git a/test/prism/result/warnings_test.rb b/test/prism/result/warnings_test.rb index aeac7f80e6..27f1119b98 100644 --- a/test/prism/result/warnings_test.rb +++ b/test/prism/result/warnings_test.rb @@ -22,6 +22,15 @@ module Prism assert_warning("a /b/", "wrap regexp in parentheses") end + def test_ambiguous_ampersand + assert_warning("a &b", "argument prefix") + assert_warning("a &:+", "argument prefix") + + refute_warning("a &:b") + refute_warning("a &:'b'") + refute_warning("a &:\"b\"") + end + def test_binary_operator [ [:**, "argument prefix"], @@ -115,6 +124,9 @@ module Prism assert_warning("case 1; when 2\n end", "mismatched indentations at 'end' with 'case'") assert_warning("case 1; in 2\n end", "mismatched indentations at 'end' with 'case'") + assert_warning(" case 1\nwhen 2\n end", "mismatched indentations at 'when' with 'case'") + refute_warning("case 1\n when 2\n when 3\nend") # case/when allows more indentation + assert_warning("-> {\n }", "mismatched indentations at '}' with '->'") assert_warning("-> do\n end", "mismatched indentations at 'end' with '->'") assert_warning("-> do\n rescue\nend", "mismatched indentations at 'rescue' with '->'") @@ -174,6 +186,10 @@ module Prism assert_warning("if true\nelsif\nfalse; end", "end of line") end + def test_numbered_reference + assert_warning("_ = _ = $999999999999999999999", "too big for a number variable, always nil") + end + def test_shareable_constant_value assert_warning("foo # shareable_constant_value: none", "ignored") assert_warning("\v # shareable_constant_value: none", "ignored") @@ -214,6 +230,8 @@ module Prism refute_warning("foo = 1", compare: false, command_line: "e") refute_warning("foo = 1", compare: false, scopes: [[]]) + refute_warning("foo(bar = 1)") + assert_warning("def foo; bar = 1; end", "unused") assert_warning("def foo; bar, = 1; end", "unused") @@ -243,6 +261,25 @@ module Prism refute_warning("def foo; bar = 1; tap { bar }; end") refute_warning("def foo; bar = 1; tap { baz = bar; baz }; end") + + refute_warning("def foo; bar = 1; end", line: -2, compare: false) + end + + def test_unused_local_variable_or_assign_with_begin_node + assert_warning(<<~RUBY, "assigned but unused variable - foo", compare: false) + var ||= begin + foo = bar + baz + end + RUBY + + assert_warning(<<~RUBY, "assigned but unused variable - foo", compare: false) + foo = false + var ||= begin + foo = true + bar + end + RUBY end def test_void_statements @@ -321,42 +358,49 @@ module Prism assert_warning("tap { redo; foo }", "statement not reached") end - def test_shebang_ending_with_carriage_return - msg = "shebang line ending with \\r may cause problems" - - assert_warning(<<~RUBY, msg, compare: false) - #!ruby\r - p(123) - RUBY - - assert_warning(<<~RUBY, msg, compare: false) - #!ruby \r - p(123) - RUBY - - assert_warning(<<~RUBY, msg, compare: false) - #!ruby -Eutf-8\r - p(123) - RUBY - - # Used with the `-x` object, to ignore the script up until the first shebang that mentioned "ruby". - assert_warning(<<~SCRIPT, msg, compare: false) - #!/usr/bin/env bash - # Some initial shell script or other content - # that Ruby should ignore - echo "This is shell script part" - exit 0 - - #! /usr/bin/env ruby -Eutf-8\r - # Ruby script starts here - puts "Hello from Ruby!" - SCRIPT - - refute_warning("#ruby not_a_shebang\r\n", compare: false) - - # CRuby doesn't emit the warning if a malformed file only has `\r` and not `\n`. - # https://bugs.ruby-lang.org/issues/20700 - refute_warning("#!ruby\r", compare: false) + if windows? + def test_shebang_ending_with_carriage_return + refute_warning("#!ruby\r\np(123)\n", compare: false) + end + else + def test_shebang_ending_with_carriage_return + msg = "shebang line ending with \\r may cause problems" + + assert_warning(<<~RUBY, msg, compare: false, main_script: true) + #!ruby\r + p(123) + RUBY + + assert_warning(<<~RUBY, msg, compare: false, main_script: true) + #!ruby \r + p(123) + RUBY + + assert_warning(<<~RUBY, msg, compare: false, main_script: true) + #!ruby -Eutf-8\r + p(123) + RUBY + + # Used with the `-x` object, to ignore the script up until the first + # shebang that mentioned "ruby". + assert_warning(<<~SCRIPT, msg, compare: false, main_script: true) + #!/usr/bin/env bash + # Some initial shell script or other content + # that Ruby should ignore + echo "This is shell script part" + exit 0 + + #! /usr/bin/env ruby -Eutf-8\r + # Ruby script starts here + puts "Hello from Ruby!" + SCRIPT + + refute_warning("#ruby not_a_shebang\r\n", compare: false, main_script: true) + + # CRuby doesn't emit the warning if a malformed file only has `\r` and + # not `\n`. https://bugs.ruby-lang.org/issues/20700. + refute_warning("#!ruby\r", compare: false, main_script: true) + end end def test_warnings_verbosity @@ -371,8 +415,8 @@ module Prism private - def assert_warning(source, *messages, compare: true) - warnings = Prism.parse(source).warnings + def assert_warning(source, *messages, compare: true, **options) + warnings = Prism.parse(source, **options).warnings assert_equal messages.length, warnings.length, "Expected #{messages.length} warning(s) in #{source.inspect}, got #{warnings.map(&:message).inspect}" warnings.zip(messages).each do |warning, message| |
