Skip to content

Commit

Permalink
+ ruby33.y: reject ambiguous anonymous arguments (#983)
Browse files Browse the repository at this point in the history
This commit tracks upstream commit ruby/ruby@a9f0961.
  • Loading branch information
iliabylich authored Jan 5, 2024
1 parent a80f672 commit f7bcf35
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 41 deletions.
85 changes: 44 additions & 41 deletions lib/parser/messages.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,47 +41,50 @@ module Parser
:triple_dot_at_eol => '... at EOL, should be parenthesized',

# Parser errors
:nth_ref_alias => 'cannot define an alias for a back-reference variable',
:begin_in_method => 'BEGIN in method',
:backref_assignment => 'cannot assign to a back-reference variable',
:invalid_assignment => 'cannot assign to a keyword',
:module_name_const => 'class or module name must be a constant literal',
:unexpected_token => 'unexpected token %{token}',
:argument_const => 'formal argument cannot be a constant',
:argument_ivar => 'formal argument cannot be an instance variable',
:argument_gvar => 'formal argument cannot be a global variable',
:argument_cvar => 'formal argument cannot be a class variable',
:duplicate_argument => 'duplicate argument name',
:empty_symbol => 'empty symbol literal',
:odd_hash => 'odd number of entries for a hash',
:singleton_literal => 'cannot define a singleton method for a literal',
:dynamic_const => 'dynamic constant assignment',
:const_reassignment => 'constant re-assignment',
:module_in_def => 'module definition in method body',
:class_in_def => 'class definition in method body',
:unexpected_percent_str => '%{type}: unknown type of percent-literal',
:block_and_blockarg => 'both block argument and literal block are passed',
:masgn_as_condition => 'multiple assignment in conditional context',
:block_given_to_yield => 'block given to yield',
:invalid_regexp => '%{message}',
:invalid_return => 'Invalid return in class/module body',
:csend_in_lhs_of_masgn => '&. inside multiple assignment destination',
:cant_assign_to_numparam => 'cannot assign to numbered parameter %{name}',
:reserved_for_numparam => '%{name} is reserved for numbered parameter',
:ordinary_param_defined => 'ordinary parameter is defined',
:numparam_used_in_outer_scope => 'numbered parameter is already used in an outer scope',
:circular_argument_reference => 'circular argument reference %{var_name}',
:pm_interp_in_var_name => 'symbol literal with interpolation is not allowed',
:lvar_name => "`%{name}' is not allowed as a local variable name",
:undefined_lvar => "no such local variable: `%{name}'",
:duplicate_variable_name => 'duplicate variable name %{name}',
:duplicate_pattern_key => 'duplicate hash pattern key %{name}',
:endless_setter => 'setter method cannot be defined in an endless method definition',
:invalid_id_to_get => 'identifier %{identifier} is not valid to get',
:forward_arg_after_restarg => '... after rest argument',
:no_anonymous_blockarg => 'no anonymous block parameter',
:no_anonymous_restarg => 'no anonymous rest parameter',
:no_anonymous_kwrestarg => 'no anonymous keyword rest parameter',
:nth_ref_alias => 'cannot define an alias for a back-reference variable',
:begin_in_method => 'BEGIN in method',
:backref_assignment => 'cannot assign to a back-reference variable',
:invalid_assignment => 'cannot assign to a keyword',
:module_name_const => 'class or module name must be a constant literal',
:unexpected_token => 'unexpected token %{token}',
:argument_const => 'formal argument cannot be a constant',
:argument_ivar => 'formal argument cannot be an instance variable',
:argument_gvar => 'formal argument cannot be a global variable',
:argument_cvar => 'formal argument cannot be a class variable',
:duplicate_argument => 'duplicate argument name',
:empty_symbol => 'empty symbol literal',
:odd_hash => 'odd number of entries for a hash',
:singleton_literal => 'cannot define a singleton method for a literal',
:dynamic_const => 'dynamic constant assignment',
:const_reassignment => 'constant re-assignment',
:module_in_def => 'module definition in method body',
:class_in_def => 'class definition in method body',
:unexpected_percent_str => '%{type}: unknown type of percent-literal',
:block_and_blockarg => 'both block argument and literal block are passed',
:masgn_as_condition => 'multiple assignment in conditional context',
:block_given_to_yield => 'block given to yield',
:invalid_regexp => '%{message}',
:invalid_return => 'Invalid return in class/module body',
:csend_in_lhs_of_masgn => '&. inside multiple assignment destination',
:cant_assign_to_numparam => 'cannot assign to numbered parameter %{name}',
:reserved_for_numparam => '%{name} is reserved for numbered parameter',
:ordinary_param_defined => 'ordinary parameter is defined',
:numparam_used_in_outer_scope => 'numbered parameter is already used in an outer scope',
:circular_argument_reference => 'circular argument reference %{var_name}',
:pm_interp_in_var_name => 'symbol literal with interpolation is not allowed',
:lvar_name => "`%{name}' is not allowed as a local variable name",
:undefined_lvar => "no such local variable: `%{name}'",
:duplicate_variable_name => 'duplicate variable name %{name}',
:duplicate_pattern_key => 'duplicate hash pattern key %{name}',
:endless_setter => 'setter method cannot be defined in an endless method definition',
:invalid_id_to_get => 'identifier %{identifier} is not valid to get',
:forward_arg_after_restarg => '... after rest argument',
:no_anonymous_blockarg => 'no anonymous block parameter',
:no_anonymous_restarg => 'no anonymous rest parameter',
:no_anonymous_kwrestarg => 'no anonymous keyword rest parameter',
:ambiguous_anonymous_restarg => 'anonymous rest parameter is also used within block',
:ambiguous_anonymous_kwrestarg => 'anonymous keyword rest parameter is also used within block',
:ambiguous_anonymous_blockarg => 'anonymous block parameter is also used within block',

# Parser warnings
:useless_else => 'else without rescue is useless',
Expand Down
15 changes: 15 additions & 0 deletions lib/parser/ruby33.y
Original file line number Diff line number Diff line change
Expand Up @@ -1100,6 +1100,11 @@ rule
diagnostic :error, :no_anonymous_blockarg, nil, val[0]
end
if @context.in_dynamic_block? && context.in_def &&
@static_env.declared_anonymous_blockarg? && @static_env.parent_has_anonymous_blockarg?
diagnostic :error, :ambiguous_anonymous_blockarg, nil, val[0]
end
result = @builder.block_pass(val[0], nil)
}
Expand Down Expand Up @@ -1136,6 +1141,11 @@ rule
diagnostic :error, :no_anonymous_restarg, nil, val[0]
end
if @context.in_dynamic_block? && context.in_def &&
@static_env.declared_anonymous_restarg? && @static_env.parent_has_anonymous_restarg?
diagnostic :error, :ambiguous_anonymous_restarg, nil, val[0]
end
result = [ @builder.forwarded_restarg(val[0]) ]
}
Expand Down Expand Up @@ -3048,6 +3058,11 @@ f_opt_paren_args: f_paren_args
diagnostic :error, :no_anonymous_kwrestarg, nil, val[0]
end
if @context.in_dynamic_block? && context.in_def &&
@static_env.declared_anonymous_kwrestarg? && @static_env.parent_has_anonymous_kwrestarg?
diagnostic :error, :ambiguous_anonymous_kwrestarg, nil, val[0]
end
result = @builder.forwarded_kwrestarg(val[0])
}
Expand Down
12 changes: 12 additions & 0 deletions lib/parser/static_environment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ def declared_anonymous_blockarg?
declared?(ANONYMOUS_BLOCKARG)
end

