8000 Improved performance in type guard logic by not evaluating a call exp… · codemuse-app/scip-python@d9b22cf · GitHub
[go: up one dir, main page]

Skip to content

Commit d9b22cf

Browse files
committed
Improved performance in type guard logic by not evaluating a call expression base type until after verifying that the call potentially narrows the reference expression.
1 parent 2f726c7 commit d9b22cf

File tree

1 file changed

+96
-85
lines changed

1 file changed

+96
-85
lines changed

packages/pyright-internal/src/analyzer/typeGuards.ts

Lines changed: 96 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -167,27 +167,27 @@ export function getTypeNarrowingCallback(
167167

168168
// Look for "type(X) is Y" or "type(X) is not Y".
169169
if (isOrIsNotOperator && testExpression.leftExpression.nodeType === ParseNodeType.Call) {
170-
const callType = evaluator.getTypeOfExpression(
171-
testExpression.leftExpression.leftExpression,
172-
EvaluatorFlags.DoNotSpecialize
173-
).type;
174-
175170
if (
176-
isInstantiableClass(callType) &&
177-
ClassType.isBuiltIn(callType, 'type') &&
178171
testExpression.leftExpression.arguments.length === 1 &&
179172
testExpression.leftExpression.arguments[0].argumentCategory === ArgumentCategory.Simple
180173
) {
181174
const arg0Expr = testExpression.leftExpression.arguments[0].valueExpression;
182175
if (ParseTreeUtils.isMatchingExpression(reference, arg0Expr)) {
183-
const classType = evaluator.makeTopLevelTypeVarsConcrete(
184-
evaluator.getTypeOfExpression(testExpression.rightExpression).type
185-
);
176+
const callType = evaluator.getTypeOfExpression(
177+
testExpression.leftExpression.leftExpression,
178+
EvaluatorFlags.DoNotSpecialize
179+
).type;
180+
181+
if (isInstantiableClass(callType) && ClassType.isBuiltIn(callType, 'type')) {
182+
const classType = evaluator.makeTopLevelTypeVarsConcrete(
183+
evaluator.getTypeOfExpression(testExpression.rightExpression).type
184+
);
186185

187-
if (isInstantiableClass(classType)) {
188-
return (type: Type) => {
189-
return narrowTypeForTypeIs(type, classType, adjIsPositiveTest);
190-
};
186+
if (isInstantiableClass(classType)) {
187+
return (type: Type) => {
188+
return narrowTypeForTypeIs(type, classType, adjIsPositiveTest);
189+
};
190+
}
191191
}
192192
}
193193
}
@@ -424,106 +424,112 @@ export function getTypeNarrowingCallback(
424424
}
425425

426426
if (testExpression.nodeType === ParseNodeType.Call) {
427-
const callType = evaluator.getTypeOfExpression(
428-
testExpression.leftExpression,
429-
EvaluatorFlags.DoNotSpecialize
430-
).type;
431-
432427
// Look for "isinstance(X, Y)" or "issubclass(X, Y)".
433-
if (
434-
isFunction(callType) &&
435-
(callType.details.builtInName === 'isinstance' || callType.details.builtInName === 'issubclass') &&
436-
testExpression.arguments.length === 2
437-
) {
428+
if (testExpression.arguments.length === 2) {
438429
// Make sure the first parameter is a supported expression type
439430
// and the second parameter is a valid class type or a tuple
440431
// of valid class types.
441-
const isInstanceCheck = callType.details.builtInName === 'isinstance';
442432
const arg0Expr = testExpression.arguments[0].valueExpression;
443433
const arg1Expr = testExpression.arguments[1].valueExpression;
444434
if (ParseTreeUtils.isMatchingExpression(reference, arg0Expr)) {
445-
const arg1Type = evaluator.getTypeOfExpression(
446-
arg1Expr,
447-
EvaluatorFlags.EvaluateStringLiteralAsType |
448-
EvaluatorFlags.ParamSpecDisallowed |
449-
EvaluatorFlags.TypeVarTupleDisallowed
435+
const callType = evaluator.getTypeOfExpression(
436+
testExpression.leftExpression,
437+
EvaluatorFlags.DoNotSpecialize
450438
).type;
451439

452-
const classTypeList = getIsInstanceClassTypes(arg1Type);
440+
if (
441+
isFunction(callType) &&
442+
(callType.details.builtInName === 'isinstance' || callType.details.builtInName === 'issubclass')
443+
) {
444+
const isInstanceCheck = callType.details.builtInName === 'isinstance';
445+
const arg1Type = evaluator.getTypeOfExpression(
446+
arg1Expr,
447+
EvaluatorFlags.EvaluateStringLiteralAsType |
448+
EvaluatorFlags.ParamSpecDisallowed |
449+
EvaluatorFlags.TypeVarTupleDisallowed
450+
).type;
453451

454-
if (classTypeList) {
455-
return (type: Type) => {
456-
const narrowedType = narrowTypeForIsInstance(
457-
evaluator,
458-
type,
459-
classTypeList,
460-
isInstanceCheck,
461-
isPositiveTest,
462-
/* allowIntersections */ false,
463-
testExpression
464-
);
465-
if (!isNever(narrowedType)) {
466-
return narrowedType;
467-
}
452+
const classTypeList = getIsInstanceClassTypes(arg1Type);
468453

469-
// Try again with intersection types allowed.
470-
return narrowTypeForIsInstance(
471-
evaluator,
472-
type,
473-
classTypeList,
474-
isInstanceCheck,
475-
isPositiveTest,
476-
/* allowIntersections */ true,
477-
testExpression
478-
);
479-
};
454+
if (classTypeList) {
455+
return (type: Type) => {
456+
const narrowedType = narrowTypeForIsInstance(
457+
evaluator,
458+
type,
459+
classTypeList,
460+
isInstanceCheck,
461+
isPositiveTest,
462+
/* allowIntersections */ false,
463+
testExpression
464+
);
465+
if (!isNever(narrowedType)) {
466+
return narrowedType;
467+
}
468+
469+
// Try again with intersection types allowed.
470+
return narrowTypeForIsInstance(
471+
evaluator,
472+
type,
473+
classTypeList,
474+
isInstanceCheck,
475+
isPositiveTest,
476+
/* allowIntersections */ true,
477+
testExpression
478+
);
479+
};
480+
}
480481
}
481482
}
482483
}
483484

484485
// Look for "callable(X)"
485-
if (
486-
isFunction(callType) &&
487-
callType.details.builtInName === 'callable' &&
488-
testExpression.arguments.length === 1
489-
) {
486+
if (testExpression.arguments.length === 1) {
490487
const arg0Expr = testExpression.arguments[0].valueExpression;
491488
if (ParseTreeUtils.isMatchingExpression(reference, arg0Expr)) {
492-
return (type: Type) => {
493-
let narrowedType = narrowTypeForCallable(
494-
evaluator,
495-
type,
496-
isPositiveTest,
497-
testExpression,
498-
/* allowIntersections */ false
499-
);
500-
if (isPositiveTest && isNever(narrowedType)) {
501-
// Try again with intersections allowed.
502-
narrowedType = narrowTypeForCallable(
489+
const callType = evaluator.getTypeOfExpression(
490+
testExpression.leftExpression,
491+
EvaluatorFlags.DoNotSpecialize
492+
).type;
493+
494+
if (isFunction(callType) && callType.details.builtInName === 'callable') {
495+
return (type: Type) => {
496+
let narrowedType = narrowTypeForCallable(
503497
evaluator,
504498
type,
505499
isPositiveTest,
506500
testExpression,
507-
/* allowIntersections */ true
501+
/* allowIntersections */ false
508502
);
509-
}
503+
if (isPositiveTest && isNever(narrowedType)) {
504+
// Try again with intersections allowed.
505+
narrowedType = narrowTypeForCallable(
506+
evaluator,
507+
type,
508+
isPositiveTest,
509+
testExpression,
510+
/* allowIntersections */ true
511+
);
512+
}
510513

511-
return narrowedType;
512-
};
514+
return narrowedType;
515+
};
516+
}
513517
}
514518
}
515519

516520
// Look for "bool(X)"
517-
if (
518-
isInstantiableClass(callType) &&
519-
ClassType.isBuiltIn(callType, 'bool') &&
520-
testExpression.arguments.length === 1 &&
521-
!testExpression.arguments[0].name
522-
) {
521+
if (testExpression.arguments.length === 1 && !testExpression.arguments[0].name) {
523522
if (ParseTreeUtils.isMatchingExpression(reference, testExpression.arguments[0].valueExpression)) {
524-
return (type: Type) => {
525-
return narrowTypeForTruthiness(evaluator, type, isPositiveTest);
526-
};
523+
const callType = evaluator.getTypeOfExpression(
524+
testExpression.leftExpression,
525+
EvaluatorFlags.DoNotSpecialize
526+
).type;
527+
528+
if (isInstantiableClass(callType) && ClassType.isBuiltIn(callType, 'bool')) {
529+
return (type: Type) => {
530+
return narrowTypeForTruthiness(evaluator, type, isPositiveTest);
531+
};
532+
}
527533
}
528534
}
529535

@@ -542,6 +548,11 @@ export function getTypeNarrowingCallback(
542548
);
543549
};
544550

551+
const callType = evaluator.getTypeOfExpression(
552+
testExpression.leftExpression,
553+
EvaluatorFlags.DoNotSpecialize
554+
).type;
555+
545556
if (isFunction(callType) && isFunctionReturnTypeGuard(callType)) {
546557
isPossiblyTypeGuard = true;
547558
} else if (

0 commit comments

Comments
 (0)
0