mirror of
https://github.com/microsoft/pyright.git
synced 2024-10-05 12:27:30 +03:00
Added the ability to clone ClassType instances for specialization purposes.
Hooked up built-in types to their corresponding base classes (e.g. List derives from list).
This commit is contained in:
parent
f3cf0c0a9b
commit
5e47a2fd18
@ -129,7 +129,8 @@ export abstract class SemanticAnalyzer extends ParseTreeWalker {
|
||||
classFlags |= ClassTypeFlags.HasDecorators;
|
||||
}
|
||||
|
||||
let classType = new ClassType(node.name.nameToken.value, classFlags);
|
||||
let classType = new ClassType(node.name.nameToken.value, classFlags,
|
||||
AnalyzerNodeInfo.getTypeSourceId(node));
|
||||
|
||||
// Don't walk the arguments for stub files because of forward
|
||||
// declarations.
|
||||
|
@ -691,8 +691,17 @@ export class TypeAnalyzer extends ParseTreeWalker {
|
||||
'Set', 'FrozenSet', 'Deque', 'ChainMap'];
|
||||
if (specialTypes.find(t => t === assignedName)) {
|
||||
// Synthesize a class.
|
||||
specialType = new ClassType(assignedName,
|
||||
ClassTypeFlags.BuiltInClass | ClassTypeFlags.SpecialBuiltIn);
|
||||
let specialClassType = new ClassType(assignedName,
|
||||
ClassTypeFlags.BuiltInClass | ClassTypeFlags.SpecialBuiltIn,
|
||||
DefaultTypeSourceId);
|
||||
|
||||
let baseClass = TypeAnnotation.getBuiltInType(this._currentScope,
|
||||
assignedName.toLowerCase());
|
||||
if (baseClass instanceof ClassType) {
|
||||
specialClassType.addBaseClass(baseClass, false);
|
||||
}
|
||||
|
||||
specialType = specialClassType;
|
||||
}
|
||||
}
|
||||
|
||||
@ -936,8 +945,17 @@ export class TypeAnalyzer extends ParseTreeWalker {
|
||||
'Final', 'Literal'];
|
||||
if (specialTypes.find(t => t === assignedName)) {
|
||||
// Synthesize a class.
|
||||
specialType = new ClassType(assignedName,
|
||||
ClassTypeFlags.BuiltInClass | ClassTypeFlags.SpecialBuiltIn);
|
||||
let specialClassType = new ClassType(assignedName,
|
||||
ClassTypeFlags.BuiltInClass | ClassTypeFlags.SpecialBuiltIn,
|
||||
AnalyzerNodeInfo.getTypeSourceId(node));
|
||||
|
||||
let baseClass = TypeAnnotation.getBuiltInType(this._currentScope,
|
||||
assignedName.toLowerCase());
|
||||
if (baseClass instanceof ClassType) {
|
||||
specialClassType.addBaseClass(baseClass, false);
|
||||
}
|
||||
|
||||
specialType = specialClassType;
|
||||
}
|
||||
|
||||
if (specialType) {
|
||||
|
@ -134,7 +134,8 @@ export class TypeAnnotation {
|
||||
}
|
||||
}
|
||||
|
||||
let classType = new ClassType(className, ClassTypeFlags.None);
|
||||
let classType = new ClassType(className, ClassTypeFlags.None,
|
||||
AnalyzerNodeInfo.getTypeSourceId(node));
|
||||
const classFields = classType.getClassFields();
|
||||
classFields.set('__class__', new Symbol(classType, DefaultTypeSourceId));
|
||||
const instanceFields = classType.getInstanceFields();
|
||||
@ -437,7 +438,7 @@ export class TypeAnnotation {
|
||||
switch (className) {
|
||||
case 'Callable': {
|
||||
// A 'Callable' with no parameters is a generic function.
|
||||
type = this._createCallableType([], diagSink);
|
||||
type = this._createCallableType(type, [], diagSink);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -450,7 +451,7 @@ export class TypeAnnotation {
|
||||
case 'Set':
|
||||
case 'Tuple':
|
||||
case 'Type': {
|
||||
type = this._createSpecialType(className, [], currentScope, diagSink);
|
||||
type = this._createSpecialType(type, [], diagSink);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -528,14 +529,6 @@ export class TypeAnnotation {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private static _validateTypeArgs(typeArgs: TypeResult[], diagSink: TextRangeDiagnosticSink) {
|
||||
// Make sure there are no redundant type args.
|
||||
// TODO - need to implement
|
||||
|
||||
// Make sure type args are reachable according to scoping rules.
|
||||
// TODO - need to implement
|
||||
}
|
||||
|
||||
private static _getTypeFromIndexExpression(node: IndexExpressionNode,
|
||||
currentScope: Scope, diagSink: TextRangeDiagnosticSink): TypeResult {
|
||||
|
||||
@ -556,12 +549,14 @@ export class TypeAnnotation {
|
||||
|
||||
switch (className) {
|
||||
case 'Callable': {
|
||||
type = this._createCallableType(typeArgs, diagSink);
|
||||
type = this._createCallableType(baseTypeResult.type,
|
||||
typeArgs, diagSink);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'Optional': {
|
||||
type = this._createOptional(node.baseExpression, typeArgs, diagSink);
|
||||
type = this._createOptional(node.baseExpression,
|
||||
typeArgs, diagSink);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -573,27 +568,26 @@ export class TypeAnnotation {
|
||||
|
||||
case 'ClassVar':
|
||||
case 'Deque':
|
||||
case 'Generic':
|
||||
case 'List':
|
||||
case 'FrozenSet':
|
||||
case 'Set': {
|
||||
type = this._createSpecialType(className, typeArgs,
|
||||
currentScope, diagSink, 1);
|
||||
type = this._createSpecialType(baseTypeResult.type, typeArgs,
|
||||
diagSink, 1);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'ChainMap':
|
||||
case 'Dict':
|
||||
case 'DefaultDict': {
|
||||
type = this._createSpecialType(className, typeArgs,
|
||||
currentScope, diagSink, 2);
|
||||
type = this._createSpecialType(baseTypeResult.type, typeArgs,
|
||||
diagSink, 2);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'Protocol':
|
||||
case 'Tuple': {
|
||||
type = this._createSpecialType(className, typeArgs,
|
||||
currentScope, diagSink);
|
||||
type = this._createSpecialType(baseTypeResult.type, typeArgs,
|
||||
diagSink);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -601,6 +595,11 @@ export class TypeAnnotation {
|
||||
type = this._createUnionType(typeArgs);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'Generic':
|
||||
type = this._createGenericType(node.baseExpression,
|
||||
baseTypeResult.type, typeArgs, diagSink);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -620,6 +619,14 @@ export class TypeAnnotation {
|
||||
return { type, isClassType, node };
|
||||
}
|
||||
|
||||
private static _validateTypeArgs(typeArgs: TypeResult[], diagSink: TextRangeDiagnosticSink) {
|
||||
// Make sure there are no redundant type args.
|
||||
// TODO - need to implement
|
||||
|
||||
// Make sure type args are reachable according to scoping rules.
|
||||
// TODO - need to implement
|
||||
}
|
||||
|
||||
private static _getTypeArgs(node: ExpressionNode, currentScope: Scope,
|
||||
diagSink: TextRangeDiagnosticSink): TypeResult[] {
|
||||
|
||||
@ -745,16 +752,35 @@ export class TypeAnnotation {
|
||||
return TypeUtils.combineTypesArray(types);
|
||||
}
|
||||
|
||||
private static _createGenericType(errorNode: ExpressionNode, classType: ClassType,
|
||||
typeArgs: TypeResult[], diagSink: TextRangeDiagnosticSink): Type {
|
||||
|
||||
// Make sure there's at least one type arg.
|
||||
if (typeArgs.length === 0) {
|
||||
diagSink.addErrorWithTextRange(
|
||||
`'Generic' requires at least one type argument`, errorNode);
|
||||
}
|
||||
|
||||
// Make sure that all of the type args are typeVars.
|
||||
typeArgs.forEach(typeArg => {
|
||||
if (!(typeArg.type instanceof TypeVarType)) {
|
||||
diagSink.addErrorWithTextRange(
|
||||
`Type argument for 'Generic' must be a type variable`, typeArg.node);
|
||||
}
|
||||
});
|
||||
|
||||
return this._createSpecialType(classType, typeArgs, diagSink);
|
||||
}
|
||||
|
||||
// Converts the type parameters for a Callable type. It should
|
||||
// have zero to two parameters. The first parameter, if present, should be
|
||||
// either an ellipsis or a list of parameter types. The second parameter, if
|
||||
// present, should specify the return type.
|
||||
private static _createCallableType(typeArgs: TypeResult[],
|
||||
diagSink: TextRangeDiagnosticSink): FunctionType {
|
||||
private static _createCallableType(classType: Type,
|
||||
typeArgs: TypeResult[], diagSink: TextRangeDiagnosticSink): FunctionType {
|
||||
|
||||
let functionType = new FunctionType(FunctionTypeFlags.None);
|
||||
functionType.setDeclaredReturnType(AnyType.create());
|
||||
let paramList: Type[] | undefined;
|
||||
|
||||
if (typeArgs.length > 0) {
|
||||
if (typeArgs[0].typeList) {
|
||||
@ -785,14 +811,23 @@ export class TypeAnnotation {
|
||||
return functionType;
|
||||
}
|
||||
|
||||
private static _createSpecialType(className: string, typeArgs: TypeResult[],
|
||||
currentScope: Scope, diagSink: TextRangeDiagnosticSink,
|
||||
paramLimit?: number): Type {
|
||||
private static _createSpecialType(classType: ClassType, typeArgs: TypeResult[],
|
||||
diagSink: TextRangeDiagnosticSink, paramLimit?: number): Type {
|
||||
|
||||
// let typeParam = this.getType(indexExpression, currentScope, diagSink, false);
|
||||
// TODO - need to implement
|
||||
let typeArgCount = typeArgs.length;
|
||||
// Make sure the parameter list count is correct.
|
||||
if (paramLimit !== undefined && typeArgCount > paramLimit) {
|
||||
diagSink.addErrorWithTextRange(
|
||||
`Expected at most ${ paramLimit } type arguments`, typeArgs[paramLimit].node);
|
||||
typeArgCount = paramLimit;
|
||||
}
|
||||
|
||||
return this.getBuiltInType(currentScope, className.toLowerCase());
|
||||
let specializedType = classType.cloneForSpecialization();
|
||||
for (let i = 0; i < typeArgCount; i++) {
|
||||
specializedType.addTypeArgument(typeArgs[i].type);
|
||||
}
|
||||
|
||||
return specializedType;
|
||||
}
|
||||
|
||||
private static _getBooleanValue(node: ExpressionNode, diagSink: TextRangeDiagnosticSink): boolean {
|
||||
|
@ -196,8 +196,9 @@ export interface BaseClass {
|
||||
}
|
||||
|
||||
interface ClassDetails {
|
||||
classFlags: ClassTypeFlags;
|
||||
className: string;
|
||||
name: string;
|
||||
flags: ClassTypeFlags;
|
||||
typeSourceId: TypeSourceId;
|
||||
baseClasses: BaseClass[];
|
||||
classFields: SymbolTable;
|
||||
instanceFields: SymbolTable;
|
||||
@ -213,14 +214,15 @@ export class ClassType extends Type {
|
||||
// specialized will have type arguments that correspond to
|
||||
// some or all of the type parameters. Unspecified type
|
||||
// parameters are undefined.
|
||||
private _typeArguments?: (Type | undefined)[];
|
||||
private _typeArguments?: (Type | Type[])[];
|
||||
|
||||
constructor(name: string, flags: ClassTypeFlags) {
|
||||
constructor(name: string, flags: ClassTypeFlags, typeSourceId: TypeSourceId) {
|
||||
super();
|
||||
|
||||
this._classDetails = {
|
||||
className: name,
|
||||
classFlags: flags,
|
||||
name,
|
||||
flags,
|
||||
typeSourceId,
|
||||
baseClasses: [],
|
||||
classFields: new SymbolTable(),
|
||||
instanceFields: new SymbolTable(),
|
||||
@ -228,12 +230,19 @@ export class ClassType extends Type {
|
||||
};
|
||||
}
|
||||
|
||||
cloneForSpecialization(): ClassType {
|
||||
let newClassType = new ClassType(this._classDetails.name,
|
||||
this._classDetails.flags, this._classDetails.typeSourceId);
|
||||
newClassType._classDetails = this._classDetails;
|
||||
return newClassType;
|
||||
}
|
||||
|
||||
isSpecialBuiltIn() {
|
||||
return !!(this._classDetails.classFlags & ClassTypeFlags.SpecialBuiltIn);
|
||||
return !!(this._classDetails.flags & ClassTypeFlags.SpecialBuiltIn);
|
||||
}
|
||||
|
||||
isBuiltIn() {
|
||||
return !!(this._classDetails.classFlags & ClassTypeFlags.BuiltInClass);
|
||||
return !!(this._classDetails.flags & ClassTypeFlags.BuiltInClass);
|
||||
}
|
||||
|
||||
isProtocol() {
|
||||
@ -249,11 +258,11 @@ export class ClassType extends Type {
|
||||
}
|
||||
|
||||
getClassName() {
|
||||
return this._classDetails.className;
|
||||
return this._classDetails.name;
|
||||
}
|
||||
|
||||
hasDecorators() {
|
||||
return !!(this._classDetails.classFlags & ClassTypeFlags.HasDecorators);
|
||||
return !!(this._classDetails.flags & ClassTypeFlags.HasDecorators);
|
||||
}
|
||||
|
||||
getBaseClasses(): BaseClass[] {
|
||||
@ -287,12 +296,47 @@ export class ClassType extends Type {
|
||||
}
|
||||
|
||||
isSame(type2: Type): boolean {
|
||||
return super.isSame(type2) &&
|
||||
this._classDetails.className === (type2 as ClassType)._classDetails.className;
|
||||
if (!super.isSame(type2)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let classType2 = type2 as ClassType;
|
||||
|
||||
// If the class details are common, it's the same class.
|
||||
if (this._classDetails === classType2._classDetails) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// In a few cases (e.g. with NamedTuple classes) we allocate a
|
||||
// new class type for every type analysis pass. To detect this
|
||||
// case, we will use the typeSourceId field.
|
||||
if (this._classDetails.typeSourceId === classType2._classDetails.typeSourceId) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
getObjectName(recursionCount = AsStringMaxRecursionCount): string {
|
||||
let objName = this._classDetails.name;
|
||||
|
||||
if (this._typeArguments) {
|
||||
objName += '[' + this._typeArguments.map(typeArg => {
|
||||
if (typeArg instanceof Type) {
|
||||
return typeArg.asStringInternal(recursionCount);
|
||||
} else {
|
||||
return '[' + typeArg.map(type => {
|
||||
return type.asStringInternal(recursionCount);
|
||||
}).join(', ') + ']';
|
||||
}
|
||||
}).join(', ') + ']';
|
||||
}
|
||||
|
||||
return objName;
|
||||
}
|
||||
|
||||
asStringInternal(recursionCount = AsStringMaxRecursionCount): string {
|
||||
return 'class ' + this._classDetails.className;
|
||||
return 'Type[' + this.getObjectName(recursionCount) + ']';
|
||||
}
|
||||
|
||||
// Determines whether this is a subclass (derived class)
|
||||
@ -305,7 +349,7 @@ export class ClassType extends Type {
|
||||
// Handle built-in types like 'dict' and 'list', which are all
|
||||
// subclasses of object even though they are not explicitly declared
|
||||
// that way.
|
||||
if (this.isBuiltIn() && type2._classDetails.className === 'object' && type2.isBuiltIn()) {
|
||||
if (this.isBuiltIn() && type2._classDetails.name === 'object' && type2.isBuiltIn()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -321,6 +365,17 @@ export class ClassType extends Type {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
addTypeArgument(typeArg: Type | Type[]) {
|
||||
if (!this._typeArguments) {
|
||||
this._typeArguments = [];
|
||||
}
|
||||
this._typeArguments.push(typeArg);
|
||||
}
|
||||
|
||||
getTypeArguments() {
|
||||
return this._typeArguments;
|
||||
}
|
||||
}
|
||||
|
||||
export class ObjectType extends Type {
|
||||
@ -345,7 +400,7 @@ export class ObjectType extends Type {
|
||||
}
|
||||
|
||||
asStringInternal(recursionCount = AsStringMaxRecursionCount): string {
|
||||
return this._classType.getClassName();
|
||||
return this._classType.getObjectName();
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user