Fixed a few bugs in type analyzer.

This commit is contained in:
Eric Traut 2019-03-15 17:29:48 -07:00
parent cbaaa0ff84
commit 00ae7f1400
4 changed files with 33 additions and 12 deletions

View File

@ -601,7 +601,7 @@ export class TypeAnalyzer extends ParseTreeWalker {
if (declaredReturnType) {
if (!TypeUtils.canAssignType(declaredReturnType, returnType)) {
this._addError(
`Expression of type '${ returnType.asString() }' cannot not assigned ` +
`Expression of type '${ returnType.asString() }' cannot be assigned ` +
`to return type '${ declaredReturnType.asString() }'`,
node.returnExpression ? node.returnExpression : node);
}
@ -974,10 +974,11 @@ export class TypeAnalyzer extends ParseTreeWalker {
ClassTypeFlags.BuiltInClass | ClassTypeFlags.SpecialBuiltIn,
AnalyzerNodeInfo.getTypeSourceId(node));
let baseClass = TypeAnnotation.getBuiltInType(this._currentScope,
let aliasClass = TypeAnnotation.getBuiltInType(this._currentScope,
assignedName.toLowerCase());
if (baseClass instanceof ClassType) {
specialClassType.addBaseClass(baseClass, false);
if (aliasClass instanceof ClassType) {
specialClassType.addBaseClass(aliasClass, false);
specialClassType.setAliasClass(aliasClass);
}
specialType = specialClassType;
@ -1453,7 +1454,7 @@ export class TypeAnalyzer extends ParseTreeWalker {
// The stdlib collections.pyi stub file defines namedtuple as a function
// rather than a class, so we need to check for it here.
if (callType.getSpecialBuiltInName() === 'namedtuple') {
exprType = TypeAnnotation.getNamedTupleType(node, false,
exprType = TypeAnnotation.createNamedTupleType(node, false,
this._currentScope, this._fileInfo.diagnosticSink);
} else {
exprType = callType.getEffectiveReturnType();
@ -1488,7 +1489,7 @@ export class TypeAnalyzer extends ParseTreeWalker {
this._currentScope, this._getConditionalDiagnosticSink());
} else if (className === 'NamedTuple') {
// Handle the NamedTuple case specially because it's a class factory.
exprType = TypeAnnotation.getNamedTupleType(node, true,
exprType = TypeAnnotation.createNamedTupleType(node, true,
this._currentScope, this._getConditionalDiagnosticSink());
}
}

View File

@ -22,7 +22,7 @@ import { Scope, ScopeType } from './scope';
import { Symbol } from './symbol';
import { AnyType, ClassType, ClassTypeFlags, FunctionParameter, FunctionType,
FunctionTypeFlags, ModuleType, NoneType, ObjectType, OverloadedFunctionType,
PropertyType, TupleType, Type, TypeVarType, UnknownType } from './types';
PropertyType, TupleType, Type, TypeVarType, UnionType, UnknownType } from './types';
import { TypeUtils } from './typeUtils';
interface TypeResult {
@ -116,7 +116,7 @@ export class TypeAnnotation {
// Creates a new custom tuple factory class with named values.
// Supports both typed and untyped variants.
static getNamedTupleType(node: CallExpressionNode, includesTypes: boolean,
static createNamedTupleType(node: CallExpressionNode, includesTypes: boolean,
currentScope: Scope, diagSink: TextRangeDiagnosticSink): ClassType {
let className = 'namedtuple';
if (node.arguments.length === 0) {
@ -134,6 +134,7 @@ export class TypeAnnotation {
let classType = new ClassType(className, ClassTypeFlags.None,
AnalyzerNodeInfo.getTypeSourceId(node));
classType.addBaseClass(this.getBuiltInType(currentScope, 'NamedTuple'), false);
const classFields = classType.getClassFields();
classFields.set('__class__', new Symbol(classType, DefaultTypeSourceId));
const instanceFields = classType.getInstanceFields();
@ -624,6 +625,10 @@ export class TypeAnnotation {
if (baseTypeResult.type instanceof ClassType) {
[type, isClassType] = this.specializeClassType(baseTypeResult.type,
node.indexExpression, currentScope, diagSink);
} else if (baseTypeResult.type instanceof UnionType) {
// TODO - need to implement
} else if (baseTypeResult.type instanceof FunctionType) {
// TODO - need to implement
} else if (!baseTypeResult.type.isAny()) {
diagSink.addErrorWithTextRange(
`'Unsupported type expression: indexed other (${ baseTypeResult.type.asString() })`,
@ -690,7 +695,7 @@ export class TypeAnnotation {
if (className === 'TypeVar') {
type = this.getTypeVarType(node, currentScope, diagSink);
} else if (className === 'NamedTuple') {
type = this.getNamedTupleType(node, true, currentScope, diagSink);
type = this.createNamedTupleType(node, true, currentScope, diagSink);
isClassType = true;
} else {
type = UnknownType.create();
@ -700,7 +705,7 @@ export class TypeAnnotation {
// The stdlib collections/__init__.pyi stub file defines namedtuple
// as a function rather than a class, so we need to check for it here.
if (baseTypeResult.type.getSpecialBuiltInName() === 'namedtuple') {
type = this.getNamedTupleType(node, false, currentScope, diagSink);
type = this.createNamedTupleType(node, false, currentScope, diagSink);
isClassType = true;
} else {
type = baseTypeResult.type.getEffectiveReturnType();

View File

@ -207,6 +207,7 @@ export class TypeUtils {
}
if (srcType.isDerivedFrom(destType)) {
// TODO - need to validate type parameter matches
return true;
}
@ -230,7 +231,9 @@ export class TypeUtils {
// Looks up a member in a class using the multiple-inheritance rules
// defined by Python. For more detials, see this note on method resolution
// order: https://www.python.org/download/releases/2.3/mro/.
static lookUpClassMember(classType: Type, memberName: string, includeInstanceFields = true): ClassMember | undefined {
static lookUpClassMember(classType: Type, memberName: string,
includeInstanceFields = true): ClassMember | undefined {
if (classType instanceof ClassType) {
// TODO - for now, use naive depth-first search.

View File

@ -193,6 +193,7 @@ interface ClassDetails {
flags: ClassTypeFlags;
typeSourceId: TypeSourceId;
baseClasses: BaseClass[];
aliasClass?: ClassType;
classFields: SymbolTable;
instanceFields: SymbolTable;
typeParameters: TypeVarType[];
@ -269,6 +270,10 @@ export class ClassType extends Type {
return this._classDetails.baseClasses;
}
setAliasClass(type: ClassType) {
this._classDetails.aliasClass = type;
}
addBaseClass(type: Type, isMetaclass: boolean) {
this._classDetails.baseClasses.push({ isMetaclass, type });
}
@ -421,7 +426,14 @@ export class ClassType extends Type {
// Determines whether this is a subclass (derived class)
// of the specified class.
isDerivedFrom(type2: ClassType): boolean {
if (type2 === this) {
if (this._classDetails === type2._classDetails) {
return true;
}
// Is one class type an alias of the other? This is used for some
// built-in types (e.g. Tuple and tuple).
if (this._classDetails.aliasClass === type2 ||
type2._classDetails.aliasClass === this) {
return true;
}