TypeScript: Throw errors for 'declare' in accessors (#13770)

This commit is contained in:
fisker Cheung authored and GitHub committed 2022-11-14 19:44:14 +08:00
1 parent 68a13957f4
commit a66236a3e1
6 files changed
+119 -42

No files matched your search

@@ -5,18 +5,9 @@ import isNonEmptyArray from "../../../utils/is-non-empty-array.js";
import isBlockComment from "../../utils/is-block-comment.js";
import isIndentableBlockComment from "../../utils/is-indentable-block-comment.js";
import visitNode from "./visit-node.js";
import { throwErrorForInvalidNodes } from "./typescript.js";
import throwSyntaxError from "./throw-ts-syntax-error.js";
function postprocess(ast, options) {
if (
options.parser === "typescript" &&
// decorators or abstract properties
/@|abstract/.test(options.originalText)
) {
throwErrorForInvalidNodes(ast, options);
}
// Keep Babel's non-standard ParenthesizedExpression nodes only if they have Closure-style type cast comments.
if (
options.parser !== "typescript" &&
+89 -32
View File
@@ -2,9 +2,28 @@ import isNonEmptyArray from "../../../utils/is-non-empty-array.js";
import visitNode from "./visit-node.js";
import throwTsSyntaxError from "./throw-ts-syntax-error.js";
// Taken from `typescript` package
const SyntaxKind = {
AbstractKeyword: 126,
SourceFile: 305,
DeclareKeyword: 135,
PropertyDeclaration: 167,
};
function getTsNodeLocation(nodeOrToken) {
const sourceFile = getSourceFileOfNode(nodeOrToken);
const [start, end] = [nodeOrToken.pos, nodeOrToken.end].map((position) => {
const { line, character: column } =
sourceFile.getLineAndCharacterOfPosition(position);
return { line: line + 1, column };
});
return { start, end };
}
// Copied from https://unpkg.com/typescript@4.8.2/lib/typescript.js
function getSourceFileOfNode(node) {
while (node && node.kind !== 305 /* SyntaxKind.SourceFile */) {
while (node && node.kind !== SyntaxKind.SourceFile) {
node = node.parent;
}
return node;
@@ -21,53 +40,91 @@ function throwErrorForInvalidDecorator(tsNode) {
const [{ expression }] = illegalDecorators;
const sourceFile = getSourceFileOfNode(expression);
const [start, end] = [expression.pos, expression.end].map((position) => {
const { line, character: column } =
sourceFile.getLineAndCharacterOfPosition(position);
return { line: line + 1, column };
});
throwTsSyntaxError({ loc: { start, end } }, "Decorators are not valid here.");
throwTsSyntaxError(
{ loc: getTsNodeLocation(expression) },
"Decorators are not valid here."
);
}
// Values of abstract property is removed since `@typescript-eslint/typescript-estree` v5
// https://github.com/typescript-eslint/typescript-eslint/releases/tag/v5.0.0
function throwErrorForInvalidAbstractProperty(tsNode, esTreeNode) {
const SYNTAX_KIND_PROPERTY_DEFINITION = 167;
const SYNTAX_KIND_ABSTRACT_KEYWORD = 126;
if (
tsNode.kind !== SYNTAX_KIND_PROPERTY_DEFINITION ||
(tsNode.modifiers &&
!tsNode.modifiers.some(
(modifier) => modifier.kind === SYNTAX_KIND_ABSTRACT_KEYWORD
))
!(
tsNode.kind === SyntaxKind.PropertyDeclaration &&
tsNode.initializer &&
esTreeNode.value === null &&
tsNode.modifiers?.some(
(modifier) => modifier.kind === SyntaxKind.AbstractKeyword
)
)
) {
return;
}
if (tsNode.initializer && esTreeNode.value === null) {
throwTsSyntaxError(
esTreeNode,
"Abstract property cannot have an initializer"
);
}
throwTsSyntaxError(
esTreeNode,
"Abstract property cannot have an initializer"
);
}
function throwErrorForInvalidNodes(ast, options) {
const { esTreeNodeToTSNodeMap, tsNodeToESTreeNodeMap } =
options.tsParseResult;
function throwErrorForInvalidDeclare(tsNode, esTreeNode) {
if (
!(
esTreeNode.type === "MethodDefinition" &&
(esTreeNode.kind === "get" || esTreeNode.kind === "set")
)
) {
return;
}
visitNode(ast, (node) => {
const tsNode = esTreeNodeToTSNodeMap.get(node);
const declareKeyword = tsNode.modifiers?.find(
(modifier) => modifier.kind === SyntaxKind.DeclareKeyword
);
if (!declareKeyword) {
return;
}
throwTsSyntaxError(
{ loc: getTsNodeLocation(declareKeyword) },
/* cspell:disable-next-line */
`'declare' is not allowed in ${esTreeNode.kind}ters.`
);
}
function getTsNode(node, tsParseResult) {
const { esTreeNodeToTSNodeMap, tsNodeToESTreeNodeMap } = tsParseResult;
const tsNode = esTreeNodeToTSNodeMap.get(node);
if (!tsNode) {
return;
}
const esTreeNode = tsNodeToESTreeNodeMap.get(tsNode);
if (esTreeNode !== node) {
return;
}
return tsNode;
}
function throwErrorForInvalidNodes(tsParseResult, options) {
if (
// decorators
// abstract properties
// declare in accessor
!/@|abstract|declare/.test(options.originalText)
) {
return;
}
visitNode(tsParseResult.ast, (esTreeNode) => {
const tsNode = getTsNode(esTreeNode, tsParseResult);
if (!tsNode) {
return;
}
const esTreeNode = tsNodeToESTreeNodeMap.get(tsNode);
if (esTreeNode !== node) {
return;
}
throwErrorForInvalidDeclare(tsNode, esTreeNode);
throwErrorForInvalidDecorator(tsNode);
throwErrorForInvalidAbstractProperty(tsNode, esTreeNode);
});
+4 -1
View File
@@ -3,6 +3,7 @@ import tryCombinations from "../../utils/try-combinations.js";
import createParser from "./utils/create-parser.js";
import replaceHashbang from "./utils/replace-hashbang.js";
import postprocess from "./postprocess/index.js";
import { throwErrorForInvalidNodes } from "./postprocess/typescript.js";
/** @type {import("@typescript-eslint/typescript-estree").TSESTreeOptions} */
const parseOptions = {
@@ -55,7 +56,9 @@ async function parse(text, options = {}) {
}
options.originalText = text;
options.tsParseResult = result;
throwErrorForInvalidNodes(result, options);
return postprocess(result.ast, options);
}
@@ -1,5 +1,25 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`declare-getter.ts [typescript] format 1`] = `
"'declare' is not allowed in getters. (1:12)
> 1 | class Foo {
| ^
> 2 | declare get foo()
| ^^^^^^^^^^
3 | }
4 |"
`;
exports[`declare-setter.ts [typescript] format 1`] = `
"'declare' is not allowed in setters. (1:12)
> 1 | class Foo {
| ^
> 2 | declare set foo(v)
| ^^^^^^^^^^
3 | }
4 |"
`;
exports[`default-escaped.ts [typescript] format 1`] = `
"Declaration or statement expected. (1:1)
> 1 | export asyn\\u{63} from "async";
@@ -0,0 +1,3 @@
class Foo {
declare get foo()
}
@@ -0,0 +1,3 @@
class Foo {
declare set foo(v)
}