Skip to content

Don't check an expression if it was checked cached #22580

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

Closed
wants to merge 3 commits into from
Closed
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
36 changes: 22 additions & 14 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -421,8 +421,8 @@ namespace ts {
let deferredGlobalTemplateStringsArrayType: ObjectType;

let deferredNodes: Node[];
const seenDeferredNodes = createMap<true>(); // For assertion that we don't defer the same node twice
let deferredUnusedIdentifierNodes: Node[];
const seenDeferredUnusedIdentifiers = createMap<true>(); // For assertion that we don't defer the same identifier twice

let flowLoopStart = 0;
let flowLoopCount = 0;
Expand Down Expand Up @@ -577,9 +577,10 @@ namespace ts {

const enum CheckMode {
Normal = 0, // Normal type checking
SkipContextSensitive = 1, // Skip context sensitive function expressions
Inferential = 2, // Inferential typing
Contextual = 3, // Normal type checking informed by a contextual type, therefore not cacheable
JustType = 1, // Just return a type and don't report errors
SkipContextSensitive = 2, // Skip context sensitive function expressions
Inferential = 3, // Inferential typing
Contextual = 4, // Normal type checking informed by a contextual type, therefore not cacheable
}

const enum CallbackCheck {
Expand Down Expand Up @@ -19573,14 +19574,14 @@ namespace ts {
const links = getNodeLinks(node);
if (!links.resolvedType) {
if (checkMode) {
return checkExpression(node, checkMode);
return checkExpressionNoCache(node, checkMode);
}
// When computing a type that we're going to cache, we need to ignore any ongoing control flow
// analysis because variables may have transient types in indeterminable states. Moving flowLoopStart
// to the top of the stack ensures all transient types are computed from a known point.
const saveFlowLoopStart = flowLoopStart;
flowLoopStart = flowLoopCount;
links.resolvedType = checkExpression(node, checkMode);
links.resolvedType = checkExpressionNoCache(node, checkMode);
flowLoopStart = saveFlowLoopStart;
}
return links.resolvedType;
Expand Down Expand Up @@ -19697,7 +19698,7 @@ namespace ts {
// Otherwise simply call checkExpression. Ideally, the entire family of checkXXX functions
// should have a parameter that indicates whether full error checking is required such that
// we can perform the optimizations locally.
return cache ? checkExpressionCached(node) : checkExpression(node);
return cache ? checkExpressionCached(node) : checkExpression(node, CheckMode.JustType);
}

/**
Expand All @@ -19715,14 +19716,19 @@ namespace ts {
return type;
}

function checkExpression(node: Expression | QualifiedName, checkMode?: CheckMode): Type {
// Do nothing if this was already checked by a `checkExpressionCached` call
return getNodeLinks(node).resolvedType || checkExpressionNoCache(node, checkMode);
}

// Checks an expression and returns its type. The contextualMapper parameter serves two purposes: When
// contextualMapper is not undefined and not equal to the identityMapper function object it indicates that the
// expression is being inferentially typed (section 4.15.2 in spec) and provides the type mapper to use in
// conjunction with the generic contextual type. When contextualMapper is equal to the identityMapper function
// object, it serves as an indicator that all contained function and arrow expressions should be considered to
// have the wildcard function type; this form of type check is used during overload resolution to exclude
// contextually typed function and arrow expressions in the initial phase.
function checkExpression(node: Expression | QualifiedName, checkMode?: CheckMode): Type {
function checkExpressionNoCache(node: Expression | QualifiedName, checkMode: CheckMode | undefined): Type {
let type: Type;
if (node.kind === SyntaxKind.QualifiedName) {
type = checkQualifiedName(<QualifiedName>node);
Expand Down Expand Up @@ -19802,7 +19808,7 @@ namespace ts {
case SyntaxKind.ParenthesizedExpression:
return checkParenthesizedExpression(<ParenthesizedExpression>node, checkMode);
case SyntaxKind.ClassExpression:
return checkClassExpression(<ClassExpression>node);
return checkClassExpression(<ClassExpression>node, checkMode);
case SyntaxKind.FunctionExpression:
case SyntaxKind.ArrowFunction:
return checkFunctionExpressionOrObjectLiteralMethod(<FunctionExpression>node, checkMode);
Expand Down Expand Up @@ -21573,7 +21579,6 @@ namespace ts {

function registerForUnusedIdentifiersCheck(node: Node) {
if (deferredUnusedIdentifierNodes) {
Debug.assert(addToSeen(seenDeferredUnusedIdentifiers, getNodeId(node)), "Deferring unused identifier check twice");
deferredUnusedIdentifierNodes.push(node);
}
}
Expand Down Expand Up @@ -23175,9 +23180,11 @@ namespace ts {
return true;
}

function checkClassExpression(node: ClassExpression): Type {
checkClassLikeDeclaration(node);
checkNodeDeferred(node);
function checkClassExpression(node: ClassExpression, checkMode: CheckMode | undefined): Type {
if (!checkMode) {
checkClassLikeDeclaration(node);
checkNodeDeferred(node);
}
return getTypeOfSymbol(getSymbolOfNode(node));
}

Expand Down Expand Up @@ -24519,6 +24526,7 @@ namespace ts {
// Delaying the type check of the body ensures foo has been assigned a type.
function checkNodeDeferred(node: Node) {
if (deferredNodes) {
Debug.assert(addToSeen(seenDeferredNodes, getNodeId(node)));
deferredNodes.push(node);
}
}
Expand Down Expand Up @@ -24584,7 +24592,7 @@ namespace ts {
}

deferredNodes = undefined;
seenDeferredUnusedIdentifiers.clear();
seenDeferredNodes.clear();
deferredUnusedIdentifierNodes = undefined;

if (isExternalOrCommonJsModule(node)) {
Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/arrayLiteralComments.types
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
=== tests/cases/compiler/arrayLiteralComments.ts ===
var testArrayWithFunc = [
>testArrayWithFunc : (string | number | (() => void) | number[] | { a: number; })[]
>[ // Function comment function() { let x = 1; }, // String comment '1', // Numeric comment 2, // Object comment { a: 1 }, // Array comment [1, 2, 3]] : (string | number | (() => void) | number[] | { a: number; })[]
>[ // Function comment function() { let x = 1; }, // String comment '1', // Numeric comment 2, // Object comment { a: 1 }, // Array comment [1, 2, 3]] : (string | number | (() => void) | { a: number; } | number[])[]

// Function comment
function() {
Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/heterogeneousArrayLiterals.types
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ var d = [{}, 1]; // {}[]

var e = [{}, Object]; // {}[]
>e : {}[]
>[{}, Object] : (ObjectConstructor | {})[]
>[{}, Object] : ({} | ObjectConstructor)[]
>{} : {}
>Object : ObjectConstructor

Expand Down