diff --git a/src/language-js/needs-parens.js b/src/language-js/needs-parens.js index ebc68e3a4fc1..e058a3dbde7c 100644 --- a/src/language-js/needs-parens.js +++ b/src/language-js/needs-parens.js @@ -5,19 +5,39 @@ const assert = require("assert"); const util = require("../common/util"); const comments = require("./comments"); -function hasClosureCompilerTypeCastComment(text, node, locEnd) { +function hasClosureCompilerTypeCastComment(text, path, locStart, locEnd) { // https://github.com/google/closure-compiler/wiki/Annotating-Types#type-casts // Syntax example: var x = /** @type {string} */ (fruit); + + const n = path.getValue(); + return ( - node.comments && - node.comments.some( - comment => - comment.leading && - comments.isBlockComment(comment) && - comment.value.match(/^\*\s*@type\s*{[^}]+}\s*$/) && - util.getNextNonSpaceNonCommentCharacter(text, comment, locEnd) === "(" - ) + util.getNextNonSpaceNonCommentCharacter(text, n, locEnd) === ")" && + (hasTypeCastComment(n) || hasAncestorTypeCastComment(0)) ); + + // for sub-item: /** @type {array} */ (numberOrString).map(x => x); + function hasAncestorTypeCastComment(index) { + const ancestor = path.getParentNode(index); + return ancestor && + util.getNextNonSpaceNonCommentCharacter(text, ancestor, locEnd) !== ")" && + /^[\s(]*$/.test(text.slice(locStart(ancestor), locStart(n))) + ? hasTypeCastComment(ancestor) || hasAncestorTypeCastComment(index + 1) + : false; + } + + function hasTypeCastComment(node) { + return ( + node.comments && + node.comments.some( + comment => + comment.leading && + comments.isBlockComment(comment) && + comment.value.match(/^\*\s*@type\s*{[^}]+}\s*$/) && + util.getNextNonSpaceNonCommentCharacter(text, comment, locEnd) === "(" + ) + ); + } } function needsParens(path, options) { @@ -46,7 +66,8 @@ function needsParens(path, options) { if ( hasClosureCompilerTypeCastComment( options.originalText, - node, + path, + options.locStart, options.locEnd ) ) { diff --git a/tests/comments/__snapshots__/jsfmt.spec.js.snap b/tests/comments/__snapshots__/jsfmt.spec.js.snap index 955848f02846..5a484faadc5c 100644 --- a/tests/comments/__snapshots__/jsfmt.spec.js.snap +++ b/tests/comments/__snapshots__/jsfmt.spec.js.snap @@ -151,6 +151,10 @@ functionCall(1 + /** @type {string} */ (value), /** @type {!Foo} */ ({})); function returnValue() { return /** @type {!Array.} */ (['hello', 'you']); } + +var newArray = /** @type {array} */ (numberOrString).map(x => x); +var newArray = /** @type {array} */ ((numberOrString)).map(x => x); +var newArray = /** @type {array} */ ((numberOrString).map(x => x)); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // test to make sure comments are attached correctly let inlineComment = /* some comment */ someReallyLongFunctionCall( @@ -171,6 +175,10 @@ function returnValue() { return /** @type {!Array.} */ (["hello", "you"]); } +var newArray = /** @type {array} */ (numberOrString).map(x => x); +var newArray = /** @type {array} */ (numberOrString).map(x => x); +var newArray = /** @type {array} */ (numberOrString.map(x => x)); + `; exports[`dangling.js 1`] = ` diff --git a/tests/comments/closure-compiler-type-cast.js b/tests/comments/closure-compiler-type-cast.js index 6fade6994137..65ffaef86bd3 100644 --- a/tests/comments/closure-compiler-type-cast.js +++ b/tests/comments/closure-compiler-type-cast.js @@ -14,3 +14,7 @@ functionCall(1 + /** @type {string} */ (value), /** @type {!Foo} */ ({})); function returnValue() { return /** @type {!Array.} */ (['hello', 'you']); } + +var newArray = /** @type {array} */ (numberOrString).map(x => x); +var newArray = /** @type {array} */ ((numberOrString)).map(x => x); +var newArray = /** @type {array} */ ((numberOrString).map(x => x));