Added support for type constraints of the form "X == None" and "X != None".

This commit is contained in:
Eric Traut 2019-12-03 23:38:10 -08:00
parent f30af50b0e
commit 1bb66de420
4 changed files with 51 additions and 10 deletions

View File

@ -1572,11 +1572,14 @@ export class Binder extends ParseTreeWalker {
}
case ParseNodeType.BinaryOperation: {
if (expression.operator === OperatorType.Is ||
expression.operator === OperatorType.IsNot) {
const isOrIsNotOperator = expression.operator === OperatorType.Is ||
expression.operator === OperatorType.IsNot;
const equalsOrNotEqualsOperator = expression.operator === OperatorType.Equals ||
expression.operator === OperatorType.NotEquals;
// Look for "X is None" or "X is not None". These are commonly-used
// patterns used in control flow.
if (isOrIsNotOperator || equalsOrNotEqualsOperator) {
// Look for "X is None", "X is not None", "X == None", "X != None".
// These are commonly-used patterns used in control flow.
if (expression.rightExpression.nodeType === ParseNodeType.Constant &&
expression.rightExpression.constType === KeywordType.None) {
@ -1584,7 +1587,8 @@ export class Binder extends ParseTreeWalker {
}
// Look for "type(X) is Y" or "type(X) is not Y".
if (expression.leftExpression.nodeType === ParseNodeType.Call &&
if (isOrIsNotOperator &&
expression.leftExpression.nodeType === ParseNodeType.Call &&
expression.leftExpression.leftExpression.nodeType === ParseNodeType.Name &&
expression.leftExpression.leftExpression.value === 'type' &&
expression.leftExpression.arguments.length === 1 &&

View File

@ -7255,13 +7255,19 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
const isPositiveTest = !!(flowNode.flags & FlowFlags.TrueCondition);
if (testExpression.nodeType === ParseNodeType.BinaryOperation) {
if (testExpression.operator === OperatorType.Is || testExpression.operator === OperatorType.IsNot) {
const isOrIsNotOperator = testExpression.operator === OperatorType.Is ||
testExpression.operator === OperatorType.IsNot;
const equalsOrNotEqualsOperator = testExpression.operator === OperatorType.Equals ||
testExpression.operator === OperatorType.NotEquals;
if (isOrIsNotOperator || equalsOrNotEqualsOperator) {
// Invert the "isPositiveTest" value if this is an "is not" operation.
const adjIsPositiveTest = testExpression.operator === OperatorType.Is ?
const adjIsPositiveTest = (testExpression.operator === OperatorType.Is ||
testExpression.operator === OperatorType.Equals) ?
isPositiveTest : !isPositiveTest;
// Look for "X is None" or "X is not None". These are commonly-used
// patterns used in control flow.
// Look for "X is None", "X is not None", "X == None", and "X != None".
// These are commonly-used patterns used in control flow.
if (testExpression.rightExpression.nodeType === ParseNodeType.Constant &&
testExpression.rightExpression.constType === KeywordType.None) {
@ -7296,7 +7302,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
}
// Look for "type(X) is Y" or "type(X) is not Y".
if (testExpression.leftExpression.nodeType === ParseNodeType.Call) {
if (isOrIsNotOperator && testExpression.leftExpression.nodeType === ParseNodeType.Call) {
const callType = getTypeOfExpression(testExpression.leftExpression.leftExpression).type;
if (callType.category === TypeCategory.Class &&
ClassType.isBuiltIn(callType, 'type') &&

View File

@ -136,6 +136,12 @@ test('TypeConstraint6', () => {
validateResults(analysisResults, 1);
});
test('TypeConstraint7', () => {
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['typeConstraint7.py']);
validateResults(analysisResults, 0);
});
test('CircularBaseClass', () => {
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['circularBaseClass.py']);

View File

@ -0,0 +1,25 @@
# This sample tests the type analyzer's handling of "X is None", "X is not None",
# "X == None" and "X != None" conditions.
# pyright: strict
from typing import Optional
def func1(x: Optional[int]):
if x is not None:
x.bit_length()
if x != None:
x.bit_length()
if x is None:
pass
else:
x.bit_length()
if x == None:
pass
else:
x.bit_length()