-
Notifications
You must be signed in to change notification settings - Fork 2k
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 handling of parameters that are complex #4430
Changes from 14 commits
ed6d9c6
5f5c039
a3f8091
658f503
baac18e
42a2039
dbd8274
c4b6d27
8fe3242
f96ce05
6e2e254
67ff10c
afd66d6
e64bb8e
ce9e04d
c581e9e
7867cd5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -220,17 +220,39 @@ exports.Base = class Base | |
|
||
# Default implementations of the common node properties and methods. Nodes | ||
# will override these with custom logic, if needed. | ||
|
||
# `children` are the properties to recurse into when tree walking. The | ||
# `children` list *is* the structure of the AST. The `parent` pointer, and | ||
# the pointer to the `children` are how you can traverse the tree. | ||
children: [] | ||
|
||
isStatement : NO | ||
jumps : NO | ||
isComplex : YES | ||
isChainable : NO | ||
isAssignable : NO | ||
isNumber : NO | ||
# `isStatement` has to do with “everything is an expression”. A few things | ||
# can’t be expressions, such as `break`. Things that `isStatement` returns | ||
# `true` for are things that can’t be used as expressions. There are some | ||
# error messages that come from `nodes.coffee` due to statements ending up | ||
# in expression position. | ||
isStatement: NO | ||
|
||
# `jumps` tells you if an expression, or an internal part of an expression | ||
# has a flow control construct (like `break`, or `continue`, or `return`, | ||
# or `throw`) that jumps out of the normal flow of control and can’t be | ||
# used as a value. This is important because things like this make no sense; | ||
# we have to disallow them. | ||
jumps: NO | ||
|
||
# If `node.isComplex() is false`, it is safe to use `node` more than once. | ||
# Otherwise you need to store the value of `node` in a variable and output | ||
# that variable several times instead. Kind of like this: `5` is not complex. | ||
# `returnFive()` is complex. It’s all about “do we need to cache this | ||
# expression?” | ||
isComplex: YES | ||
|
||
unwrap : THIS | ||
unfoldSoak : NO | ||
isChainable: NO | ||
isAssignable: NO | ||
isNumber: NO | ||
|
||
unwrap: THIS | ||
unfoldSoak: NO | ||
|
||
# Is this node used to assign a certain variable? | ||
assigns: NO | ||
|
@@ -1946,12 +1968,18 @@ exports.Code = class Code extends Base | |
# the function definition. | ||
else | ||
if param.isComplex() | ||
# This parameter is destructured. So add a statement to the function | ||
# body assigning it, e.g. `(arg) => { var a = arg.a; }` or with a | ||
# default value if it has one. | ||
# This parameter cannot be declared or assigned in the parameter | ||
# list. So add a statement to the function body assigning it, e.g. | ||
# `(arg) => { var a = arg.a; }` or with a default value if it has | ||
# one. | ||
val = ref = param.asReference o | ||
val = new Op '?', ref, param.value if param.value | ||
exprs.push new Assign new Value(param.name), val, '=', param: yes | ||
if param.value? | ||
condition = new Literal param.name.value + ' === undefined' | ||
ifTrue = new Assign new Value(param.name), param.value, '=', param: yes | ||
exprs.push new If condition, ifTrue | ||
else | ||
val = new Op '?', ref, param.value if param.value | ||
exprs.push new Assign new Value(param.name), val, '=', param: yes | ||
|
||
# If this parameter comes before the splat or expansion, it will go | ||
# in the function definition parameter list. | ||
|
@@ -2112,7 +2140,39 @@ exports.Param = class Param extends Base | |
@reference = node | ||
|
||
isComplex: -> | ||
@name.isComplex() or @value instanceof Call | ||
# Should this parameter be declared in the function body, rather than in | ||
# the parameter list? We don’t want to pull out any parameter that | ||
# has a value, or else we break default values; but we want things like | ||
# generator functions and iterators to evaluate in the correct order, | ||
# which they might not do unless we pull them out into the function body. | ||
if @name.isComplex() | ||
yes | ||
else if @value? and @value.isComplex() | ||
if @value instanceof Value | ||
unless @value.base.isComplex() | ||
no | ||
else | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd either flip this |
||
# Objects and arrays are always complex, but unless they contain any | ||
# complex children, they’re safe to leave in the parameter list. | ||
if @value.base instanceof Arr or @value.base instanceof Obj | ||
hasComplexChild = no | ||
@value.base.traverseChildren no, (node) -> | ||
if node.isComplex() | ||
# This can be further refined. An empty object will evaluate as | ||
# not having any complex children, but as long as it has at least | ||
# one property it will be considered complex. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you give examples on ...
|
||
hasComplexChild = yes | ||
no # Stop traversing. | ||
hasComplexChild | ||
else | ||
yes | ||
else | ||
# Other types of `@value`s in the CoffeeScript codebase: | ||
# `Call`, `Class`, `SuperCall`, `Op`. | ||
# All of these should be declared and assigned in the function body. | ||
yes | ||
else | ||
no | ||
|
||
# Iterates the name or names of a `Param`. | ||
# In a sense, a destructured parameter represents multiple JS parameters. This | ||
|
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.
Should we replace
undefined
withvoid 0
for consistency?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.
This could be (roughly)
condition = new Op '==', param, new UndefinedLiteral
, and would then remain consistent.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.
Why are we using
void 0
in the first place? Because it’s a few characters shorter? Because apparently many years ago it was possible to redefineundefined
? SO.I would think that
undefined
is more readable, and since one of our goals is to produce human-readable code, we should replace allvoid 0
s withundefined
.