def parent_has_anonymous_blockarg?
@stack.any? { |variables| variables.include?(ANONYMOUS_BLOCKARG) }
end

def declare_anonymous_restarg
declare(ANONYMOUS_RESTARG)
end
Expand All @@ -71,6 +75,10 @@ def declared_anonymous_restarg?
declared?(ANONYMOUS_RESTARG)
end

def parent_has_anonymous_restarg?
@stack.any? { |variables| variables.include?(ANONYMOUS_RESTARG) }
end

def declare_anonymous_kwrestarg
declare(ANONYMOUS_KWRESTARG)
end
Expand All @@ -79,6 +87,10 @@ def declared_anonymous_kwrestarg?
declared?(ANONYMOUS_KWRESTARG)
end

def parent_has_anonymous_kwrestarg?
@stack.any? { |variables| variables.include?(ANONYMOUS_KWRESTARG) }
end

def empty?
@stack.empty?
end
Expand Down
48 changes: 48 additions & 0 deletions test/test_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11440,4 +11440,52 @@ def test_it_warning_in_33
'it = 1; 0.times { it }',
ALL_VERSIONS)
end

def test_anonymous_params_in_nested_scopes
assert_diagnoses(
[:error, :ambiguous_anonymous_blockarg, {}],
'def b(&) ->(&) {c(&)} end',
' ^ location',
SINCE_3_3)
assert_diagnoses(
[:error, :ambiguous_anonymous_restarg, {}],
'def b(*) ->(*) {c(*)} end',
' ^ location',
SINCE_3_3)
assert_diagnoses(
[:error, :ambiguous_anonymous_restarg, {}],
'def b(a, *) ->(*) {c(1, *)} end',
' ^ location',
SINCE_3_3)
assert_diagnoses(
[:error, :ambiguous_anonymous_restarg, {}],
'def b(*) ->(a, *) {c(*)} end',
' ^ location',
SINCE_3_3)
assert_diagnoses(
[:error, :ambiguous_anonymous_kwrestarg, {}],
'def b(**) ->(**) {c(**)} end',
' ^^ location',
SINCE_3_3)
assert_diagnoses(
[:error, :ambiguous_anonymous_kwrestarg, {}],
'def b(k:, **) ->(**) {c(k: 1, **)} end',
' ^^ location',
SINCE_3_3)
assert_diagnoses(
[:error, :ambiguous_anonymous_kwrestarg, {}],
'def b(**) ->(k:, **) {c(**)} end',
' ^^ location',
SINCE_3_3)

refute_diagnoses(
'def b(&) ->(&) {c()} end',
SINCE_3_3)
refute_diagnoses(
'def b(*) ->(*) {c()} end',
SINCE_3_3)
refute_diagnoses(
'def b(**) ->(**) {c()} end',
SINCE_3_3)
end
end

0 comments on commit f7bcf35

Please sign in to comment.