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

[CS2] Fix #4631: Expansion that becomes rest parameter causes runtime error #4634

Merged
merged 4 commits into from
Aug 17, 2017

Conversation

zdenko
Copy link
Collaborator

@zdenko zdenko commented Aug 7, 2017

Closes #4631.
After added support for the spread dots on the left side, dots in array slice, e.g. ([a...b]) were misinterpreted and the code was compiled to an implicit call. The error occurred when there was a space around the dots.

Example:

# wrong
arr[a ... b] => arr[a(...b)]
# correct
arr[a ... b] => arr.slice(a, b)

@GeoffreyBooth
Copy link
Collaborator

LGTM. @helixbass? @connec?

@connec
Copy link
Collaborator

connec commented Aug 7, 2017

My only suggestion is to add a regression test.

Also, does arr[(a ...b)] work as expected (e.g. arr[a(...b)]?)

@zdenko zdenko force-pushed the expansion-rest-bug branch from 29e1720 to 3d5f8ec Compare August 9, 2017 05:03
@zdenko
Copy link
Collaborator Author

zdenko commented Aug 9, 2017

@connec Tests are added. Detection of the implicit call with dots on the left is improved as I also found few margin error cases, i.e. { a:1, b ... }

@@ -267,7 +267,8 @@ exports.Rewriter = class Rewriter
# Added support for spread dots on the left side: f ...a
if (tag in IMPLICIT_FUNC and token.spaced or
tag is '?' and i > 0 and not tokens[i - 1].spaced) and
(nextTag in IMPLICIT_CALL or nextTag is '...' or
(nextTag in IMPLICIT_CALL or
(nextTag is '...' and @tag(i + 2) in IMPLICIT_CALL and not @findTagsBackwards(i, ['INDEX_START', '['])) or
Copy link
Collaborator

Choose a reason for hiding this comment

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

@zdenko why and @tag(i + 2) in IMPLICIT_CALL? Seems a little arbitrary and tests don't fail without it

Also there's trailing whitespace in the preceding line. I'd recommend setting up your editor to highlight trailing whitespace so it's obvious

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@helixbass tests don't fail because there are no many tests for spred dots on the left side.
@tag(i +2) is needed to catch cases like { a:1, b ... } (note the space after the b).
In this case tag (b) is in IMPLICIT_FUNC and nextTag is ..., so I need to check@tag (i + 2). Otherwise, the rewriter will compile this into
[IDENTIFIER b] [CALL_START (] [... ...] [CALL_END )].

Copy link
Collaborator

Choose a reason for hiding this comment

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

Maybe we should add a test for it?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I was thinking the same, but I'm not sure what would be a better option: a) put all tests for spread dots in one file, or b) add test cases in files (e.g. arrays, assignment, functions,...).

Basically, we need to cover all cases with spread dots preceding by space:

{ a: 1, b ... }

{
  a:1
  b ...
}

f = (a ...) ->

[
  a
  b ...
]

I would opt for option (a).

Copy link
Collaborator

Choose a reason for hiding this comment

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

I guess with the current test layout you'd need to put some in:

  • arrays (array spreads)
  • assignment (array/object rest destructuring)
  • functions (argument rest arguments)
  • function_invocation (spread arguments)
  • objects (object spreads) - though there are no object spread tests there currently?
  • ranges (disambiguating spreads from ranges)
  • slicing_and_splicing (disambiguating spreads from slices and splices)

That is quite spread out, but probably the easiest thing to do for now. We may want a new rests_and_spreads test file where we can move all the rest/spread tests so all these cases are in one place.

@GeoffreyBooth
Copy link
Collaborator

@helixbass or @connec, any further notes?

@GeoffreyBooth GeoffreyBooth modified the milestone: 2.0.0 Aug 14, 2017
@helixbass
Copy link
Collaborator

@GeoffreyBooth my comment still stands. No tests fail when removing and @tag(i + 2) in IMPLICIT_CALL and without diving fully into this myself I'm not super sold by @zdenko's reply

My comment was largely prompted by it seeming like using IMPLICIT_CALL there couldn't really make semantic sense - quoting the source, IMPLICIT_CALL is: If preceded by an IMPLICIT_FUNC, indicates a function invocation. The way it's being used here, it's not (immediately) preceded by an IMPLICIT_FUNC (but rather by ...), so I don't see how there could be a grammatical basis for using IMPLICIT_CALL here rather than something more specific. But I'm not prepared to evaluate that more fully until there are tests giving some sort of explanation of why it's there at all

@zdenko
Copy link
Collaborator Author

zdenko commented Aug 14, 2017

@GeoffreyBooth, @helixbass I'm preparing tests for these cases. It shouldn't take much longer.

@zdenko zdenko force-pushed the expansion-rest-bug branch 3 times, most recently from d6b7695 to 91b2b0d Compare August 15, 2017 00:06
@zdenko zdenko force-pushed the expansion-rest-bug branch from 91b2b0d to a4dde9c Compare August 15, 2017 00:28
@GeoffreyBooth
Copy link
Collaborator

@zdenko thanks for all the extra tests!

What about @helixbass’ comment above, about not needing and @tag(i + 2) in IMPLICIT_CALL?

Also do you mind taking a look at #4260? It seems possibly related to this.

@zdenko
Copy link
Collaborator Author

zdenko commented Aug 15, 2017

@GeoffreyBooth new tests are cases for and @tag(i + 2) in IMPLICIT_CALL.
When left side spread was added it didn't occur to me that people might put space before/after dots.
One example of this is #4631, e.g. array ranges [a ... b].
So, when I checked other possible places with spread dots, I've found that rewriter misinterpreted implicit calls when dots followed IMPLICIT_FUNC tag.
Example:

obj = {a:1, p ..., b:2}

({
    a: p
    b
    r ...
 }) ->

In these cases, rewriter would compile p ..., into p(..., {b:2}) and r ... into r(...).

You might also try these in the current branch:

coffee -te '({a, p ..., b:2}) ->'
coffee -te '(a, r ...) ->'
coffee -te 'obj1 = {a, f() ...}'

Perhaps I didn't explain the need for @tag(i + 2) check well, or I was too terse.
Basically, checking of @tag(i + 2) is not needed for recognizing the implicit call, but to prevent misinterpretation in other cases, i.e. when the code is not implicit func call.
So, in the current branch, this will work as expected:

f ...     a             #  f(...a) 
f() ...   b             #  f()(...b) 
f? ...    c             #  if (typeof f === "function") { f(...c) }
h[0] ...  d             #  h[0](...d) 

I don't think there were any test cases before with space before/after the dots, so this is the reason no test failed.

@GeoffreyBooth
Copy link
Collaborator

@helixbass what do you think?

This is the last PR holding up 2.0.0.

@helixbass
Copy link
Collaborator

@zdenko @GeoffreyBooth ok I see how it's being used. It's really the set of tokens that can start something that comes after a left-... grammatically. I think reusing the name IMPLICIT_CALL makes it a little confusing for the reader. But I don't know a better name and the tokens in IMPLICIT_CALL seem close enough to the right set of tokens to get the job done

Found another object spread destructuring bug and just opened as #4651 so probably want to fix that too before 2.0.0?

@GeoffreyBooth
Copy link
Collaborator

Thanks for that other ticket @helixbass. In the interest of taking one thing at a time, do you think we can merge this PR in? And #4651 can get its own PR.

@helixbass
Copy link
Collaborator

@GeoffreyBooth ya I didn't mean to imply that this should wait, they're unrelated

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.

4 participants