Handle closure compiler type cast syntax correctly (#2484)
* Handle closure compiler type cast syntax correctly Fixes https://github.com/prettier/prettier/issues/1445 * Move closure type cast detection to needParens in fast-path.js * every => some and added additional check for leading commentmaster
parent
a666a29aa3
commit
26842e4d69
|
@ -281,7 +281,7 @@ function attach(comments, ast, text) {
|
|||
) ||
|
||||
handleObjectPropertyAssignment(enclosingNode, precedingNode, comment) ||
|
||||
handleCommentInEmptyParens(text, enclosingNode, comment) ||
|
||||
handleMethodNameComments(enclosingNode, precedingNode, comment) ||
|
||||
handleMethodNameComments(text, enclosingNode, precedingNode, comment) ||
|
||||
handleOnlyComments(enclosingNode, ast, comment, isLastComment)
|
||||
) {
|
||||
// We're good
|
||||
|
@ -337,7 +337,8 @@ function breakTies(tiesToBreak, text) {
|
|||
// Iterate backwards through tiesToBreak, examining the gaps
|
||||
// between the tied comments. In order to qualify as leading, a
|
||||
// comment must be separated from followingNode by an unbroken series of
|
||||
// whitespace-only gaps (or other comments).
|
||||
// gaps (or other comments). Gaps should only contain whitespace or open
|
||||
// parentheses.
|
||||
let indexOfFirstLeadingComment;
|
||||
for (
|
||||
indexOfFirstLeadingComment = tieCount;
|
||||
|
@ -348,13 +349,14 @@ function breakTies(tiesToBreak, text) {
|
|||
assert.strictEqual(comment.precedingNode, precedingNode);
|
||||
assert.strictEqual(comment.followingNode, followingNode);
|
||||
|
||||
const gap = text.slice(locEnd(comment), gapEndPos);
|
||||
if (/\S/.test(gap)) {
|
||||
// The gap string contained something other than whitespace.
|
||||
const gap = text.slice(locEnd(comment), gapEndPos).trim();
|
||||
if (gap === "" || /^\(+$/.test(gap)) {
|
||||
gapEndPos = locStart(comment);
|
||||
} else {
|
||||
// The gap string contained something other than whitespace or open
|
||||
// parentheses.
|
||||
break;
|
||||
}
|
||||
|
||||
gapEndPos = locStart(comment);
|
||||
}
|
||||
|
||||
tiesToBreak.forEach((comment, i) => {
|
||||
|
@ -551,7 +553,7 @@ function handleObjectPropertyAssignment(enclosingNode, precedingNode, comment) {
|
|||
return false;
|
||||
}
|
||||
|
||||
function handleMethodNameComments(enclosingNode, precedingNode, comment) {
|
||||
function handleMethodNameComments(text, enclosingNode, precedingNode, comment) {
|
||||
// This is only needed for estree parsers (flow, typescript) to attach
|
||||
// after a method name:
|
||||
// obj = { fn /*comment*/() {} };
|
||||
|
@ -561,7 +563,10 @@ function handleMethodNameComments(enclosingNode, precedingNode, comment) {
|
|||
(enclosingNode.type === "Property" ||
|
||||
enclosingNode.type === "MethodDefinition") &&
|
||||
precedingNode.type === "Identifier" &&
|
||||
enclosingNode.key === precedingNode
|
||||
enclosingNode.key === precedingNode &&
|
||||
// special Property case: { key: /*comment*/(value) };
|
||||
// comment should be attached to value instead of key
|
||||
getNextNonSpaceNonCommentCharacter(text, precedingNode) !== ":"
|
||||
) {
|
||||
addTrailingComment(precedingNode, comment);
|
||||
return true;
|
||||
|
|
|
@ -151,6 +151,12 @@ FastPath.prototype.needsParens = function(options) {
|
|||
return false;
|
||||
}
|
||||
|
||||
// Closure compiler requires that type casted expressions to be surrounded by
|
||||
// parentheses.
|
||||
if (util.hasClosureCompilerTypeCastComment(options.originalText, node)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Identifiers never need parentheses.
|
||||
if (node.type === "Identifier") {
|
||||
return false;
|
||||
|
|
16
src/util.js
16
src/util.js
|
@ -441,6 +441,21 @@ function isBlockComment(comment) {
|
|||
return comment.type === "Block" || comment.type === "CommentBlock";
|
||||
}
|
||||
|
||||
function hasClosureCompilerTypeCastComment(text, node) {
|
||||
// https://github.com/google/closure-compiler/wiki/Annotating-Types#type-casts
|
||||
// Syntax example: var x = /** @type {string} */ (fruit);
|
||||
return (
|
||||
node.comments &&
|
||||
node.comments.some(
|
||||
comment =>
|
||||
comment.leading &&
|
||||
isBlockComment(comment) &&
|
||||
comment.value.match(/^\*\s*@type\s*{[^}]+}\s*$/) &&
|
||||
getNextNonSpaceNonCommentCharacter(text, comment) === "("
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function getAlignmentSize(value, tabWidth, startIndex) {
|
||||
startIndex = startIndex || 0;
|
||||
|
||||
|
@ -484,5 +499,6 @@ module.exports = {
|
|||
startsWithNoLookaheadToken,
|
||||
hasBlockComments,
|
||||
isBlockComment,
|
||||
hasClosureCompilerTypeCastComment,
|
||||
getAlignmentSize
|
||||
};
|
||||
|
|
|
@ -107,6 +107,45 @@ React.render(
|
|||
|
||||
`;
|
||||
|
||||
exports[`closure-compiler-type-cast.js 1`] = `
|
||||
// test to make sure comments are attached correctly
|
||||
let inlineComment = /* some comment */ (
|
||||
someReallyLongFunctionCall(withLots, ofArguments));
|
||||
|
||||
let object = {
|
||||
key: /* some comment */ (someReallyLongFunctionCall(withLots, ofArguments))
|
||||
};
|
||||
|
||||
// preserve parens only for type casts
|
||||
let assignment = /** @type {string} */ (getValue());
|
||||
|
||||
functionCall(1 + /** @type {string} */ (value), /** @type {!Foo} */ ({}));
|
||||
|
||||
function returnValue() {
|
||||
return /** @type {!Array.<string>} */ (['hello', 'you']);
|
||||
}
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// test to make sure comments are attached correctly
|
||||
let inlineComment = /* some comment */ someReallyLongFunctionCall(
|
||||
withLots,
|
||||
ofArguments
|
||||
);
|
||||
|
||||
let object = {
|
||||
key: /* some comment */ someReallyLongFunctionCall(withLots, ofArguments)
|
||||
};
|
||||
|
||||
// preserve parens only for type casts
|
||||
let assignment = /** @type {string} */ (getValue());
|
||||
|
||||
functionCall(1 + /** @type {string} */ (value), /** @type {!Foo} */ ({}));
|
||||
|
||||
function returnValue() {
|
||||
return /** @type {!Array.<string>} */ (["hello", "you"]);
|
||||
}
|
||||
|
||||
`;
|
||||
|
||||
exports[`dangling.js 1`] = `
|
||||
var x = {/* dangling */};
|
||||
var x = {
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
// test to make sure comments are attached correctly
|
||||
let inlineComment = /* some comment */ (
|
||||
someReallyLongFunctionCall(withLots, ofArguments));
|
||||
|
||||
let object = {
|
||||
key: /* some comment */ (someReallyLongFunctionCall(withLots, ofArguments))
|
||||
};
|
||||
|
||||
// preserve parens only for type casts
|
||||
let assignment = /** @type {string} */ (getValue());
|
||||
|
||||
functionCall(1 + /** @type {string} */ (value), /** @type {!Foo} */ ({}));
|
||||
|
||||
function returnValue() {
|
||||
return /** @type {!Array.<string>} */ (['hello', 'you']);
|
||||
}
|
Loading…
Reference in New Issue