diff --git a/server/src/analyzer/completionProvider.ts b/server/src/analyzer/completionProvider.ts index 226136c7a..ddcefa7a5 100644 --- a/server/src/analyzer/completionProvider.ts +++ b/server/src/analyzer/completionProvider.ts @@ -266,9 +266,9 @@ export class CompletionProvider { // Determine the kind. let itemKind: CompletionItemKind = CompletionItemKind.Variable; - if (item.declarations) { - itemKind = this._convertSymbolCategoryToItemKind( - item.declarations[0].category); + const declarations = item.getDeclarations(); + if (declarations.length > 0) { + itemKind = this._convertSymbolCategoryToItemKind(declarations[0].category); } completionItem.kind = itemKind; completionList.items.push(completionItem); diff --git a/server/src/analyzer/expressionEvaluator.ts b/server/src/analyzer/expressionEvaluator.ts index 692afb5cb..84985da94 100644 --- a/server/src/analyzer/expressionEvaluator.ts +++ b/server/src/analyzer/expressionEvaluator.ts @@ -20,9 +20,9 @@ import { ArgumentCategory, AssignmentNode, AwaitExpressionNode, BinaryExpressionNode, CallExpressionNode, ClassNode, ConstantNode, DecoratorNode, DictionaryExpandEntryNode, DictionaryKeyEntryNode, DictionaryNode, EllipsisNode, ErrorExpressionNode, ExpressionNode, IndexExpressionNode, - IndexItemsNode, LambdaNode, ListComprehensionForNode, ListComprehensionIfNode, ListComprehensionNode, - ListNode, MemberAccessExpressionNode, NameNode, NumberNode, ParameterCategory, - ParseNode, SetNode, SliceExpressionNode, StatementListNode, + IndexItemsNode, LambdaNode, ListComprehensionForNode, ListComprehensionIfNode, + ListComprehensionNode, ListNode, MemberAccessExpressionNode, NameNode, NumberNode, + ParameterCategory, ParseNode, SetNode, SliceExpressionNode, StatementListNode, StringNode, TernaryExpressionNode, TupleExpressionNode, TypeAnnotationExpressionNode, UnaryExpressionNode, UnpackExpressionNode, YieldExpressionNode, YieldFromExpressionNode } from '../parser/parseNodes'; @@ -402,8 +402,8 @@ export class ExpressionEvaluator { } }); - classType.getClassFields().set('__init__', Symbol.create(initType, DefaultTypeSourceId)); - classType.getClassFields().set('__new__', Symbol.create(newType, DefaultTypeSourceId)); + classType.getClassFields().set('__init__', Symbol.createWithType(initType, DefaultTypeSourceId)); + classType.getClassFields().set('__new__', Symbol.createWithType(newType, DefaultTypeSourceId)); } getTypingType(symbolName: string): Type | undefined { @@ -1711,9 +1711,7 @@ export class ExpressionEvaluator { // Use the cached class type and update it if this isn't the first // analysis path. If this is the first pass, allocate a new ClassType. let classType = cachedCallType as ClassType; - if (classType) { - assert(classType instanceof ClassType); - } else { + if (!(classType instanceof ClassType)) { classType = new ClassType(className, ClassTypeFlags.None, AnalyzerNodeInfo.getTypeSourceId(errorNode)); @@ -1723,7 +1721,7 @@ export class ExpressionEvaluator { } const classFields = classType.getClassFields(); - classFields.set('__class__', Symbol.create(classType, DefaultTypeSourceId)); + classFields.set('__class__', Symbol.createWithType(classType, DefaultTypeSourceId)); const instanceFields = classType.getInstanceFields(); let builtInTupleType = ScopeUtils.getBuiltInType(this._scope, 'Tuple'); @@ -1766,7 +1764,7 @@ export class ExpressionEvaluator { }; constructorType.addParameter(paramInfo); - instanceFields.set(entryName, Symbol.create(entryType, DefaultTypeSourceId)); + instanceFields.set(entryName, Symbol.createWithType(entryType, DefaultTypeSourceId)); } }); } else if (entriesArg.valueExpression instanceof ListNode) { @@ -1830,7 +1828,7 @@ export class ExpressionEvaluator { constructorType.addParameter(paramInfo); - instanceFields.set(entryName, Symbol.create(entryType, DefaultTypeSourceId)); + instanceFields.set(entryName, Symbol.createWithType(entryType, DefaultTypeSourceId)); }); } else { // A dynamic expression was used, so we can't evaluate @@ -1852,19 +1850,19 @@ export class ExpressionEvaluator { initType.addParameter(selfParameter); TypeUtils.addDefaultFunctionParameters(initType); - classFields.set('__new__', Symbol.create(constructorType, DefaultTypeSourceId)); - classFields.set('__init__', Symbol.create(initType, DefaultTypeSourceId)); + classFields.set('__new__', Symbol.createWithType(constructorType, DefaultTypeSourceId)); + classFields.set('__init__', Symbol.createWithType(initType, DefaultTypeSourceId)); let keysItemType = new FunctionType(FunctionTypeFlags.None); keysItemType.setDeclaredReturnType(ScopeUtils.getBuiltInObject(this._scope, 'list', [ScopeUtils.getBuiltInObject(this._scope, 'str')])); - classFields.set('keys', Symbol.create(keysItemType, DefaultTypeSourceId)); - classFields.set('items', Symbol.create(keysItemType, DefaultTypeSourceId)); + classFields.set('keys', Symbol.createWithType(keysItemType, DefaultTypeSourceId)); + classFields.set('items', Symbol.createWithType(keysItemType, DefaultTypeSourceId)); let lenType = new FunctionType(FunctionTypeFlags.InstanceMethod); lenType.setDeclaredReturnType(ScopeUtils.getBuiltInObject(this._scope, 'int')); lenType.addParameter(selfParameter); - classFields.set('__len__', Symbol.create(lenType, DefaultTypeSourceId)); + classFields.set('__len__', Symbol.createWithType(lenType, DefaultTypeSourceId)); if (addGenericGetAttribute) { let getAttribType = new FunctionType(FunctionTypeFlags.InstanceMethod); @@ -1875,7 +1873,7 @@ export class ExpressionEvaluator { name: 'name', type: ScopeUtils.getBuiltInObject(this._scope, 'str') }); - classFields.set('__getattribute__', Symbol.create(getAttribType, DefaultTypeSourceId)); + classFields.set('__getattribute__', Symbol.createWithType(getAttribType, DefaultTypeSourceId)); } } @@ -2415,8 +2413,8 @@ export class ExpressionEvaluator { const targetExpr = comprehension.targetExpression; if (targetExpr instanceof NameNode) { - const symbol = this._scope.addSymbol(targetExpr.nameToken.value); - symbol.setTypeForSource(itemType, AnalyzerNodeInfo.getTypeSourceId(targetExpr)); + const symbol = this._scope.addSymbol(targetExpr.nameToken.value, false); + symbol.setInferredTypeForSource(itemType, AnalyzerNodeInfo.getTypeSourceId(targetExpr)); } else { // TODO - need to implement understoodType = false; diff --git a/server/src/analyzer/inferredType.ts b/server/src/analyzer/inferredType.ts index 6416e6929..71fb8a70a 100644 --- a/server/src/analyzer/inferredType.ts +++ b/server/src/analyzer/inferredType.ts @@ -10,7 +10,7 @@ * analysis proceeds. */ -import { ClassType, ObjectType, Type, UnboundType } from './types'; +import { ClassType, ObjectType, Type, UnboundType, UnknownType } from './types'; import { TypeUtils } from './typeUtils'; // A type can be inferred from multiple sources. Each sources @@ -28,7 +28,6 @@ export const DefaultTypeSourceId: TypeSourceId = 0; export class InferredType { private _sources: InferredTypeSource[] = []; - private _defaultType: Type; private _combinedType: Type; // Some inferred types need to be wrapped in another @@ -36,9 +35,8 @@ export class InferredType { // be wrapped in an Iterable[]. private _genericClassWrapper: ClassType | undefined; - constructor(defaultType?: Type) { - this._defaultType = defaultType || UnboundType.create(); - this._combinedType = this._defaultType; + constructor() { + this._combinedType = UnknownType.create(); } setGenericClassWrapper(classType: ClassType) { @@ -114,7 +112,7 @@ export class InferredType { } if (!newCombinedType) { - newCombinedType = this._defaultType; + newCombinedType = UnknownType.create(); } if (!newCombinedType.isSame(this._combinedType)) { diff --git a/server/src/analyzer/scope.ts b/server/src/analyzer/scope.ts index cb45db9c2..bae913d2e 100644 --- a/server/src/analyzer/scope.ts +++ b/server/src/analyzer/scope.ts @@ -70,8 +70,8 @@ export class Scope { private _breaksFromLoop = false; // Inferred return and yield types for the scope. - private _returnType = new InferredType(UnknownType.create()); - private _yieldType = new InferredType(UnknownType.create()); + private _returnType = new InferredType(); + private _yieldType = new InferredType(); // Active type constraints for this scope -- used for conditional // scopes where the condition constrains the types of certain @@ -148,8 +148,8 @@ export class Scope { } // Adds a new untyped symbol to the scope. - addSymbol(name: string): Symbol { - let symbol = new Symbol(); + addSymbol(name: string, isInitiallyUnbound: boolean): Symbol { + let symbol = new Symbol(isInitiallyUnbound); this._symbolTable.set(name, symbol); return symbol; } diff --git a/server/src/analyzer/semanticAnalyzer.ts b/server/src/analyzer/semanticAnalyzer.ts index 47f93af15..d740e11b9 100644 --- a/server/src/analyzer/semanticAnalyzer.ts +++ b/server/src/analyzer/semanticAnalyzer.ts @@ -22,9 +22,9 @@ import * as assert from 'assert'; import { DiagnosticLevel } from '../common/configOptions'; import { PythonVersion } from '../common/pythonVersion'; import { TextRange } from '../common/textRange'; -import { AwaitExpressionNode, ClassNode, DelNode, ErrorExpressionNode, +import { AwaitExpressionNode, ClassNode, ErrorExpressionNode, ExpressionNode, FunctionNode, GlobalNode, IfNode, LambdaNode, ModuleNameNode, - ModuleNode, NameNode, NonlocalNode, RaiseNode, StringNode, SuiteNode, TryNode, + ModuleNode, NonlocalNode, RaiseNode, StringNode, SuiteNode, TryNode, WhileNode, YieldExpressionNode, YieldFromExpressionNode } from '../parser/parseNodes'; import { StringTokenFlags } from '../parser/tokenizerTypes'; import { ScopeUtils } from '../scopeUtils'; @@ -32,7 +32,7 @@ import { AnalyzerFileInfo } from './analyzerFileInfo'; import { AnalyzerNodeInfo } from './analyzerNodeInfo'; import { ExpressionUtils } from './expressionUtils'; import { ImportType } from './importResult'; -import { DefaultTypeSourceId, TypeSourceId } from './inferredType'; +import { DefaultTypeSourceId } from './inferredType'; import { ParseTreeUtils } from './parseTreeUtils'; import { ParseTreeWalker } from './parseTreeWalker'; import { Scope, ScopeType } from './scope'; @@ -186,10 +186,10 @@ export abstract class SemanticAnalyzer extends ParseTreeWalker { }); if (nonMetaclassBaseClassCount === 0) { - let objectType = ScopeUtils.getBuiltInType(this._currentScope, 'object'); + const objectType = ScopeUtils.getBuiltInType(this._currentScope, 'object'); // Make sure we don't have 'object' derive from itself. Infinite // recursion will result. - if (objectType !== classType) { + if (!classType.isBuiltIn() || classType.getClassName() !== 'object') { classType.addBaseClass(objectType, false); } } @@ -199,18 +199,16 @@ export abstract class SemanticAnalyzer extends ParseTreeWalker { let analyzer = new ClassScopeAnalyzer(node, this._currentScope, classType, this._fileInfo); this._queueSubScopeAnalyzer(analyzer); - // Don't bind the name of the class until after we've done the - // first pass of its scope analysis. This guarantees that we'll flag - // any references to the as-yet-undeclared class as an error. + // Add the class symbol. We do this in the semantic analyzer to speed + // up overall analysis times. Without this, the type analyzer needs + // to do more passes to resolve classes. this._addSymbolToPermanentScope(node.name.nameToken.value, classType, - AnalyzerNodeInfo.getTypeSourceId(node.name), node.name); + AnalyzerNodeInfo.getTypeSourceId(node.name)); return false; } visitFunction(node: FunctionNode): boolean { - const containingClass = ParseTreeUtils.getEnclosingClass(node, true); - // The "__new__" magic method is not an instance method. // It acts as a static method instead. let functionFlags = FunctionTypeFlags.None; @@ -254,11 +252,6 @@ export abstract class SemanticAnalyzer extends ParseTreeWalker { } } - // Use an unknown type for now since we don't do any - // decorator processing in this pass. - this._addSymbolToPermanentScope(node.name.nameToken.value, - UnknownType.create(), AnalyzerNodeInfo.getTypeSourceId(node.name)); - AnalyzerNodeInfo.setExpressionType(node, functionType); AnalyzerNodeInfo.setExpressionType(node.name, functionType); @@ -422,7 +415,7 @@ export abstract class SemanticAnalyzer extends ParseTreeWalker { // Don't overwrite the implicit bound names that have already // been added to the scope. if (!this._currentScope.lookUpSymbol(name)) { - this._currentScope.addSymbol(name); + this._currentScope.addSymbol(name, true); } }); } @@ -439,7 +432,7 @@ export abstract class SemanticAnalyzer extends ParseTreeWalker { // Finds the nearest permanent scope (as opposed to temporary scope) and // adds a new symbol with the specified name if it doesn't already exist. protected _addSymbolToPermanentScope(nameValue: string, type: Type, - typeSourceId: TypeSourceId, warnIfDuplicateNode?: NameNode) { + typeSourceId = DefaultTypeSourceId) { const permanentScope = ScopeUtils.getPermanentScope(this._currentScope); assert(permanentScope.getType() !== ScopeType.Temporary); @@ -447,15 +440,13 @@ export abstract class SemanticAnalyzer extends ParseTreeWalker { let symbol = permanentScope.lookUpSymbol(nameValue); if (!symbol) { - symbol = this._currentScope.addSymbol(nameValue); - } else if (warnIfDuplicateNode) { - if (symbol.inferredType.getSourceCount() > 0) { - this._fileInfo.diagnosticSink.addWarningWithTextRange( - `'${ nameValue }' is already defined`, warnIfDuplicateNode); - } + // Add the symbol. Assume that symbols with a default type source ID + // are "implicit" symbols added to the scope. These are not initially unbound. + symbol = this._currentScope.addSymbol(nameValue, + typeSourceId !== DefaultTypeSourceId); } - symbol.inferredType.addSource(type, typeSourceId); + symbol.setInferredTypeForSource(type, typeSourceId); } private _validateYieldUsage(node: YieldExpressionNode | YieldFromExpressionNode) { @@ -548,19 +539,14 @@ export class ModuleScopeAnalyzer extends SemanticAnalyzer { private _bindImplicitNames() { // List taken from https://docs.python.org/3/reference/import.html#__name__ this._addSymbolToPermanentScope('__name__', - ScopeUtils.getBuiltInObject(this._currentScope, 'str'), DefaultTypeSourceId); - this._addSymbolToPermanentScope('__loader__', - AnyType.create(), DefaultTypeSourceId); + ScopeUtils.getBuiltInObject(this._currentScope, 'str')); + this._addSymbolToPermanentScope('__loader__', AnyType.create()); this._addSymbolToPermanentScope('__package__', - ScopeUtils.getBuiltInObject(this._currentScope, 'str'), DefaultTypeSourceId); - this._addSymbolToPermanentScope('__spec__', - AnyType.create(), DefaultTypeSourceId); - this._addSymbolToPermanentScope('__path__', - ScopeUtils.getBuiltInObject(this._currentScope, 'str'), DefaultTypeSourceId); - this._addSymbolToPermanentScope('__file__', - ScopeUtils.getBuiltInObject(this._currentScope, 'str'), DefaultTypeSourceId); - this._addSymbolToPermanentScope('__cached__', - ScopeUtils.getBuiltInObject(this._currentScope, 'str'), DefaultTypeSourceId); + ScopeUtils.getBuiltInObject(this._currentScope, 'str')); + this._addSymbolToPermanentScope('__spec__', AnyType.create()); + this._addSymbolToPermanentScope('__path__', ScopeUtils.getBuiltInObject(this._currentScope, 'str')); + this._addSymbolToPermanentScope('__file__', ScopeUtils.getBuiltInObject(this._currentScope, 'str')); + this._addSymbolToPermanentScope('__cached__', ScopeUtils.getBuiltInObject(this._currentScope, 'str')); } } @@ -595,14 +581,10 @@ export class ClassScopeAnalyzer extends SemanticAnalyzer { private _bindImplicitNames() { let classType = AnalyzerNodeInfo.getExpressionType(this._scopedNode); assert(classType instanceof ClassType); - this._addSymbolToPermanentScope('__class__', - classType!, DefaultTypeSourceId); - this._addSymbolToPermanentScope('__dict__', - AnyType.create(), DefaultTypeSourceId); - this._addSymbolToPermanentScope('__doc__', - ScopeUtils.getBuiltInObject(this._currentScope, 'str'), DefaultTypeSourceId); - this._addSymbolToPermanentScope('__name__', - ScopeUtils.getBuiltInObject(this._currentScope, 'str'), DefaultTypeSourceId); + this._addSymbolToPermanentScope('__class__', classType!); + this._addSymbolToPermanentScope('__dict__', AnyType.create()); + this._addSymbolToPermanentScope('__doc__', ScopeUtils.getBuiltInObject(this._currentScope, 'str')); + this._addSymbolToPermanentScope('__name__', ScopeUtils.getBuiltInObject(this._currentScope, 'str')); } } @@ -636,30 +618,19 @@ export class FunctionScopeAnalyzer extends SemanticAnalyzer { private _bindImplicitNames() { // List taken from https://docs.python.org/3/reference/datamodel.html - this._addSymbolToPermanentScope('__doc__', - ScopeUtils.getBuiltInObject(this._currentScope, 'str'), DefaultTypeSourceId); - this._addSymbolToPermanentScope('__name__', - ScopeUtils.getBuiltInObject(this._currentScope, 'str'), DefaultTypeSourceId); + this._addSymbolToPermanentScope('__doc__', ScopeUtils.getBuiltInObject(this._currentScope, 'str')); + this._addSymbolToPermanentScope('__name__', ScopeUtils.getBuiltInObject(this._currentScope, 'str')); if (this._fileInfo.executionEnvironment.pythonVersion >= PythonVersion.V33) { - this._addSymbolToPermanentScope('__qualname__', - ScopeUtils.getBuiltInObject(this._currentScope, 'str'), DefaultTypeSourceId); + this._addSymbolToPermanentScope('__qualname__', ScopeUtils.getBuiltInObject(this._currentScope, 'str')); } - this._addSymbolToPermanentScope('__module__', - ScopeUtils.getBuiltInObject(this._currentScope, 'str'), DefaultTypeSourceId); - this._addSymbolToPermanentScope('__defaults__', - AnyType.create(), DefaultTypeSourceId); - this._addSymbolToPermanentScope('__code__', - AnyType.create(), DefaultTypeSourceId); - this._addSymbolToPermanentScope('__globals__', - AnyType.create(), DefaultTypeSourceId); - this._addSymbolToPermanentScope('__dict__', - AnyType.create(), DefaultTypeSourceId); - this._addSymbolToPermanentScope('__closure__', - AnyType.create(), DefaultTypeSourceId); - this._addSymbolToPermanentScope('__annotations__', - AnyType.create(), DefaultTypeSourceId); - this._addSymbolToPermanentScope('__kwdefaults__', - AnyType.create(), DefaultTypeSourceId); + this._addSymbolToPermanentScope('__module__', ScopeUtils.getBuiltInObject(this._currentScope, 'str')); + this._addSymbolToPermanentScope('__defaults__', AnyType.create()); + this._addSymbolToPermanentScope('__code__', AnyType.create()); + this._addSymbolToPermanentScope('__globals__', AnyType.create()); + this._addSymbolToPermanentScope('__dict__', AnyType.create()); + this._addSymbolToPermanentScope('__closure__', AnyType.create()); + this._addSymbolToPermanentScope('__annotations__', AnyType.create()); + this._addSymbolToPermanentScope('__kwdefaults__', AnyType.create()); } } diff --git a/server/src/analyzer/symbol.ts b/server/src/analyzer/symbol.ts index ccd3afedf..3991c9507 100644 --- a/server/src/analyzer/symbol.ts +++ b/server/src/analyzer/symbol.ts @@ -43,45 +43,73 @@ export interface Declaration { export class Symbol { // Inferred type of the symbol. - inferredType: InferredType = new InferredType(); + private _inferredType: InferredType = new InferredType(); // Information about the node that declared the value - // i.e. where the editor will take the user if "show definition" // is selected. Multiple declarations can exist for variables, // properties, and functions (in the case of @overload). - declarations?: Declaration[]; + private _declarations?: Declaration[]; - static create(type: Type, typeSourceId: TypeSourceId) { - const newSymbol = new Symbol(); - newSymbol.setTypeForSource(type, typeSourceId); + // Indicates that the symbol is initially unbound and can + // later be unbound through a delete operation. + private _isInitiallyUnbound: boolean; + + constructor(isInitiallyUnbound: boolean) { + this._isInitiallyUnbound = isInitiallyUnbound; + } + + static createWithType(type: Type, typeSourceId: TypeSourceId) { + const newSymbol = new Symbol(true); + newSymbol.setInferredTypeForSource(type, typeSourceId); return newSymbol; } + isInitiallyUnbound() { + return this._isInitiallyUnbound; + } + // Returns true if inferred type changed. - setTypeForSource(type: Type, typeSourceId: TypeSourceId): boolean { - return this.inferredType.addSource(type, typeSourceId); + setInferredTypeForSource(type: Type, typeSourceId: TypeSourceId): boolean { + return this._inferredType.addSource(type, typeSourceId); + } + + getInferredType() { + return this._inferredType.getType(); } addDeclaration(declaration: Declaration) { - if (this.declarations) { + if (this._declarations) { // See if this node was already identified as a declaration. If so, // replace it. Otherwise, add it as a new declaration to the end of // the list. - let declIndex = this.declarations.findIndex(decl => decl.node === declaration.node); + let declIndex = this._declarations.findIndex(decl => decl.node === declaration.node); if (declIndex >= 0) { // This declaration has already been added. Update the declared // type if it's available. The other fields in the declaration // should be the same from one analysis pass to the next. if (declaration.declaredType) { - this.declarations[declIndex].declaredType = declaration.declaredType; + this._declarations[declIndex].declaredType = declaration.declaredType; } } else { - this.declarations.push(declaration); + this._declarations.push(declaration); } } else { - this.declarations = [declaration]; + this._declarations = [declaration]; } } + + getDeclarationCount() { + return this._declarations ? this._declarations.length : 0; + } + + hasDeclarations() { + return this.getDeclarationCount() > 0; + } + + getDeclarations() { + return this._declarations ? this._declarations : []; + } } // Maps names to symbol information. diff --git a/server/src/analyzer/typeAnalyzer.ts b/server/src/analyzer/typeAnalyzer.ts index 34ef42455..d940f4d3f 100644 --- a/server/src/analyzer/typeAnalyzer.ts +++ b/server/src/analyzer/typeAnalyzer.ts @@ -335,7 +335,7 @@ export class TypeAnalyzer extends ParseTreeWalker { } } else { // There is no annotation, and we can't infer the type. - if (param.name && param.category === ParameterCategory.Simple) { + if (param.name) { this._addDiagnostic(this._fileInfo.configOptions.reportUnknownParameterType, `Type of '${ param.name.nameToken.value }' is unknown`, param.name); @@ -519,10 +519,10 @@ export class TypeAnalyzer extends ParseTreeWalker { // Cache the type for the hover provider. this._getTypeOfExpression(param.name); - // Set the declaration on the node for the definition language service. + // Set the declaration on the node for the definition provider. const symbol = this._currentScope.lookUpSymbol(param.name.nameToken.value); - if (symbol && symbol.declarations) { - AnalyzerNodeInfo.setDeclaration(param.name, symbol.declarations[0]); + if (symbol && symbol.hasDeclarations()) { + AnalyzerNodeInfo.setDeclaration(param.name, symbol.getDeclarations()[0]); } let declaration: Declaration | undefined; @@ -907,120 +907,8 @@ export class TypeAnalyzer extends ParseTreeWalker { visitAssignment(node: AssignmentNode): boolean { // Special-case the typing.pyi file, which contains some special // types that the type analyzer needs to interpret differently. - if (this._fileInfo.isTypingStubFile) { - if (node.leftExpression instanceof NameNode) { - const assignedName = node.leftExpression.nameToken.value; - let specialType: Type | undefined; - - if (assignedName === 'Any') { - specialType = AnyType.create(); - } else { - const specialTypes = ['overload', 'TypeVar', '_promote', 'no_type_check', - 'NoReturn', 'Union', 'Optional', 'List', 'Dict', 'DefaultDict', - 'Set', 'FrozenSet', 'Deque', 'ChainMap']; - if (specialTypes.find(t => t === assignedName)) { - const aliasMap: { [name: string]: AliasMapEntry } = { - 'List': { alias: 'list', module: 'builtins' }, - 'Dict': { alias: 'dict', module: 'builtins' }, - 'DefaultDict': { alias: 'defaultdict', module: 'collections' }, - 'Set': { alias: 'set', module: 'builtins' }, - 'FrozenSet': { alias: 'frozenset', module: 'builtins' }, - 'Deque': { alias: 'deque', module: 'collections' }, - 'ChainMap': { alias: 'ChainMap', module: 'collections' } - }; - - // Synthesize a class. - let specialClassType = new ClassType(assignedName, - ClassTypeFlags.BuiltInClass | ClassTypeFlags.SpecialBuiltIn, - DefaultTypeSourceId); - - // See if we need to locate an alias class to bind it to. - const aliasMapEntry = aliasMap[assignedName]; - if (aliasMapEntry) { - let aliasClass: Type | undefined; - const aliasName = aliasMapEntry.alias; - - if (aliasMapEntry.module === 'builtins') { - aliasClass = ScopeUtils.getBuiltInType(this._currentScope, aliasName); - } else if (aliasMapEntry.module === 'collections') { - // The typing.pyi file imports collections. - let collectionsScope = this._findCollectionsImportScope(); - if (collectionsScope) { - const symbolInfo = collectionsScope.lookUpSymbol(aliasName); - if (symbolInfo) { - aliasClass = TypeUtils.getEffectiveTypeOfSymbol(symbolInfo); - } - } - } - - if (aliasClass instanceof ClassType) { - specialClassType.addBaseClass(aliasClass, false); - specialClassType.setAliasClass(aliasClass); - } - } - - specialType = specialClassType; - } - } - - if (specialType) { - let declaration: Declaration = { - category: SymbolCategory.Class, - node: node.leftExpression, - path: this._fileInfo.filePath, - range: convertOffsetsToRange(node.leftExpression.start, - node.leftExpression.end, this._fileInfo.lines) - }; - this._assignTypeToNameNode(node.leftExpression, specialType, declaration); - this._updateExpressionTypeForNode(node.leftExpression, specialType); - return false; - } - } else if (node.leftExpression instanceof TypeAnnotationExpressionNode && - node.leftExpression.valueExpression instanceof NameNode) { - - const nameNode = node.leftExpression.valueExpression; - const assignedName = nameNode.nameToken.value; - let specialType: Type | undefined; - - const specialTypes = ['Tuple', 'Generic', 'Protocol', 'Callable', - 'Type', 'ClassVar', 'Final', 'Literal']; - if (specialTypes.find(t => t === assignedName)) { - // Synthesize a class. - let specialClassType = new ClassType(assignedName, - ClassTypeFlags.BuiltInClass | ClassTypeFlags.SpecialBuiltIn, - AnalyzerNodeInfo.getTypeSourceId(node)); - - let aliasClass = ScopeUtils.getBuiltInType(this._currentScope, - assignedName.toLowerCase()); - if (aliasClass instanceof ClassType) { - specialClassType.setAliasClass(aliasClass); - - let specializedBaseClass = TypeUtils.specializeType(aliasClass, undefined); - specialClassType.addBaseClass(specializedBaseClass, false); - } else { - // The other classes derive from 'object'. - let objBaseClass = ScopeUtils.getBuiltInType(this._currentScope, 'object'); - if (objBaseClass instanceof ClassType) { - specialClassType.addBaseClass(objBaseClass, false); - } - } - - specialType = specialClassType; - } - - if (specialType) { - let declaration: Declaration = { - category: SymbolCategory.Class, - node: nameNode, - path: this._fileInfo.filePath, - range: convertOffsetsToRange(nameNode.start, - nameNode.end, this._fileInfo.lines) - }; - this._assignTypeToNameNode(nameNode, specialType, declaration); - this._updateExpressionTypeForNode(nameNode, specialType); - return false; - } - } + if (this._handleTypingStubAssignment(node)) { + return false; } let valueType = this._getTypeOfExpression(node.rightExpression); @@ -1134,7 +1022,7 @@ export class TypeAnalyzer extends ParseTreeWalker { // If there's no declaration assigned to this name node, assign one // for the hover provider. if (!AnalyzerNodeInfo.getDeclaration(node)) { - if (symbolInScope && symbolInScope.symbol.declarations) { + if (symbolInScope && symbolInScope.symbol.hasDeclarations()) { AnalyzerNodeInfo.setDeclaration(node, TypeUtils.getPrimaryDeclarationOfSymbol(symbolInScope.symbol)!); } @@ -1189,8 +1077,8 @@ export class TypeAnalyzer extends ParseTreeWalker { if (expr instanceof NameNode) { let symbolWithScope = this._currentScope.lookUpSymbolRecursive(expr.nameToken.value); if (symbolWithScope) { - if (symbolWithScope.symbol.declarations) { - const category = symbolWithScope.symbol.declarations[0].category; + if (symbolWithScope.symbol.hasDeclarations()) { + const category = symbolWithScope.symbol.getDeclarations()[0].category; if (category === SymbolCategory.Function || category === SymbolCategory.Method) { this._addError('Del should not be applied to function', expr); } else if (category === SymbolCategory.Class) { @@ -1247,8 +1135,8 @@ export class TypeAnalyzer extends ParseTreeWalker { range: { start: { line: 0, column: 0 }, end: { line: 0, column: 0 }} }; - let newSymbol = Symbol.create(implicitModuleType, DefaultTypeSourceId); - newSymbol.declarations = [declaration]; + let newSymbol = Symbol.createWithType(implicitModuleType, DefaultTypeSourceId); + newSymbol.addDeclaration(declaration); moduleFields.set(implicitImport.name, newSymbol); } } @@ -1305,9 +1193,9 @@ export class TypeAnalyzer extends ParseTreeWalker { const moduleFields = moduleType.getFields(); moduleFields.forEach((boundValue, fieldName) => { this._addSymbolToPermanentScope(fieldName); - this._addTypeSourceToName(fieldName, boundValue.inferredType.getType(), + this._addTypeSourceToName(fieldName, TypeUtils.getEffectiveTypeOfSymbol(boundValue), AnalyzerNodeInfo.getTypeSourceId(node), - boundValue.declarations ? boundValue.declarations[0] : undefined); + boundValue.hasDeclarations() ? boundValue.getDeclarations()[0] : undefined); }); // Import the fields in the current permanent scope. @@ -1325,7 +1213,7 @@ export class TypeAnalyzer extends ParseTreeWalker { const name = importAs.name.nameToken.value; let symbolType: Type | undefined; const aliasNode = importAs.alias || importAs.name; - let declaration: Declaration | undefined; + let declarations: Declaration[] = []; // Is the name referring to an implicit import? let implicitImport = importInfo!.implicitImports.find(impImport => impImport.name === name); @@ -1336,8 +1224,11 @@ export class TypeAnalyzer extends ParseTreeWalker { this._fileInfo.importMap[implicitImport.path].parseTree) { symbolType = moduleType; - declaration = AnalyzerNodeInfo.getDeclaration( + const moduleDecl = AnalyzerNodeInfo.getDeclaration( this._fileInfo.importMap[implicitImport.path].parseTree); + if (moduleDecl) { + declarations = [moduleDecl]; + } } } else { let moduleType = this._getModuleTypeForImportPath(importInfo, resolvedPath); @@ -1346,8 +1237,8 @@ export class TypeAnalyzer extends ParseTreeWalker { const symbol = moduleFields.get(name); if (symbol) { symbolType = TypeUtils.getEffectiveTypeOfSymbol(symbol); - if (symbol.declarations) { - declaration = symbol.declarations[0]; + if (symbol.hasDeclarations()) { + declarations = symbol.getDeclarations(); } } else { this._addError( @@ -1367,14 +1258,15 @@ export class TypeAnalyzer extends ParseTreeWalker { this._updateExpressionTypeForNode(importAs.alias, symbolType); } - if (declaration) { - AnalyzerNodeInfo.setDeclaration(importAs.name, declaration); + if (declarations.length > 0) { + AnalyzerNodeInfo.setDeclaration(importAs.name, declarations[0]); if (importAs.alias) { - AnalyzerNodeInfo.setDeclaration(importAs.name, declaration); + AnalyzerNodeInfo.setDeclaration(importAs.name, declarations[0]); } } - this._assignTypeToNameNode(aliasNode, symbolType, declaration); + this._assignTypeToNameNode(aliasNode, symbolType, + declarations.length > 0 ? declarations[0] : undefined); }); } } else { @@ -1452,6 +1344,141 @@ export class TypeAnalyzer extends ParseTreeWalker { return false; } + // Handles some special-case assignment statements that are found + // within the typings.pyi file. + private _handleTypingStubAssignment(node: AssignmentNode): boolean { + if (!this._fileInfo.isTypingStubFile) { + return false; + } + + if (node.leftExpression instanceof NameNode) { + const assignedName = node.leftExpression.nameToken.value; + let specialType: Type | undefined; + + if (assignedName === 'Any') { + specialType = AnyType.create(); + } else { + const specialTypes = ['overload', 'TypeVar', '_promote', 'no_type_check', + 'NoReturn', 'Union', 'Optional', 'List', 'Dict', 'DefaultDict', + 'Set', 'FrozenSet', 'Deque', 'ChainMap']; + if (specialTypes.find(t => t === assignedName)) { + const aliasMap: { [name: string]: AliasMapEntry } = { + 'List': { alias: 'list', module: 'builtins' }, + 'Dict': { alias: 'dict', module: 'builtins' }, + 'DefaultDict': { alias: 'defaultdict', module: 'collections' }, + 'Set': { alias: 'set', module: 'builtins' }, + 'FrozenSet': { alias: 'frozenset', module: 'builtins' }, + 'Deque': { alias: 'deque', module: 'collections' }, + 'ChainMap': { alias: 'ChainMap', module: 'collections' } + }; + + // Synthesize a class. + const specialClassType = new ClassType(assignedName, + ClassTypeFlags.BuiltInClass | ClassTypeFlags.SpecialBuiltIn, + DefaultTypeSourceId); + + // See if we need to locate an alias class to bind it to. + const aliasMapEntry = aliasMap[assignedName]; + if (aliasMapEntry) { + let aliasClass: Type | undefined; + const aliasName = aliasMapEntry.alias; + + if (aliasMapEntry.module === 'builtins') { + aliasClass = ScopeUtils.getBuiltInType(this._currentScope, aliasName); + } else if (aliasMapEntry.module === 'collections') { + // The typing.pyi file imports collections. + let collectionsScope = this._findCollectionsImportScope(); + if (collectionsScope) { + const symbolInfo = collectionsScope.lookUpSymbol(aliasName); + if (symbolInfo) { + aliasClass = TypeUtils.getEffectiveTypeOfSymbol(symbolInfo); + } + } + } + + if (aliasClass instanceof ClassType) { + specialClassType.addBaseClass(aliasClass, false); + specialClassType.setAliasClass(aliasClass); + specialType = specialClassType; + } else { + // The alias class has not yet been created. Use an unknown + // type and hope that in the next analysis pass we'll get + // the real type. + specialType = UnknownType.create(); + } + } else { + specialType = specialClassType; + } + } + } + + if (specialType) { + let declaration: Declaration = { + category: SymbolCategory.Class, + node: node.leftExpression, + path: this._fileInfo.filePath, + range: convertOffsetsToRange(node.leftExpression.start, + node.leftExpression.end, this._fileInfo.lines) + }; + this._assignTypeToNameNode(node.leftExpression, specialType, declaration); + this._updateExpressionTypeForNode(node.leftExpression, specialType); + return true; + } + } else if (node.leftExpression instanceof TypeAnnotationExpressionNode && + node.leftExpression.valueExpression instanceof NameNode) { + + const nameNode = node.leftExpression.valueExpression; + const assignedName = nameNode.nameToken.value; + let specialType: Type | undefined; + + const specialTypes = ['Tuple', 'Generic', 'Protocol', 'Callable', + 'Type', 'ClassVar', 'Final', 'Literal']; + if (specialTypes.find(t => t === assignedName)) { + // Synthesize a class. + const specialClassType = new ClassType(assignedName, + ClassTypeFlags.BuiltInClass | ClassTypeFlags.SpecialBuiltIn, + AnalyzerNodeInfo.getTypeSourceId(node)); + + const aliasClass = ScopeUtils.getBuiltInType(this._currentScope, + assignedName.toLowerCase()); + if (aliasClass instanceof ClassType) { + specialClassType.setAliasClass(aliasClass); + + const specializedBaseClass = TypeUtils.specializeType(aliasClass, undefined); + specialClassType.addBaseClass(specializedBaseClass, false); + specialType = specialClassType; + } else { + // The other classes derive from 'object'. + const objBaseClass = ScopeUtils.getBuiltInType(this._currentScope, 'object'); + if (objBaseClass instanceof ClassType) { + specialClassType.addBaseClass(objBaseClass, false); + specialType = specialClassType; + } else { + // The base class has not yet been created. Use an unknown + // type and hope that in the next analysis pass we'll get + // the real type. + specialType = UnknownType.create(); + } + } + } + + if (specialType) { + let declaration: Declaration = { + category: SymbolCategory.Class, + node: nameNode, + path: this._fileInfo.filePath, + range: convertOffsetsToRange(nameNode.start, + nameNode.end, this._fileInfo.lines) + }; + this._assignTypeToNameNode(nameNode, specialType, declaration); + this._updateExpressionTypeForNode(nameNode, specialType); + return true; + } + } + + return false; + } + // Transforms the parameter type based on its category. If it's a simple parameter, // no transform is applied. If it's a var-arg or keword-arg parameter, the type // is wrapped in a List or Dict. @@ -1562,8 +1589,9 @@ export class TypeAnalyzer extends ParseTreeWalker { private _addDeclarationToSymbol(symbol: Symbol, declaration: Declaration, errorNode: ExpressionNode) { // Are we adding a new declaration with a declared type? - if (symbol.declarations && declaration.declaredType) { - const declWithDefinedType = symbol.declarations.find(decl => !!decl.declaredType); + const prevDeclarations = symbol.getDeclarations(); + if (prevDeclarations.length > 0 && declaration.declaredType) { + const declWithDefinedType = prevDeclarations.find(decl => !!decl.declaredType); if (declWithDefinedType && declaration.node !== declWithDefinedType.node) { // If we're adding a declaration, make sure it's the same type as an existing declaration. @@ -2175,7 +2203,7 @@ export class TypeAnalyzer extends ParseTreeWalker { // to generate two sources because the types may different, and the analysis // won't converge if we use the same source ID for both. const sourceId = AnalyzerNodeInfo.getTypeSourceId(typeAnnotationNode || node.memberName); - if (symbol.setTypeForSource(typeOfExpr, sourceId)) { + if (symbol.setInferredTypeForSource(typeOfExpr, sourceId)) { this._setAnalysisChanged(); } @@ -2193,9 +2221,9 @@ export class TypeAnalyzer extends ParseTreeWalker { // name, but there's also now an instance variable introduced. Combine the // type of the class variable with that of the new instance variable. if (memberInfo.symbol && !memberInfo.isInstanceMember && isInstanceMember) { - if (memberInfo.symbol.declarations) { - inheritedDeclaration = memberInfo.symbol.declarations.find(decl => !!decl.declaredType); - // declaredType = TypeUtils.getDeclaredTypeOfSymbol(memberInfo.symbol); + const prevDeclarations = memberInfo.symbol.getDeclarations(); + if (prevDeclarations.length > 0) { + inheritedDeclaration = prevDeclarations.find(decl => !!decl.declaredType); } typeOfExpr = TypeUtils.combineTypes([typeOfExpr, memberInfo.symbolType]); @@ -2208,7 +2236,7 @@ export class TypeAnalyzer extends ParseTreeWalker { } if (addNewMemberToLocalClass) { - let newSymbol = Symbol.create(typeOfExpr, AnalyzerNodeInfo.getTypeSourceId(node.memberName)); + let newSymbol = Symbol.createWithType(typeOfExpr, AnalyzerNodeInfo.getTypeSourceId(node.memberName)); // If this is an instance variable that has a corresponding class varible // with a defined type, it should inherit that declaration (and declared type). @@ -2313,7 +2341,8 @@ export class TypeAnalyzer extends ParseTreeWalker { let implicitModuleType = this._getModuleTypeForImportPath( undefined, implicitImport.path); if (implicitModuleType) { - symbolTable.set(implicitImport.name, Symbol.create(implicitModuleType, DefaultTypeSourceId)); + symbolTable.set(implicitImport.name, Symbol.createWithType( + implicitModuleType, DefaultTypeSourceId)); } }); @@ -2504,7 +2533,7 @@ export class TypeAnalyzer extends ParseTreeWalker { private _addNamedTargetToCurrentScope(node: ExpressionNode) { if (node instanceof NameNode) { - this._currentScope.addSymbol(node.nameToken.value); + this._currentScope.addSymbol(node.nameToken.value, true); } else if (node instanceof TypeAnnotationExpressionNode) { this._addNamedTargetToCurrentScope(node.valueExpression); } else if (node instanceof TupleExpressionNode) { @@ -2521,7 +2550,7 @@ export class TypeAnalyzer extends ParseTreeWalker { private _bindMultiPartModuleNameToType(nameParts: NameNode[], type: ModuleType, declaration?: Declaration): void { let targetSymbolTable = this._currentScope.getSymbolTable(); - let symbol = Symbol.create(type, DefaultTypeSourceId); + let symbol = Symbol.createWithType(type, DefaultTypeSourceId); if (declaration) { symbol.addDeclaration(declaration); } @@ -2557,7 +2586,7 @@ export class TypeAnalyzer extends ParseTreeWalker { // to the next part of the name. let newPartialModule = new ModuleType(new SymbolTable()); newPartialModule.setIsPartialModule(); - targetSymbolTable.set(name, Symbol.create(newPartialModule, DefaultTypeSourceId)); + targetSymbolTable.set(name, Symbol.createWithType(newPartialModule, DefaultTypeSourceId)); targetSymbolTable = newPartialModule.getFields(); } } @@ -2573,13 +2602,9 @@ export class TypeAnalyzer extends ParseTreeWalker { const symbolWithScope = this._currentScope.lookUpSymbolRecursive(nameValue); if (symbolWithScope) { - if (symbolWithScope.symbol.declarations) { - const declWithDefinedType = symbolWithScope.symbol.declarations.find( - decl => !!decl.declaredType); - - if (declWithDefinedType) { - declaredType = declWithDefinedType.declaredType!; - } + const primaryDecl = TypeUtils.getPrimaryDeclarationOfSymbol(symbolWithScope.symbol); + if (primaryDecl) { + declaredType = primaryDecl.declaredType!; } } else { // We should never get here. @@ -2610,7 +2635,7 @@ export class TypeAnalyzer extends ParseTreeWalker { assert(permanentScope.getType() !== ScopeType.Temporary); if (!permanentScope.lookUpSymbol(name)) { - permanentScope.addSymbol(name); + permanentScope.addSymbol(name, false); } } @@ -2626,7 +2651,7 @@ export class TypeAnalyzer extends ParseTreeWalker { const symbolWithScope = this._currentScope.lookUpSymbolRecursive(name); if (symbolWithScope) { - if (symbolWithScope.symbol.inferredType.addSource(type, typeSourceId)) { + if (symbolWithScope.symbol.setInferredTypeForSource(type, typeSourceId)) { if (symbolWithScope.scope.getType() !== ScopeType.Temporary) { this._setAnalysisChanged(); } @@ -2699,21 +2724,21 @@ export class TypeAnalyzer extends ParseTreeWalker { let classMemberInfo = TypeUtils.lookUpClassMember( baseType.getClassType(), memberNameValue); if (classMemberInfo) { - if (classMemberInfo.symbol && classMemberInfo.symbol.declarations) { + if (classMemberInfo.symbol && classMemberInfo.symbol.hasDeclarations()) { AnalyzerNodeInfo.setDeclaration(memberName, TypeUtils.getPrimaryDeclarationOfSymbol(classMemberInfo.symbol)!); } } } else if (baseType instanceof ModuleType) { let moduleMemberInfo = baseType.getFields().get(memberNameValue); - if (moduleMemberInfo && moduleMemberInfo.declarations) { + if (moduleMemberInfo && moduleMemberInfo.hasDeclarations()) { AnalyzerNodeInfo.setDeclaration(memberName, TypeUtils.getPrimaryDeclarationOfSymbol(moduleMemberInfo)!); } } else if (baseType instanceof ClassType) { let classMemberInfo = TypeUtils.lookUpClassMember(baseType, memberNameValue, ClassMemberLookupFlags.SkipInstanceVariables); - if (classMemberInfo && classMemberInfo.symbol && classMemberInfo.symbol.declarations) { + if (classMemberInfo && classMemberInfo.symbol && classMemberInfo.symbol.hasDeclarations()) { AnalyzerNodeInfo.setDeclaration(memberName, TypeUtils.getPrimaryDeclarationOfSymbol(classMemberInfo.symbol)!); } diff --git a/server/src/analyzer/typeUtils.ts b/server/src/analyzer/typeUtils.ts index 2073f0002..375d45f23 100644 --- a/server/src/analyzer/typeUtils.ts +++ b/server/src/analyzer/typeUtils.ts @@ -687,7 +687,7 @@ export class TypeUtils { // The class derives from an unknown type, so all bets are off // when trying to find a member. Return an unknown symbol. return { - symbol: Symbol.create(UnknownType.create(), DefaultTypeSourceId), + symbol: Symbol.createWithType(UnknownType.create(), DefaultTypeSourceId), isInstanceMember: false, classType: UnknownType.create(), symbolType: UnknownType.create() @@ -704,12 +704,13 @@ export class TypeUtils { return declaredType; } - return symbol.inferredType.getType(); + return symbol.getInferredType(); } static getDeclaredTypeOfSymbol(symbol: Symbol): Type | undefined { - if (symbol.declarations) { - const declWithDeclaredType = symbol.declarations.find(decl => decl.declaredType !== undefined); + const declarations = symbol.getDeclarations(); + if (declarations.length > 0) { + const declWithDeclaredType = declarations.find(decl => decl.declaredType !== undefined); if (declWithDeclaredType) { return declWithDeclaredType.declaredType; } @@ -721,14 +722,14 @@ export class TypeUtils { // Returns the first declaration with a declared type. If no such // declaration exists, returns the first declaration. static getPrimaryDeclarationOfSymbol(symbol: Symbol): Declaration | undefined { - if (symbol.declarations) { - const declWithDeclaredType = symbol.declarations.find( - decl => decl.declaredType !== undefined); + const declarations = symbol.getDeclarations(); + if (declarations.length > 0) { + const declWithDeclaredType = declarations.find(decl => decl.declaredType !== undefined); if (declWithDeclaredType) { return declWithDeclaredType; } - return symbol.declarations[0]; + return declarations[0]; } return undefined; @@ -1461,33 +1462,33 @@ export class TypeUtils { // Validate that the type arguments match. const srcTypeArgs = curSrcType.getTypeArguments(); if (srcTypeArgs) { - assert(srcType.isSpecialBuiltIn() || srcTypeArgs.length === ancestorTypeArgs.length); + if (srcType.isSpecialBuiltIn() || srcTypeArgs.length === ancestorTypeArgs.length) { + for (let srcArgIndex = 0; srcArgIndex < srcTypeArgs.length; srcArgIndex++) { + const srcTypeArg = srcTypeArgs[srcArgIndex]; - for (let srcArgIndex = 0; srcArgIndex < srcTypeArgs.length; srcArgIndex++) { - const srcTypeArg = srcTypeArgs[srcArgIndex]; + // In most cases, the ancestor type param count should match, but + // there are a few special cases where this isn't true (e.g. assigning + // a Tuple[X, Y, Z] to a tuple[W]). + const ancestorArgIndex = srcArgIndex >= ancestorTypeParams.length ? + ancestorTypeParams.length - 1 : srcArgIndex; + const typeParam = ancestorTypeParams[ancestorArgIndex]; + const ancestorTypeArg = ancestorTypeArgs[ancestorArgIndex]; - // In most cases, the ancestor type param count should match, but - // there are a few special cases where this isn't true (e.g. assigning - // a Tuple[X, Y, Z] to a tuple[W]). - const ancestorArgIndex = srcArgIndex >= ancestorTypeParams.length ? - ancestorTypeParams.length - 1 : srcArgIndex; - const typeParam = ancestorTypeParams[ancestorArgIndex]; - const ancestorTypeArg = ancestorTypeArgs[ancestorArgIndex]; - - if (typeParam.isCovariant()) { - if (!this.canAssignType(ancestorTypeArg, srcTypeArg, - diag.createAddendum(), undefined, true, recursionCount + 1)) { - return false; - } - } else if (typeParam.isContravariant()) { - if (!this.canAssignType(srcTypeArg, ancestorTypeArg, - diag.createAddendum(), undefined, true, recursionCount + 1)) { - return false; - } - } else { - if (!this.canAssignType(ancestorTypeArg, srcTypeArg, - diag.createAddendum(), undefined, false, recursionCount + 1)) { - return false; + if (typeParam.isCovariant()) { + if (!this.canAssignType(ancestorTypeArg, srcTypeArg, + diag.createAddendum(), undefined, true, recursionCount + 1)) { + return false; + } + } else if (typeParam.isContravariant()) { + if (!this.canAssignType(srcTypeArg, ancestorTypeArg, + diag.createAddendum(), undefined, true, recursionCount + 1)) { + return false; + } + } else { + if (!this.canAssignType(ancestorTypeArg, srcTypeArg, + diag.createAddendum(), undefined, false, recursionCount + 1)) { + return false; + } } } } diff --git a/server/src/analyzer/types.ts b/server/src/analyzer/types.ts index 02a7e9167..71844da81 100644 --- a/server/src/analyzer/types.ts +++ b/server/src/analyzer/types.ts @@ -63,7 +63,7 @@ export enum TypeCategory { export type LiteralValue = number | boolean | string; -const MaxRecursionCount = 20; +const MaxRecursionCount = 16; export type InheritanceChain = (ClassType | UnknownType)[]; @@ -685,8 +685,8 @@ export class FunctionType extends Type { this._functionDetails = { flags, parameters: [], - inferredReturnType: new InferredType(UnknownType.create()), - inferredYieldType: new InferredType(UnknownType.create()) + inferredReturnType: new InferredType(), + inferredYieldType: new InferredType() }; }