Skip to content
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

Add MasgnNode class for masgn nodes #203

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

dvandersluis
Copy link
Member

Follows #201. I actually ended up needing masgn to have its own class with expression defined in order to do the refactoring I wanted to, so this adds it.

Because masgn nodes are more involved than the other assignment types, this class has a different interface than the other assignments, except for expression which works the same. Rather than name that the other assignment classes have, MasgnNode has names (since there will inherently be more than one). It also is able to handle nested mlhses.

@dvandersluis
Copy link
Member Author

Sorry I missed this in my previous PR @marcandre 😅 🙌

Copy link
Contributor

@marcandre marcandre left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

masgn is complicated, isn't it? 😅

PR looks like a good start, but you seem to have overlooked 2 important cases: foo.bar, foo[:baz], ... = ....
Please describe results of your proposed methods and add tests.

Also:

  • each_descendant should only be used on the first child. Please add a test for x, y = 1, z+=2 for example. Even then, some corner cases could be problematic: foo[x+=1], bar = ...

  • I doubt each_assignment is necessary. Performance of enumerators is typically quite poor, especially for short lists

@dvandersluis
Copy link
Member Author

I knew there were things I wasn't thinking of, thanks @marcandre I'll fix up for those cases!

@dvandersluis
Copy link
Member Author

dvandersluis commented Aug 13, 2021

@marcandre updated now.

I have now introduced a MlhsNode as well, which the assignments logic has been moved to (and each_assignment has been removed). MlhsNode#assignments takes care of finding nested assignments (in splats or other mlhs nodes), and also now returns send nodes to cover your edge cases. I also have a tests covering casgn nodes now, which were previously missing.

MasgnNode#names is now able to handle send nodes as well (by just returning the source, I don't think there's a better solution for foo.bar or foo[:bar], etc.).

I also added values and array? to make it easier to process the values in the expression.

For symmetry, MasgnNode now has lhs (returns the mlhs node) and rhs (alias of expression) methods.

Please let me know your thoughts!

@dvandersluis
Copy link
Member Author

The "modern" spec was failing on indexasgn nodes so that's handled now too.

context 'with nested assignment on LHS' do
let(:source) { 'a, b[c+=1] = z' }

it { is_expected.to eq([:a, 'b[c+=1]']) }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have a use for 'b[c+=1]'? It seems like a bad result for a name, and string vs symbol is a bit ugly. What about :"b[]" instead?

context 'with a method chain on LHS' do
let(:source) { 'a, b.c = z' }

it { is_expected.to eq([:a, 'b.c']) }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same with 'b.c'. How about :c= or :"#c"?

it { is_expected.to eq(%i[a b c]) }
end

context 'with nested assignment on LHS' do
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrong description

Comment on lines +44 to +70
context 'with variables' do
it { is_expected.to eq(s(:send, nil, :z)) }
end

context 'with a LHS splat' do
let(:source) { 'x, *y = z' }

it { is_expected.to eq(s(:send, nil, :z)) }
end

context 'with multiple RHS values' do
let(:source) { 'x, y = 1, 2' }

it { is_expected.to eq(s(:array, s(:int, 1), s(:int, 2))) }
end

context 'with an RHS splat' do
let(:source) { 'x, y = *z' }

it { is_expected.to eq(s(:array, s(:splat, s(:send, nil, :z)))) }
end

context 'with assignment on RHS' do
let(:source) { 'x, y = 1, z+=2' }

it { is_expected.to eq(s(:array, s(:int, 1), s(:op_asgn, s(:lvasgn, :z), :+, s(:int, 2)))) }
end
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is overkill for such a simple method. How about a single test here?

Comment on lines +39 to +42
# @return [Array<Node>] values being assigned on the RHS of the multiple assignment
def values
array? ? expression.children : [expression]
end
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you justify when / how this is useful? I find it a bad idea that x, y = z and x, y = [z] produce the same values even though they are very different

Comment on lines +78 to +83
context 'when the RHS has a single value' do
let(:source) { 'x, y = z' }

it { is_expected.to eq([s(:send, nil, :z)]) }
end

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this method is to remain, then needs a test for 'x, y = [z]'

end

# @return [Boolean] whether the expression has multiple values
def array?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have a need for this public method? I find the definition dubious, as x, y = z and x, y = *z are basically the same yet have a different array? result.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants