Skip to content

Commit

Permalink
JavaScript: fix edge cases in type cast comment detection regex (#5793)
Browse files Browse the repository at this point in the history
Fix edge cases involving {...} ([record @type](http://usejsdoc.org/tags-type.html)) and newlines in prettier's detection code for type cast comments
  • Loading branch information
Yang Su authored and ikatyang committed Jan 30, 2019
1 parent 847a78e commit 47da080
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 1 deletion.
17 changes: 17 additions & 0 deletions CHANGELOG.unreleased.md
Expand Up @@ -67,3 +67,20 @@ Examples:
<!-- Output (Prettier master) -->
Prix·:·32·€
```

- JavaScript: fix record type cast comment detection ([#5793] by [@yangsu])

Previously, type cast comments with record types were ignored and prettier
stripped the subsequent parens. Prettier master handles these cases correctly.

<!-- prettier-ignore -->
```js
// Input
const v = /** @type {{key: number}} */ (value);

// Output (Prettier stable)
const v = /** @type {{key: number}} */ value;

// Output (Prettier master)
const v = /** @type {{key: number}} */ (value);
```
28 changes: 27 additions & 1 deletion src/language-js/needs-parens.js
Expand Up @@ -34,11 +34,37 @@ function hasClosureCompilerTypeCastComment(text, path, locStart, locEnd) {
comment =>
comment.leading &&
comments.isBlockComment(comment) &&
comment.value.match(/^\*\s*@type\s*{[^}]+}\s*$/) &&
isTypeCastComment(comment.value) &&
util.getNextNonSpaceNonCommentCharacter(text, comment, locEnd) === "("
)
);
}

function isTypeCastComment(comment) {
const trimmed = comment.trim();
if (!/^\*\s*@type\s*\{[^]+\}$/.test(trimmed)) {
return false;
}
let isCompletelyClosed = false;
let unpairedBracketCount = 0;
for (const char of trimmed) {
if (char === "{") {
if (isCompletelyClosed) {
return false;
}
unpairedBracketCount++;
} else if (char === "}") {
if (unpairedBracketCount === 0) {
return false;
}
unpairedBracketCount--;
if (unpairedBracketCount === 0) {
isCompletelyClosed = true;
}
}
}
return unpairedBracketCount === 0;
}
}

function needsParens(path, options) {
Expand Down
52 changes: 52 additions & 0 deletions tests/comments/__snapshots__/jsfmt.spec.js.snap
Expand Up @@ -350,6 +350,32 @@ var newArray = /** @type {array} */ (numberOrString).map(x => x);
var newArray = /** @type {array} */ ((numberOrString)).map(x => x);
var newArray = /** @type {array} */ ((numberOrString).map(x => x));
const data = functionCall(
arg1,
arg2,
/** @type {{height: number, width: number}} */ (arg3));
// Invalid type casts
const v = /** @type {} */ (value);
const v = /** @type {}} */ (value);
const v = /** @type } */ (value);
const v = /** @type { */ (value);
const v = /** @type {{} */ (value);
const style = /** @type {{
width: number,
height: number,
marginTop: number,
marginLeft: number,
marginRight: number,
marginBottom: number,
}} */ ({
width,
height,
...margins,
});
=====================================output=====================================
// test to make sure comments are attached correctly
let inlineComment = /* some comment */ someReallyLongFunctionCall(
Expand All @@ -374,6 +400,32 @@ var newArray = /** @type {array} */ (numberOrString).map(x => x);
var newArray = /** @type {array} */ (numberOrString).map(x => x);
var newArray = /** @type {array} */ (numberOrString.map(x => x));
const data = functionCall(
arg1,
arg2,
/** @type {{height: number, width: number}} */ (arg3)
);
// Invalid type casts
const v = /** @type {} */ value;
const v = /** @type {}} */ value;
const v = /** @type } */ value;
const v = /** @type { */ value;
const v = /** @type {{} */ value;
const style = /** @type {{
width: number,
height: number,
marginTop: number,
marginLeft: number,
marginRight: number,
marginBottom: number,
}} */ ({
width,
height,
...margins
});
================================================================================
`;
Expand Down
26 changes: 26 additions & 0 deletions tests/comments/closure-compiler-type-cast.js
Expand Up @@ -18,3 +18,29 @@ function returnValue() {
var newArray = /** @type {array} */ (numberOrString).map(x => x);
var newArray = /** @type {array} */ ((numberOrString)).map(x => x);
var newArray = /** @type {array} */ ((numberOrString).map(x => x));


const data = functionCall(
arg1,
arg2,
/** @type {{height: number, width: number}} */ (arg3));

// Invalid type casts
const v = /** @type {} */ (value);
const v = /** @type {}} */ (value);
const v = /** @type } */ (value);
const v = /** @type { */ (value);
const v = /** @type {{} */ (value);

const style = /** @type {{
width: number,
height: number,
marginTop: number,
marginLeft: number,
marginRight: number,
marginBottom: number,
}} */ ({
width,
height,
...margins,
});

0 comments on commit 47da080

Please sign in to comment.