Fixed a bug that led to a false negative when analyzing code with an async context manager that swallows exceptions.

This commit is contained in:
Eric Traut 2023-07-19 07:58:15 -07:00
parent b953f79228
commit 312667173c
3 changed files with 25 additions and 3 deletions

View File

@ -1686,7 +1686,22 @@ export function getCodeFlowEngine(
const exitType = evaluator.getTypeOfObjectMember(node, cmType, exitMethodName)?.type;
if (exitType && isFunction(exitType) && exitType.details.declaredReturnType) {
const returnType = exitType.details.declaredReturnType;
let returnType = exitType.details.declaredReturnType;
// If it's an __aexit__ method, its return type will typically be wrapped
// in a Coroutine, so we need to extract the return type from the third
// type argument.
if (isAsync && FunctionType.isAsync(exitType)) {
if (
isClassInstance(returnType) &&
ClassType.isBuiltIn(returnType, 'Coroutine') &&
returnType.typeArguments &&
returnType.typeArguments.length >= 3
) {
returnType = returnType.typeArguments[2];
}
}
cmSwallowsExceptions = false;
if (isClassInstance(returnType) && ClassType.isBuiltIn(returnType, 'bool')) {
if (returnType.literalValue === undefined || returnType.literalValue === true) {

View File

@ -164,7 +164,7 @@ test('With2', () => {
test('With3', () => {
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['with3.py']);
TestUtils.validateResults(analysisResults, 4);
TestUtils.validateResults(analysisResults, 5);
});
test('With4', () => {

View File

@ -2,7 +2,7 @@
# that suppress exceptions, as indicated by a return type of "bool"
# for the __exit__ or __aexit__ method.
from contextlib import suppress
from contextlib import suppress, AsyncExitStack
def test1() -> None:
@ -64,3 +64,10 @@ def test4() -> None:
# This should generate an error because the
# code is reachable.
return 1
# This should generate an error because AsyncExitStack
# may swallow exceptions.
async def test5() -> str:
async with AsyncExitStack():
return "from exit stack"