diff --git a/server/src/analyzer/typeUtils.ts b/server/src/analyzer/typeUtils.ts index a29d91cad..20e9a8787 100644 --- a/server/src/analyzer/typeUtils.ts +++ b/server/src/analyzer/typeUtils.ts @@ -322,33 +322,24 @@ export function canAssignType(destType: Type, srcType: Type, diag: DiagnosticAdd return true; } - if (srcType.category === TypeCategory.Class) { - if (destType.category === TypeCategory.Object) { - const destClassType = destType.classType; - if (ClassType.isBuiltIn(destClassType)) { - // Is the dest a generic "type" object? - const destClassName = ClassType.getClassName(destClassType); - if (destClassName === 'type') { - return true; - } + // Is the src a specialized "Type" object? + if (srcType.category === TypeCategory.Object && + ClassType.isBuiltIn(srcType.classType, 'Type')) { - if (destClassName === 'Type') { - const destTypeArgs = ClassType.getTypeArguments(destClassType); - if (destTypeArgs && destTypeArgs.length >= 1) { - return canAssignType(destTypeArgs[0], - ObjectType.create(srcType), diag.createAddendum(), typeVarMap, - flags, recursionCount + 1); - } - } - - // All classes derive from object. - if (destClassName === 'object') { - return true; - } + const srcTypeArgs = ClassType.getTypeArguments(srcType.classType); + if (srcTypeArgs && srcTypeArgs.length >= 1) { + if (isAnyOrUnknown(srcTypeArgs[0])) { + return true; + } else if (srcTypeArgs[0].category === TypeCategory.Object) { + return canAssignType(destType, + srcTypeArgs[0].classType, diag.createAddendum(), typeVarMap, + flags, recursionCount + 1); } } + } - if (destType.category === TypeCategory.Class) { + if (destType.category === TypeCategory.Class) { + if (srcType.category === TypeCategory.Class) { return _canAssignClass(destType, srcType, diag, typeVarMap, flags, recursionCount + 1, false); } @@ -357,13 +348,49 @@ export function canAssignType(destType: Type, srcType: Type, diag: DiagnosticAdd if (destType.category === TypeCategory.Object) { const destClassType = destType.classType; + // Is the dest a generic "type" object? + if (ClassType.isBuiltIn(destClassType, 'type')) { + if (srcType.category === TypeCategory.Class || + srcType.category === TypeCategory.Function || + srcType.category === TypeCategory.OverloadedFunction) { + + return true; + } + } + + // Is the dest a specialized "Type" object? + if (ClassType.isBuiltIn(destClassType, 'Type')) { + const destTypeArgs = ClassType.getTypeArguments(destClassType); + if (destTypeArgs && destTypeArgs.length >= 1) { + if (isAnyOrUnknown(destTypeArgs[0])) { + return true; + } else if (destTypeArgs[0].category === TypeCategory.Object) { + return canAssignType(destTypeArgs[0].classType, + srcType, diag.createAddendum(), typeVarMap, + flags, recursionCount + 1); + } else if (destTypeArgs[0].category === TypeCategory.TypeVar) { + if (srcType.category === TypeCategory.Class) { + return canAssignType(destTypeArgs[0], + ObjectType.create(srcType), diag.createAddendum(), typeVarMap, + flags, recursionCount + 1); + } else if (srcType.category === TypeCategory.Function || + srcType.category === TypeCategory.OverloadedFunction) { + + return canAssignType(destTypeArgs[0], + srcType, diag.createAddendum(), typeVarMap, + flags, recursionCount + 1); + } + } + } + } + if (srcType.category === TypeCategory.Object) { const destLiteral = destType.literalValue; if (destLiteral !== undefined) { const srcLiteral = srcType.literalValue; if (srcLiteral !== destLiteral) { diag.addMessage(`'${ srcLiteral ? printLiteralValue(srcType) : printType(srcType) }' ` + - `cannot be assigned to '${ printLiteralValue(destType) }'`); + `cannot be assigned to '${ printLiteralValue(destType) }'`); return false; } @@ -398,6 +425,11 @@ export function canAssignType(destType: Type, srcType: Type, diag: DiagnosticAdd if (ClassType.isBuiltIn(destClassType, 'ModuleType')) { return true; } + } else if (srcType.category === TypeCategory.Class) { + // All classes are assignable to "object". + if (ClassType.isBuiltIn(destType.classType, 'object')) { + return true; + } } } @@ -484,6 +516,8 @@ export function canAssignType(destType: Type, srcType: Type, diag: DiagnosticAdd return false; } + diag.addMessage(`'${ printType(srcType) }' ` + + `cannot be assigned to '${ printType(destType) }'`); return false; } diff --git a/server/src/tests/samples/genericTypes3.py b/server/src/tests/samples/genericTypes3.py new file mode 100644 index 000000000..21845cc58 --- /dev/null +++ b/server/src/tests/samples/genericTypes3.py @@ -0,0 +1,11 @@ +# This sample tests the ability for a Callable generic +# to be assigned to a Type[_T] parameter. + +from typing import Callable, cast + +FUNC = Callable[[int], int] +def foo(i: int) -> int: + return 42 + +bar = cast(FUNC, foo) + diff --git a/server/src/tests/typeAnalyzer.test.ts b/server/src/tests/typeAnalyzer.test.ts index 006539c0c..9f46e32dd 100644 --- a/server/src/tests/typeAnalyzer.test.ts +++ b/server/src/tests/typeAnalyzer.test.ts @@ -622,6 +622,12 @@ test('GenericTypes2', () => { validateResults(analysisResults, 1); }); +test('GenericTypes3', () => { + const analysisResults = TestUtils.typeAnalyzeSampleFiles(['genericTypes3.py']); + + validateResults(analysisResults, 0); +}); + test('TypedDict1', () => { const analysisResults = TestUtils.typeAnalyzeSampleFiles(['typedDict1.py']);