Fixed regression that caused a false positive error related to incorrect usage of a type variable within a type alias definition.

This commit is contained in:
Eric Traut 2021-11-08 10:04:09 -08:00
parent aee583b94b
commit ca02505965
3 changed files with 39 additions and 6 deletions

View File

@ -183,6 +183,7 @@ import {
TypedDictEntry, TypedDictEntry,
TypeSourceId, TypeSourceId,
TypeVarScopeId, TypeVarScopeId,
TypeVarScopeType,
TypeVarType, TypeVarType,
UnboundType, UnboundType,
UnionType, UnionType,
@ -3479,7 +3480,10 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
type = TypeVarType.cloneForScopeId( type = TypeVarType.cloneForScopeId(
type, type,
getScopeIdForNode(enclosingScope), getScopeIdForNode(enclosingScope),
enclosingScope.name.value enclosingScope.name.value,
enclosingScope.nodeType === ParseNodeType.Function
? TypeVarScopeType.Function
: TypeVarScopeType.Class
); );
} else { } else {
fail('AssociateTypeVarsWithCurrentScope flag was set but enclosing scope not found'); fail('AssociateTypeVarsWithCurrentScope flag was set but enclosing scope not found');
@ -3682,7 +3686,8 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
type: TypeVarType.cloneForScopeId( type: TypeVarType.cloneForScopeId(
type, type,
leftType.details.recursiveTypeAliasScopeId, leftType.details.recursiveTypeAliasScopeId,
leftType.details.recursiveTypeAliasName leftType.details.recursiveTypeAliasName,
TypeVarScopeType.TypeAlias
), ),
foundInterveningClass: false, foundInterveningClass: false,
}; };
@ -11832,7 +11837,9 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
const fileInfo = AnalyzerNodeInfo.getFileInfo(name); const fileInfo = AnalyzerNodeInfo.getFileInfo(name);
const typeAliasScopeId = getScopeIdForNode(name); const typeAliasScopeId = getScopeIdForNode(name);
const boundTypeVars = typeParameters.filter((typeVar) => typeVar.scopeId !== typeAliasScopeId); const boundTypeVars = typeParameters.filter(
(typeVar) => typeVar.scopeId !== typeAliasScopeId && typeVar.scopeType === TypeVarScopeType.Class
);
if (boundTypeVars.length > 0) { if (boundTypeVars.length > 0) {
addError( addError(
Localizer.Diagnostic.genericTypeAliasBoundTypeVar().format({ Localizer.Diagnostic.genericTypeAliasBoundTypeVar().format({
@ -12544,7 +12551,12 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
typeVar.details.isSynthesized = true; typeVar.details.isSynthesized = true;
typeVar.scopeId = getScopeIdForNode(initDeclNode); typeVar.scopeId = getScopeIdForNode(initDeclNode);
typeVar.details.boundType = UnknownType.create(); typeVar.details.boundType = UnknownType.create();
return TypeVarType.cloneForScopeId(typeVar, getScopeIdForNode(node), node.name.value); return TypeVarType.cloneForScopeId(
typeVar,
getScopeIdForNode(node),
node.name.value,
TypeVarScopeType.Class
);
}); });
} }
} }

View File

@ -50,6 +50,7 @@ import {
OverloadedFunctionType, OverloadedFunctionType,
Type, Type,
TypedDictEntry, TypedDictEntry,
TypeVarScopeType,
TypeVarType, TypeVarType,
UnknownType, UnknownType,
} from './types'; } from './types';
@ -300,7 +301,12 @@ export function synthesizeTypedDictClassMethods(
const typeVarScopeId = evaluator.getScopeIdForNode(node); const typeVarScopeId = evaluator.getScopeIdForNode(node);
let defaultTypeVar = TypeVarType.createInstance(`__${classType.details.name}_default`); let defaultTypeVar = TypeVarType.createInstance(`__${classType.details.name}_default`);
defaultTypeVar.details.isSynthesized = true; defaultTypeVar.details.isSynthesized = true;
defaultTypeVar = TypeVarType.cloneForScopeId(defaultTypeVar, typeVarScopeId, classType.details.name); defaultTypeVar = TypeVarType.cloneForScopeId(
defaultTypeVar,
typeVarScopeId,
classType.details.name,
TypeVarScopeType.Function
);
const createGetMethod = (keyType: Type, valueType: Type, includeDefault: boolean) => { const createGetMethod = (keyType: Type, valueType: Type, includeDefault: boolean) => {
const getOverload = FunctionType.createInstance( const getOverload = FunctionType.createInstance(

View File

@ -1716,6 +1716,12 @@ export interface TypeVarDetails {
export type ParamSpecAccess = 'args' | 'kwargs'; export type ParamSpecAccess = 'args' | 'kwargs';
export const enum TypeVarScopeType {
Class,
Function,
TypeAlias,
}
export interface TypeVarType extends TypeBase { export interface TypeVarType extends TypeBase {
category: TypeCategory.TypeVar; category: TypeCategory.TypeVar;
details: TypeVarDetails; details: TypeVarDetails;
@ -1728,6 +1734,9 @@ export interface TypeVarType extends TypeBase {
// so it should be used only for error messages. // so it should be used only for error messages.
scopeName?: string | undefined; scopeName?: string | undefined;
// If the TypeVar is bound to a scope, this is the scope type.
scopeType?: TypeVarScopeType;
// String formatted as <name>.<scopeId>. // String formatted as <name>.<scopeId>.
nameWithScope?: string | undefined; nameWithScope?: string | undefined;
@ -1779,11 +1788,17 @@ export namespace TypeVarType {
return newInstance; return newInstance;
} }
export function cloneForScopeId(type: TypeVarType, scopeId: string, scopeName: string) { export function cloneForScopeId(
type: TypeVarType,
scopeId: string,
scopeName: string,
scopeType: TypeVarScopeType
) {
const newInstance: TypeVarType = { ...type }; const newInstance: TypeVarType = { ...type };
newInstance.nameWithScope = makeNameWithScope(type.details.name, scopeId); newInstance.nameWithScope = makeNameWithScope(type.details.name, scopeId);
newInstance.scopeId = scopeId; newInstance.scopeId = scopeId;
newInstance.scopeName = scopeName; newInstance.scopeName = scopeName;
newInstance.scopeType = scopeType;
return newInstance; return newInstance;
} }