From f7d169fbced0c6fc6c4388fe1c22c1ac57de021d Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Tue, 21 Jun 2022 12:07:07 +0300 Subject: [PATCH 01/10] init --- src/parser.ts | 29 +++++++++++++++++++++++++++++ tests/parser/type.ts | 6 ++++++ tests/parser/type.ts.fixture.ts | 4 ++++ 3 files changed, 39 insertions(+) diff --git a/src/parser.ts b/src/parser.ts index b9dc2fc7b3..e222de1966 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -3478,6 +3478,28 @@ export class Parser extends DiagnosticEmitter { return null; } + private checkRecurseDefinitionForType(identifierName: string, type: TypeNode): bool { + switch (type.kind) { + case NodeKind.NAMEDTYPE: + return (type).name.identifier.text == identifierName; + + case NodeKind.FUNCTIONTYPE: { + let fnType = type; + if (this.checkRecurseDefinitionForType(identifierName, fnType.returnType)) { + return true; + } + let params = fnType.parameters; + for (let i = 0, k = params.length; i < k; i++) { + if (this.checkRecurseDefinitionForType(identifierName, params[i].type)) { + return true; + } + } + break; + } + } + return false; + } + parseTypeDeclaration( tn: Tokenizer, flags: CommonFlags, @@ -3499,6 +3521,13 @@ export class Parser extends DiagnosticEmitter { tn.skip(Token.BAR); let type = this.parseType(tn); if (!type) return null; + if (this.checkRecurseDefinitionForType(name.text, type)) { + this.error( + DiagnosticCode.Not_implemented_0, + tn.range(), "recursion in type aliases" + ); + return null; + } let ret = Node.createTypeDeclaration( name, decorators, diff --git a/tests/parser/type.ts b/tests/parser/type.ts index dc6e5175db..825a45c703 100644 --- a/tests/parser/type.ts +++ b/tests/parser/type.ts @@ -6,3 +6,9 @@ export type uint64_t = u64; export type T1 = | int32_t; export type T2 = | int32_t; + +// disallow type recursion +export type T3 = T3 | null; +export type T4 = (x: T4) => i32; +export type T5 = () => T5; +export type T6 = () => T6; diff --git a/tests/parser/type.ts.fixture.ts b/tests/parser/type.ts.fixture.ts index d58bbc9ce4..1fb443afd2 100644 --- a/tests/parser/type.ts.fixture.ts +++ b/tests/parser/type.ts.fixture.ts @@ -3,3 +3,7 @@ type int32_t = i32; export type uint64_t = u64; export type T1 = int32_t; export type T2 = int32_t; +// ERROR 100: "Not implemented: recursion in type aliases" in type.ts(11,23+4) +// ERROR 100: "Not implemented: recursion in type aliases" in type.ts(12,29+3) +// ERROR 100: "Not implemented: recursion in type aliases" in type.ts(13,24+2) +// ERROR 100: "Not implemented: recursion in type aliases" in type.ts(14,31+1) From d6a31746f312765517e167b4fdefdcc0f1ead18e Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Tue, 21 Jun 2022 12:55:27 +0300 Subject: [PATCH 02/10] rename helper function --- src/parser.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/parser.ts b/src/parser.ts index e222de1966..95843fd5c1 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -3478,19 +3478,19 @@ export class Parser extends DiagnosticEmitter { return null; } - private checkRecurseDefinitionForType(identifierName: string, type: TypeNode): bool { + private checkRecursiveTypeDeclaration(identifierName: string, type: TypeNode): bool { switch (type.kind) { case NodeKind.NAMEDTYPE: return (type).name.identifier.text == identifierName; case NodeKind.FUNCTIONTYPE: { let fnType = type; - if (this.checkRecurseDefinitionForType(identifierName, fnType.returnType)) { + if (this.checkRecursiveTypeDeclaration(identifierName, fnType.returnType)) { return true; } let params = fnType.parameters; for (let i = 0, k = params.length; i < k; i++) { - if (this.checkRecurseDefinitionForType(identifierName, params[i].type)) { + if (this.checkRecursiveTypeDeclaration(identifierName, params[i].type)) { return true; } } @@ -3521,7 +3521,7 @@ export class Parser extends DiagnosticEmitter { tn.skip(Token.BAR); let type = this.parseType(tn); if (!type) return null; - if (this.checkRecurseDefinitionForType(name.text, type)) { + if (this.checkRecursiveTypeDeclaration(name.text, type)) { this.error( DiagnosticCode.Not_implemented_0, tn.range(), "recursion in type aliases" From e598a4c06e2e2d24d1f75b10e22849faa0546cb9 Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Wed, 22 Jun 2022 09:05:43 +0300 Subject: [PATCH 03/10] uppercase --- src/parser.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parser.ts b/src/parser.ts index 95843fd5c1..97e75143d8 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -3524,7 +3524,7 @@ export class Parser extends DiagnosticEmitter { if (this.checkRecursiveTypeDeclaration(name.text, type)) { this.error( DiagnosticCode.Not_implemented_0, - tn.range(), "recursion in type aliases" + tn.range(), "Recursion in type aliases" ); return null; } From 307a27927a805365554795eb1be7309a7d951d35 Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Wed, 22 Jun 2022 09:08:46 +0300 Subject: [PATCH 04/10] update fixture --- tests/parser/type.ts.fixture.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/parser/type.ts.fixture.ts b/tests/parser/type.ts.fixture.ts index 1fb443afd2..bc5a6cab6b 100644 --- a/tests/parser/type.ts.fixture.ts +++ b/tests/parser/type.ts.fixture.ts @@ -3,7 +3,7 @@ type int32_t = i32; export type uint64_t = u64; export type T1 = int32_t; export type T2 = int32_t; -// ERROR 100: "Not implemented: recursion in type aliases" in type.ts(11,23+4) -// ERROR 100: "Not implemented: recursion in type aliases" in type.ts(12,29+3) -// ERROR 100: "Not implemented: recursion in type aliases" in type.ts(13,24+2) -// ERROR 100: "Not implemented: recursion in type aliases" in type.ts(14,31+1) +// ERROR 100: "Not implemented: Recursion in type aliases" in type.ts(11,23+4) +// ERROR 100: "Not implemented: Recursion in type aliases" in type.ts(12,29+3) +// ERROR 100: "Not implemented: Recursion in type aliases" in type.ts(13,24+2) +// ERROR 100: "Not implemented: Recursion in type aliases" in type.ts(14,31+1) From 3519ca0e192cd91cc1e3fb281e60a493095ca5ef Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Wed, 22 Jun 2022 10:15:23 +0300 Subject: [PATCH 05/10] improve diagnostics --- src/diagnosticMessages.json | 1 + src/parser.ts | 45 +++++++++++++++++++++------------ tests/parser/type.ts.fixture.ts | 2 +- 3 files changed, 31 insertions(+), 17 deletions(-) diff --git a/src/diagnosticMessages.json b/src/diagnosticMessages.json index 5c9a03e6f2..a69e5d608f 100644 --- a/src/diagnosticMessages.json +++ b/src/diagnosticMessages.json @@ -158,6 +158,7 @@ "Variable '{0}' used before its declaration.": 2448, "Cannot redeclare block-scoped variable '{0}'" : 2451, "The type argument for type parameter '{0}' cannot be inferred from the usage. Consider specifying the type arguments explicitly.": 2453, + "Type alias '{0}' circularly references itself.": 2456, "Type '{0}' has no property '{1}'.": 2460, "The '{0}' operator cannot be applied to type '{1}'.": 2469, "In 'const' enum declarations member initializer must be constant expression.": 2474, diff --git a/src/parser.ts b/src/parser.ts index 97e75143d8..612c51e29b 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -3478,26 +3478,31 @@ export class Parser extends DiagnosticEmitter { return null; } - private checkRecursiveTypeDeclaration(identifierName: string, type: TypeNode): bool { + private getRecursiveDepthForTypeDeclaration( + identifierName: string, + type: TypeNode, + depth: i32 = 0 + ): i32 { switch (type.kind) { - case NodeKind.NAMEDTYPE: - return (type).name.identifier.text == identifierName; - + case NodeKind.NAMEDTYPE: { + if ((type).name.identifier.text == identifierName) { + return depth; + } + break; + } case NodeKind.FUNCTIONTYPE: { let fnType = type; - if (this.checkRecursiveTypeDeclaration(identifierName, fnType.returnType)) { - return true; - } + let res = this.getRecursiveDepthForTypeDeclaration(identifierName, fnType.returnType, depth + 1); + if (res != -1) return res; let params = fnType.parameters; for (let i = 0, k = params.length; i < k; i++) { - if (this.checkRecursiveTypeDeclaration(identifierName, params[i].type)) { - return true; - } + res = this.getRecursiveDepthForTypeDeclaration(identifierName, params[i].type, depth + 1); + if (res != -1) return res; } break; } } - return false; + return -1; } parseTypeDeclaration( @@ -3521,11 +3526,19 @@ export class Parser extends DiagnosticEmitter { tn.skip(Token.BAR); let type = this.parseType(tn); if (!type) return null; - if (this.checkRecursiveTypeDeclaration(name.text, type)) { - this.error( - DiagnosticCode.Not_implemented_0, - tn.range(), "Recursion in type aliases" - ); + let depth = this.getRecursiveDepthForTypeDeclaration(name.text, type); + if (depth >= 0) { + if (depth == 0) { + this.error( + DiagnosticCode.Type_alias_0_circularly_references_itself, + tn.range(), name.text + ); + } else { + this.error( + DiagnosticCode.Not_implemented_0, + tn.range(), "Recursion in type aliases" + ); + } return null; } let ret = Node.createTypeDeclaration( diff --git a/tests/parser/type.ts.fixture.ts b/tests/parser/type.ts.fixture.ts index bc5a6cab6b..12f3ee239d 100644 --- a/tests/parser/type.ts.fixture.ts +++ b/tests/parser/type.ts.fixture.ts @@ -3,7 +3,7 @@ type int32_t = i32; export type uint64_t = u64; export type T1 = int32_t; export type T2 = int32_t; -// ERROR 100: "Not implemented: Recursion in type aliases" in type.ts(11,23+4) +// ERROR 2456: "Type alias 'T3' circularly references itself." in type.ts(11,23+4) // ERROR 100: "Not implemented: Recursion in type aliases" in type.ts(12,29+3) // ERROR 100: "Not implemented: Recursion in type aliases" in type.ts(13,24+2) // ERROR 100: "Not implemented: Recursion in type aliases" in type.ts(14,31+1) From 641efa7dbeda5689c3cb9238d0a6b3de6421d5af Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Thu, 23 Jun 2022 12:56:02 +0300 Subject: [PATCH 06/10] also verify type arguments --- src/parser.ts | 10 ++++++++++ tests/parser/type.ts | 2 ++ tests/parser/type.ts.fixture.ts | 2 ++ 3 files changed, 14 insertions(+) diff --git a/src/parser.ts b/src/parser.ts index 612c51e29b..3212061b84 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -3485,6 +3485,16 @@ export class Parser extends DiagnosticEmitter { ): i32 { switch (type.kind) { case NodeKind.NAMEDTYPE: { + let typeArguments = (type).typeArguments; + if (typeArguments) { + for (let i = 0, k = typeArguments.length; i < k; i++) { + let typeArg = typeArguments[i]; + if (typeArg.kind == NodeKind.NAMEDTYPE) { + let res = this.getRecursiveDepthForTypeDeclaration(identifierName, typeArg, depth + 1); + if (res != -1) return res; + } + } + } if ((type).name.identifier.text == identifierName) { return depth; } diff --git a/tests/parser/type.ts b/tests/parser/type.ts index 825a45c703..59d8579b63 100644 --- a/tests/parser/type.ts +++ b/tests/parser/type.ts @@ -12,3 +12,5 @@ export type T3 = T3 | null; export type T4 = (x: T4) => i32; export type T5 = () => T5; export type T6 = () => T6; +export type T7 = Array; +export type T8 = Map>; diff --git a/tests/parser/type.ts.fixture.ts b/tests/parser/type.ts.fixture.ts index 12f3ee239d..baa1c30c25 100644 --- a/tests/parser/type.ts.fixture.ts +++ b/tests/parser/type.ts.fixture.ts @@ -7,3 +7,5 @@ export type T2 = int32_t; // ERROR 100: "Not implemented: Recursion in type aliases" in type.ts(12,29+3) // ERROR 100: "Not implemented: Recursion in type aliases" in type.ts(13,24+2) // ERROR 100: "Not implemented: Recursion in type aliases" in type.ts(14,31+1) +// ERROR 100: "Not implemented: Recursion in type aliases" in type.ts(15,26+1) +// ERROR 100: "Not implemented: Recursion in type aliases" in type.ts(16,39+1) From f7434039613d7aa7fabd30da1e68522a5078dd88 Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Thu, 23 Jun 2022 13:09:18 +0300 Subject: [PATCH 07/10] better --- src/parser.ts | 17 +++++++---------- tests/parser/type.ts | 1 + tests/parser/type.ts.fixture.ts | 1 + 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/parser.ts b/src/parser.ts index 3212061b84..78e197e4e3 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -3483,18 +3483,15 @@ export class Parser extends DiagnosticEmitter { type: TypeNode, depth: i32 = 0 ): i32 { + let typeArguments = (type).typeArguments; + if (typeArguments) { + for (let i = 0, k = typeArguments.length; i < k; i++) { + let res = this.getRecursiveDepthForTypeDeclaration(identifierName, typeArguments[i], depth + 1); + if (res != -1) return res; + } + } switch (type.kind) { case NodeKind.NAMEDTYPE: { - let typeArguments = (type).typeArguments; - if (typeArguments) { - for (let i = 0, k = typeArguments.length; i < k; i++) { - let typeArg = typeArguments[i]; - if (typeArg.kind == NodeKind.NAMEDTYPE) { - let res = this.getRecursiveDepthForTypeDeclaration(identifierName, typeArg, depth + 1); - if (res != -1) return res; - } - } - } if ((type).name.identifier.text == identifierName) { return depth; } diff --git a/tests/parser/type.ts b/tests/parser/type.ts index 59d8579b63..8a9c28dac2 100644 --- a/tests/parser/type.ts +++ b/tests/parser/type.ts @@ -14,3 +14,4 @@ export type T5 = () => T5; export type T6 = () => T6; export type T7 = Array; export type T8 = Map>; +export type T9 = Array<() => T9>; diff --git a/tests/parser/type.ts.fixture.ts b/tests/parser/type.ts.fixture.ts index baa1c30c25..0977bdd3a5 100644 --- a/tests/parser/type.ts.fixture.ts +++ b/tests/parser/type.ts.fixture.ts @@ -9,3 +9,4 @@ export type T2 = int32_t; // ERROR 100: "Not implemented: Recursion in type aliases" in type.ts(14,31+1) // ERROR 100: "Not implemented: Recursion in type aliases" in type.ts(15,26+1) // ERROR 100: "Not implemented: Recursion in type aliases" in type.ts(16,39+1) +// ERROR 100: "Not implemented: Recursion in type aliases" in type.ts(17,32+1) From 55b57d57ae9482a8864c9386cfda97c9fbd6f850 Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Thu, 23 Jun 2022 13:15:08 +0300 Subject: [PATCH 08/10] var --- src/parser.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parser.ts b/src/parser.ts index 78e197e4e3..e6ff5c026a 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -3483,7 +3483,7 @@ export class Parser extends DiagnosticEmitter { type: TypeNode, depth: i32 = 0 ): i32 { - let typeArguments = (type).typeArguments; + var typeArguments = (type).typeArguments; if (typeArguments) { for (let i = 0, k = typeArguments.length; i < k; i++) { let res = this.getRecursiveDepthForTypeDeclaration(identifierName, typeArguments[i], depth + 1); From 0e87ec7f7be4e377baf7b353bf6a848e4fca88f4 Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Thu, 23 Jun 2022 13:20:25 +0300 Subject: [PATCH 09/10] revert --- src/parser.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/parser.ts b/src/parser.ts index e6ff5c026a..664435260e 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -3483,15 +3483,15 @@ export class Parser extends DiagnosticEmitter { type: TypeNode, depth: i32 = 0 ): i32 { - var typeArguments = (type).typeArguments; - if (typeArguments) { - for (let i = 0, k = typeArguments.length; i < k; i++) { - let res = this.getRecursiveDepthForTypeDeclaration(identifierName, typeArguments[i], depth + 1); - if (res != -1) return res; - } - } switch (type.kind) { case NodeKind.NAMEDTYPE: { + let typeArguments = (type).typeArguments; + if (typeArguments) { + for (let i = 0, k = typeArguments.length; i < k; i++) { + let res = this.getRecursiveDepthForTypeDeclaration(identifierName, typeArguments[i], depth + 1); + if (res != -1) return res; + } + } if ((type).name.identifier.text == identifierName) { return depth; } From 27de94df776d9079be390cc618152f6dd6991664 Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Thu, 23 Jun 2022 13:36:12 +0300 Subject: [PATCH 10/10] more tests --- tests/parser/type.ts | 1 + tests/parser/type.ts.fixture.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/tests/parser/type.ts b/tests/parser/type.ts index 8a9c28dac2..7099da72d8 100644 --- a/tests/parser/type.ts +++ b/tests/parser/type.ts @@ -15,3 +15,4 @@ export type T6 = () => T6; export type T7 = Array; export type T8 = Map>; export type T9 = Array<() => T9>; +export type T10 = T6; diff --git a/tests/parser/type.ts.fixture.ts b/tests/parser/type.ts.fixture.ts index 0977bdd3a5..c6814cac17 100644 --- a/tests/parser/type.ts.fixture.ts +++ b/tests/parser/type.ts.fixture.ts @@ -10,3 +10,4 @@ export type T2 = int32_t; // ERROR 100: "Not implemented: Recursion in type aliases" in type.ts(15,26+1) // ERROR 100: "Not implemented: Recursion in type aliases" in type.ts(16,39+1) // ERROR 100: "Not implemented: Recursion in type aliases" in type.ts(17,32+1) +// ERROR 100: "Not implemented: Recursion in type aliases" in type.ts(18,25+1)