Merge branch 'main' into henrymercer/diagnostics-verify-one-based

This commit is contained in:
Henry Mercer
2023-04-26 17:26:36 +01:00
committed by GitHub
211 changed files with 30278 additions and 21451 deletions

View File

@@ -43,6 +43,9 @@ function isTypeAlwaysSafeToExpand(type: ts.Type): boolean {
return false;
}
}
if (type.aliasSymbol != null) {
return false;
}
return true;
}
@@ -447,7 +450,7 @@ export class TypeTable {
this.isInShallowTypeContext ? TypeExtractionState.PendingShallow : TypeExtractionState.PendingFull);
// If the type is the self-type for a named type (not a generic instantiation of it),
// emit the self-type binding for that type.
if (content.startsWith("reference;") && !(isTypeReference(type) && type.target !== type)) {
if (content.startsWith("reference;") && isTypeSelfReference(type)) {
this.selfTypes.symbols.push(this.getSymbolId(type.aliasSymbol || type.symbol));
this.selfTypes.selfTypes.push(id);
}
@@ -533,7 +536,7 @@ export class TypeTable {
let enclosingType = getEnclosingTypeOfThisType(type);
if (enclosingType != null) {
return "this;" + this.getId(enclosingType, false);
} else if (symbol.parent == null) {
} else if (symbol.parent == null || isFunctionTypeOrTypeAlias(symbol.declarations?.[0])) {
// The type variable is bound on a call signature. Only extract it by name.
return "lextypevar;" + symbol.name;
} else {
@@ -614,14 +617,14 @@ export class TypeTable {
// cannot be written using TypeScript syntax - so we ignore them entirely.
return null;
}
return this.makeTypeStringVector("union", unionType.types);
return this.makeDeduplicatedTypeStringVector("union", unionType.types);
}
if (flags & ts.TypeFlags.Intersection) {
let intersectionType = type as ts.IntersectionType;
if (intersectionType.types.length === 0) {
return null; // Ignore malformed type.
}
return this.makeTypeStringVector("intersection", intersectionType.types);
return this.makeDeduplicatedTypeStringVector("intersection", intersectionType.types);
}
if (isTypeReference(type) && (type.target.objectFlags & ts.ObjectFlags.Tuple)) {
// Encode the minimum length and presence of rest element in the first two parts of the type string.
@@ -784,6 +787,27 @@ export class TypeTable {
return hash;
}
/**
* Returns the given string with the IDs of the given types appended,
* ignoring duplicates, and each separated by `;`.
*/
private makeDeduplicatedTypeStringVector(tag: string, types: ReadonlyArray<ts.Type>, length = types.length): string | null {
let seenIds = new Set<number>();
let numberOfSeenIds = 0;
let hash = tag;
for (let i = 0; i < length; ++i) {
let id = this.getId(types[i], false);
if (id == null) return null;
seenIds.add(id);
if (seenIds.size > numberOfSeenIds) {
// This ID was not seen before
++numberOfSeenIds;
hash += ";" + id;
}
}
return hash;
}
/** Returns the type of `symbol` or `null` if it could not be computed. */
private tryGetTypeOfSymbol(symbol: ts.Symbol) {
try {
@@ -1328,3 +1352,35 @@ export class TypeTable {
}
}
}
function isFunctionTypeOrTypeAlias(declaration: ts.Declaration | undefined) {
if (declaration == null) return false;
return declaration.kind === ts.SyntaxKind.FunctionType || declaration.kind === ts.SyntaxKind.TypeAliasDeclaration;
}
/**
* Given a `type` whose type-string is known to be a `reference`, returns true if this is the self-type for the referenced type.
*
* For example, for `type Foo<R> = ...` this returns true if `type` is `Foo<R>`.
*/
function isTypeSelfReference(type: ts.Type) {
if (type.aliasSymbol != null) {
const { aliasTypeArguments } = type;
if (aliasTypeArguments == null) return true;
let declaration = type.aliasSymbol.declarations?.[0];
if (declaration == null || declaration.kind !== ts.SyntaxKind.TypeAliasDeclaration) return false;
let alias = declaration as ts.TypeAliasDeclaration;
for (let i = 0; i < aliasTypeArguments.length; ++i) {
if (aliasTypeArguments[i].symbol?.declarations?.[0] !== alias.typeParameters[i]) {
return false;
}
}
return true;
} else if (isTypeReference(type)) {
return type.target === type;
} else {
// Return true because we know we have mapped this type to kind `reference`, and in the cases
// not covered above (i.e. generic types) it is always a self-reference.
return true;
}
}

View File

@@ -0,0 +1,5 @@
---
category: fix
---
* Fixes an issue that would cause TypeScript extraction to hang in rare cases when extracting
code containing recursive generic type aliases.

View File

@@ -0,0 +1,15 @@
typeAliases
| tst.ts:1:8:1:41 | type Fo ... R \| A>; |
| tst.ts:3:8:3:42 | type Ba ... R, A]>; |
| tst.ts:5:8:5:47 | type Ba ... => A>; |
typeAliasType
| tst.ts:1:8:1:41 | type Fo ... R \| A>; | Foo<R> |
| tst.ts:3:8:3:42 | type Ba ... R, A]>; | Bar<R> |
| tst.ts:5:8:5:47 | type Ba ... => A>; | Baz<R> |
getAliasedType
| Bar<R> | <A>() => Bar<[R, A]> |
| Bar<[R, A]> | <A>() => Bar<[[R, A], A]> |
| Baz<(x: R) => A> | <A>() => Baz<(x: (x: R) => A) => A> |
| Baz<R> | <A>() => Baz<(x: R) => A> |
| Foo<R \| A> | <A>() => Foo<R \| A> |
| Foo<R> | <A>() => Foo<R \| A> |

View File

@@ -0,0 +1,8 @@
import javascript
// The extractor would hang on this test case, it doesn't matter too much what the output of the test is.
query TypeAliasDeclaration typeAliases() { any() }
query Type typeAliasType(TypeAliasDeclaration decl) { result = decl.getTypeName().getType() }
query Type getAliasedType(TypeAliasReference ref) { result = ref.getAliasedType() }

View File

@@ -0,0 +1,3 @@
{
"include": ["."]
}

View File

@@ -0,0 +1,5 @@
export type Foo<R> = <A>() => Foo<R | A>;
export type Bar<R> = <A>() => Bar<[R, A]>;
export type Baz<R> = <A>() => Baz<(x: R) => A>;