mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-26 10:43:51 -05:00
Add js-equivalent test for the binary expression stress and introduce trampoline into getJSSyntacticDiagnosticsForFile (#36724)
* Add js-equivalent test for the binary expression stress and introduce trampiline into getJSSyntacticDiagnosticsForFile * Update src/compiler/parser.ts Comment text update Co-Authored-By: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> * Fix lint Co-authored-by: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
This commit is contained in:
@@ -522,6 +522,76 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
/**
|
||||
* Invokes a callback for each child of the given node. The 'cbNode' callback is invoked for all child nodes
|
||||
* stored in properties. If a 'cbNodes' callback is specified, it is invoked for embedded arrays; additionally,
|
||||
* unlike `forEachChild`, embedded arrays are flattened and the 'cbNode' callback is invoked for each element.
|
||||
* If a callback returns a truthy value, iteration stops and that value is returned. Otherwise, undefined is returned.
|
||||
*
|
||||
* @param node a given node to visit its children
|
||||
* @param cbNode a callback to be invoked for all child nodes
|
||||
* @param cbNodes a callback to be invoked for embedded array
|
||||
*
|
||||
* @remarks Unlike `forEachChild`, `forEachChildRecursively` handles recursively invoking the traversal on each child node found,
|
||||
* and while doing so, handles traversing the structure without relying on the callstack to encode the tree structure.
|
||||
*/
|
||||
export function forEachChildRecursively<T>(rootNode: Node, cbNode: (node: Node, parent: Node) => T | "skip" | undefined, cbNodes?: (nodes: NodeArray<Node>, parent: Node) => T | "skip" | undefined): T | undefined {
|
||||
|
||||
const stack: Node[] = [rootNode];
|
||||
while (stack.length) {
|
||||
const parent = stack.pop()!;
|
||||
const res = visitAllPossibleChildren(parent, gatherPossibleChildren(parent));
|
||||
if (res) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
function gatherPossibleChildren(node: Node) {
|
||||
const children: (Node | NodeArray<Node>)[] = [];
|
||||
forEachChild(node, addWorkItem, addWorkItem); // By using a stack above and `unshift` here, we emulate a depth-first preorder traversal
|
||||
return children;
|
||||
|
||||
function addWorkItem(n: Node | NodeArray<Node>) {
|
||||
children.unshift(n);
|
||||
}
|
||||
}
|
||||
|
||||
function visitAllPossibleChildren(parent: Node, children: readonly (Node | NodeArray<Node>)[]) {
|
||||
for (const child of children) {
|
||||
if (isArray(child)) {
|
||||
if (cbNodes) {
|
||||
const res = cbNodes(child, parent);
|
||||
if (res) {
|
||||
if (res === "skip") continue;
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = child.length - 1; i >= 0; i--) {
|
||||
const realChild = child[i];
|
||||
const res = cbNode(realChild, parent);
|
||||
if (res) {
|
||||
if (res === "skip") continue;
|
||||
return res;
|
||||
}
|
||||
stack.push(realChild);
|
||||
}
|
||||
}
|
||||
else {
|
||||
stack.push(child);
|
||||
const res = cbNode(child, parent);
|
||||
if (res) {
|
||||
if (res === "skip") continue;
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function createSourceFile(fileName: string, sourceText: string, languageVersion: ScriptTarget, setParentNodes = false, scriptKind?: ScriptKind): SourceFile {
|
||||
performance.mark("beforeParse");
|
||||
let result: SourceFile;
|
||||
@@ -903,31 +973,14 @@ namespace ts {
|
||||
// a syntax tree, and no semantic features, then the binding process is an unnecessary
|
||||
// overhead. This functions allows us to set all the parents, without all the expense of
|
||||
// binding.
|
||||
forEachChildRecursively(rootNode, bindParentToChild);
|
||||
|
||||
const stack: Node[] = [rootNode];
|
||||
while (stack.length) {
|
||||
const parent = stack.pop()!;
|
||||
bindParentToChildren(parent, gatherChildren(parent));
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
function gatherChildren(node: Node) {
|
||||
const children: Node[] = [];
|
||||
forEachChild(node, n => { children.unshift(n); }); // By using a stack above and `unshift` here, we emulate a depth-first preorder traversal
|
||||
return children;
|
||||
}
|
||||
|
||||
function bindParentToChildren(parent: Node, children: readonly Node[]) {
|
||||
for (const child of children) {
|
||||
if (child.parent === parent) continue; // already bound, assume subtree is bound
|
||||
child.parent = parent;
|
||||
stack.push(child);
|
||||
if (hasJSDocNodes(child)) {
|
||||
for (const jsDoc of child.jsDoc!) {
|
||||
jsDoc.parent = child;
|
||||
stack.push(jsDoc);
|
||||
}
|
||||
function bindParentToChild(child: Node, parent: Node) {
|
||||
child.parent = parent;
|
||||
if (hasJSDocNodes(child)) {
|
||||
for (const doc of child.jsDoc!) {
|
||||
bindParentToChild(doc, child);
|
||||
forEachChildRecursively(doc, bindParentToChild);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1779,12 +1779,12 @@ namespace ts {
|
||||
function getJSSyntacticDiagnosticsForFile(sourceFile: SourceFile): DiagnosticWithLocation[] {
|
||||
return runWithCancellationToken(() => {
|
||||
const diagnostics: DiagnosticWithLocation[] = [];
|
||||
let parent: Node = sourceFile;
|
||||
walk(sourceFile);
|
||||
walk(sourceFile, sourceFile);
|
||||
forEachChildRecursively(sourceFile, walk, walkArray);
|
||||
|
||||
return diagnostics;
|
||||
|
||||
function walk(node: Node) {
|
||||
function walk(node: Node, parent: Node) {
|
||||
// Return directly from the case if the given node doesnt want to visit each child
|
||||
// Otherwise break to visit each child
|
||||
|
||||
@@ -1794,7 +1794,7 @@ namespace ts {
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
if ((<ParameterDeclaration | PropertyDeclaration | MethodDeclaration>parent).questionToken === node) {
|
||||
diagnostics.push(createDiagnosticForNode(node, Diagnostics.The_0_modifier_can_only_be_used_in_TypeScript_files, "?"));
|
||||
return;
|
||||
return "skip";
|
||||
}
|
||||
// falls through
|
||||
case SyntaxKind.MethodSignature:
|
||||
@@ -1808,7 +1808,7 @@ namespace ts {
|
||||
// type annotation
|
||||
if ((<FunctionLikeDeclaration | VariableDeclaration | ParameterDeclaration | PropertyDeclaration>parent).type === node) {
|
||||
diagnostics.push(createDiagnosticForNode(node, Diagnostics.Type_annotations_can_only_be_used_in_TypeScript_files));
|
||||
return;
|
||||
return "skip";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1816,65 +1816,60 @@ namespace ts {
|
||||
case SyntaxKind.ImportClause:
|
||||
if ((node as ImportClause).isTypeOnly) {
|
||||
diagnostics.push(createDiagnosticForNode(node.parent, Diagnostics._0_declarations_can_only_be_used_in_TypeScript_files, "import type"));
|
||||
return;
|
||||
return "skip";
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.ExportDeclaration:
|
||||
if ((node as ExportDeclaration).isTypeOnly) {
|
||||
diagnostics.push(createDiagnosticForNode(node, Diagnostics._0_declarations_can_only_be_used_in_TypeScript_files, "export type"));
|
||||
return;
|
||||
return "skip";
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.ImportEqualsDeclaration:
|
||||
diagnostics.push(createDiagnosticForNode(node, Diagnostics.import_can_only_be_used_in_TypeScript_files));
|
||||
return;
|
||||
return "skip";
|
||||
case SyntaxKind.ExportAssignment:
|
||||
if ((<ExportAssignment>node).isExportEquals) {
|
||||
diagnostics.push(createDiagnosticForNode(node, Diagnostics.export_can_only_be_used_in_TypeScript_files));
|
||||
return;
|
||||
return "skip";
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.HeritageClause:
|
||||
const heritageClause = <HeritageClause>node;
|
||||
if (heritageClause.token === SyntaxKind.ImplementsKeyword) {
|
||||
diagnostics.push(createDiagnosticForNode(node, Diagnostics.implements_clauses_can_only_be_used_in_TypeScript_files));
|
||||
return;
|
||||
return "skip";
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.InterfaceDeclaration:
|
||||
const interfaceKeyword = tokenToString(SyntaxKind.InterfaceKeyword);
|
||||
Debug.assertIsDefined(interfaceKeyword);
|
||||
diagnostics.push(createDiagnosticForNode(node, Diagnostics._0_declarations_can_only_be_used_in_TypeScript_files, interfaceKeyword));
|
||||
return;
|
||||
return "skip";
|
||||
case SyntaxKind.ModuleDeclaration:
|
||||
const moduleKeyword = node.flags & NodeFlags.Namespace ? tokenToString(SyntaxKind.NamespaceKeyword) : tokenToString(SyntaxKind.ModuleKeyword);
|
||||
Debug.assertIsDefined(moduleKeyword);
|
||||
diagnostics.push(createDiagnosticForNode(node, Diagnostics._0_declarations_can_only_be_used_in_TypeScript_files, moduleKeyword));
|
||||
return;
|
||||
return "skip";
|
||||
case SyntaxKind.TypeAliasDeclaration:
|
||||
diagnostics.push(createDiagnosticForNode(node, Diagnostics.Type_aliases_can_only_be_used_in_TypeScript_files));
|
||||
return;
|
||||
return "skip";
|
||||
case SyntaxKind.EnumDeclaration:
|
||||
const enumKeyword = Debug.checkDefined(tokenToString(SyntaxKind.EnumKeyword));
|
||||
diagnostics.push(createDiagnosticForNode(node, Diagnostics._0_declarations_can_only_be_used_in_TypeScript_files, enumKeyword));
|
||||
return;
|
||||
return "skip";
|
||||
case SyntaxKind.NonNullExpression:
|
||||
diagnostics.push(createDiagnosticForNode(node, Diagnostics.Non_null_assertions_can_only_be_used_in_TypeScript_files));
|
||||
return;
|
||||
return "skip";
|
||||
case SyntaxKind.AsExpression:
|
||||
diagnostics.push(createDiagnosticForNode((node as AsExpression).type, Diagnostics.Type_assertion_expressions_can_only_be_used_in_TypeScript_files));
|
||||
return;
|
||||
return "skip";
|
||||
case SyntaxKind.TypeAssertionExpression:
|
||||
Debug.fail(); // Won't parse these in a JS file anyway, as they are interpreted as JSX.
|
||||
}
|
||||
|
||||
const prevParent = parent;
|
||||
parent = node;
|
||||
forEachChild(node, walk, walkArray);
|
||||
parent = prevParent;
|
||||
}
|
||||
|
||||
function walkArray(nodes: NodeArray<Node>) {
|
||||
function walkArray(nodes: NodeArray<Node>, parent: Node) {
|
||||
if (parent.decorators === nodes && !options.experimentalDecorators) {
|
||||
diagnostics.push(createDiagnosticForNode(parent, Diagnostics.Experimental_support_for_decorators_is_a_feature_that_is_subject_to_change_in_a_future_release_Set_the_experimentalDecorators_option_in_your_tsconfig_or_jsconfig_to_remove_this_warning));
|
||||
}
|
||||
@@ -1892,14 +1887,15 @@ namespace ts {
|
||||
// Check type parameters
|
||||
if (nodes === (<DeclarationWithTypeParameterChildren>parent).typeParameters) {
|
||||
diagnostics.push(createDiagnosticForNodeArray(nodes, Diagnostics.Type_parameter_declarations_can_only_be_used_in_TypeScript_files));
|
||||
return;
|
||||
return "skip";
|
||||
}
|
||||
// falls through
|
||||
|
||||
case SyntaxKind.VariableStatement:
|
||||
// Check modifiers
|
||||
if (nodes === parent.modifiers) {
|
||||
return checkModifiers(parent.modifiers, parent.kind === SyntaxKind.VariableStatement);
|
||||
checkModifiers(parent.modifiers, parent.kind === SyntaxKind.VariableStatement);
|
||||
return "skip";
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.PropertyDeclaration:
|
||||
@@ -1910,14 +1906,14 @@ namespace ts {
|
||||
diagnostics.push(createDiagnosticForNode(modifier, Diagnostics.The_0_modifier_can_only_be_used_in_TypeScript_files, tokenToString(modifier.kind)));
|
||||
}
|
||||
}
|
||||
return;
|
||||
return "skip";
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.Parameter:
|
||||
// Check modifiers of parameter declaration
|
||||
if (nodes === (<ParameterDeclaration>parent).modifiers) {
|
||||
diagnostics.push(createDiagnosticForNodeArray(nodes, Diagnostics.Parameter_modifiers_can_only_be_used_in_TypeScript_files));
|
||||
return;
|
||||
return "skip";
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.CallExpression:
|
||||
@@ -1929,14 +1925,10 @@ namespace ts {
|
||||
// Check type arguments
|
||||
if (nodes === (<NodeWithTypeArguments>parent).typeArguments) {
|
||||
diagnostics.push(createDiagnosticForNodeArray(nodes, Diagnostics.Type_arguments_can_only_be_used_in_TypeScript_files));
|
||||
return;
|
||||
return "skip";
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
for (const node of nodes) {
|
||||
walk(node);
|
||||
}
|
||||
}
|
||||
|
||||
function checkModifiers(modifiers: NodeArray<Modifier>, isConstValid: boolean) {
|
||||
|
||||
Reference in New Issue
Block a user