Skip to content
This repository has been archived by the owner on May 19, 2018. It is now read-only.

Fix parsing object rest spread #149

Merged
merged 5 commits into from Sep 30, 2016
Merged

Fix parsing object rest spread #149

merged 5 commits into from Sep 30, 2016

Conversation

danez
Copy link
Member

@danez danez commented Sep 29, 2016

This makes object-rest-spread behave according to spec and only
allow one spread/rest operator and enforces it to be the last
param in the object.

Also move all object-rest-spread tests to a own folder.

The fix is pretty simple, as soon as we parsed the spread operator we expect the object to end.

The only thing I'm not 100% sure is if trailing commas are aloud, but I would assume it is the same as with array spread to be not allowed. (see test 8) //cc @sebmarkbage

Fixes #148

@@ -0,0 +1,33 @@
{
Copy link
Member

Choose a reason for hiding this comment

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

This file looks unrelated?

Copy link
Member Author

Choose a reason for hiding this comment

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

Upps removed

@@ -713,7 +713,8 @@ pp.parseObj = function (isPattern, refShorthandDefaultPos) {
prop = this.parseSpread();
prop.type = isPattern ? "RestProperty" : "SpreadProperty";
node.properties.push(prop);
continue;
this.expect(tt.braceR);
Copy link
Member

Choose a reason for hiding this comment

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

Is it easy to have have more specific error messages for these?

let { ...x, y, z } = obj; - spread has to be the last element (would result in the same value as the right hand side)
let { x, ...y, ...z } = obj; - cannot have multiple spreads (would result in the same value for both y and z)?

Copy link
Member Author

Choose a reason for hiding this comment

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

Done, I also added a separate message when having an invalid trailing comma

@sebmarkbage
Copy link

sebmarkbage commented Sep 29, 2016

The single rest at the end only applies to binding let { x, ...y } = obj; and assignment ({ x, ...y } = obj). It doesn't apply to spread let obj = { ...x, y, ...z }; // works.

I think you need to make this conditional on the isPattern flag, no?

@hzoo
Copy link
Member

hzoo commented Sep 29, 2016

Yeah only for rest 👍

The failing test from babel is example of this (valid)

babel-plugin-transform-object-rest-spread/object rest spread expression:
     SyntaxError: /home/travis/build/babel/babylon/build/babel/packages/babel-plugin-transform-object-rest-spread/test/fixtures/object-rest-spread/expression/actual.js: Unexpected token (1:10)
  > 1 | ({ x, ...y, a, ...b, c });
      if (isPattern) {
        this.expect(tt.braceR);
        break;
      } else {
        continue;
      }

This makes object-rest-spread behave according to spec and only
allow one spread/rest operator and enforces it to be the last
param in the object.

Also move all object-rest-spread tests to a own folder.
@danez
Copy link
Member Author

danez commented Sep 29, 2016

Is trailing comma allowed in this case?

const { x, ...s, } = obj;

because this seems not to be allowed:

const [ x, ...s, ] = arr;

@sebmarkbage
Copy link

sebmarkbage commented Sep 29, 2016

@danez No, trailing comma is not allowed. EDIT: The rationale for this is that the use case for trailing comma is that you can add something at the end without affecting the line above. E.g. for version control software. However, that is never the case here.

@codecov-io
Copy link

codecov-io commented Sep 29, 2016

Current coverage is 94.49% (diff: 100%)

Merging #149 into master will increase coverage by 0.02%

@@             master       #149   diff @@
==========================================
  Files            19         19          
  Lines          3111       3124    +13   
  Methods         327        327          
  Messages          0          0          
  Branches        818        823     +5   
==========================================
+ Hits           2939       2952    +13   
  Misses           94         94          
  Partials         78         78          

Powered by Codecov. Last update 9cc0981...47bc157

@hzoo
Copy link
Member

hzoo commented Sep 29, 2016

👍 Other than I think we could give a better message

break;
const position = this.state.start;
if (firstRestLocation !== null) {
this.unexpected(firstRestLocation, "Cannot have multiple rest elements in destruction expression");
Copy link
Member

Choose a reason for hiding this comment

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

Maybe "Cannot have multiple rest elements in a destructuring assignment" or "Cannot have multiple rest elements when destructuring"?

Copy link
Member Author

Choose a reason for hiding this comment

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

I did not use assignment as I think this is not an assignment if I'm not mistaken:

function ({a,b,...c,...c}) {}

But this would issue the same error. Should add a test for it if there isn't one yet.

Can use "Cannot have multiple rest elements when destructuring".

Copy link
Member

Choose a reason for hiding this comment

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

Can use "Cannot have multiple rest elements when destructuring". 👍

@@ -753,6 +769,10 @@ pp.parseObj = function (isPattern, refShorthandDefaultPos) {
node.properties.push(prop);
}

if (firstRestLocation !== null) {
Copy link
Member

Choose a reason for hiding this comment

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

These can be moved right below the while (!this.eat(tt.braceR)) { block right? - I guess it was left at the end as convention? Works either way

Copy link
Member Author

Choose a reason for hiding this comment

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

no it has to be at the end after the loop, because otherwise the multiple destructuring check does not work.

Basically the logic is:

loop through all params:
   if spread
       if havent seen spread before AND this is not the end of the object pattern: record that we have seen a spread
       else if havent seen spread before: warning that this is already the second spread

if we finished the loop and recorded a spread: error that it was not at the end

Copy link
Member

Choose a reason for hiding this comment

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

Oh I think I misread the last }!

@hzoo
Copy link
Member

hzoo commented Sep 30, 2016

Just gotta fix the error messages in the tests 👍

@istarkov
Copy link

istarkov commented Oct 3, 2016

This PR does not work for arrow functions,

const x = ({ ...props, y }) => 1; // THIS WORK
function xx({ ...props, y }) { return 1;} // THIS FAIL

@danez danez deleted the object-rest-spread branch October 3, 2016 10:50
@danez
Copy link
Member Author

danez commented Oct 3, 2016

Good catch, thanks for reporting.

@istarkov
Copy link

istarkov commented Oct 3, 2016

This catch will cost me a day to fix hundreds of my code lines ;-) (Will read the specs in the future)

kristofdegrave pushed a commit to kristofdegrave/babylon that referenced this pull request Oct 27, 2016
* Fix parsing object rest

This makes object-rest-spread behave according to spec and only
allow one rest operator and enforces it to be the last
param in the object.

Also move all object-rest-spread tests to a own folder.

* Show nicer error messages
@hzoo hzoo mentioned this pull request Apr 12, 2018
4 tasks
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Fix destruction parsing
5 participants