forked from decaffeinate/decaffeinate
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: add implicit function call parens in the normalize step
The code in decaffeinate#301 is actually parsed incorrectly by the CoffeeScript parser (even before decaffeinate-parser does anything interesting). It says that the function body ends at the end of the file, so decaffeinate was inserting a close-curly-brace at the end of the file, which was incorrect. However, the function application was correctly parsed, and explicitly putting parens for the function application causes the CoffeeScript parser to work again, so we can work around this issue by inserting parens for all implicit functions before the MainStage. I think this will also fix a bunch of other problems caused by implicit function calls. For example, it also happens to fix decaffeinate#269. In the codebase that I'm trying to decaffeinate, it fixes 49 out of the 104 files with decaffeinate failures that I hadn't categorized. I added the heuristic that we put the close-paren on the next line if the last arg is a multi-line expression. This fixed the formatting in almost every test, except the change introduced two test issues: * Object literal formatting sometimes had an excessive newline in non-implicit function calls ( http://decaffeinate-project.org/repl/#?evaluate=true&code=a(b%2C%0A%20%20c%3A%20d%0A%20%20e%3A%20f%0A%29 ) so adding parens exposed the issue. It's pretty minor, though. I think. * In another test, the added parens interfered with some other operations in the normalize step in a way that will be fixed by Rich-Harris/magic-string#89 Closes decaffeinate#301. Closes decaffeinate#269.
- Loading branch information
1 parent
8dc0b5f
commit 5fb0898
Showing
8 changed files
with
107 additions
and
29 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
53 changes: 53 additions & 0 deletions
53
src/stages/normalize/patchers/FunctionApplicationPatcher.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import NodePatcher from './../../../patchers/NodePatcher.js'; | ||
import type { Editor, Node, ParseContext } from './../../../patchers/types.js'; | ||
import { CALL_START } from 'coffee-lex'; | ||
|
||
export default class FunctionApplicationPatcher extends NodePatcher { | ||
fn: NodePatcher; | ||
args: Array<NodePatcher>; | ||
|
||
constructor(node: Node, context: ParseContext, editor: Editor, fn: NodePatcher, args: Array<NodePatcher>) { | ||
super(node, context, editor); | ||
this.fn = fn; | ||
this.args = args; | ||
} | ||
|
||
patchAsExpression() { | ||
let implicitCall = this.isImplicitCall(); | ||
let { args } = this; | ||
|
||
this.fn.patch(); | ||
|
||
if (implicitCall && args.length === 0) { | ||
this.insert(this.fn.outerEnd, '()'); | ||
return; | ||
} | ||
|
||
if (implicitCall) { | ||
let firstArg = args[0]; | ||
let hasOneArg = args.length === 1; | ||
let firstArgIsOnNextLine = !firstArg ? false : | ||
/[\r\n]/.test(this.context.source.slice(this.fn.outerEnd, firstArg.outerStart)); | ||
if ((hasOneArg && firstArg.node.virtual) || firstArgIsOnNextLine) { | ||
this.insert(this.fn.outerEnd, '('); | ||
} else { | ||
this.overwrite(this.fn.outerEnd, firstArg.outerStart, '('); | ||
} | ||
} | ||
|
||
args.forEach(arg => arg.patch()); | ||
|
||
if (implicitCall) { | ||
let lastArg = args[args.length - 1]; | ||
if (lastArg.isMultiline()) { | ||
this.appendLineAfter(')'); | ||
} else { | ||
this.insert(this.innerEnd, ')'); | ||
} | ||
} | ||
} | ||
|
||
isImplicitCall() { | ||
return !this.fn.hasSourceTokenAfter(CALL_START); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -81,7 +81,8 @@ describe('objects', () => { | |
a(b, { | ||
c: d, | ||
e: f | ||
}); | ||
} | ||
); | ||
`); | ||
}); | ||
|
||
|