diff --git a/packages/pyright-internal/src/analyzer/codeFlowEngine.ts b/packages/pyright-internal/src/analyzer/codeFlowEngine.ts index 11a9cd9f0..05456ceb2 100644 --- a/packages/pyright-internal/src/analyzer/codeFlowEngine.ts +++ b/packages/pyright-internal/src/analyzer/codeFlowEngine.ts @@ -95,7 +95,7 @@ export interface CodeFlowAnalyzer { } export interface CodeFlowEngine { - createCodeFlowAnalyzer: (typeAtStart: TypeResult | undefined) => CodeFlowAnalyzer; + createCodeFlowAnalyzer: () => CodeFlowAnalyzer; isFlowNodeReachable: (flowNode: FlowNode, sourceFlowNode?: FlowNode, ignoreNoReturn?: boolean) => boolean; narrowConstrainedTypeVar: (flowNode: FlowNode, typeVar: TypeVarType) => Type | undefined; printControlFlowGraph: ( @@ -173,11 +173,7 @@ export function getCodeFlowEngine( // Creates a new code flow analyzer that can be used to narrow the types // of the expressions within an execution context. Each code flow analyzer // instance maintains a cache of types it has already determined. - // The caller should pass a typeAtStart value because the code flow - // analyzer may cache types based on this value, but the typeAtStart - // may vary depending on the context in which the code flow analysis - // is performed. - function createCodeFlowAnalyzer(typeAtStart: TypeResult | undefined): CodeFlowAnalyzer { + function createCodeFlowAnalyzer(): CodeFlowAnalyzer { const flowNodeTypeCacheSet = new Map(); function getFlowNodeTypeCacheForReference(referenceKey: string) { @@ -513,10 +509,6 @@ export function getCodeFlowEngine( } } - if (flowTypeResult && !isFlowNodeReachable(flowNode)) { - flowTypeResult = undefined; - } - return setCacheEntry(curFlowNode, flowTypeResult?.type, !!flowTypeResult?.isIncomplete); } @@ -1226,8 +1218,6 @@ export function getCodeFlowEngine( curFlowNode.flags & (FlowFlags.VariableAnnotation | FlowFlags.Assignment | - FlowFlags.TrueCondition | - FlowFlags.FalseCondition | FlowFlags.WildcardImport | FlowFlags.NarrowForPattern | FlowFlags.ExhaustedMatch) @@ -1235,15 +1225,19 @@ export function getCodeFlowEngine( const typedFlowNode = curFlowNode as | FlowVariableAnnotation | FlowAssignment - | FlowCondition | FlowWildcardImport - | FlowCondition | FlowExhaustedMatch; curFlowNode = typedFlowNode.antecedent; continue; } - if (curFlowNode.flags & (FlowFlags.TrueNeverCondition | FlowFlags.FalseNeverCondition)) { + if ( + curFlowNode.flags & + (FlowFlags.TrueCondition | + FlowFlags.FalseCondition | + FlowFlags.TrueNeverCondition | + FlowFlags.FalseNeverCondition) + ) { const conditionalFlowNode = curFlowNode as FlowCondition; if (conditionalFlowNode.reference) { // Make sure the reference type has a declared type. If not, @@ -1365,7 +1359,7 @@ export function getCodeFlowEngine( // Protect against infinite recursion. if (isReachableRecursionSet.has(flowNode.id)) { - return true; + return false; } isReachableRecursionSet.add(flowNode.id); diff --git a/packages/pyright-internal/src/analyzer/typeEvaluator.ts b/packages/pyright-internal/src/analyzer/typeEvaluator.ts index 80ed28ffa..d164e50b6 100644 --- a/packages/pyright-internal/src/analyzer/typeEvaluator.ts +++ b/packages/pyright-internal/src/analyzer/typeEvaluator.ts @@ -19876,7 +19876,7 @@ export function createTypeEvaluator( } // Allocate a new code flow analyzer. - const analyzer = codeFlowEngine.createCodeFlowAnalyzer(typeAtStart); + const analyzer = codeFlowEngine.createCodeFlowAnalyzer(); if (entries) { entries.push({ typeAtStart, codeFlowAnalyzer: analyzer }); } else { @@ -22181,7 +22181,7 @@ export function createTypeEvaluator( const prevTypeCache = returnTypeInferenceTypeCache; returnTypeInferenceContextStack.push({ functionNode, - codeFlowAnalyzer: codeFlowEngine.createCodeFlowAnalyzer(/* typeAtStart */ undefined), + codeFlowAnalyzer: codeFlowEngine.createCodeFlowAnalyzer(), }); try { diff --git a/packages/pyright-internal/src/tests/checker.test.ts b/packages/pyright-internal/src/tests/checker.test.ts index 3f00f3abc..5a359a640 100644 --- a/packages/pyright-internal/src/tests/checker.test.ts +++ b/packages/pyright-internal/src/tests/checker.test.ts @@ -165,7 +165,7 @@ test('With2', () => { test('With3', () => { const analysisResults = TestUtils.typeAnalyzeSampleFiles(['with3.py']); - TestUtils.validateResults(analysisResults, 5); + TestUtils.validateResults(analysisResults, 6); }); test('With4', () => { diff --git a/packages/pyright-internal/src/tests/samples/constrainedTypeVar13.py b/packages/pyright-internal/src/tests/samples/constrainedTypeVar13.py index 18f64591f..e7362b9e3 100644 --- a/packages/pyright-internal/src/tests/samples/constrainedTypeVar13.py +++ b/packages/pyright-internal/src/tests/samples/constrainedTypeVar13.py @@ -49,21 +49,21 @@ class Class1(Generic[_T1, _T2, _T3, _P, Unpack[_Ts]]): # This should generate an error. return [0] - if cond: + if cond or 3 > 2: if isinstance(val1, str): # This should generate an error. return [0] else: return [0] - if cond: + if cond or 3 > 2: if isinstance(val3, B): return [B()] else: # This should generate an error. return [C()] - if cond: + if cond or 3 > 2: if not isinstance(val3, B) and not isinstance(val3, C): return [A()] diff --git a/packages/pyright-internal/src/tests/samples/tryExcept4.py b/packages/pyright-internal/src/tests/samples/tryExcept4.py index 74cba7b33..6a0cbdab3 100644 --- a/packages/pyright-internal/src/tests/samples/tryExcept4.py +++ b/packages/pyright-internal/src/tests/samples/tryExcept4.py @@ -1,7 +1,10 @@ # This sample validates that the exception type provided # within a raise statement is valid. -a: bool = True +from random import random + + +a: bool = True if random() > 0.5 else False class CustomException1(BaseException): @@ -11,10 +14,10 @@ class CustomException1(BaseException): # This should generate an error because CustomException1 # requires an argument to instantiate. -if a: +if a or 2 > 1: raise CustomException1 -if a: +if a or 2 > 1: raise CustomException1(3) @@ -24,5 +27,5 @@ class CustomException2: # This should generate an error because # the exception doesn't derive from BaseException. -if a: +if a or 2 > 1: raise CustomException2 diff --git a/packages/pyright-internal/src/tests/samples/unreachable1.py b/packages/pyright-internal/src/tests/samples/unreachable1.py index d3a38067a..d67513bec 100644 --- a/packages/pyright-internal/src/tests/samples/unreachable1.py +++ b/packages/pyright-internal/src/tests/samples/unreachable1.py @@ -1,8 +1,8 @@ # This sample tests the detection and reporting of unreachable code. -from abc import abstractmethod import os import sys +from abc import abstractmethod from typing import NoReturn @@ -109,3 +109,19 @@ def func10(): return # This should be marked unreachable. b = e.errno + + +def func11(obj: str) -> list: + if isinstance(obj, str): + return [] + else: + # This should be marked as unreachable. + return obj + + +def func12(obj: str) -> list: + if isinstance(obj, str): + return [] + + # This should be marked as unreachable. + return obj diff --git a/packages/pyright-internal/src/tests/samples/with3.py b/packages/pyright-internal/src/tests/samples/with3.py index 60a6fa535..ff466089a 100644 --- a/packages/pyright-internal/src/tests/samples/with3.py +++ b/packages/pyright-internal/src/tests/samples/with3.py @@ -17,8 +17,7 @@ def test1() -> None: raise RuntimeError() return - # This should generate an error because - # the code is not unreachable. + # This should generate an error. c = "hi" + 3 with memoryview(x): diff --git a/packages/pyright-internal/src/tests/typeEvaluator1.test.ts b/packages/pyright-internal/src/tests/typeEvaluator1.test.ts index c20bab940..331544667 100644 --- a/packages/pyright-internal/src/tests/typeEvaluator1.test.ts +++ b/packages/pyright-internal/src/tests/typeEvaluator1.test.ts @@ -26,7 +26,7 @@ import * as TestUtils from './testUtils'; test('Unreachable1', () => { const analysisResults = TestUtils.typeAnalyzeSampleFiles(['unreachable1.py']); - TestUtils.validateResults(analysisResults, 0, 0, 2, 1, 4); + TestUtils.validateResults(analysisResults, 0, 0, 2, 1, 6); }); test('Builtins1', () => {