diff --git a/spec/compiler/parser/parser_spec.cr b/spec/compiler/parser/parser_spec.cr index 8e4e0777b813..d1dfd594fdf2 100644 --- a/spec/compiler/parser/parser_spec.cr +++ b/spec/compiler/parser/parser_spec.cr @@ -253,6 +253,25 @@ module Crystal assert_syntax_error "foo { |(#{kw}))| }", "cannot use '#{kw}' as a block parameter name", 1, 9 end + # #10917 + %w( + bar? bar! + ).each do |name| + assert_syntax_error "def foo(#{name}); end", "invalid param name", 1, 14 + assert_syntax_error "def foo(foo #{name}); end", "invalid param name", 1, 17 + it_parses "def foo(#{name} foo); end", Def.new("foo", [Arg.new("foo", external_name: name.to_s)]) + + assert_syntax_error "macro foo(#{name}); end", "invalid param name", 1, 16 + assert_syntax_error "macro foo(foo #{name}); end", "invalid param name", 1, 19 + it_parses "macro foo(#{name} foo); end", Macro.new("foo", [Arg.new("foo", external_name: name.to_s)], body: MacroLiteral.new(" ")) + + it_parses "foo(#{name})", Call.new(nil, "foo", [name.call] of ASTNode) + it_parses "foo #{name}", Call.new(nil, "foo", [name.call] of ASTNode) + + assert_syntax_error "foo { |#{name})| }", "invalid param name", 1, 12 + assert_syntax_error "foo { |(#{name}))| }", "invalid param name", 1, 13 + end + it_parses "def self.foo\n1\nend", Def.new("foo", body: 1.int32, receiver: "self".var) it_parses "def self.foo()\n1\nend", Def.new("foo", body: 1.int32, receiver: "self".var) it_parses "def self.foo=\n1\nend", Def.new("foo=", body: 1.int32, receiver: "self".var) diff --git a/src/compiler/crystal/syntax/parser.cr b/src/compiler/crystal/syntax/parser.cr index 08a04342a27d..21d349ca6b59 100644 --- a/src/compiler/crystal/syntax/parser.cr +++ b/src/compiler/crystal/syntax/parser.cr @@ -3938,6 +3938,8 @@ module Crystal raise "when specified, external name must be different than internal name", @token end + check_valid_param_name(arg_name) + uses_arg = false do_next_token = true when .instance_var? @@ -4001,6 +4003,7 @@ module Crystal raise "cannot use '#{invalid_internal_name}' as a parameter name", invalid_internal_name end arg_name = external_name + check_valid_param_name(arg_name) else unexpected_token end @@ -4050,6 +4053,12 @@ module Crystal end end + def check_valid_param_name(param_name) + if param_name.ends_with?(/\?|!/) + raise "invalid param name" + end + end + def parse_if(check_end = true) location = @token.location @@ -4368,6 +4377,7 @@ module Crystal end arg_name = @token.value.to_s + check_valid_param_name(arg_name) if all_names.includes?(arg_name) raise "duplicated block parameter name: #{arg_name}", @token @@ -4389,6 +4399,7 @@ module Crystal end sub_arg_name = @token.value.to_s + check_valid_param_name(sub_arg_name) if all_names.includes?(sub_arg_name) raise "duplicated block parameter name: #{sub_arg_name}", @token