-
-
Notifications
You must be signed in to change notification settings - Fork 139
Open
Labels
Description
Did you check existing issues?
- I have read all the tree-sitter docs if it relates to using the parser
- I have searched the existing issues of tree-sitter-typescript
Tree-Sitter CLI Version, if relevant (output of tree-sitter --version)
tree-sitter 0.25.6 (bf655c0beaf4943573543fa77c58e8006ff34971)
Describe the bug
Main Issue
Tree-sitter TypeScript parser fails to parse multiple anonymous generic call signatures (e.g., <T>(value: T): T) when they are separated only by line breaks without semicolons.
Compilation in typescriptlang.org/play
Input code:
interface TestInterface {
<T>(value: T): T
<T>(value: T, other: any): T
}.js (empty, which makes sense)
"use strict";.d.ts
interface TestInterface {
<T>(value: T): T;
<T>(value: T, other: any): T;
}Test Suite
Here is a test suite I wrote when narrowing the bug
import TreeSitter from "tree-sitter";
import pkg from "tree-sitter-typescript";
const { typescript } = pkg;
describe("Treesitter parsing error repro", () => {
const testDirectParsing = (code: string): boolean => {
const parser = new TreeSitter();
parser.setLanguage(typescript);
const tree = parser.parse(code);
const rootNode = tree.rootNode;
return !rootNode.hasError;
};
test("Interface with two generic method overloads fails", () => {
const failingInterface = `interface TestInterface {
<T>(value: T): T
<T>(value: T, other: any): T
}`;
const result = testDirectParsing(failingInterface);
expect(result).toBe(false);
});
test("Interface with three generic method overloads fails", () => {
const failingInterface = `interface TestInterface {
<T>(value: T): T
<T>(value: T, other: any): T
<T>(value: T, other: any, last: string): T
}`;
const result = testDirectParsing(failingInterface);
expect(result).toBe(false);
});
test("Interface with two constructor overloads succeeds", () => {
const failingInterface = `interface TestInterface {
new <T>(value: T): T
new <T>(value: T, other: any): T
}`;
const result = testDirectParsing(failingInterface);
expect(result).toBe(true);
});
test("Interface index signature overloads succeeds", () => {
const failingInterface = `interface TestInterface {
<T>(value: T): T
[key: string]: any
}`;
const result = testDirectParsing(failingInterface);
expect(result).toBe(true);
});
test("Interface mixed signature types succeeds", () => {
const failingInterface = `interface TestInterface {
<T>(value: T): T
process<T>(value: T): T
}`;
const result = testDirectParsing(failingInterface);
expect(result).toBe(true);
});
test("Interface with two generic method overloads and method signatures succeeds", () => {
const failingInterface = `interface TestInterface {
process<T>(value: T): T
process<T>(value: T, other: any): T
}`;
const result = testDirectParsing(failingInterface);
expect(result).toBe(true);
});
test("Interface with two non-generic method overloads succeeds", () => {
const failingInterface = `interface TestInterface {
method(a: string): string
method(a: number): number
}`;
const result = testDirectParsing(failingInterface);
expect(result).toBe(true);
});
test("Interface with two generic method overloads and semicolon separation succeeds", () => {
const failingInterface = `interface TestInterface {
<T>(value: T): T;
<T>(value: T, other: any): T;
}`;
const result = testDirectParsing(failingInterface);
expect(result).toBe(true);
});
test("Interface with two generic method overloads and single semicolon separation succeeds", () => {
const failingInterface = `interface TestInterface {
<T>(value: T): T;
<T>(value: T, other: any): T
}`;
const result = testDirectParsing(failingInterface);
expect(result).toBe(true);
});
test("Interface with two simple attributes succeeds", () => {
const failingInterface = `interface TestInterface {
prop1: string
prop2: string
}`;
const result = testDirectParsing(failingInterface);
expect(result).toBe(true);
});
test("Single generic method works", () => {
const workingInterface = `interface TestInterface {
<T>(value: T): T
}`;
const result = testDirectParsing(workingInterface);
expect(result).toBe(true);
});
test("Single generic method works with 2 parameters", () => {
const workingInterface = `interface TestInterface {
<T>(value: T, other: any): T
}`;
const result = testDirectParsing(workingInterface);
expect(result).toBe(true);
});
});Steps To Reproduce/Bad Parse Tree
(program [0, 0] - [4, 0]
(interface_declaration [0, 0] - [2, 4]
name: (type_identifier [0, 10] - [0, 23])
body: (interface_body [0, 24] - [2, 4]
(call_signature [1, 1] - [2, 4]
type_parameters: (type_parameters [1, 1] - [1, 4]
(type_parameter [1, 2] - [1, 3]
name: (type_identifier [1, 2] - [1, 3])))
parameters: (formal_parameters [1, 4] - [1, 14]
(required_parameter [1, 5] - [1, 13]
pattern: (identifier [1, 5] - [1, 10])
type: (type_annotation [1, 10] - [1, 13]
(type_identifier [1, 12] - [1, 13]))))
return_type: (type_annotation [1, 14] - [2, 4]
(generic_type [1, 16] - [2, 4]
name: (type_identifier [1, 16] - [1, 17])
type_arguments: (type_arguments [2, 1] - [2, 4]
(type_identifier [2, 2] - [2, 3])))))))
(ERROR [2, 4] - [3, 1]
parameters: (formal_parameters [2, 4] - [2, 26]
(required_parameter [2, 5] - [2, 13]
pattern: (identifier [2, 5] - [2, 10])
type: (type_annotation [2, 10] - [2, 13]
(type_identifier [2, 12] - [2, 13])))
(required_parameter [2, 15] - [2, 25]
pattern: (identifier [2, 15] - [2, 20])
type: (type_annotation [2, 20] - [2, 25]
(predefined_type [2, 22] - [2, 25]))))
return_type: (type_annotation [2, 26] - [2, 29]
(type_identifier [2, 28] - [2, 29]))))
test_interface.ts Parse: 0.42 ms 178 bytes/ms (MISSING "}" [2, 4] - [2, 4])
Expected Behavior/Parse Tree
(interface_body [0, 24] - [3, 1]
(call_signature [1, 1] - [1, 17] // First signature
type_parameters: (type_parameters [1, 1] - [1, 4] ...)
parameters: (formal_parameters [1, 4] - [1, 14] ...)
return_type: (type_annotation [1, 14] - [1, 17] ...))
(call_signature [2, 1] - [2, 29] // Second signature
type_parameters: (type_parameters [2, 1] - [2, 4] ...)
parameters: (formal_parameters [2, 4] - [2, 26] ...)
return_type: (type_annotation [2, 26] - [2, 29] ...)))
Repro
interface TestInterface {
<T>(value: T): T
<T>(value: T, other: any): T
}