Simplified semanticAnalyzer.

Cleaned up interface to Symbol class and added the notion of "initiallyBound".
Simplified InferredType.
Did some cleanup in typeAnalyzer.
This commit is contained in:
Eric Traut 2019-05-04 16:33:38 -07:00
parent f121957579
commit e8286aa894
9 changed files with 324 additions and 303 deletions

View File

@ -266,9 +266,9 @@ export class CompletionProvider {
// Determine the kind. // Determine the kind.
let itemKind: CompletionItemKind = CompletionItemKind.Variable; let itemKind: CompletionItemKind = CompletionItemKind.Variable;
if (item.declarations) { const declarations = item.getDeclarations();
itemKind = this._convertSymbolCategoryToItemKind( if (declarations.length > 0) {
item.declarations[0].category); itemKind = this._convertSymbolCategoryToItemKind(declarations[0].category);
} }
completionItem.kind = itemKind; completionItem.kind = itemKind;
completionList.items.push(completionItem); completionList.items.push(completionItem);

View File

@ -20,9 +20,9 @@ import { ArgumentCategory, AssignmentNode, AwaitExpressionNode,
BinaryExpressionNode, CallExpressionNode, ClassNode, ConstantNode, BinaryExpressionNode, CallExpressionNode, ClassNode, ConstantNode,
DecoratorNode, DictionaryExpandEntryNode, DictionaryKeyEntryNode, DictionaryNode, DecoratorNode, DictionaryExpandEntryNode, DictionaryKeyEntryNode, DictionaryNode,
EllipsisNode, ErrorExpressionNode, ExpressionNode, IndexExpressionNode, EllipsisNode, ErrorExpressionNode, ExpressionNode, IndexExpressionNode,
IndexItemsNode, LambdaNode, ListComprehensionForNode, ListComprehensionIfNode, ListComprehensionNode, IndexItemsNode, LambdaNode, ListComprehensionForNode, ListComprehensionIfNode,
ListNode, MemberAccessExpressionNode, NameNode, NumberNode, ParameterCategory, ListComprehensionNode, ListNode, MemberAccessExpressionNode, NameNode, NumberNode,
ParseNode, SetNode, SliceExpressionNode, StatementListNode, ParameterCategory, ParseNode, SetNode, SliceExpressionNode, StatementListNode,
StringNode, TernaryExpressionNode, TupleExpressionNode, StringNode, TernaryExpressionNode, TupleExpressionNode,
TypeAnnotationExpressionNode, UnaryExpressionNode, UnpackExpressionNode, TypeAnnotationExpressionNode, UnaryExpressionNode, UnpackExpressionNode,
YieldExpressionNode, YieldFromExpressionNode } from '../parser/parseNodes'; YieldExpressionNode, YieldFromExpressionNode } from '../parser/parseNodes';
@ -402,8 +402,8 @@ export class ExpressionEvaluator {
} }
}); });
classType.getClassFields().set('__init__', Symbol.create(initType, DefaultTypeSourceId)); classType.getClassFields().set('__init__', Symbol.createWithType(initType, DefaultTypeSourceId));
classType.getClassFields().set('__new__', Symbol.create(newType, DefaultTypeSourceId)); classType.getClassFields().set('__new__', Symbol.createWithType(newType, DefaultTypeSourceId));
} }
getTypingType(symbolName: string): Type | undefined { 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 // 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. // analysis path. If this is the first pass, allocate a new ClassType.
let classType = cachedCallType as ClassType; let classType = cachedCallType as ClassType;
if (classType) { if (!(classType instanceof ClassType)) {
assert(classType instanceof ClassType);
} else {
classType = new ClassType(className, ClassTypeFlags.None, classType = new ClassType(className, ClassTypeFlags.None,
AnalyzerNodeInfo.getTypeSourceId(errorNode)); AnalyzerNodeInfo.getTypeSourceId(errorNode));
@ -1723,7 +1721,7 @@ export class ExpressionEvaluator {
} }
const classFields = classType.getClassFields(); const classFields = classType.getClassFields();
classFields.set('__class__', Symbol.create(classType, DefaultTypeSourceId)); classFields.set('__class__', Symbol.createWithType(classType, DefaultTypeSourceId));
const instanceFields = classType.getInstanceFields(); const instanceFields = classType.getInstanceFields();
let builtInTupleType = ScopeUtils.getBuiltInType(this._scope, 'Tuple'); let builtInTupleType = ScopeUtils.getBuiltInType(this._scope, 'Tuple');
@ -1766,7 +1764,7 @@ export class ExpressionEvaluator {
}; };
constructorType.addParameter(paramInfo); constructorType.addParameter(paramInfo);
instanceFields.set(entryName, Symbol.create(entryType, DefaultTypeSourceId)); instanceFields.set(entryName, Symbol.createWithType(entryType, DefaultTypeSourceId));
} }
}); });
} else if (entriesArg.valueExpression instanceof ListNode) { } else if (entriesArg.valueExpression instanceof ListNode) {
@ -1830,7 +1828,7 @@ export class ExpressionEvaluator {
constructorType.addParameter(paramInfo); constructorType.addParameter(paramInfo);
instanceFields.set(entryName, Symbol.create(entryType, DefaultTypeSourceId)); instanceFields.set(entryName, Symbol.createWithType(entryType, DefaultTypeSourceId));
}); });
} else { } else {
// A dynamic expression was used, so we can't evaluate // A dynamic expression was used, so we can't evaluate
@ -1852,19 +1850,19 @@ export class ExpressionEvaluator {
initType.addParameter(selfParameter); initType.addParameter(selfParameter);
TypeUtils.addDefaultFunctionParameters(initType); TypeUtils.addDefaultFunctionParameters(initType);
classFields.set('__new__', Symbol.create(constructorType, DefaultTypeSourceId)); classFields.set('__new__', Symbol.createWithType(constructorType, DefaultTypeSourceId));
classFields.set('__init__', Symbol.create(initType, DefaultTypeSourceId)); classFields.set('__init__', Symbol.createWithType(initType, DefaultTypeSourceId));
let keysItemType = new FunctionType(FunctionTypeFlags.None); let keysItemType = new FunctionType(FunctionTypeFlags.None);
keysItemType.setDeclaredReturnType(ScopeUtils.getBuiltInObject(this._scope, 'list', keysItemType.setDeclaredReturnType(ScopeUtils.getBuiltInObject(this._scope, 'list',
[ScopeUtils.getBuiltInObject(this._scope, 'str')])); [ScopeUtils.getBuiltInObject(this._scope, 'str')]));
classFields.set('keys', Symbol.create(keysItemType, DefaultTypeSourceId)); classFields.set('keys', Symbol.createWithType(keysItemType, DefaultTypeSourceId));
classFields.set('items', Symbol.create(keysItemType, DefaultTypeSourceId)); classFields.set('items', Symbol.createWithType(keysItemType, DefaultTypeSourceId));
let lenType = new FunctionType(FunctionTypeFlags.InstanceMethod); let lenType = new FunctionType(FunctionTypeFlags.InstanceMethod);
lenType.setDeclaredReturnType(ScopeUtils.getBuiltInObject(this._scope, 'int')); lenType.setDeclaredReturnType(ScopeUtils.getBuiltInObject(this._scope, 'int'));
lenType.addParameter(selfParameter); lenType.addParameter(selfParameter);
classFields.set('__len__', Symbol.create(lenType, DefaultTypeSourceId)); classFields.set('__len__', Symbol.createWithType(lenType, DefaultTypeSourceId));
if (addGenericGetAttribute) { if (addGenericGetAttribute) {
let getAttribType = new FunctionType(FunctionTypeFlags.InstanceMethod); let getAttribType = new FunctionType(FunctionTypeFlags.InstanceMethod);
@ -1875,7 +1873,7 @@ export class ExpressionEvaluator {
name: 'name', name: 'name',
type: ScopeUtils.getBuiltInObject(this._scope, 'str') 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; const targetExpr = comprehension.targetExpression;
if (targetExpr instanceof NameNode) { if (targetExpr instanceof NameNode) {
const symbol = this._scope.addSymbol(targetExpr.nameToken.value); const symbol = this._scope.addSymbol(targetExpr.nameToken.value, false);
symbol.setTypeForSource(itemType, AnalyzerNodeInfo.getTypeSourceId(targetExpr)); symbol.setInferredTypeForSource(itemType, AnalyzerNodeInfo.getTypeSourceId(targetExpr));
} else { } else {
// TODO - need to implement // TODO - need to implement
understoodType = false; understoodType = false;

View File

@ -10,7 +10,7 @@
* analysis proceeds. * analysis proceeds.
*/ */
import { ClassType, ObjectType, Type, UnboundType } from './types'; import { ClassType, ObjectType, Type, UnboundType, UnknownType } from './types';
import { TypeUtils } from './typeUtils'; import { TypeUtils } from './typeUtils';
// A type can be inferred from multiple sources. Each sources // A type can be inferred from multiple sources. Each sources
@ -28,7 +28,6 @@ export const DefaultTypeSourceId: TypeSourceId = 0;
export class InferredType { export class InferredType {
private _sources: InferredTypeSource[] = []; private _sources: InferredTypeSource[] = [];
private _defaultType: Type;
private _combinedType: Type; private _combinedType: Type;
// Some inferred types need to be wrapped in another // Some inferred types need to be wrapped in another
@ -36,9 +35,8 @@ export class InferredType {
// be wrapped in an Iterable[]. // be wrapped in an Iterable[].
private _genericClassWrapper: ClassType | undefined; private _genericClassWrapper: ClassType | undefined;
constructor(defaultType?: Type) { constructor() {
this._defaultType = defaultType || UnboundType.create(); this._combinedType = UnknownType.create();
this._combinedType = this._defaultType;
} }
setGenericClassWrapper(classType: ClassType) { setGenericClassWrapper(classType: ClassType) {
@ -114,7 +112,7 @@ export class InferredType {
} }
if (!newCombinedType) { if (!newCombinedType) {
newCombinedType = this._defaultType; newCombinedType = UnknownType.create();
} }
if (!newCombinedType.isSame(this._combinedType)) { if (!newCombinedType.isSame(this._combinedType)) {

View File

@ -70,8 +70,8 @@ export class Scope {
private _breaksFromLoop = false; private _breaksFromLoop = false;
// Inferred return and yield types for the scope. // Inferred return and yield types for the scope.
private _returnType = new InferredType(UnknownType.create()); private _returnType = new InferredType();
private _yieldType = new InferredType(UnknownType.create()); private _yieldType = new InferredType();
// Active type constraints for this scope -- used for conditional // Active type constraints for this scope -- used for conditional
// scopes where the condition constrains the types of certain // scopes where the condition constrains the types of certain
@ -148,8 +148,8 @@ export class Scope {
} }
// Adds a new untyped symbol to the scope. // Adds a new untyped symbol to the scope.
addSymbol(name: string): Symbol { addSymbol(name: string, isInitiallyUnbound: boolean): Symbol {
let symbol = new Symbol(); let symbol = new Symbol(isInitiallyUnbound);
this._symbolTable.set(name, symbol); this._symbolTable.set(name, symbol);
return symbol; return symbol;
} }

View File

@ -22,9 +22,9 @@ import * as assert from 'assert';
import { DiagnosticLevel } from '../common/configOptions'; import { DiagnosticLevel } from '../common/configOptions';
import { PythonVersion } from '../common/pythonVersion'; import { PythonVersion } from '../common/pythonVersion';
import { TextRange } from '../common/textRange'; import { TextRange } from '../common/textRange';
import { AwaitExpressionNode, ClassNode, DelNode, ErrorExpressionNode, import { AwaitExpressionNode, ClassNode, ErrorExpressionNode,
ExpressionNode, FunctionNode, GlobalNode, IfNode, LambdaNode, ModuleNameNode, 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'; WhileNode, YieldExpressionNode, YieldFromExpressionNode } from '../parser/parseNodes';
import { StringTokenFlags } from '../parser/tokenizerTypes'; import { StringTokenFlags } from '../parser/tokenizerTypes';
import { ScopeUtils } from '../scopeUtils'; import { ScopeUtils } from '../scopeUtils';
@ -32,7 +32,7 @@ import { AnalyzerFileInfo } from './analyzerFileInfo';
import { AnalyzerNodeInfo } from './analyzerNodeInfo'; import { AnalyzerNodeInfo } from './analyzerNodeInfo';
import { ExpressionUtils } from './expressionUtils'; import { ExpressionUtils } from './expressionUtils';
import { ImportType } from './importResult'; import { ImportType } from './importResult';
import { DefaultTypeSourceId, TypeSourceId } from './inferredType'; import { DefaultTypeSourceId } from './inferredType';
import { ParseTreeUtils } from './parseTreeUtils'; import { ParseTreeUtils } from './parseTreeUtils';
import { ParseTreeWalker } from './parseTreeWalker'; import { ParseTreeWalker } from './parseTreeWalker';
import { Scope, ScopeType } from './scope'; import { Scope, ScopeType } from './scope';
@ -186,10 +186,10 @@ export abstract class SemanticAnalyzer extends ParseTreeWalker {
}); });
if (nonMetaclassBaseClassCount === 0) { 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 // Make sure we don't have 'object' derive from itself. Infinite
// recursion will result. // recursion will result.
if (objectType !== classType) { if (!classType.isBuiltIn() || classType.getClassName() !== 'object') {
classType.addBaseClass(objectType, false); classType.addBaseClass(objectType, false);
} }
} }
@ -199,18 +199,16 @@ export abstract class SemanticAnalyzer extends ParseTreeWalker {
let analyzer = new ClassScopeAnalyzer(node, this._currentScope, classType, this._fileInfo); let analyzer = new ClassScopeAnalyzer(node, this._currentScope, classType, this._fileInfo);
this._queueSubScopeAnalyzer(analyzer); this._queueSubScopeAnalyzer(analyzer);
// Don't bind the name of the class until after we've done the // Add the class symbol. We do this in the semantic analyzer to speed
// first pass of its scope analysis. This guarantees that we'll flag // up overall analysis times. Without this, the type analyzer needs
// any references to the as-yet-undeclared class as an error. // to do more passes to resolve classes.
this._addSymbolToPermanentScope(node.name.nameToken.value, classType, this._addSymbolToPermanentScope(node.name.nameToken.value, classType,
AnalyzerNodeInfo.getTypeSourceId(node.name), node.name); AnalyzerNodeInfo.getTypeSourceId(node.name));
return false; return false;
} }
visitFunction(node: FunctionNode): boolean { visitFunction(node: FunctionNode): boolean {
const containingClass = ParseTreeUtils.getEnclosingClass(node, true);
// The "__new__" magic method is not an instance method. // The "__new__" magic method is not an instance method.
// It acts as a static method instead. // It acts as a static method instead.
let functionFlags = FunctionTypeFlags.None; 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, functionType);
AnalyzerNodeInfo.setExpressionType(node.name, 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 // Don't overwrite the implicit bound names that have already
// been added to the scope. // been added to the scope.
if (!this._currentScope.lookUpSymbol(name)) { 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 // 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. // adds a new symbol with the specified name if it doesn't already exist.
protected _addSymbolToPermanentScope(nameValue: string, type: Type, protected _addSymbolToPermanentScope(nameValue: string, type: Type,
typeSourceId: TypeSourceId, warnIfDuplicateNode?: NameNode) { typeSourceId = DefaultTypeSourceId) {
const permanentScope = ScopeUtils.getPermanentScope(this._currentScope); const permanentScope = ScopeUtils.getPermanentScope(this._currentScope);
assert(permanentScope.getType() !== ScopeType.Temporary); assert(permanentScope.getType() !== ScopeType.Temporary);
@ -447,15 +440,13 @@ export abstract class SemanticAnalyzer extends ParseTreeWalker {
let symbol = permanentScope.lookUpSymbol(nameValue); let symbol = permanentScope.lookUpSymbol(nameValue);
if (!symbol) { if (!symbol) {
symbol = this._currentScope.addSymbol(nameValue); // Add the symbol. Assume that symbols with a default type source ID
} else if (warnIfDuplicateNode) { // are "implicit" symbols added to the scope. These are not initially unbound.
if (symbol.inferredType.getSourceCount() > 0) { symbol = this._currentScope.addSymbol(nameValue,
this._fileInfo.diagnosticSink.addWarningWithTextRange( typeSourceId !== DefaultTypeSourceId);
`'${ nameValue }' is already defined`, warnIfDuplicateNode);
}
} }
symbol.inferredType.addSource(type, typeSourceId); symbol.setInferredTypeForSource(type, typeSourceId);
} }
private _validateYieldUsage(node: YieldExpressionNode | YieldFromExpressionNode) { private _validateYieldUsage(node: YieldExpressionNode | YieldFromExpressionNode) {
@ -548,19 +539,14 @@ export class ModuleScopeAnalyzer extends SemanticAnalyzer {
private _bindImplicitNames() { private _bindImplicitNames() {
// List taken from https://docs.python.org/3/reference/import.html#__name__ // List taken from https://docs.python.org/3/reference/import.html#__name__
this._addSymbolToPermanentScope('__name__', this._addSymbolToPermanentScope('__name__',
ScopeUtils.getBuiltInObject(this._currentScope, 'str'), DefaultTypeSourceId); ScopeUtils.getBuiltInObject(this._currentScope, 'str'));
this._addSymbolToPermanentScope('__loader__', this._addSymbolToPermanentScope('__loader__', AnyType.create());
AnyType.create(), DefaultTypeSourceId);
this._addSymbolToPermanentScope('__package__', this._addSymbolToPermanentScope('__package__',
ScopeUtils.getBuiltInObject(this._currentScope, 'str'), DefaultTypeSourceId); ScopeUtils.getBuiltInObject(this._currentScope, 'str'));
this._addSymbolToPermanentScope('__spec__', this._addSymbolToPermanentScope('__spec__', AnyType.create());
AnyType.create(), DefaultTypeSourceId); this._addSymbolToPermanentScope('__path__', ScopeUtils.getBuiltInObject(this._currentScope, 'str'));
this._addSymbolToPermanentScope('__path__', this._addSymbolToPermanentScope('__file__', ScopeUtils.getBuiltInObject(this._currentScope, 'str'));
ScopeUtils.getBuiltInObject(this._currentScope, 'str'), DefaultTypeSourceId); this._addSymbolToPermanentScope('__cached__', ScopeUtils.getBuiltInObject(this._currentScope, 'str'));
this._addSymbolToPermanentScope('__file__',
ScopeUtils.getBuiltInObject(this._currentScope, 'str'), DefaultTypeSourceId);
this._addSymbolToPermanentScope('__cached__',
ScopeUtils.getBuiltInObject(this._currentScope, 'str'), DefaultTypeSourceId);
} }
} }
@ -595,14 +581,10 @@ export class ClassScopeAnalyzer extends SemanticAnalyzer {
private _bindImplicitNames() { private _bindImplicitNames() {
let classType = AnalyzerNodeInfo.getExpressionType(this._scopedNode); let classType = AnalyzerNodeInfo.getExpressionType(this._scopedNode);
assert(classType instanceof ClassType); assert(classType instanceof ClassType);
this._addSymbolToPermanentScope('__class__', this._addSymbolToPermanentScope('__class__', classType!);
classType!, DefaultTypeSourceId); this._addSymbolToPermanentScope('__dict__', AnyType.create());
this._addSymbolToPermanentScope('__dict__', this._addSymbolToPermanentScope('__doc__', ScopeUtils.getBuiltInObject(this._currentScope, 'str'));
AnyType.create(), DefaultTypeSourceId); this._addSymbolToPermanentScope('__name__', ScopeUtils.getBuiltInObject(this._currentScope, 'str'));
this._addSymbolToPermanentScope('__doc__',
ScopeUtils.getBuiltInObject(this._currentScope, 'str'), DefaultTypeSourceId);
this._addSymbolToPermanentScope('__name__',
ScopeUtils.getBuiltInObject(this._currentScope, 'str'), DefaultTypeSourceId);
} }
} }
@ -636,30 +618,19 @@ export class FunctionScopeAnalyzer extends SemanticAnalyzer {
private _bindImplicitNames() { private _bindImplicitNames() {
// List taken from https://docs.python.org/3/reference/datamodel.html // List taken from https://docs.python.org/3/reference/datamodel.html
this._addSymbolToPermanentScope('__doc__', this._addSymbolToPermanentScope('__doc__', ScopeUtils.getBuiltInObject(this._currentScope, 'str'));
ScopeUtils.getBuiltInObject(this._currentScope, 'str'), DefaultTypeSourceId); this._addSymbolToPermanentScope('__name__', ScopeUtils.getBuiltInObject(this._currentScope, 'str'));
this._addSymbolToPermanentScope('__name__',
ScopeUtils.getBuiltInObject(this._currentScope, 'str'), DefaultTypeSourceId);
if (this._fileInfo.executionEnvironment.pythonVersion >= PythonVersion.V33) { if (this._fileInfo.executionEnvironment.pythonVersion >= PythonVersion.V33) {
this._addSymbolToPermanentScope('__qualname__', this._addSymbolToPermanentScope('__qualname__', ScopeUtils.getBuiltInObject(this._currentScope, 'str'));
ScopeUtils.getBuiltInObject(this._currentScope, 'str'), DefaultTypeSourceId);
} }
this._addSymbolToPermanentScope('__module__', this._addSymbolToPermanentScope('__module__', ScopeUtils.getBuiltInObject(this._currentScope, 'str'));
ScopeUtils.getBuiltInObject(this._currentScope, 'str'), DefaultTypeSourceId); this._addSymbolToPermanentScope('__defaults__', AnyType.create());
this._addSymbolToPermanentScope('__defaults__', this._addSymbolToPermanentScope('__code__', AnyType.create());
AnyType.create(), DefaultTypeSourceId); this._addSymbolToPermanentScope('__globals__', AnyType.create());
this._addSymbolToPermanentScope('__code__', this._addSymbolToPermanentScope('__dict__', AnyType.create());
AnyType.create(), DefaultTypeSourceId); this._addSymbolToPermanentScope('__closure__', AnyType.create());
this._addSymbolToPermanentScope('__globals__', this._addSymbolToPermanentScope('__annotations__', AnyType.create());
AnyType.create(), DefaultTypeSourceId); this._addSymbolToPermanentScope('__kwdefaults__', AnyType.create());
this._addSymbolToPermanentScope('__dict__',
AnyType.create(), DefaultTypeSourceId);
this._addSymbolToPermanentScope('__closure__',
AnyType.create(), DefaultTypeSourceId);
this._addSymbolToPermanentScope('__annotations__',
AnyType.create(), DefaultTypeSourceId);
this._addSymbolToPermanentScope('__kwdefaults__',
AnyType.create(), DefaultTypeSourceId);
} }
} }

View File

@ -43,45 +43,73 @@ export interface Declaration {
export class Symbol { export class Symbol {
// Inferred type of the symbol. // Inferred type of the symbol.
inferredType: InferredType = new InferredType(); private _inferredType: InferredType = new InferredType();
// Information about the node that declared the value - // Information about the node that declared the value -
// i.e. where the editor will take the user if "show definition" // i.e. where the editor will take the user if "show definition"
// is selected. Multiple declarations can exist for variables, // is selected. Multiple declarations can exist for variables,
// properties, and functions (in the case of @overload). // properties, and functions (in the case of @overload).
declarations?: Declaration[]; private _declarations?: Declaration[];
static create(type: Type, typeSourceId: TypeSourceId) { // Indicates that the symbol is initially unbound and can
const newSymbol = new Symbol(); // later be unbound through a delete operation.
newSymbol.setTypeForSource(type, typeSourceId); 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; return newSymbol;
} }
isInitiallyUnbound() {
return this._isInitiallyUnbound;
}
// Returns true if inferred type changed. // Returns true if inferred type changed.
setTypeForSource(type: Type, typeSourceId: TypeSourceId): boolean { setInferredTypeForSource(type: Type, typeSourceId: TypeSourceId): boolean {
return this.inferredType.addSource(type, typeSourceId); return this._inferredType.addSource(type, typeSourceId);
}
getInferredType() {
return this._inferredType.getType();
} }
addDeclaration(declaration: Declaration) { addDeclaration(declaration: Declaration) {
if (this.declarations) { if (this._declarations) {
// See if this node was already identified as a declaration. If so, // 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 // replace it. Otherwise, add it as a new declaration to the end of
// the list. // 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) { if (declIndex >= 0) {
// This declaration has already been added. Update the declared // This declaration has already been added. Update the declared
// type if it's available. The other fields in the declaration // type if it's available. The other fields in the declaration
// should be the same from one analysis pass to the next. // should be the same from one analysis pass to the next.
if (declaration.declaredType) { if (declaration.declaredType) {
this.declarations[declIndex].declaredType = declaration.declaredType; this._declarations[declIndex].declaredType = declaration.declaredType;
} }
} else { } else {
this.declarations.push(declaration); this._declarations.push(declaration);
} }
} else { } 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. // Maps names to symbol information.

View File

@ -335,7 +335,7 @@ export class TypeAnalyzer extends ParseTreeWalker {
} }
} else { } else {
// There is no annotation, and we can't infer the type. // 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, this._addDiagnostic(this._fileInfo.configOptions.reportUnknownParameterType,
`Type of '${ param.name.nameToken.value }' is unknown`, `Type of '${ param.name.nameToken.value }' is unknown`,
param.name); param.name);
@ -519,10 +519,10 @@ export class TypeAnalyzer extends ParseTreeWalker {
// Cache the type for the hover provider. // Cache the type for the hover provider.
this._getTypeOfExpression(param.name); 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); const symbol = this._currentScope.lookUpSymbol(param.name.nameToken.value);
if (symbol && symbol.declarations) { if (symbol && symbol.hasDeclarations()) {
AnalyzerNodeInfo.setDeclaration(param.name, symbol.declarations[0]); AnalyzerNodeInfo.setDeclaration(param.name, symbol.getDeclarations()[0]);
} }
let declaration: Declaration | undefined; let declaration: Declaration | undefined;
@ -907,120 +907,8 @@ export class TypeAnalyzer extends ParseTreeWalker {
visitAssignment(node: AssignmentNode): boolean { visitAssignment(node: AssignmentNode): boolean {
// Special-case the typing.pyi file, which contains some special // Special-case the typing.pyi file, which contains some special
// types that the type analyzer needs to interpret differently. // types that the type analyzer needs to interpret differently.
if (this._fileInfo.isTypingStubFile) { if (this._handleTypingStubAssignment(node)) {
if (node.leftExpression instanceof NameNode) { return false;
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;
}
}
} }
let valueType = this._getTypeOfExpression(node.rightExpression); 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 // If there's no declaration assigned to this name node, assign one
// for the hover provider. // for the hover provider.
if (!AnalyzerNodeInfo.getDeclaration(node)) { if (!AnalyzerNodeInfo.getDeclaration(node)) {
if (symbolInScope && symbolInScope.symbol.declarations) { if (symbolInScope && symbolInScope.symbol.hasDeclarations()) {
AnalyzerNodeInfo.setDeclaration(node, AnalyzerNodeInfo.setDeclaration(node,
TypeUtils.getPrimaryDeclarationOfSymbol(symbolInScope.symbol)!); TypeUtils.getPrimaryDeclarationOfSymbol(symbolInScope.symbol)!);
} }
@ -1189,8 +1077,8 @@ export class TypeAnalyzer extends ParseTreeWalker {
if (expr instanceof NameNode) { if (expr instanceof NameNode) {
let symbolWithScope = this._currentScope.lookUpSymbolRecursive(expr.nameToken.value); let symbolWithScope = this._currentScope.lookUpSymbolRecursive(expr.nameToken.value);
if (symbolWithScope) { if (symbolWithScope) {
if (symbolWithScope.symbol.declarations) { if (symbolWithScope.symbol.hasDeclarations()) {
const category = symbolWithScope.symbol.declarations[0].category; const category = symbolWithScope.symbol.getDeclarations()[0].category;
if (category === SymbolCategory.Function || category === SymbolCategory.Method) { if (category === SymbolCategory.Function || category === SymbolCategory.Method) {
this._addError('Del should not be applied to function', expr); this._addError('Del should not be applied to function', expr);
} else if (category === SymbolCategory.Class) { } 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 }} range: { start: { line: 0, column: 0 }, end: { line: 0, column: 0 }}
}; };
let newSymbol = Symbol.create(implicitModuleType, DefaultTypeSourceId); let newSymbol = Symbol.createWithType(implicitModuleType, DefaultTypeSourceId);
newSymbol.declarations = [declaration]; newSymbol.addDeclaration(declaration);
moduleFields.set(implicitImport.name, newSymbol); moduleFields.set(implicitImport.name, newSymbol);
} }
} }
@ -1305,9 +1193,9 @@ export class TypeAnalyzer extends ParseTreeWalker {
const moduleFields = moduleType.getFields(); const moduleFields = moduleType.getFields();
moduleFields.forEach((boundValue, fieldName) => { moduleFields.forEach((boundValue, fieldName) => {
this._addSymbolToPermanentScope(fieldName); this._addSymbolToPermanentScope(fieldName);
this._addTypeSourceToName(fieldName, boundValue.inferredType.getType(), this._addTypeSourceToName(fieldName, TypeUtils.getEffectiveTypeOfSymbol(boundValue),
AnalyzerNodeInfo.getTypeSourceId(node), AnalyzerNodeInfo.getTypeSourceId(node),
boundValue.declarations ? boundValue.declarations[0] : undefined); boundValue.hasDeclarations() ? boundValue.getDeclarations()[0] : undefined);
}); });
// Import the fields in the current permanent scope. // Import the fields in the current permanent scope.
@ -1325,7 +1213,7 @@ export class TypeAnalyzer extends ParseTreeWalker {
const name = importAs.name.nameToken.value; const name = importAs.name.nameToken.value;
let symbolType: Type | undefined; let symbolType: Type | undefined;
const aliasNode = importAs.alias || importAs.name; const aliasNode = importAs.alias || importAs.name;
let declaration: Declaration | undefined; let declarations: Declaration[] = [];
// Is the name referring to an implicit import? // Is the name referring to an implicit import?
let implicitImport = importInfo!.implicitImports.find(impImport => impImport.name === name); let implicitImport = importInfo!.implicitImports.find(impImport => impImport.name === name);
@ -1336,8 +1224,11 @@ export class TypeAnalyzer extends ParseTreeWalker {
this._fileInfo.importMap[implicitImport.path].parseTree) { this._fileInfo.importMap[implicitImport.path].parseTree) {
symbolType = moduleType; symbolType = moduleType;
declaration = AnalyzerNodeInfo.getDeclaration( const moduleDecl = AnalyzerNodeInfo.getDeclaration(
this._fileInfo.importMap[implicitImport.path].parseTree); this._fileInfo.importMap[implicitImport.path].parseTree);
if (moduleDecl) {
declarations = [moduleDecl];
}
} }
} else { } else {
let moduleType = this._getModuleTypeForImportPath(importInfo, resolvedPath); let moduleType = this._getModuleTypeForImportPath(importInfo, resolvedPath);
@ -1346,8 +1237,8 @@ export class TypeAnalyzer extends ParseTreeWalker {
const symbol = moduleFields.get(name); const symbol = moduleFields.get(name);
if (symbol) { if (symbol) {
symbolType = TypeUtils.getEffectiveTypeOfSymbol(symbol); symbolType = TypeUtils.getEffectiveTypeOfSymbol(symbol);
if (symbol.declarations) { if (symbol.hasDeclarations()) {
declaration = symbol.declarations[0]; declarations = symbol.getDeclarations();
} }
} else { } else {
this._addError( this._addError(
@ -1367,14 +1258,15 @@ export class TypeAnalyzer extends ParseTreeWalker {
this._updateExpressionTypeForNode(importAs.alias, symbolType); this._updateExpressionTypeForNode(importAs.alias, symbolType);
} }
if (declaration) { if (declarations.length > 0) {
AnalyzerNodeInfo.setDeclaration(importAs.name, declaration); AnalyzerNodeInfo.setDeclaration(importAs.name, declarations[0]);
if (importAs.alias) { 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 { } else {
@ -1452,6 +1344,141 @@ export class TypeAnalyzer extends ParseTreeWalker {
return false; 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, // 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 // no transform is applied. If it's a var-arg or keword-arg parameter, the type
// is wrapped in a List or Dict. // is wrapped in a List or Dict.
@ -1562,8 +1589,9 @@ export class TypeAnalyzer extends ParseTreeWalker {
private _addDeclarationToSymbol(symbol: Symbol, declaration: Declaration, errorNode: ExpressionNode) { private _addDeclarationToSymbol(symbol: Symbol, declaration: Declaration, errorNode: ExpressionNode) {
// Are we adding a new declaration with a declared type? // Are we adding a new declaration with a declared type?
if (symbol.declarations && declaration.declaredType) { const prevDeclarations = symbol.getDeclarations();
const declWithDefinedType = symbol.declarations.find(decl => !!decl.declaredType); if (prevDeclarations.length > 0 && declaration.declaredType) {
const declWithDefinedType = prevDeclarations.find(decl => !!decl.declaredType);
if (declWithDefinedType && declaration.node !== declWithDefinedType.node) { if (declWithDefinedType && declaration.node !== declWithDefinedType.node) {
// If we're adding a declaration, make sure it's the same type as an existing declaration. // 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 // to generate two sources because the types may different, and the analysis
// won't converge if we use the same source ID for both. // won't converge if we use the same source ID for both.
const sourceId = AnalyzerNodeInfo.getTypeSourceId(typeAnnotationNode || node.memberName); const sourceId = AnalyzerNodeInfo.getTypeSourceId(typeAnnotationNode || node.memberName);
if (symbol.setTypeForSource(typeOfExpr, sourceId)) { if (symbol.setInferredTypeForSource(typeOfExpr, sourceId)) {
this._setAnalysisChanged(); this._setAnalysisChanged();
} }
@ -2193,9 +2221,9 @@ export class TypeAnalyzer extends ParseTreeWalker {
// name, but there's also now an instance variable introduced. Combine the // name, but there's also now an instance variable introduced. Combine the
// type of the class variable with that of the new instance variable. // type of the class variable with that of the new instance variable.
if (memberInfo.symbol && !memberInfo.isInstanceMember && isInstanceMember) { if (memberInfo.symbol && !memberInfo.isInstanceMember && isInstanceMember) {
if (memberInfo.symbol.declarations) { const prevDeclarations = memberInfo.symbol.getDeclarations();
inheritedDeclaration = memberInfo.symbol.declarations.find(decl => !!decl.declaredType); if (prevDeclarations.length > 0) {
// declaredType = TypeUtils.getDeclaredTypeOfSymbol(memberInfo.symbol); inheritedDeclaration = prevDeclarations.find(decl => !!decl.declaredType);
} }
typeOfExpr = TypeUtils.combineTypes([typeOfExpr, memberInfo.symbolType]); typeOfExpr = TypeUtils.combineTypes([typeOfExpr, memberInfo.symbolType]);
@ -2208,7 +2236,7 @@ export class TypeAnalyzer extends ParseTreeWalker {
} }
if (addNewMemberToLocalClass) { 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 // If this is an instance variable that has a corresponding class varible
// with a defined type, it should inherit that declaration (and declared type). // 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( let implicitModuleType = this._getModuleTypeForImportPath(
undefined, implicitImport.path); undefined, implicitImport.path);
if (implicitModuleType) { 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) { private _addNamedTargetToCurrentScope(node: ExpressionNode) {
if (node instanceof NameNode) { if (node instanceof NameNode) {
this._currentScope.addSymbol(node.nameToken.value); this._currentScope.addSymbol(node.nameToken.value, true);
} else if (node instanceof TypeAnnotationExpressionNode) { } else if (node instanceof TypeAnnotationExpressionNode) {
this._addNamedTargetToCurrentScope(node.valueExpression); this._addNamedTargetToCurrentScope(node.valueExpression);
} else if (node instanceof TupleExpressionNode) { } else if (node instanceof TupleExpressionNode) {
@ -2521,7 +2550,7 @@ export class TypeAnalyzer extends ParseTreeWalker {
private _bindMultiPartModuleNameToType(nameParts: NameNode[], type: ModuleType, private _bindMultiPartModuleNameToType(nameParts: NameNode[], type: ModuleType,
declaration?: Declaration): void { declaration?: Declaration): void {
let targetSymbolTable = this._currentScope.getSymbolTable(); let targetSymbolTable = this._currentScope.getSymbolTable();
let symbol = Symbol.create(type, DefaultTypeSourceId); let symbol = Symbol.createWithType(type, DefaultTypeSourceId);
if (declaration) { if (declaration) {
symbol.addDeclaration(declaration); symbol.addDeclaration(declaration);
} }
@ -2557,7 +2586,7 @@ export class TypeAnalyzer extends ParseTreeWalker {
// to the next part of the name. // to the next part of the name.
let newPartialModule = new ModuleType(new SymbolTable()); let newPartialModule = new ModuleType(new SymbolTable());
newPartialModule.setIsPartialModule(); newPartialModule.setIsPartialModule();
targetSymbolTable.set(name, Symbol.create(newPartialModule, DefaultTypeSourceId)); targetSymbolTable.set(name, Symbol.createWithType(newPartialModule, DefaultTypeSourceId));
targetSymbolTable = newPartialModule.getFields(); targetSymbolTable = newPartialModule.getFields();
} }
} }
@ -2573,13 +2602,9 @@ export class TypeAnalyzer extends ParseTreeWalker {
const symbolWithScope = this._currentScope.lookUpSymbolRecursive(nameValue); const symbolWithScope = this._currentScope.lookUpSymbolRecursive(nameValue);
if (symbolWithScope) { if (symbolWithScope) {
if (symbolWithScope.symbol.declarations) { const primaryDecl = TypeUtils.getPrimaryDeclarationOfSymbol(symbolWithScope.symbol);
const declWithDefinedType = symbolWithScope.symbol.declarations.find( if (primaryDecl) {
decl => !!decl.declaredType); declaredType = primaryDecl.declaredType!;
if (declWithDefinedType) {
declaredType = declWithDefinedType.declaredType!;
}
} }
} else { } else {
// We should never get here. // We should never get here.
@ -2610,7 +2635,7 @@ export class TypeAnalyzer extends ParseTreeWalker {
assert(permanentScope.getType() !== ScopeType.Temporary); assert(permanentScope.getType() !== ScopeType.Temporary);
if (!permanentScope.lookUpSymbol(name)) { 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); const symbolWithScope = this._currentScope.lookUpSymbolRecursive(name);
if (symbolWithScope) { if (symbolWithScope) {
if (symbolWithScope.symbol.inferredType.addSource(type, typeSourceId)) { if (symbolWithScope.symbol.setInferredTypeForSource(type, typeSourceId)) {
if (symbolWithScope.scope.getType() !== ScopeType.Temporary) { if (symbolWithScope.scope.getType() !== ScopeType.Temporary) {
this._setAnalysisChanged(); this._setAnalysisChanged();
} }
@ -2699,21 +2724,21 @@ export class TypeAnalyzer extends ParseTreeWalker {
let classMemberInfo = TypeUtils.lookUpClassMember( let classMemberInfo = TypeUtils.lookUpClassMember(
baseType.getClassType(), memberNameValue); baseType.getClassType(), memberNameValue);
if (classMemberInfo) { if (classMemberInfo) {
if (classMemberInfo.symbol && classMemberInfo.symbol.declarations) { if (classMemberInfo.symbol && classMemberInfo.symbol.hasDeclarations()) {
AnalyzerNodeInfo.setDeclaration(memberName, AnalyzerNodeInfo.setDeclaration(memberName,
TypeUtils.getPrimaryDeclarationOfSymbol(classMemberInfo.symbol)!); TypeUtils.getPrimaryDeclarationOfSymbol(classMemberInfo.symbol)!);
} }
} }
} else if (baseType instanceof ModuleType) { } else if (baseType instanceof ModuleType) {
let moduleMemberInfo = baseType.getFields().get(memberNameValue); let moduleMemberInfo = baseType.getFields().get(memberNameValue);
if (moduleMemberInfo && moduleMemberInfo.declarations) { if (moduleMemberInfo && moduleMemberInfo.hasDeclarations()) {
AnalyzerNodeInfo.setDeclaration(memberName, AnalyzerNodeInfo.setDeclaration(memberName,
TypeUtils.getPrimaryDeclarationOfSymbol(moduleMemberInfo)!); TypeUtils.getPrimaryDeclarationOfSymbol(moduleMemberInfo)!);
} }
} else if (baseType instanceof ClassType) { } else if (baseType instanceof ClassType) {
let classMemberInfo = TypeUtils.lookUpClassMember(baseType, memberNameValue, let classMemberInfo = TypeUtils.lookUpClassMember(baseType, memberNameValue,
ClassMemberLookupFlags.SkipInstanceVariables); ClassMemberLookupFlags.SkipInstanceVariables);
if (classMemberInfo && classMemberInfo.symbol && classMemberInfo.symbol.declarations) { if (classMemberInfo && classMemberInfo.symbol && classMemberInfo.symbol.hasDeclarations()) {
AnalyzerNodeInfo.setDeclaration(memberName, AnalyzerNodeInfo.setDeclaration(memberName,
TypeUtils.getPrimaryDeclarationOfSymbol(classMemberInfo.symbol)!); TypeUtils.getPrimaryDeclarationOfSymbol(classMemberInfo.symbol)!);
} }

View File

@ -687,7 +687,7 @@ export class TypeUtils {
// The class derives from an unknown type, so all bets are off // The class derives from an unknown type, so all bets are off
// when trying to find a member. Return an unknown symbol. // when trying to find a member. Return an unknown symbol.
return { return {
symbol: Symbol.create(UnknownType.create(), DefaultTypeSourceId), symbol: Symbol.createWithType(UnknownType.create(), DefaultTypeSourceId),
isInstanceMember: false, isInstanceMember: false,
classType: UnknownType.create(), classType: UnknownType.create(),
symbolType: UnknownType.create() symbolType: UnknownType.create()
@ -704,12 +704,13 @@ export class TypeUtils {
return declaredType; return declaredType;
} }
return symbol.inferredType.getType(); return symbol.getInferredType();
} }
static getDeclaredTypeOfSymbol(symbol: Symbol): Type | undefined { static getDeclaredTypeOfSymbol(symbol: Symbol): Type | undefined {
if (symbol.declarations) { const declarations = symbol.getDeclarations();
const declWithDeclaredType = symbol.declarations.find(decl => decl.declaredType !== undefined); if (declarations.length > 0) {
const declWithDeclaredType = declarations.find(decl => decl.declaredType !== undefined);
if (declWithDeclaredType) { if (declWithDeclaredType) {
return declWithDeclaredType.declaredType; return declWithDeclaredType.declaredType;
} }
@ -721,14 +722,14 @@ export class TypeUtils {
// Returns the first declaration with a declared type. If no such // Returns the first declaration with a declared type. If no such
// declaration exists, returns the first declaration. // declaration exists, returns the first declaration.
static getPrimaryDeclarationOfSymbol(symbol: Symbol): Declaration | undefined { static getPrimaryDeclarationOfSymbol(symbol: Symbol): Declaration | undefined {
if (symbol.declarations) { const declarations = symbol.getDeclarations();
const declWithDeclaredType = symbol.declarations.find( if (declarations.length > 0) {
decl => decl.declaredType !== undefined); const declWithDeclaredType = declarations.find(decl => decl.declaredType !== undefined);
if (declWithDeclaredType) { if (declWithDeclaredType) {
return declWithDeclaredType; return declWithDeclaredType;
} }
return symbol.declarations[0]; return declarations[0];
} }
return undefined; return undefined;
@ -1461,33 +1462,33 @@ export class TypeUtils {
// Validate that the type arguments match. // Validate that the type arguments match.
const srcTypeArgs = curSrcType.getTypeArguments(); const srcTypeArgs = curSrcType.getTypeArguments();
if (srcTypeArgs) { 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++) { // In most cases, the ancestor type param count should match, but
const srcTypeArg = srcTypeArgs[srcArgIndex]; // 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 if (typeParam.isCovariant()) {
// there are a few special cases where this isn't true (e.g. assigning if (!this.canAssignType(ancestorTypeArg, srcTypeArg,
// a Tuple[X, Y, Z] to a tuple[W]). diag.createAddendum(), undefined, true, recursionCount + 1)) {
const ancestorArgIndex = srcArgIndex >= ancestorTypeParams.length ? return false;
ancestorTypeParams.length - 1 : srcArgIndex; }
const typeParam = ancestorTypeParams[ancestorArgIndex]; } else if (typeParam.isContravariant()) {
const ancestorTypeArg = ancestorTypeArgs[ancestorArgIndex]; if (!this.canAssignType(srcTypeArg, ancestorTypeArg,
diag.createAddendum(), undefined, true, recursionCount + 1)) {
if (typeParam.isCovariant()) { return false;
if (!this.canAssignType(ancestorTypeArg, srcTypeArg, }
diag.createAddendum(), undefined, true, recursionCount + 1)) { } else {
return false; if (!this.canAssignType(ancestorTypeArg, srcTypeArg,
} diag.createAddendum(), undefined, false, recursionCount + 1)) {
} else if (typeParam.isContravariant()) { return false;
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;
} }
} }
} }

View File

@ -63,7 +63,7 @@ export enum TypeCategory {
export type LiteralValue = number | boolean | string; export type LiteralValue = number | boolean | string;
const MaxRecursionCount = 20; const MaxRecursionCount = 16;
export type InheritanceChain = (ClassType | UnknownType)[]; export type InheritanceChain = (ClassType | UnknownType)[];
@ -685,8 +685,8 @@ export class FunctionType extends Type {
this._functionDetails = { this._functionDetails = {
flags, flags,
parameters: [], parameters: [],
inferredReturnType: new InferredType(UnknownType.create()), inferredReturnType: new InferredType(),
inferredYieldType: new InferredType(UnknownType.create()) inferredYieldType: new InferredType()
}; };
} }