Skip to content

[WIP] Improve optional chaining checker performance #33794

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Oct 19, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/compiler/binder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -948,7 +948,7 @@ namespace ts {
}
if (expression.kind === SyntaxKind.TrueKeyword && flags & FlowFlags.FalseCondition ||
expression.kind === SyntaxKind.FalseKeyword && flags & FlowFlags.TrueCondition) {
if (!isOptionalChainRoot(expression.parent)) {
if (!isExpressionOfOptionalChainRoot(expression)) {
return unreachableFlow;
}
}
Expand Down
300 changes: 167 additions & 133 deletions src/compiler/checker.ts

Large diffs are not rendered by default.

26 changes: 18 additions & 8 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3443,8 +3443,7 @@ namespace ts {
resolvedReturnType: Type,
typePredicate: TypePredicate | undefined,
minArgumentCount: number,
hasRestParameter: boolean,
hasLiteralTypes: boolean,
flags: SignatureFlags
): Signature;
/* @internal */ createSymbol(flags: SymbolFlags, name: __String): TransientSymbol;
/* @internal */ createIndexInfo(type: Type, isReadonly: boolean, declaration?: SignatureDeclaration): IndexInfo;
Expand Down Expand Up @@ -4655,7 +4654,22 @@ namespace ts {
Construct,
}

/* @internal */
export const enum SignatureFlags {
None = 0,
HasRestParameter = 1 << 0, // Indicates last parameter is rest parameter
HasLiteralTypes = 1 << 1, // Indicates signature is specialized
IsOptionalCall = 1 << 2, // Indicates signature comes from a CallChain

// We do not propagate `IsOptionalCall` to instantiated signatures, as that would result in us
// attempting to add `| undefined` on each recursive call to `getReturnTypeOfSignature` when
// instantiating the return type.
PropagatingFlags = HasRestParameter | HasLiteralTypes,
}

export interface Signature {
/* @internal */ flags: SignatureFlags;
/* @internal */ checker?: TypeChecker;
declaration?: SignatureDeclaration | JSDocSignature; // Originating declaration
typeParameters?: readonly TypeParameter[]; // Type parameters (undefined if non-generic)
parameters: readonly Symbol[]; // Parameters
Expand All @@ -4672,10 +4686,6 @@ namespace ts {
/* @internal */
minArgumentCount: number; // Number of non-optional parameters
/* @internal */
hasRestParameter: boolean; // True if last parameter is rest parameter
/* @internal */
hasLiteralTypes: boolean; // True if specialized
/* @internal */
target?: Signature; // Instantiation target
/* @internal */
mapper?: TypeMapper; // Instantiation mapper
Expand All @@ -4686,11 +4696,11 @@ namespace ts {
/* @internal */
canonicalSignatureCache?: Signature; // Canonical version of signature (deferred)
/* @internal */
optionalCallSignatureCache?: Signature; // Optional chained call version of signature (deferred)
/* @internal */
isolatedSignatureType?: ObjectType; // A manufactured type that just contains the signature for purposes of signature comparison
/* @internal */
instantiations?: Map<Signature>; // Generic signature instantiation cache
/* @internal */
isOptionalCall?: boolean;
}

export const enum IndexKind {
Expand Down
17 changes: 15 additions & 2 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5913,6 +5913,14 @@ namespace ts {
|| kind === SyntaxKind.CallExpression);
}

/**
* Determines whether a node is the expression preceding an optional chain (i.e. `a` in `a?.b`).
*/
/* @internal */
export function isExpressionOfOptionalChainRoot(node: Node): node is Expression & { parent: OptionalChainRoot } {
return isOptionalChainRoot(node.parent) && node.parent.expression === node;
}

export function isNewExpression(node: Node): node is NewExpression {
return node.kind === SyntaxKind.NewExpression;
}
Expand Down Expand Up @@ -7312,7 +7320,7 @@ namespace ts {
getSourceFileConstructor(): new (kind: SyntaxKind.SourceFile, pos?: number, end?: number) => SourceFile;
getSymbolConstructor(): new (flags: SymbolFlags, name: __String) => Symbol;
getTypeConstructor(): new (checker: TypeChecker, flags: TypeFlags) => Type;
getSignatureConstructor(): new (checker: TypeChecker) => Signature;
getSignatureConstructor(): new (checker: TypeChecker, flags: SignatureFlags) => Signature;
getSourceMapSourceConstructor(): new (fileName: string, text: string, skipTrivia?: (pos: number) => number) => SourceMapSource;
}

Expand All @@ -7333,7 +7341,12 @@ namespace ts {
}
}

function Signature() {}
function Signature(this: Signature, checker: TypeChecker, flags: SignatureFlags) {
this.flags = flags;
if (Debug.isDebugging) {
this.checker = checker;
}
}

function Node(this: Node, kind: SyntaxKind, pos: number, end: number) {
this.pos = pos;
Expand Down
6 changes: 3 additions & 3 deletions src/services/codefixes/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,14 +233,14 @@ namespace ts.codefix {
let someSigHasRestParameter = false;
for (const sig of signatures) {
minArgumentCount = Math.min(sig.minArgumentCount, minArgumentCount);
if (sig.hasRestParameter) {
if (signatureHasRestParameter(sig)) {
someSigHasRestParameter = true;
}
if (sig.parameters.length >= maxArgsSignature.parameters.length && (!sig.hasRestParameter || maxArgsSignature.hasRestParameter)) {
if (sig.parameters.length >= maxArgsSignature.parameters.length && (!signatureHasRestParameter(sig) || signatureHasRestParameter(maxArgsSignature))) {
maxArgsSignature = sig;
}
}
const maxNonRestArgs = maxArgsSignature.parameters.length - (maxArgsSignature.hasRestParameter ? 1 : 0);
const maxNonRestArgs = maxArgsSignature.parameters.length - (signatureHasRestParameter(maxArgsSignature) ? 1 : 0);
const maxArgsParameterSymbolNames = maxArgsSignature.parameters.map(symbol => symbol.name);

const parameters = createDummyParameters(maxNonRestArgs, maxArgsParameterSymbolNames, /* types */ undefined, minArgumentCount, /*inJs*/ false);
Expand Down
2 changes: 1 addition & 1 deletion src/services/codefixes/inferFromUsage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1054,7 +1054,7 @@ namespace ts.codefix {
}
const returnType = combineFromUsage(combineUsages(calls.map(call => call.return_)));
// TODO: GH#18217
return checker.createSignature(/*declaration*/ undefined!, /*typeParameters*/ undefined, /*thisParameter*/ undefined, parameters, returnType, /*typePredicate*/ undefined, length, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false);
return checker.createSignature(/*declaration*/ undefined!, /*typeParameters*/ undefined, /*thisParameter*/ undefined, parameters, returnType, /*typePredicate*/ undefined, length, SignatureFlags.None);
}

function addCandidateType(usage: Usage, type: Type | undefined) {
Expand Down
7 changes: 4 additions & 3 deletions src/services/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,7 @@ namespace ts {
}

class SignatureObject implements Signature {
flags: SignatureFlags;
checker: TypeChecker;
declaration!: SignatureDeclaration;
typeParameters?: TypeParameter[];
Expand All @@ -473,8 +474,6 @@ namespace ts {
resolvedTypePredicate: TypePredicate | undefined;
minTypeArgumentCount!: number;
minArgumentCount!: number;
hasRestParameter!: boolean;
hasLiteralTypes!: boolean;

// Undefined is used to indicate the value has not been computed. If, after computing, the
// symbol has no doc comment, then the empty array will be returned.
Expand All @@ -484,9 +483,11 @@ namespace ts {
// symbol has no doc comment, then the empty array will be returned.
jsDocTags?: JSDocTagInfo[];

constructor(checker: TypeChecker) {
constructor(checker: TypeChecker, flags: SignatureFlags) {
this.checker = checker;
this.flags = flags;
}

getDeclaration(): SignatureDeclaration {
return this.declaration;
}
Expand Down
2 changes: 1 addition & 1 deletion src/services/stringCompletions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ namespace ts.Completions.StringCompletions {
const candidates: Signature[] = [];
checker.getResolvedSignature(argumentInfo.invocation, candidates, argumentInfo.argumentCount);
const types = flatMap(candidates, candidate => {
if (!candidate.hasRestParameter && argumentInfo.argumentCount > candidate.parameters.length) return;
if (!signatureHasRestParameter(candidate) && argumentInfo.argumentCount > candidate.parameters.length) return;
const type = checker.getParameterType(candidate, argumentInfo.argumentIndex);
isNewIdentifier = isNewIdentifier || !!(type.flags & TypeFlags.String);
return getStringLiteralTypes(type, uniques);
Expand Down