Skip to content

Fix #4752: Error on calling super with @params in a derived class constructor #4754

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

Merged
merged 2 commits into from
Oct 26, 2017

Conversation

GeoffreyBooth
Copy link
Collaborator

Fixes #4752, in the sense that now we throw a compiler error rather than outputting invalid JavaScript. If we want to go further and create some placeholder variables to engage in sleight of hand to make the impossible possible, well, that can be a follow-up PR.

class A
  constructor: (@name) ->
    alert @name, 'name'
    
class B extends A
  constructor: (@name) ->
    super @name
test.coffee:7:5: error: Can't call super with @params in derived class constructors
    super @name
    ^^^^^^^^^^^

@GeoffreyBooth GeoffreyBooth requested review from connec and removed request for connec October 20, 2017 04:51
@GeoffreyBooth
Copy link
Collaborator Author

Anyone familiar with the codebase available to review this? @vendethiel? @xixixao?

This one's only two lines plus a test.

@vendethiel
Copy link
Collaborator

vendethiel commented Oct 23, 2017

This won't catch code like class extends A then constructor: (@a) -> super((=> @a)()), but I'm not sure if that's supposedly valid. (or if super(=> @a) in general is valid).

@GeoffreyBooth
Copy link
Collaborator Author

It looks like super(=> a) is valid, though class extends A then constructor: (@a) -> super((=> @a)()) shouldn’t be valid for the same reason that class extends A then constructor: (@a) -> super(@a) shouldn’t be. Yikes this is tricky . . . I guess to do it right we need to traverse down the tree of everything passed to super as arguments, and check them all for this references. Maybe it’s easier to just do the shenanigans that @connec was thinking about, although those will surely have their own difficult cases.

@vendethiel
Copy link
Collaborator

It looks like super(=> a) is valid

I meant specifically using => @a. It doesn't error at compile time, but I can't seem to get the try it online to evaluate code.

@GeoffreyBooth
Copy link
Collaborator Author

I meant specifically using => @a. It doesn’t error at compile time, but I can’t seem to get the try it online to evaluate code.

That’s the issue: we’re generating valid JavaScript, that just happens not to correspond to the user’s intent. See:

class extends parent
  constructor: (@arg) ->
    super(=> @arg)
(class extends parent {
  constructor(arg) {
    super(() => {
      return this.arg;
    });
    this.arg = arg;
  }
});

Babel allows this, but it’s not the user’s intent because this.arg isn’t defined when it’s used inside the super argument.

@GeoffreyBooth
Copy link
Collaborator Author

Okay @vendethiel, I’ve found a way to search the tree of nodes that make up super‘s arguments, to find any references to this within them. This covers the case you pointed out above.

@vendethiel
Copy link
Collaborator

vendethiel commented Oct 24, 2017

This goes a bit too deep:

coffee -bcs
class extends A then constructor: (@a) -> super(class then constructor: (@b) -> @b)

[stdin]:1:74: error: Can't call super with @params in derived class constructors
class extends A then constructor: (@a) -> super(class then constructor: (@b) -> @b)

@GeoffreyBooth
Copy link
Collaborator Author

I tried using Base::contains, which stops traversing at scope boundaries, but it failed your example. I ran into a similar issue with function parameters, in that our checks don't work too well when someone puts gobs of code in there. Ultimately I think we couldn't find a solution that was perfectly precise, that went deep enough but not too deep, and so we opted to err on the side of too deep. The thinking was that blocks of code in function parameters is an edge case, easily refactored, and it's better to throw an unnecessary error than potentially miss throwing one.

I think the same could be said for this case. Would you agree?

@vendethiel
Copy link
Collaborator

Base::contains (or rather Base::traverseChildren) doesn't differentiate "scope crossing" between whether it is this-aware. This might be doable, this might not be, this might not be worth it. It's just a decision we take, I'm fine with it whichever way we decide.

@GeoffreyBooth
Copy link
Collaborator Author

Thanks. I guess let's leave this open another day to see if anyone else has an opinion. Any other notes?

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.

2 participants