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) ||
|
handleObjectPropertyAssignment(enclosingNode, precedingNode, comment) ||
|
||||||
handleCommentInEmptyParens(text, enclosingNode, comment) ||
|
handleCommentInEmptyParens(text, enclosingNode, comment) ||
|
||||||
handleMethodNameComments(enclosingNode, precedingNode, comment) ||
|
handleMethodNameComments(text, enclosingNode, precedingNode, comment) ||
|
||||||
handleOnlyComments(enclosingNode, ast, comment, isLastComment)
|
handleOnlyComments(enclosingNode, ast, comment, isLastComment)
|
||||||
) {
|
) {
|
||||||
// We're good
|
// We're good
|
||||||
|
@ -337,7 +337,8 @@ function breakTies(tiesToBreak, text) {
|
||||||
// Iterate backwards through tiesToBreak, examining the gaps
|
// Iterate backwards through tiesToBreak, examining the gaps
|
||||||
// between the tied comments. In order to qualify as leading, a
|
// between the tied comments. In order to qualify as leading, a
|
||||||
// comment must be separated from followingNode by an unbroken series of
|
// 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;
|
let indexOfFirstLeadingComment;
|
||||||
for (
|
for (
|
||||||
indexOfFirstLeadingComment = tieCount;
|
indexOfFirstLeadingComment = tieCount;
|
||||||
|
@ -348,13 +349,14 @@ function breakTies(tiesToBreak, text) {
|
||||||
assert.strictEqual(comment.precedingNode, precedingNode);
|
assert.strictEqual(comment.precedingNode, precedingNode);
|
||||||
assert.strictEqual(comment.followingNode, followingNode);
|
assert.strictEqual(comment.followingNode, followingNode);
|
||||||
|
|
||||||
const gap = text.slice(locEnd(comment), gapEndPos);
|
const gap = text.slice(locEnd(comment), gapEndPos).trim();
|
||||||
if (/\S/.test(gap)) {
|
if (gap === "" || /^\(+$/.test(gap)) {
|
||||||
// The gap string contained something other than whitespace.
|
gapEndPos = locStart(comment);
|
||||||
|
} else {
|
||||||
|
// The gap string contained something other than whitespace or open
|
||||||
|
// parentheses.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
gapEndPos = locStart(comment);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tiesToBreak.forEach((comment, i) => {
|
tiesToBreak.forEach((comment, i) => {
|
||||||
|
@ -551,7 +553,7 @@ function handleObjectPropertyAssignment(enclosingNode, precedingNode, comment) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleMethodNameComments(enclosingNode, precedingNode, comment) {
|
function handleMethodNameComments(text, enclosingNode, precedingNode, comment) {
|
||||||
// This is only needed for estree parsers (flow, typescript) to attach
|
// This is only needed for estree parsers (flow, typescript) to attach
|
||||||
// after a method name:
|
// after a method name:
|
||||||
// obj = { fn /*comment*/() {} };
|
// obj = { fn /*comment*/() {} };
|
||||||
|
@ -561,7 +563,10 @@ function handleMethodNameComments(enclosingNode, precedingNode, comment) {
|
||||||
(enclosingNode.type === "Property" ||
|
(enclosingNode.type === "Property" ||
|
||||||
enclosingNode.type === "MethodDefinition") &&
|
enclosingNode.type === "MethodDefinition") &&
|
||||||
precedingNode.type === "Identifier" &&
|
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);
|
addTrailingComment(precedingNode, comment);
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -151,6 +151,12 @@ FastPath.prototype.needsParens = function(options) {
|
||||||
return false;
|
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.
|
// Identifiers never need parentheses.
|
||||||
if (node.type === "Identifier") {
|
if (node.type === "Identifier") {
|
||||||
return false;
|
return false;
|
||||||
|
|
16
src/util.js
16
src/util.js
|
@ -441,6 +441,21 @@ function isBlockComment(comment) {
|
||||||
return comment.type === "Block" || comment.type === "CommentBlock";
|
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) {
|
function getAlignmentSize(value, tabWidth, startIndex) {
|
||||||
startIndex = startIndex || 0;
|
startIndex = startIndex || 0;
|
||||||
|
|
||||||
|
@ -484,5 +499,6 @@ module.exports = {
|
||||||
startsWithNoLookaheadToken,
|
startsWithNoLookaheadToken,
|
||||||
hasBlockComments,
|
hasBlockComments,
|
||||||
isBlockComment,
|
isBlockComment,
|
||||||
|
hasClosureCompilerTypeCastComment,
|
||||||
getAlignmentSize
|
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`] = `
|
exports[`dangling.js 1`] = `
|
||||||
var x = {/* dangling */};
|
var x = {/* dangling */};
|
||||||
var x = {
|
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