mirror of
https://github.com/microsoft/pyright.git
synced 2024-10-05 12:27:30 +03:00
Continued implementation of generic classes.
This commit is contained in:
parent
5e47a2fd18
commit
f0bf4a1f74
@ -15,6 +15,8 @@ import { ParseResults } from '../parser/parser';
|
||||
import { AnalyzerNodeInfo } from './analyzerNodeInfo';
|
||||
import { ParseTreeUtils } from './parseTreeUtils';
|
||||
import { SymbolCategory } from './symbol';
|
||||
import { ClassType } from './types';
|
||||
import { TypeUtils } from './typeUtils';
|
||||
|
||||
export class HoverProvider {
|
||||
static getHoverForPosition(parseResults: ParseResults,
|
||||
@ -80,7 +82,8 @@ export class HoverProvider {
|
||||
|
||||
case SymbolCategory.Class: {
|
||||
if (node instanceof NameNode) {
|
||||
return '```\n(class) ' + node.nameToken.value + '\n```';
|
||||
return '```\n(class) ' + node.nameToken.value +
|
||||
this._getTypeText(node) + '\n```';
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -131,6 +131,16 @@ export class TypeAnalyzer extends ParseTreeWalker {
|
||||
}
|
||||
});
|
||||
|
||||
// Update the type parameters for the class.
|
||||
let typeParameters: TypeVarType[] = [];
|
||||
classType.getBaseClasses().forEach(baseClass => {
|
||||
TypeUtils.addTypeVarsToListIfUnique(typeParameters,
|
||||
TypeUtils.getTypeVarArgumentsRecursive(baseClass.type));
|
||||
});
|
||||
if (classType.setTypeParameters(typeParameters)) {
|
||||
this._setAnalysisChanged();
|
||||
}
|
||||
|
||||
this._enterScope(node, () => {
|
||||
this.walk(node.suite);
|
||||
});
|
||||
@ -1394,7 +1404,15 @@ export class TypeAnalyzer extends ParseTreeWalker {
|
||||
} else {
|
||||
let skipFirstMethodParam = this._isCallOnObjectOrClass(node);
|
||||
if (callType instanceof FunctionType) {
|
||||
exprType = callType.getEffectiveReturnType();
|
||||
// The stdlib collections.pyi stub file defines namedtuple as a function
|
||||
// rather than a class, so we need to check for it here.
|
||||
if (node.leftExpression instanceof NameNode &&
|
||||
node.leftExpression.nameToken.value === 'namedtuple') {
|
||||
exprType = TypeAnnotation.getNamedTupleType(node, false,
|
||||
this._currentScope, this._fileInfo.diagnosticSink);
|
||||
} else {
|
||||
exprType = callType.getEffectiveReturnType();
|
||||
}
|
||||
} else if (callType instanceof OverloadedFunctionType) {
|
||||
// Determine which of the overloads (if any) match.
|
||||
let functionType = this._findOverloadedFunctionType(
|
||||
@ -1923,14 +1941,27 @@ export class TypeAnalyzer extends ParseTreeWalker {
|
||||
|
||||
// Tries to match the arguments of a call to the constructor for a class.
|
||||
private _validateConstructorArguments(node: CallExpressionNode, type: ClassType) {
|
||||
const initMethodType = TypeUtils.lookUpClassMember(type, '__init__');
|
||||
if (!initMethodType) {
|
||||
if (node.arguments.length > 0) {
|
||||
this._addError(
|
||||
`Expected 0 arguments to '${ type.getClassName() }' constructor`, node);
|
||||
let validatedTypes = false;
|
||||
const initMethodMember = TypeUtils.lookUpClassMember(type, '__init__', false);
|
||||
if (initMethodMember && initMethodMember.symbol) {
|
||||
const initMethodType = TypeUtils.getEffectiveTypeOfMember(initMethodMember);
|
||||
this._validateCallArguments(node, initMethodType, true);
|
||||
validatedTypes = true;
|
||||
}
|
||||
|
||||
if (!validatedTypes) {
|
||||
// If there's no init method, check for a constructor.
|
||||
const constructorMember = TypeUtils.lookUpClassMember(type, '__new__', false);
|
||||
if (constructorMember && constructorMember.symbol) {
|
||||
const constructorMethodType = TypeUtils.getEffectiveTypeOfMember(constructorMember);
|
||||
this._validateCallArguments(node, constructorMethodType, true);
|
||||
validatedTypes = true;
|
||||
}
|
||||
} else if (initMethodType instanceof FunctionType) {
|
||||
this._validateFunctionArguments(node, initMethodType, true);
|
||||
}
|
||||
|
||||
if (!validatedTypes && node.arguments.length > 0) {
|
||||
this._addError(
|
||||
`Expected 0 arguments to '${ type.getClassName() }' constructor`, node);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2120,8 +2151,8 @@ export class TypeAnalyzer extends ParseTreeWalker {
|
||||
return type;
|
||||
}
|
||||
|
||||
private _enterTemporaryScope(callback: () => void, isConditional?: boolean,
|
||||
isNotExecuted?: boolean) {
|
||||
private _enterTemporaryScope(callback: () => void, isConditional ? : boolean,
|
||||
isNotExecuted ? : boolean) {
|
||||
let prevScope = this._currentScope;
|
||||
let newScope = new Scope(ScopeType.Temporary, prevScope);
|
||||
if (isConditional) {
|
||||
|
@ -10,10 +10,10 @@
|
||||
|
||||
import { TextRangeDiagnosticSink } from '../common/diagnosticSink';
|
||||
import StringMap from '../common/stringMap';
|
||||
import { ArgumentCategory, CallExpressionNode, ConstantNode, DictionaryNode,
|
||||
import { ArgumentCategory, CallExpressionNode, ConstantNode,
|
||||
EllipsisNode, ExpressionNode, FunctionNode, IndexExpressionNode, ListNode,
|
||||
MemberAccessExpressionNode, NameNode, NumberNode, ParameterCategory,
|
||||
SetNode, StringNode, TupleExpressionNode } from '../parser/parseNodes';
|
||||
StringNode, TupleExpressionNode } from '../parser/parseNodes';
|
||||
import { KeywordToken, KeywordType, QuoteTypeFlags, TokenType } from '../parser/tokenizerTypes';
|
||||
import { AnalyzerNodeInfo } from './analyzerNodeInfo';
|
||||
import { DefaultTypeSourceId } from './inferredType';
|
||||
@ -29,7 +29,6 @@ interface TypeResult {
|
||||
type: Type;
|
||||
typeList?: TypeResult[];
|
||||
isClassType?: boolean;
|
||||
typeVarsRecursive?: TypeVarType[];
|
||||
node: ExpressionNode;
|
||||
}
|
||||
|
||||
@ -336,6 +335,8 @@ export class TypeAnnotation {
|
||||
return [decoratedType, warnIfDuplicate];
|
||||
}
|
||||
|
||||
// Similar to the public getType method except that it returns a full
|
||||
// TypeResult object with additional information.
|
||||
private static _getType(node: ExpressionNode, currentScope: Scope, diagSink: TextRangeDiagnosticSink,
|
||||
classNamesImplyObjects = true, transformBuiltInTypes = true): TypeResult {
|
||||
let typeResult: TypeResult | undefined;
|
||||
@ -393,21 +394,6 @@ export class TypeAnnotation {
|
||||
if (typeResult.isClassType) {
|
||||
classNamesImplyObjects = false;
|
||||
}
|
||||
} else if (node instanceof ListNode) {
|
||||
// TODO - need to implement
|
||||
typeResult = { type: UnknownType.create(), node };
|
||||
// diagSink.addErrorWithTextRange(
|
||||
// `'Unsupported type expression: list`, node);
|
||||
} else if (node instanceof DictionaryNode) {
|
||||
// TODO - need to implement
|
||||
typeResult = { type: UnknownType.create(), node };
|
||||
diagSink.addErrorWithTextRange(
|
||||
`'Unsupported type expression: dictionary`, node);
|
||||
} else if (node instanceof SetNode) {
|
||||
// TODO - need to implement
|
||||
typeResult = { type: UnknownType.create(), node };
|
||||
diagSink.addErrorWithTextRange(
|
||||
`'Unsupported type expression: set`, node);
|
||||
}
|
||||
|
||||
if (typeResult && classNamesImplyObjects) {
|
||||
@ -419,7 +405,7 @@ export class TypeAnnotation {
|
||||
}
|
||||
|
||||
diagSink.addErrorWithTextRange(
|
||||
`Unknown type '${ ParseTreeUtils.printExpression(node) }'`, node);
|
||||
`Unknown type expression '${ ParseTreeUtils.printExpression(node) }'`, node);
|
||||
return { type: UnknownType.create(), node };
|
||||
}
|
||||
|
||||
@ -604,7 +590,8 @@ export class TypeAnnotation {
|
||||
}
|
||||
|
||||
if (!type) {
|
||||
// TODO - need to implement generic support
|
||||
type = this._createSpecializedClassType(baseTypeResult.type,
|
||||
typeArgs, diagSink);
|
||||
}
|
||||
} else if (!baseTypeResult.type.isAny()) {
|
||||
diagSink.addErrorWithTextRange(
|
||||
@ -620,9 +607,6 @@ export class TypeAnnotation {
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@ -684,7 +668,8 @@ export class TypeAnnotation {
|
||||
} else if (baseTypeResult.type instanceof FunctionType) {
|
||||
// The stdlib collections.pyi stub file defines namedtuple as a function
|
||||
// rather than a class, so we need to check for it here.
|
||||
if (node.leftExpression instanceof NameNode && node.leftExpression.nameToken.value === 'namedtuple') {
|
||||
if (node.leftExpression instanceof NameNode &&
|
||||
node.leftExpression.nameToken.value === 'namedtuple') {
|
||||
type = this.getNamedTupleType(node, false, currentScope, diagSink);
|
||||
isClassType = true;
|
||||
} else {
|
||||
@ -761,11 +746,22 @@ export class TypeAnnotation {
|
||||
`'Generic' requires at least one type argument`, errorNode);
|
||||
}
|
||||
|
||||
// Make sure that all of the type args are typeVars.
|
||||
// Make sure that all of the type args are typeVars and are unique.
|
||||
let uniqueTypeVars: TypeVarType[] = [];
|
||||
typeArgs.forEach(typeArg => {
|
||||
if (!(typeArg.type instanceof TypeVarType)) {
|
||||
diagSink.addErrorWithTextRange(
|
||||
`Type argument for 'Generic' must be a type variable`, typeArg.node);
|
||||
} else {
|
||||
for (let typeVar of uniqueTypeVars) {
|
||||
if (typeVar === typeArg.type) {
|
||||
diagSink.addErrorWithTextRange(
|
||||
`Type argument for 'Generic' must be unique`, typeArg.node);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uniqueTypeVars.push(typeArg.type);
|
||||
}
|
||||
});
|
||||
|
||||
@ -811,11 +807,39 @@ export class TypeAnnotation {
|
||||
return functionType;
|
||||
}
|
||||
|
||||
private static _createSpecializedClassType(classType: ClassType,
|
||||
typeArgs: TypeResult[], diagSink: TextRangeDiagnosticSink): Type {
|
||||
|
||||
let typeArgCount = typeArgs.length;
|
||||
|
||||
// Make sure the argument list count is correct.
|
||||
let typeParameters = classType.getTypeParameters();
|
||||
if (typeArgCount > typeParameters.length) {
|
||||
if (typeParameters.length === 0) {
|
||||
diagSink.addErrorWithTextRange(`No type arguments were expected`,
|
||||
typeArgs[typeParameters.length].node);
|
||||
} else {
|
||||
diagSink.addErrorWithTextRange(
|
||||
`Expected at most ${ typeParameters.length } type arguments`,
|
||||
typeArgs[typeParameters.length].node);
|
||||
}
|
||||
typeArgCount = typeParameters.length;
|
||||
}
|
||||
|
||||
let specializedClass = classType.cloneForSpecialization();
|
||||
|
||||
// TODO - need to verify constraints of arguments
|
||||
specializedClass.setTypeArguments(typeArgs.map(t => t.type));
|
||||
|
||||
return specializedClass;
|
||||
}
|
||||
|
||||
private static _createSpecialType(classType: ClassType, typeArgs: TypeResult[],
|
||||
diagSink: TextRangeDiagnosticSink, paramLimit?: number): Type {
|
||||
|
||||
let typeArgCount = typeArgs.length;
|
||||
// Make sure the parameter list count is correct.
|
||||
|
||||
// Make sure the argument list count is correct.
|
||||
if (paramLimit !== undefined && typeArgCount > paramLimit) {
|
||||
diagSink.addErrorWithTextRange(
|
||||
`Expected at most ${ paramLimit } type arguments`, typeArgs[paramLimit].node);
|
||||
@ -823,9 +847,7 @@ export class TypeAnnotation {
|
||||
}
|
||||
|
||||
let specializedType = classType.cloneForSpecialization();
|
||||
for (let i = 0; i < typeArgCount; i++) {
|
||||
specializedType.addTypeArgument(typeArgs[i].type);
|
||||
}
|
||||
specializedType.setTypeArguments(typeArgs.map(t => t.type));
|
||||
|
||||
return specializedType;
|
||||
}
|
||||
|
@ -341,4 +341,46 @@ export class TypeUtils {
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
static addTypeVarToListIfUnique(list: TypeVarType[], type: TypeVarType) {
|
||||
if (list.find(t => t === type) === undefined) {
|
||||
list.push(type);
|
||||
}
|
||||
}
|
||||
|
||||
// Combines two lists of type var types, maintaining the combined order
|
||||
// but removing any duplicates.
|
||||
static addTypeVarsToListIfUnique(list1: TypeVarType[], list2: TypeVarType[]) {
|
||||
for (let t of list2) {
|
||||
this.addTypeVarToListIfUnique(list1, t);
|
||||
}
|
||||
}
|
||||
|
||||
static getTypeVarArgumentsRecursive(type: Type): TypeVarType[] {
|
||||
if (type instanceof TypeVarType) {
|
||||
return [type];
|
||||
} else if (type instanceof ClassType) {
|
||||
let combinedList: TypeVarType[] = [];
|
||||
let typeArgs = type.getTypeArguments();
|
||||
|
||||
if (typeArgs) {
|
||||
typeArgs.forEach(typeArg => {
|
||||
if (typeArg instanceof Type) {
|
||||
this.addTypeVarsToListIfUnique(combinedList,
|
||||
this.getTypeVarArgumentsRecursive(typeArg));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return combinedList;
|
||||
} else if (type instanceof UnionType) {
|
||||
let combinedList: TypeVarType[] = [];
|
||||
for (let subtype of type.getTypes()) {
|
||||
this.addTypeVarsToListIfUnique(combinedList,
|
||||
this.getTypeVarArgumentsRecursive(subtype));
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ export enum TypeCategory {
|
||||
TypeVar
|
||||
}
|
||||
|
||||
const AsStringMaxRecursionCount = 4;
|
||||
const AsStringMaxRecursionCount = 20;
|
||||
|
||||
export abstract class Type {
|
||||
abstract category: TypeCategory;
|
||||
@ -81,7 +81,7 @@ export abstract class Type {
|
||||
}
|
||||
|
||||
asString(): string {
|
||||
return this.asStringInternal(AsStringMaxRecursionCount);
|
||||
return this.asStringInternal(0);
|
||||
}
|
||||
|
||||
abstract asStringInternal(recursionCount: number): string;
|
||||
@ -112,7 +112,7 @@ export class UnboundType extends Type {
|
||||
return true;
|
||||
}
|
||||
|
||||
asStringInternal(recursionCount = AsStringMaxRecursionCount): string {
|
||||
asStringInternal(recursionCount = 0): string {
|
||||
return 'Unbound';
|
||||
}
|
||||
}
|
||||
@ -130,7 +130,7 @@ export class UnknownType extends Type {
|
||||
return true;
|
||||
}
|
||||
|
||||
asStringInternal(recursionCount = AsStringMaxRecursionCount): string {
|
||||
asStringInternal(recursionCount = 0): string {
|
||||
return 'Unknown';
|
||||
}
|
||||
}
|
||||
@ -162,7 +162,7 @@ export class ModuleType extends Type {
|
||||
return this._isPartialModule;
|
||||
}
|
||||
|
||||
asStringInternal(recursionCount = AsStringMaxRecursionCount): string {
|
||||
asStringInternal(recursionCount = 0): string {
|
||||
return 'Module';
|
||||
}
|
||||
}
|
||||
@ -183,13 +183,6 @@ export enum ClassTypeFlags {
|
||||
SpecialBuiltIn = 0x04
|
||||
}
|
||||
|
||||
// export class TypeReference extends Type {
|
||||
// // A generic type instantiation will have one or more type
|
||||
// // arguments and an associated target class type.
|
||||
// private _typeArguments: Type[] = [];
|
||||
// private _targetType?: ClassType;
|
||||
// }
|
||||
|
||||
export interface BaseClass {
|
||||
isMetaclass: boolean;
|
||||
type: Type;
|
||||
@ -295,6 +288,36 @@ export class ClassType extends Type {
|
||||
this._classDetails.instanceFields = nameMap;
|
||||
}
|
||||
|
||||
setTypeArguments(typeArgs: (Type | Type[])[]) {
|
||||
this._typeArguments = typeArgs;
|
||||
}
|
||||
|
||||
getTypeArguments() {
|
||||
return this._typeArguments;
|
||||
}
|
||||
|
||||
getTypeParameters() {
|
||||
return this._classDetails.typeParameters;
|
||||
}
|
||||
|
||||
setTypeParameters(params: TypeVarType[]): boolean {
|
||||
let didParametersChange = false;
|
||||
|
||||
if (this._classDetails.typeParameters.length === params.length) {
|
||||
for (let i = 0; i < params.length; i++) {
|
||||
if (!params[i].isSame(this._classDetails.typeParameters[i])) {
|
||||
didParametersChange = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
didParametersChange = true;
|
||||
}
|
||||
|
||||
this._classDetails.typeParameters = params;
|
||||
|
||||
return didParametersChange;
|
||||
}
|
||||
|
||||
isSame(type2: Type): boolean {
|
||||
if (!super.isSame(type2)) {
|
||||
return false;
|
||||
@ -317,26 +340,35 @@ export class ClassType extends Type {
|
||||
return false;
|
||||
}
|
||||
|
||||
getObjectName(recursionCount = AsStringMaxRecursionCount): string {
|
||||
getObjectName(recursionCount = 0): string {
|
||||
let objName = this._classDetails.name;
|
||||
|
||||
// If there is a type arguments array, it's a specialized class.
|
||||
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(', ') + ']';
|
||||
}
|
||||
if (this._typeArguments.length > 0) {
|
||||
objName += '[' + this._typeArguments.map(typeArg => {
|
||||
if (typeArg instanceof Type) {
|
||||
return typeArg.asStringInternal(recursionCount + 1);
|
||||
} else {
|
||||
return '[' + typeArg.map(type => {
|
||||
return type.asStringInternal(recursionCount + 1);
|
||||
}).join(', ') + ']';
|
||||
}
|
||||
}).join(', ') + ']';
|
||||
}
|
||||
} else if (this._classDetails.typeParameters.length > 0) {
|
||||
objName += '[' + this._classDetails.typeParameters.map(typeArg => {
|
||||
return typeArg.asStringInternal(recursionCount + 1);
|
||||
}).join(', ') + ']';
|
||||
}
|
||||
|
||||
return objName;
|
||||
}
|
||||
|
||||
asStringInternal(recursionCount = AsStringMaxRecursionCount): string {
|
||||
return 'Type[' + this.getObjectName(recursionCount) + ']';
|
||||
asStringInternal(recursionCount = 0): string {
|
||||
// Return the same string that we'd use for an instance
|
||||
// of the class.
|
||||
return this.getObjectName(recursionCount + 1);
|
||||
}
|
||||
|
||||
// Determines whether this is a subclass (derived class)
|
||||
@ -365,17 +397,6 @@ 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 {
|
||||
@ -399,8 +420,8 @@ export class ObjectType extends Type {
|
||||
this._classType.isSame((type2 as ObjectType)._classType);
|
||||
}
|
||||
|
||||
asStringInternal(recursionCount = AsStringMaxRecursionCount): string {
|
||||
return this._classType.getObjectName();
|
||||
asStringInternal(recursionCount = 0): string {
|
||||
return this._classType.getObjectName(recursionCount + 1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -501,7 +522,7 @@ export class FunctionType extends Type {
|
||||
this._functionDetails.flags &= ~FunctionTypeFlags.HasCustomDecorators;
|
||||
}
|
||||
|
||||
asStringInternal(recursionCount = AsStringMaxRecursionCount): string {
|
||||
asStringInternal(recursionCount = 0): string {
|
||||
let paramTypeString = this._functionDetails.parameters.map(param => {
|
||||
let paramString = '';
|
||||
if (param.category === ParameterCategory.VarArgList) {
|
||||
@ -515,8 +536,8 @@ export class FunctionType extends Type {
|
||||
}
|
||||
|
||||
if (param.category === ParameterCategory.Simple) {
|
||||
const paramTypeString = recursionCount > 0 ?
|
||||
param.type.asStringInternal(recursionCount - 1) : '';
|
||||
const paramTypeString = recursionCount < AsStringMaxRecursionCount ?
|
||||
param.type.asStringInternal(recursionCount + 1) : '';
|
||||
paramString += ': ' + paramTypeString;
|
||||
}
|
||||
return paramString;
|
||||
@ -524,8 +545,8 @@ export class FunctionType extends Type {
|
||||
|
||||
let returnTypeString = 'Any';
|
||||
const returnType = this.getEffectiveReturnType();
|
||||
returnTypeString = recursionCount > 0 ?
|
||||
returnType.asStringInternal(recursionCount - 1) : '';
|
||||
returnTypeString = recursionCount < AsStringMaxRecursionCount ?
|
||||
returnType.asStringInternal(recursionCount + 1) : '';
|
||||
|
||||
return `(${ paramTypeString }) -> ${ returnTypeString }`;
|
||||
}
|
||||
@ -559,9 +580,9 @@ export class OverloadedFunctionType extends Type {
|
||||
}
|
||||
}
|
||||
|
||||
asStringInternal(recursionCount = AsStringMaxRecursionCount): string {
|
||||
asStringInternal(recursionCount = 0): string {
|
||||
const overloads = this._overloads.map(overload =>
|
||||
overload.type.asStringInternal(recursionCount));
|
||||
overload.type.asStringInternal(recursionCount + 1));
|
||||
return `Overload[${ overloads.join(', ') }]`;
|
||||
}
|
||||
}
|
||||
@ -598,10 +619,10 @@ export class PropertyType extends Type {
|
||||
return this._getter.getEffectiveReturnType();
|
||||
}
|
||||
|
||||
asStringInternal(recursionCount = AsStringMaxRecursionCount): string {
|
||||
asStringInternal(recursionCount = 0): string {
|
||||
const returnType = this._getter.getEffectiveReturnType();
|
||||
let returnTypeString = recursionCount > 0 ?
|
||||
returnType.asStringInternal(recursionCount - 1) : '';
|
||||
let returnTypeString = recursionCount < AsStringMaxRecursionCount ?
|
||||
returnType.asStringInternal(recursionCount + 1) : '';
|
||||
return returnTypeString;
|
||||
}
|
||||
}
|
||||
@ -615,7 +636,7 @@ export class NoneType extends Type {
|
||||
return this._instance;
|
||||
}
|
||||
|
||||
asStringInternal(recursionCount = AsStringMaxRecursionCount): string {
|
||||
asStringInternal(recursionCount = 0): string {
|
||||
return 'None';
|
||||
}
|
||||
}
|
||||
@ -715,15 +736,15 @@ export class UnionType extends Type {
|
||||
return this._types.find(t => t.isSame(type)) !== undefined;
|
||||
}
|
||||
|
||||
asStringInternal(recursionCount = AsStringMaxRecursionCount): string {
|
||||
asStringInternal(recursionCount = 0): string {
|
||||
if (this._types.find(t => t.category === TypeCategory.None) !== undefined) {
|
||||
const optionalType = recursionCount > 0 ?
|
||||
this.removeOptional().asStringInternal(recursionCount - 1) : '';
|
||||
const optionalType = recursionCount < AsStringMaxRecursionCount ?
|
||||
this.removeOptional().asStringInternal(recursionCount + 1) : '';
|
||||
return 'Optional[' + optionalType + ']';
|
||||
}
|
||||
|
||||
const unionTypeString = recursionCount > 0 ?
|
||||
this._types.map(t => t.asStringInternal(recursionCount - 1)).join(', ') : '';
|
||||
const unionTypeString = recursionCount < AsStringMaxRecursionCount ?
|
||||
this._types.map(t => t.asStringInternal(recursionCount + 1)).join(', ') : '';
|
||||
|
||||
return 'Union[' + unionTypeString + ']';
|
||||
}
|
||||
@ -766,9 +787,9 @@ export class TupleType extends Type {
|
||||
!type2Tuple._entryTypes[index].isSame(t)) === undefined;
|
||||
}
|
||||
|
||||
asStringInternal(recursionCount = AsStringMaxRecursionCount): string {
|
||||
let tupleTypeString = recursionCount > 0 ?
|
||||
this._entryTypes.map(t => t.asStringInternal(recursionCount - 1)).join(', ') : '';
|
||||
asStringInternal(recursionCount = 0): string {
|
||||
let tupleTypeString = recursionCount < AsStringMaxRecursionCount ?
|
||||
this._entryTypes.map(t => t.asStringInternal(recursionCount + 1)).join(', ') : '';
|
||||
return 'Tuple[' + tupleTypeString + ']';
|
||||
}
|
||||
}
|
||||
@ -787,6 +808,10 @@ export class TypeVarType extends Type {
|
||||
this._name = name;
|
||||
}
|
||||
|
||||
getName() {
|
||||
return this._name;
|
||||
}
|
||||
|
||||
getConstraints() {
|
||||
return this._constraints;
|
||||
}
|
||||
@ -833,13 +858,19 @@ export class TypeVarType extends Type {
|
||||
return true;
|
||||
}
|
||||
|
||||
asStringInternal(recursionCount = AsStringMaxRecursionCount): string {
|
||||
let params: string[] = [this._name];
|
||||
asStringInternal(recursionCount = 0): string {
|
||||
// Print the name in a simplified form if it's embedded
|
||||
// inside another type string.
|
||||
if (recursionCount > 0) {
|
||||
for (let constraint of this._constraints) {
|
||||
params.push(constraint.asStringInternal(recursionCount - 1));
|
||||
return this._name;
|
||||
} else {
|
||||
let params: string[] = [this._name];
|
||||
if (recursionCount < AsStringMaxRecursionCount) {
|
||||
for (let constraint of this._constraints) {
|
||||
params.push(constraint.asStringInternal(recursionCount + 1));
|
||||
}
|
||||
}
|
||||
return 'TypeVar[' + params.join(', ') + ']';
|
||||
}
|
||||
return 'TypeVar[' + params.join(', ') + ']';
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user