-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix: Disallow setters with multiple arguments #6324
Fix: Disallow setters with multiple arguments #6324
Conversation
@@ -3344,6 +3347,10 @@ module Crystal | |||
index += 1 | |||
end | |||
|
|||
if is_setter && args.size > 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't is_setter
be replaced by name.ends_with?('=')
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh yeah! good catch
Also check named args, no splat, etc. |
@asterite added some checks for named args, splat, kwargs, and then also block args, since there's a similar issue calling those as well |
@@ -3332,6 +3333,7 @@ module Crystal | |||
if block_arg = extras.block_arg | |||
compute_block_arg_yields block_arg | |||
check :")" | |||
found_block = true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
!block_arg.nil? == found_block
So the found_block
var can be removed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, but I also see found_splat
, etc., so it's fine to keep this variable 👍
spec/compiler/parser/parser_spec.cr
Outdated
@@ -174,6 +174,13 @@ module Crystal | |||
assert_syntax_error "def foo!=; end", "unexpected token: !=" | |||
assert_syntax_error "def foo?=(x); end", "unexpected token: ?" | |||
|
|||
# 5856 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
# #5856
spec/compiler/parser/parser_spec.cr
Outdated
@@ -174,6 +174,13 @@ module Crystal | |||
assert_syntax_error "def foo!=; end", "unexpected token: !=" | |||
assert_syntax_error "def foo?=(x); end", "unexpected token: ?" | |||
|
|||
# 5856 | |||
assert_syntax_error "def foo=(a,b); end", "setter method 'foo=' cannot receive more than one argument" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/receive/have/
. "receive" is more associated to the call site. Same for the others. Just my opinion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
seems reasonable. have
is a better hint at something static too.
@@ -3344,6 +3346,14 @@ module Crystal | |||
index += 1 | |||
end | |||
|
|||
if name.ends_with?('=') && !name.ends_with?("[]=") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The second test can be name == "[]="
. There can't be anything before []=
in a method name.
However, is there a particular reason why []=
can be called like any method but all other setters can't?
If not, the same rules should apply (with args.size != 2
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With []=
you can do var[1, 2] = 3
to implement a matrix type for example
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, that's a great use case. You can even do var[1, foo: 2] = 3
👀
But it still doesn't explain foo.[]=(foo) do |baz| end
😃
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
doh! I'll switch that to an equality check on name
. @bew's totally right on with the []=
with multi-arg case. There's at least one place in stdlib where []=
is given splat args and kwargs too.
Good catch on the block case for []=
. I'll address that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
crystal/src/compiler/crystal/syntax/parser.cr
Lines 649 to 653 in 8d28cbb
# If we have `f.x=(exp1).a.b.c`, consider it the same as `f.x = (exp1).a.b.c` | |
# and not as `(f.x = exp1).a.b.c` because a difference in space | |
# should not make a difference in semantic (#4399) | |
# The only exception is doing a splat, in which case this can only | |
# be expanded arguments for the call. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And because (exp1, exp2)
is not a valid syntax, f.x=(exp1, exp2)
is not a valid syntax either, so there's no way to pass multiple arguments there. It's the same as in Ruby (I copied it from Ruby, actually).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And the exception for splat mentioned there maybe should be removed (it doesn't work in Ruby, so...)
|
@maxfierke |
To clarify: Currently, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you @maxfierke 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't like the special treatment of []=
regarding blocks.
Where are setters with blocks used in the stdlib? Maybe it is better to disallow blocks in setters.
So far the only difference of []=
with respect other setters is that []=
allows multiple arguments because of multiple indices. m[i, j] = v
@bcardiff Are you referring to the changes in src/object.cr? blocks are disallowed for all setters with this PR. The special case here for |
Oh, I missed that it was inside a But I am still missing why there is an |
@bcardiff ahhh! I assume you're referring to here. That is to disallow calls to |
Ahhh! The parsing snippet is because |
Fixes #5856 by throwing a syntax error when defining a setter method that accepts more than one argument, seeing as there is no way to call it.