mirror of
https://github.com/microsoft/pyright.git
synced 2024-09-11 16:06:39 +03:00
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:
parent
edfe2aa268
commit
659fdfab75
@ -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".
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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());
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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 {
|
||||
|
@ -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';
|
||||
}
|
||||
|
@ -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
|
10
packages/pyright-internal/src/tests/samples/typeAlias20.py
Normal file
10
packages/pyright-internal/src/tests/samples/typeAlias20.py
Normal 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 = {"": ""}
|
@ -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:
|
||||
|
@ -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']);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user