Improved handling of typing.TypeAlias so it can be aliased and re-exported from other files. This addresses https://github.com/microsoft/pyright/issues/4197.

This commit is contained in:
Eric Traut 2023-03-02 15:10:15 -07:00
parent edfe2aa268
commit 659fdfab75
18 changed files with 222 additions and 167 deletions

View File

@ -1706,7 +1706,7 @@ export class Binder extends ParseTreeWalker {
}
override visitImportFrom(node: ImportFromNode): boolean {
const typingSymbolsOfInterest = ['Final', 'TypeAlias', 'ClassVar', 'Required', 'NotRequired', 'Annotated'];
const typingSymbolsOfInterest = ['Final', 'ClassVar', 'Required', 'NotRequired', 'Annotated'];
const dataclassesSymbolsOfInterest = ['InitVar'];
const importInfo = AnalyzerNodeInfo.getImportInfo(node.module);
@ -3581,23 +3581,10 @@ export class Binder extends ParseTreeWalker {
const symbolWithScope = this._currentScope.lookUpSymbolRecursive(name.value);
if (symbolWithScope && symbolWithScope.symbol) {
const finalInfo = this._isAnnotationFinal(typeAnnotation);
const isExplicitTypeAlias = this._isAnnotationTypeAlias(typeAnnotation);
let typeAnnotationNode: ExpressionNode | undefined = typeAnnotation;
let innerTypeAnnotationNode: ExpressionNode | undefined = typeAnnotation;
if (isExplicitTypeAlias) {
typeAnnotationNode = undefined;
innerTypeAnnotationNode = undefined;
// Type aliases are allowed only in the global or class scope.
if (
this._currentScope.type !== ScopeType.Class &&
this._currentScope.type !== ScopeType.Module &&
this._currentScope.type !== ScopeType.Builtin
) {
this._addError(Localizer.Diagnostic.typeAliasNotInModuleOrClass(), typeAnnotation);
}
} else if (finalInfo.isFinal) {
if (finalInfo.isFinal) {
innerTypeAnnotationNode = finalInfo.finalTypeNode;
if (!finalInfo.finalTypeNode) {
typeAnnotationNode = undefined;
@ -3640,8 +3627,7 @@ export class Binder extends ParseTreeWalker {
isFinal: finalInfo.isFinal,
isRequired: this._isRequiredAnnotation(innerTypeAnnotationNode),
isNotRequired: this._isNotRequiredAnnotation(innerTypeAnnotationNode),
typeAliasAnnotation: isExplicitTypeAlias ? typeAnnotation : undefined,
typeAliasName: isExplicitTypeAlias ? target : undefined,
typeAliasName: target,
path: this._fileInfo.filePath,
typeAnnotationNode,
range: convertTextRangeToRange(name, this._fileInfo.lines),
@ -3897,14 +3883,6 @@ export class Binder extends ParseTreeWalker {
return false;
}
private _isAnnotationTypeAlias(typeAnnotation: ExpressionNode | undefined) {
if (!typeAnnotation) {
return false;
}
return this._isTypingAnnotation(typeAnnotation, 'TypeAlias');
}
// Determines whether a member access expression is referring to a
// member of a class (either a class or instance member). This will
// typically take the form "self.x" or "cls.x".

View File

@ -1148,6 +1148,7 @@ export class Checker extends ParseTreeWalker {
override visitAssignment(node: AssignmentNode): boolean {
this._evaluator.evaluateTypesForStatement(node);
if (node.typeAnnotationComment) {
this._evaluator.getType(node.typeAnnotationComment);
@ -1164,6 +1165,27 @@ export class Checker extends ParseTreeWalker {
}
}
// If this isn't a class or global scope, explicit type aliases are not allowed.
if (node.leftExpression.nodeType === ParseNodeType.TypeAnnotation) {
const annotationType = this._evaluator.getTypeOfAnnotation(node.leftExpression.typeAnnotation);
if (isClassInstance(annotationType) && ClassType.isBuiltIn(annotationType, 'TypeAlias')) {
const scope = getScopeForNode(node);
if (scope) {
if (
scope.type !== ScopeType.Class &&
scope.type !== ScopeType.Module &&
scope.type !== ScopeType.Builtin
) {
this._evaluator.addError(
Localizer.Diagnostic.typeAliasNotInModuleOrClass(),
node.leftExpression.typeAnnotation
);
}
}
}
}
return true;
}
@ -2987,7 +3009,7 @@ export class Checker extends ParseTreeWalker {
addPrimaryDeclInfo(diag);
}
} else if (otherDecl.type === DeclarationType.Function) {
const primaryType = this._evaluator.getTypeForDeclaration(primaryDecl);
const primaryType = this._evaluator.getTypeForDeclaration(primaryDecl)?.type;
let duplicateIsOk = false;
// If the return type has not yet been inferred, do so now.
@ -2995,7 +3017,7 @@ export class Checker extends ParseTreeWalker {
this._evaluator.getFunctionInferredReturnType(primaryType);
}
const otherType = this._evaluator.getTypeForDeclaration(otherDecl);
const otherType = this._evaluator.getTypeForDeclaration(otherDecl)?.type;
const suite1 = ParseTreeUtils.getEnclosingSuite(primaryDecl.node);
const suite2 = ParseTreeUtils.getEnclosingSuite(otherDecl.node);
@ -3055,14 +3077,14 @@ export class Checker extends ParseTreeWalker {
}
}
} else if (otherDecl.type === DeclarationType.Variable) {
const primaryType = this._evaluator.getTypeForDeclaration(primaryDecl);
const primaryType = this._evaluator.getTypeForDeclaration(primaryDecl)?.type;
if (otherDecl.typeAnnotationNode) {
if (otherDecl.node.nodeType === ParseNodeType.Name) {
let duplicateIsOk = false;
// It's OK if they both have the same declared type.
const otherType = this._evaluator.getTypeForDeclaration(otherDecl);
const otherType = this._evaluator.getTypeForDeclaration(otherDecl)?.type;
if (primaryType && otherType && isTypeSame(primaryType, otherType)) {
duplicateIsOk = true;
}
@ -4485,7 +4507,7 @@ export class Checker extends ParseTreeWalker {
const param = paramListDetails.params[paramIndex].param;
if (param.hasDeclaredType && param.typeAnnotation) {
const fieldType = this._evaluator.getDeclaredTypeOfSymbol(symbol);
const fieldType = this._evaluator.getDeclaredTypeOfSymbol(symbol)?.type;
const paramType = FunctionType.getEffectiveParameterType(
postInitType,
paramListDetails.params[paramIndex].index

View File

@ -164,9 +164,6 @@ export interface VariableDeclaration extends DeclarationBase {
// and other complex (more dynamic) class definitions with typed variables.
isRuntimeTypeExpression?: boolean;
// Points to the "TypeAlias" annotation described in PEP 613.
typeAliasAnnotation?: ExpressionNode | undefined;
// If the declaration is a type alias, points to the alias name.
typeAliasName?: NameNode | undefined;

View File

@ -503,11 +503,11 @@ export function assignProperty(
accessors.forEach((accessorInfo) => {
const destAccessSymbol = destPropertyType.details.fields.get(accessorInfo.name);
let destAccessType = destAccessSymbol ? evaluator.getDeclaredTypeOfSymbol(destAccessSymbol) : undefined;
let destAccessType = destAccessSymbol ? evaluator.getDeclaredTypeOfSymbol(destAccessSymbol)?.type : undefined;
if (destAccessType && isFunction(destAccessType)) {
const srcAccessSymbol = srcPropertyType.details.fields.get(accessorInfo.name);
let srcAccessType = srcAccessSymbol ? evaluator.getDeclaredTypeOfSymbol(srcAccessSymbol) : undefined;
let srcAccessType = srcAccessSymbol ? evaluator.getDeclaredTypeOfSymbol(srcAccessSymbol)?.type : undefined;
if (!srcAccessType || !isFunction(srcAccessType)) {
diag?.addMessage(accessorInfo.missingDiagMsg());

View File

@ -230,7 +230,7 @@ function assignClassToProtocolInternal(
diag?.addMessage(Localizer.DiagnosticAddendum.protocolMemberMissing().format({ name }));
typesAreConsistent = false;
} else {
let destMemberType = evaluator.getDeclaredTypeOfSymbol(symbol);
let destMemberType = evaluator.getDeclaredTypeOfSymbol(symbol)?.type;
if (destMemberType) {
// Partially specialize the type of the symbol based on the MRO class.
// We can skip this if it's the dest class because it is already
@ -496,7 +496,7 @@ export function assignModuleToProtocol(
diag?.addMessage(Localizer.DiagnosticAddendum.protocolMemberMissing().format({ name }));
typesAreConsistent = false;
} else {
let destMemberType = evaluator.getDeclaredTypeOfSymbol(symbol);
let destMemberType = evaluator.getDeclaredTypeOfSymbol(symbol)?.type;
if (destMemberType) {
destMemberType = partiallySpecializeType(destMemberType, destType);

View File

@ -212,10 +212,7 @@ export class Symbol {
curDecl.isFinal = true;
}
if (declaration.typeAliasAnnotation) {
curDecl.typeAliasAnnotation = declaration.typeAliasAnnotation;
curDecl.typeAliasName = declaration.typeAliasName;
}
curDecl.typeAliasName = declaration.typeAliasName;
if (!curDecl.inferredTypeSource && declaration.inferredTypeSource) {
curDecl.inferredTypeSource = declaration.inferredTypeSource;

View File

@ -132,7 +132,7 @@ export function getOverloadedFunctionDocStringsInherited(
for (const classMember of memberIterator) {
const inheritedDecl = classMember.symbol.getDeclarations().slice(-1)[0];
const declType = evaluator.getTypeForDeclaration(inheritedDecl);
const declType = evaluator.getTypeForDeclaration(inheritedDecl)?.type;
if (declType) {
docStrings = _getOverloadedFunctionDocStrings(declType, inheritedDecl, sourceMapper);
if (docStrings && docStrings.length > 0) {
@ -315,7 +315,7 @@ function _getPropertyDocStringInherited(
return;
}
const declaredType = evaluator.getTypeForDeclaration(decl);
const declaredType = evaluator.getTypeForDeclaration(decl)?.type;
if (!declaredType || !isMaybeDescriptorInstance(declaredType)) {
return;
}
@ -338,7 +338,7 @@ function _getPropertyDocStringInherited(
if (decls) {
for (const decl of decls) {
if (isFunctionDeclaration(decl)) {
const declaredType = evaluator.getTypeForDeclaration(decl);
const declaredType = evaluator.getTypeForDeclaration(decl)?.type;
if (declaredType && isMaybeDescriptorInstance(declaredType)) {
const docString = _getFunctionDocStringFromDeclaration(decl, sourceMapper);
if (docString) {

View File

@ -158,6 +158,7 @@ import {
CallSignature,
CallSignatureInfo,
ClassTypeResult,
DeclaredSymbolTypeInfo,
EffectiveTypeResult,
EvaluatorFlags,
EvaluatorUsage,
@ -2296,7 +2297,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
// where the type isn't declared in this class but is in
// a parent class.
if (
getDeclaredTypeOfSymbol(symbol, expression) === undefined &&
!getDeclaredTypeOfSymbol(symbol, expression)?.type &&
symbolWithScope.scope.type === ScopeType.Class
) {
const enclosingClass = ParseTreeUtils.getEnclosingClassOrFunction(expression);
@ -2403,7 +2404,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
}
if (symbol) {
let declaredType = getDeclaredTypeOfSymbol(symbol);
let declaredType = getDeclaredTypeOfSymbol(symbol)?.type;
if (declaredType) {
// If it's a descriptor, we need to get the setter type.
if (isClassInstance(declaredType)) {
@ -3073,7 +3074,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
}
const declarations = symbolWithScope.symbol.getDeclarations();
let declaredType = getDeclaredTypeOfSymbol(symbolWithScope.symbol);
let declaredType = getDeclaredTypeOfSymbol(symbolWithScope.symbol)?.type;
const fileInfo = AnalyzerNodeInfo.getFileInfo(nameNode);
// If this is a class scope and there is no type declared for this class variable,
@ -3097,7 +3098,10 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
// We found an existing declared type. Make sure the type is assignable.
let destType = type;
if (declaredType) {
const isTypeAlias =
!!declaredType && isClassInstance(declaredType) && ClassType.isBuiltIn(declaredType, 'TypeAlias');
if (declaredType && !isTypeAlias) {
let diagAddendum = new DiagnosticAddendum();
if (!assignType(declaredType, type, diagAddendum)) {
@ -3349,7 +3353,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
}
} else {
// Is the target a property?
const declaredType = getDeclaredTypeOfSymbol(memberInfo.symbol);
const declaredType = getDeclaredTypeOfSymbol(memberInfo.symbol)?.type;
if (declaredType && !isProperty(declaredType)) {
// Handle the case where there is a class variable defined with the same
// name, but there's also now an instance variable introduced. Combine the
@ -5427,7 +5431,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
isInstantiableClass(containingClassType) &&
ClassType.isSameGenericClass(containingClassType, classType)
) {
type = getDeclaredTypeOfSymbol(memberInfo.symbol) ?? UnknownType.create();
type = getDeclaredTypeOfSymbol(memberInfo.symbol)?.type ?? UnknownType.create();
if (type && isInstantiableClass(memberInfo.classType)) {
type = partiallySpecializeType(type, memberInfo.classType);
}
@ -16650,7 +16654,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
const paramSymbol = AnalyzerNodeInfo.getTypeParameterSymbol(param.name);
assert(paramSymbol);
const typeOfParam = getDeclaredTypeOfSymbol(paramSymbol, param.name);
const typeOfParam = getDeclaredTypeOfSymbol(paramSymbol, param.name)?.type;
if (!typeOfParam || !isTypeVar(typeOfParam)) {
return;
}
@ -20390,80 +20394,84 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
return declarations;
}
function getTypeForDeclaration(declaration: Declaration): Type | undefined {
function getTypeForDeclaration(declaration: Declaration): DeclaredSymbolTypeInfo {
switch (declaration.type) {
case DeclarationType.Intrinsic: {
if (declaration.intrinsicType === 'Any') {
return AnyType.create();
return { type: AnyType.create() };
}
if (declaration.intrinsicType === 'class') {
const classNode = ParseTreeUtils.getEnclosingClass(declaration.node) as ClassNode;
const classTypeInfo = getTypeOfClass(classNode);
return classTypeInfo?.classType;
return { type: classTypeInfo?.classType };
}
const strType = getBuiltInObject(declaration.node, 'str');
const intType = getBuiltInObject(declaration.node, 'int');
if (isClassInstance(intType) && isClassInstance(strType)) {
if (declaration.intrinsicType === 'str') {
return strType;
return { type: strType };
}
if (declaration.intrinsicType === 'str | None') {
return combineTypes([strType, NoneType.createInstance()]);
return { type: combineTypes([strType, NoneType.createInstance()]) };
}
if (declaration.intrinsicType === 'int') {
return intType;
return { type: intType };
}
if (declaration.intrinsicType === 'Iterable[str]') {
const iterableType = getBuiltInType(declaration.node, 'Iterable');
if (isInstantiableClass(iterableType)) {
return ClassType.cloneAsInstance(
ClassType.cloneForSpecialization(
iterableType,
[strType],
/* isTypeArgumentExplicit */ true
)
);
return {
type: ClassType.cloneAsInstance(
ClassType.cloneForSpecialization(
iterableType,
[strType],
/* isTypeArgumentExplicit */ true
)
),
};
}
}
if (declaration.intrinsicType === 'Dict[str, Any]') {
const dictType = getBuiltInType(declaration.node, 'dict');
if (isInstantiableClass(dictType)) {
return ClassType.cloneAsInstance(
ClassType.cloneForSpecialization(
dictType,
[strType, AnyType.create()],
/* isTypeArgumentExplicit */ true
)
);
return {
type: ClassType.cloneAsInstance(
ClassType.cloneForSpecialization(
dictType,
[strType, AnyType.create()],
/* isTypeArgumentExplicit */ true
)
),
};
}
}
}
return UnknownType.create();
return { type: UnknownType.create() };
}
case DeclarationType.Class: {
const classTypeInfo = getTypeOfClass(declaration.node);
return classTypeInfo?.decoratedType;
return { type: classTypeInfo?.decoratedType };
}
case DeclarationType.SpecialBuiltInClass: {
return getTypeOfAnnotation(declaration.node.typeAnnotation);
return { type: getTypeOfAnnotation(declaration.node.typeAnnotation) };
}
case DeclarationType.Function: {
const functionTypeInfo = getTypeOfFunction(declaration.node);
return functionTypeInfo?.decoratedType;
return { type: functionTypeInfo?.decoratedType };
}
case DeclarationType.TypeAlias: {
return getTypeOfTypeAlias(declaration.node);
return { type: getTypeOfTypeAlias(declaration.node) };
}
case DeclarationType.Parameter: {
@ -20488,28 +20496,27 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
if (typeAnnotationNode) {
const declaredType = getTypeOfParameterAnnotation(typeAnnotationNode, declaration.node.category);
return transformVariadicParamType(
declaration.node,
declaration.node.category,
adjustParameterAnnotatedType(declaration.node, declaredType)
);
return {
type: transformVariadicParamType(
declaration.node,
declaration.node.category,
adjustParameterAnnotatedType(declaration.node, declaredType)
),
};
}
return undefined;
return { type: undefined };
}
case DeclarationType.TypeParameter: {
return getTypeOfTypeParameter(declaration.node);
return { type: getTypeOfTypeParameter(declaration.node) };
}
case DeclarationType.Variable: {
const typeAnnotationNode = declaration.typeAnnotationNode;
if (typeAnnotationNode) {
const typeAliasNode = isDeclaredTypeAlias(typeAnnotationNode)
? ParseTreeUtils.getTypeAnnotationNode(typeAnnotationNode)
: undefined;
let declaredType: Type;
let declaredType: Type | undefined;
if (declaration.isRuntimeTypeExpression) {
declaredType = convertToInstance(
@ -20538,27 +20545,23 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
transformTypeForPossibleEnumClass(
evaluatorInterface,
declaration.node,
() => declaredType
) || declaredType;
() => declaredType!
) ?? declaredType;
}
if (typeAliasNode && typeAliasNode.valueExpression.nodeType === ParseNodeType.Name) {
declaredType = transformTypeForTypeAlias(
declaredType,
typeAliasNode.valueExpression,
declaration.node
);
if (isClassInstance(declaredType) && ClassType.isBuiltIn(declaredType, 'TypeAlias')) {
return { type: undefined, isTypeAlias: true };
}
return declaredType;
return { type: declaredType };
}
}
return undefined;
return { type: undefined };
}
case DeclarationType.Alias: {
return undefined;
return { type: undefined };
}
}
}
@ -20772,8 +20775,8 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
}
const declaredType = getTypeForDeclaration(resolvedDecl);
if (declaredType) {
return declaredType;
if (declaredType.type) {
return declaredType.type;
}
// If this is part of a "py.typed" package, don't fall back on type inference
@ -20986,29 +20989,6 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
usageNode?: NameNode,
useLastDecl = false
): EffectiveTypeResult {
// If there's a declared type, it takes precedence over inferred types.
if (symbol.hasTypedDeclarations()) {
const declaredType = getDeclaredTypeOfSymbol(symbol, usageNode);
const typedDecls = symbol.getTypedDeclarations();
let isIncomplete = false;
if (declaredType) {
if (isFunction(declaredType) && FunctionType.isPartiallyEvaluated(declaredType)) {
isIncomplete = true;
} else if (isClass(declaredType) && ClassType.isPartiallyEvaluated(declaredType)) {
isIncomplete = true;
}
}
return {
type: declaredType ?? UnknownType.create(),
isIncomplete,
includesVariableDecl: typedDecls.some((decl) => decl.type === DeclarationType.Variable),
includesIllegalTypeAliasDecl: !typedDecls.every((decl) => isPossibleTypeAliasDeclaration(decl)),
isRecursiveDefinition: !declaredType,
};
}
// Look in the cache to see if we've computed this already.
let cacheEntries = effectiveTypeCache.get(symbol.id);
const usageNodeId = usageNode ? usageNode.id : undefined;
@ -21025,6 +21005,41 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
}
}
let declaredTypeInfo: DeclaredSymbolTypeInfo | undefined;
// If there's a declared type, it takes precedence over inferred types.
if (symbol.hasTypedDeclarations()) {
declaredTypeInfo = getDeclaredTypeOfSymbol(symbol, usageNode);
const declaredType = declaredTypeInfo?.type;
const hasMetadata =
!!declaredTypeInfo.isClassVar || !!declaredTypeInfo.isFinal || !!declaredTypeInfo.isTypeAlias;
if (declaredType || !hasMetadata) {
let isIncomplete = false;
if (declaredType) {
if (isFunction(declaredType) && FunctionType.isPartiallyEvaluated(declaredType)) {
isIncomplete = true;
} else if (isClass(declaredType) && ClassType.isPartiallyEvaluated(declaredType)) {
isIncomplete = true;
}
}
const typedDecls = symbol.getTypedDeclarations();
const result: EffectiveTypeResult = {
type: declaredType ?? UnknownType.create(),
isIncomplete,
includesVariableDecl: typedDecls.some((decl) => decl.type === DeclarationType.Variable),
includesIllegalTypeAliasDecl: !typedDecls.every((decl) => isPossibleTypeAliasDeclaration(decl)),
isRecursiveDefinition: !declaredType,
};
addToEffectiveTypeCache(result);
return result;
}
}
// Infer the type.
const typesToCombine: Type[] = [];
const decls = symbol.getDeclarations();
@ -21037,13 +21052,16 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
// Limit the number of declarations to explore.
if (decls.length > maxDeclarationsToUseForInference) {
return {
const result: EffectiveTypeResult = {
type: UnknownType.create(),
isIncomplete: false,
includesVariableDecl: false,
includesIllegalTypeAliasDecl: !decls.every((decl) => isPossibleTypeAliasDeclaration(decl)),
isRecursiveDefinition: false,
};
addToEffectiveTypeCache(result);
return result;
}
// If the caller has requested that we use only the last decl, we
@ -21117,17 +21135,6 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
resolvedDecl.inferredTypeSource?.parent?.nodeType === ParseNodeType.Assignment
) {
evaluateTypesForAssignmentStatement(resolvedDecl.inferredTypeSource.parent);
if (isExplicitTypeAliasDeclaration(resolvedDecl)) {
if (resolvedDecl.typeAliasAnnotation) {
// Mark "TypeAlias" declaration as accessed.
getTypeOfAnnotation(resolvedDecl.typeAliasAnnotation, {
isVariableAnnotation: true,
allowFinal: ParseTreeUtils.isFinalAllowedForAssignmentTarget(resolvedDecl.node),
allowClassVar: ParseTreeUtils.isClassVarAllowedForAssignmentTarget(resolvedDecl.node),
});
}
}
}
if (pushSymbolResolution(symbol, decl)) {
@ -21219,13 +21226,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
};
if (!includesSpeculativeResult) {
// Add the entry to the cache so we don't need to compute it next time.
if (!cacheEntries) {
cacheEntries = new Map<string, EffectiveTypeResult>();
effectiveTypeCache.set(symbol.id, cacheEntries);
}
cacheEntries.set(effectiveTypeCacheKey, result);
addToEffectiveTypeCache(result);
}
return result;
@ -21238,19 +21239,35 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
includesIllegalTypeAliasDecl: !decls.every((decl) => isPossibleTypeAliasDeclaration(decl)),
isRecursiveDefinition: false,
};
function addToEffectiveTypeCache(result: EffectiveTypeResult) {
// Add the entry to the cache so we don't need to compute it next time.
if (!cacheEntries) {
cacheEntries = new Map<string, EffectiveTypeResult>();
effectiveTypeCache.set(symbol.id, cacheEntries);
}
cacheEntries.set(effectiveTypeCacheKey, result);
}
}
function getDeclaredTypeOfSymbol(symbol: Symbol, usageNode?: NameNode): Type | undefined {
// If a declaration has an explicit type (e.g. a variable with an annotation),
// this function evaluates the type and returns it. If the symbol has no
// explicit declared type, its type will need to be inferred instead. In some
// cases, non-type information (such as Final or ClassVar attributes) may be
// provided, but type inference is still required. In such cases, the attributes
// are returned as flags.
function getDeclaredTypeOfSymbol(symbol: Symbol, usageNode?: NameNode): DeclaredSymbolTypeInfo {
const synthesizedType = symbol.getSynthesizedType();
if (synthesizedType) {
return synthesizedType;
return { type: synthesizedType };
}
let typedDecls = symbol.getTypedDeclarations();
if (typedDecls.length === 0) {
// There was no declaration with a defined type.
return undefined;
return { type: undefined };
}
// If there is more than one typed decl, filter out any that are not
@ -21274,7 +21291,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
});
if (filteredTypedDecls.length === 0) {
return UnboundType.create();
return { type: UnboundType.create() };
}
typedDecls = filteredTypedDecls;
@ -21293,13 +21310,13 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
// for recursive symbol resolution, return it as the resolved type.
const partialType = getSymbolResolutionPartialType(symbol, decl);
if (partialType) {
return partialType;
return { type: partialType };
}
if (getIndexOfSymbolResolution(symbol, decl) < 0) {
if (pushSymbolResolution(symbol, decl)) {
try {
const type = getTypeForDeclaration(decl);
const declaredTypeInfo = getTypeForDeclaration(decl);
// If there was recursion detected, don't use this declaration.
// The exception is it's a class declaration because getTypeOfClass
@ -21308,7 +21325,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
// circular dependency between the "type" and "object" classes in
// builtins.pyi (since "object" is a "type" and "type" is an "object").
if (popSymbolResolution(symbol) || decl.type === DeclarationType.Class) {
return type;
return declaredTypeInfo;
}
} catch (e: any) {
// Clean up the stack before rethrowing.
@ -21321,7 +21338,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
declIndex--;
}
return undefined;
return { type: undefined };
}
function inferReturnTypeIfNecessary(type: Type) {
@ -21849,7 +21866,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
const memberInfo = lookUpClassMember(srcType, name);
assert(memberInfo !== undefined);
let destMemberType = getDeclaredTypeOfSymbol(symbol);
let destMemberType = getDeclaredTypeOfSymbol(symbol)?.type;
if (destMemberType) {
const srcMemberType = getTypeOfMember(memberInfo!);
destMemberType = partiallySpecializeType(destMemberType, destType);
@ -22211,7 +22228,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
const fgetSymbol = propertyClass.details.fields.get('fget');
if (fgetSymbol) {
const fgetType = getDeclaredTypeOfSymbol(fgetSymbol);
const fgetType = getDeclaredTypeOfSymbol(fgetSymbol)?.type;
if (fgetType && isFunction(fgetType)) {
return getFunctionEffectiveReturnType(fgetType, /* args */ undefined, inferTypeIfNeeded);
}
@ -25812,7 +25829,20 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
}
function isExplicitTypeAliasDeclaration(decl: Declaration): boolean {
return decl.type === DeclarationType.Variable && !!decl.typeAliasAnnotation;
if (decl.type !== DeclarationType.Variable || !decl.typeAnnotationNode) {
return false;
}
if (
decl.typeAnnotationNode.nodeType !== ParseNodeType.Name &&
decl.typeAnnotationNode.nodeType !== ParseNodeType.MemberAccess &&
decl.typeAnnotationNode.nodeType !== ParseNodeType.StringList
) {
return false;
}
const type = getTypeOfAnnotation(decl.typeAnnotationNode, { isVariableAnnotation: true, allowClassVar: true });
return isClassInstance(type) && ClassType.isBuiltIn(type, 'TypeAlias');
}
function isPossibleTypeAliasDeclaration(decl: Declaration): boolean {

View File

@ -347,6 +347,13 @@ export interface PrintTypeOptions {
useTypingUnpack?: boolean;
}
export interface DeclaredSymbolTypeInfo {
type: Type | undefined;
isFinal?: boolean;
isClassVar?: boolean;
isTypeAlias?: boolean;
}
export interface TypeEvaluator {
runWithCancellationToken<T>(token: CancellationToken, callback: () => T): T;
@ -389,7 +396,7 @@ export interface TypeEvaluator {
getDeclarationsForStringNode: (node: StringNode) => Declaration[] | undefined;
getDeclarationsForNameNode: (node: NameNode, skipUnreachableCode?: boolean) => Declaration[] | undefined;
getTypeForDeclaration: (declaration: Declaration) => Type | undefined;
getTypeForDeclaration: (declaration: Declaration) => DeclaredSymbolTypeInfo;
resolveAliasDeclaration: (
declaration: Declaration,
resolveLocalNames: boolean,
@ -421,7 +428,7 @@ export interface TypeEvaluator {
callback: (expandedSubtype: Type, unexpandedSubtype: Type) => Type | undefined
) => Type;
lookUpSymbolRecursive: (node: ParseNode, name: string, honorCodeFlow: boolean) => SymbolWithScope | undefined;
getDeclaredTypeOfSymbol: (symbol: Symbol) => Type | undefined;
getDeclaredTypeOfSymbol: (symbol: Symbol) => DeclaredSymbolTypeInfo;
getEffectiveTypeOfSymbol: (symbol: Symbol) => Type;
getEffectiveTypeOfSymbolForUsage: (
symbol: Symbol,

View File

@ -97,7 +97,7 @@ export class CallHierarchyProvider {
parseRoot = declaration.node;
} else if (declaration.type === DeclarationType.Class) {
// Look up the __init__ method for this class.
const classType = evaluator.getTypeForDeclaration(declaration);
const classType = evaluator.getTypeForDeclaration(declaration)?.type;
if (classType && isInstantiableClass(classType)) {
// Don't perform a recursive search of parent classes in this
// case because we don't want to find an inherited __init__
@ -456,7 +456,7 @@ function getSymbolKind(declaration: Declaration, evaluator: TypeEvaluator): Symb
case DeclarationType.Function:
if (declaration.isMethod) {
const declType = evaluator.getTypeForDeclaration(declaration);
const declType = evaluator.getTypeForDeclaration(declaration)?.type;
if (declType && isMaybeDescriptorInstance(declType, /* requireSetter */ false)) {
symbolKind = SymbolKind.Property;
} else {

View File

@ -1197,7 +1197,7 @@ export class CompletionProvider {
let decl = getLastTypedDeclaredForSymbol(symbol);
if (decl && decl.type === DeclarationType.Function) {
if (StringUtils.isPatternInSymbol(partialName.value, name)) {
const declaredType = this._evaluator.getTypeForDeclaration(decl);
const declaredType = this._evaluator.getTypeForDeclaration(decl)?.type;
if (!declaredType) {
return;
}
@ -1847,7 +1847,7 @@ export class CompletionProvider {
if (member?.symbol.hasDeclarations()) {
const declaration = member.symbol.getDeclarations()[0];
if (isFunctionDeclaration(declaration) && declaration.isMethod) {
const getItemType = this._evaluator.getTypeForDeclaration(declaration);
const getItemType = this._evaluator.getTypeForDeclaration(declaration)?.type;
if (getItemType && isFunction(getItemType) && getItemType.details.parameters.length === 2) {
return getItemType.details.parameters[1].type;
}

View File

@ -206,7 +206,7 @@ export class DefinitionProvider {
if (isFunctionDeclaration(resolvedDecl)) {
// Handle overloaded function case
const functionType = evaluator.getTypeForDeclaration(resolvedDecl);
const functionType = evaluator.getTypeForDeclaration(resolvedDecl)?.type;
if (functionType && isOverloadedFunction(functionType)) {
for (const overloadDecl of functionType.overloads
.map((o) => o.details.declaration)

View File

@ -179,7 +179,7 @@ function getSymbolKind(name: string, declaration: Declaration, evaluator?: TypeE
case DeclarationType.Function:
if (declaration.isMethod) {
const declType = evaluator?.getTypeForDeclaration(declaration);
const declType = evaluator?.getTypeForDeclaration(declaration)?.type;
if (declType && isMaybeDescriptorInstance(declType, /* requireSetter */ false)) {
symbolKind = SymbolKind.Property;
} else {

View File

@ -331,7 +331,7 @@ export class HoverProvider {
let label = 'function';
let isProperty = false;
if (resolvedDecl.isMethod) {
const declaredType = evaluator.getTypeForDeclaration(resolvedDecl);
const declaredType = evaluator.getTypeForDeclaration(resolvedDecl)?.type;
isProperty = !!declaredType && isMaybeDescriptorInstance(declaredType, /* requireSetter */ false);
label = isProperty ? 'property' : 'method';
}

View File

@ -0,0 +1,4 @@
# This sample is used to test that TypeAlias can be aliased
# and re-exported. It is used with typeAlias20.py.
from typing import TypeAlias as TA

View File

@ -0,0 +1,10 @@
# This sample is used to test that TypeAlias can be aliased
# and re-exported. It is used with typeAlias19.py.
from .typeAlias19 import TA as TA2
TA3 = TA2
x: TA2 = dict[str, str]
y: x = {"": ""}

View File

@ -1,6 +1,7 @@
# This sample tests the handling of the Python 3.9
# TypeAlias feature as documented in PEP 613.
import sys
from typing import Type, TypeAlias as TA, Union, cast
type1: TA = Union[int, str]
@ -29,6 +30,8 @@ requires_string(type2)
# is later declared as a TypeAlias.
my_type3 = int
# This should generate an error because it is obscured
# by another type alias declaration.
my_type3: "TA" = Union[int, str]
# This should generate an error because the symbol
@ -53,9 +56,10 @@ SimpleAlias = int
ExplicitAlias: TA = int
SimpleNonAlias: Type[int] = int
reveal_type(SimpleAlias, expected_text="Type[int]")
reveal_type(ExplicitAlias, expected_text="Type[int]")
reveal_type(SimpleNonAlias, expected_text="Type[int]")
if sys.version_info > (3, 9):
reveal_type(SimpleAlias, expected_text="Type[int]")
reveal_type(ExplicitAlias, expected_text="Type[int]")
reveal_type(SimpleNonAlias, expected_text="Type[int]")
class ClassB:

View File

@ -513,11 +513,11 @@ test('TypeAlias4', () => {
configOptions.defaultPythonVersion = PythonVersion.V3_9;
const analysisResults3_9 = TestUtils.typeAnalyzeSampleFiles(['typeAlias4.py'], configOptions);
TestUtils.validateResults(analysisResults3_9, 11);
TestUtils.validateResults(analysisResults3_9, 1);
configOptions.defaultPythonVersion = PythonVersion.V3_10;
const analysisResults3_10 = TestUtils.typeAnalyzeSampleFiles(['typeAlias4.py'], configOptions);
TestUtils.validateResults(analysisResults3_10, 10);
TestUtils.validateResults(analysisResults3_10, 11);
});
test('TypeAlias5', () => {
@ -609,6 +609,12 @@ test('TypeAlias18', () => {
TestUtils.validateResults(analysisResults, 4);
});
test('TypeAlias20', () => {
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['typeAlias20.py']);
TestUtils.validateResults(analysisResults, 0);
});
test('RecursiveTypeAlias1', () => {
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['recursiveTypeAlias1.py']);