Skip to content

Commit 0dc4755

Browse files
Fix #4747: Flow local variables (#4753)
* Fix #4706: Comments before a PARAM_START token stay before that token * Simplify nodes * Add function-in-function test * Fix #4706: Comments after class name should go after the identifier that's after `class`, not the variable assigned to * Fix #4706: Top-level identifiers with trailing comments get wrapped in parentheses (around the comment too) so that Flow doesn't interpret it as a JavaScript label * Cleanup * If the source has parentheses wrapping an identifier followed by a block comment, output those parentheses rather than optimizing them away; this is a requirement of Flow, to distinguish from JavaScript labels * More tests for Flow comments * For local variables with trailing inline herecomments, output the comments up in the variable declarations line for Flow compatibility
1 parent 6faa7f2 commit 0dc4755

File tree

5 files changed

+96
-9
lines changed

5 files changed

+96
-9
lines changed

lib/coffeescript/nodes.js

+33-5
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/coffeescript/scope.js

+3-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/nodes.coffee

+23-2
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ exports.Base = class Base
179179
# `compileToFragments` method has logic for outputting comments.
180180
unshiftCommentFragment commentFragment
181181
else
182+
fragments.push @makeCode '' if fragments.length is 0
182183
if commentFragment.unshift
183184
fragments[0].precedingComments ?= []
184185
fragments[0].precedingComments.push commentFragment
@@ -570,7 +571,13 @@ exports.Block = class Block extends Base
570571
fragments.push @makeCode '\n' if i
571572
fragments.push @makeCode "#{@tab}var "
572573
if declars
573-
fragments.push @makeCode scope.declaredVariables().join(', ')
574+
declaredVariables = scope.declaredVariables()
575+
for declaredVariable, declaredVariablesIndex in declaredVariables
576+
fragments.push @makeCode declaredVariable
577+
if Object::hasOwnProperty.call o.scope.comments, declaredVariable
578+
fragments.push o.scope.comments[declaredVariable]...
579+
if declaredVariablesIndex isnt declaredVariables.length - 1
580+
fragments.push @makeCode ', '
574581
if assigns
575582
fragments.push @makeCode ",\n#{@tab + TAB}" if declars
576583
fragments.push @makeCode scope.assignedVariables().join(",\n#{@tab + TAB}")
@@ -2155,7 +2162,7 @@ exports.Assign = class Assign extends Base
21552162
message = isUnassignable name.value
21562163
name.error message if message
21572164

2158-
# `moduleDeclaration` can be `'import'` or `'export'`
2165+
# `moduleDeclaration` can be `'import'` or `'export'`.
21592166
@checkAssignability o, name
21602167
if @moduleDeclaration
21612168
o.scope.add name.value, @moduleDeclaration
@@ -2167,6 +2174,20 @@ exports.Assign = class Assign extends Base
21672174
'param'
21682175
else
21692176
o.scope.find name.value
2177+
# If this assignment identifier has one or more herecomments
2178+
# attached, output them as part of the declarations line (unless
2179+
# other herecomments are already staged there) for compatibility
2180+
# with Flow typing. Don’t do this if this assignment is for a
2181+
# class, e.g. `ClassName = class ClassName {`, as Flow requires
2182+
# the comment to be between the class name and the `{`.
2183+
if name.comments and not o.scope.comments[name.value] and
2184+
@value not instanceof Class and
2185+
name.comments.every((comment) -> comment.here and not comment.multiline)
2186+
commentsNode = new IdentifierLiteral name.value
2187+
commentsNode.comments = name.comments
2188+
commentFragments = []
2189+
@compileCommentFragments o, commentsNode, commentFragments
2190+
o.scope.comments[name.value] = commentFragments
21702191

21712192
if @value instanceof Code
21722193
if @value.isStatic

src/scope.litcoffee

+3-1
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,12 @@ Initialize a scope with its parent, for lookups up the chain,
1111
as well as a reference to the **Block** node it belongs to, which is
1212
where it should declare its variables, a reference to the function that
1313
it belongs to, and a list of variables referenced in the source code
14-
and therefore should be avoided when generating variables.
14+
and therefore should be avoided when generating variables. Also track comments
15+
that should be output as part of variable declarations.
1516

1617
constructor: (@parent, @expressions, @method, @referencedVars) ->
1718
@variables = [{name: 'arguments', type: 'arguments'}]
19+
@comments = {}
1820
@positions = {}
1921
@utilities = {} unless @parent
2022

test/comments.coffee

+34
Original file line numberDiff line numberDiff line change
@@ -1073,3 +1073,37 @@ test "#4706: Flow comments for function spread", ->
10731073
var method;
10741074
10751075
method = (...rest/*: Array<string> */) => {};'''
1076+
1077+
test "#4747: Flow comments for local variable declaration", ->
1078+
eqJS 'a ###: number ### = 1', '''
1079+
var a/*: number */;
1080+
1081+
a = 1;
1082+
'''
1083+
1084+
test "#4747: Flow comments for local variable declarations", ->
1085+
eqJS '''
1086+
a ###: number ### = 1
1087+
b ###: string ### = 'c'
1088+
''', '''
1089+
var a/*: number */, b/*: string */;
1090+
1091+
a = 1;
1092+
1093+
b = 'c';
1094+
'''
1095+
1096+
test "#4747: Flow comments for local variable declarations with reassignment", ->
1097+
eqJS '''
1098+
a ###: number ### = 1
1099+
b ###: string ### = 'c'
1100+
a ### some other comment ### = 2
1101+
''', '''
1102+
var a/*: number */, b/*: string */;
1103+
1104+
a = 1;
1105+
1106+
b = 'c';
1107+
1108+
a/* some other comment */ = 2;
1109+
'''

0 commit comments

Comments
 (0)