mirror of
https://github.com/microsoft/pyright.git
synced 2024-10-05 12:27:30 +03:00
Fixed more bugs in expression evaluator.
This commit is contained in:
parent
5c0c0ce016
commit
64ef65546b
@ -69,73 +69,6 @@ export class ExpressionEvaluator {
|
||||
return typeResult.type;
|
||||
}
|
||||
|
||||
// Specializes the specified (potentially generic) class type using
|
||||
// the specified type arguments, reporting errors as appropriate.
|
||||
// Returns the specialized type and a boolean indicating whether
|
||||
// the type indiciates a class type (true) or an object type (false).
|
||||
specializeClassType(classType: ClassType, typeArgNode: ExpressionNode,
|
||||
flags: EvaluatorFlags): Type {
|
||||
let typeArgs = this._getTypeArgs(typeArgNode);
|
||||
|
||||
// Handle the special-case classes that are not defined
|
||||
// in the type stubs.
|
||||
if (classType.isSpecialBuiltIn()) {
|
||||
const className = classType.getClassName();
|
||||
|
||||
switch (className) {
|
||||
case 'Callable': {
|
||||
return this._createCallableType(typeArgs);
|
||||
}
|
||||
|
||||
case 'Optional': {
|
||||
return this._createOptional(typeArgNode, typeArgs);
|
||||
}
|
||||
|
||||
case 'Type': {
|
||||
return this._createTypeType(typeArgNode, typeArgs);
|
||||
}
|
||||
|
||||
case 'ClassVar':
|
||||
case 'Deque':
|
||||
case 'List':
|
||||
case 'FrozenSet':
|
||||
case 'Set': {
|
||||
return this._createSpecialType(classType, typeArgs, flags, 1);
|
||||
}
|
||||
|
||||
case 'ChainMap':
|
||||
case 'Dict':
|
||||
case 'DefaultDict': {
|
||||
return this._createSpecialType(classType, typeArgs, flags, 2);
|
||||
}
|
||||
|
||||
case 'Protocol':
|
||||
case 'Tuple': {
|
||||
return this._createSpecialType(classType, typeArgs, flags);
|
||||
}
|
||||
|
||||
case 'Union': {
|
||||
return this._createUnionType(typeArgs);
|
||||
}
|
||||
|
||||
case 'Generic':
|
||||
if (flags & EvaluatorFlags.ConvertClassToObject) {
|
||||
this._addError(`Generic allowed only as base class`, typeArgNode);
|
||||
}
|
||||
return this._createGenericType(typeArgNode, classType, typeArgs);
|
||||
}
|
||||
}
|
||||
|
||||
if (classType === ScopeUtils.getBuiltInType(this._scope, 'type')) {
|
||||
// The built-in 'type' class isn't defined as a generic class.
|
||||
// It needs to be special-cased here.
|
||||
return this._createTypeType(typeArgNode, typeArgs);
|
||||
}
|
||||
|
||||
let specializedType = this._createSpecializedClassType(classType, typeArgs);
|
||||
return this._convertClassToObject(specializedType, flags);
|
||||
}
|
||||
|
||||
// Determines if the function node is a property accessor (getter, setter, deleter).
|
||||
getPropertyType(node: FunctionNode, type: FunctionType): PropertyType | undefined {
|
||||
if (ParseTreeUtils.functionHasDecorator(node, 'property')) {
|
||||
@ -300,52 +233,40 @@ export class ExpressionEvaluator {
|
||||
return typeResult;
|
||||
}
|
||||
|
||||
private _getTypeFromName(node: NameNode, flags: EvaluatorFlags): TypeResult | undefined {
|
||||
private _getTypeFromName(node: NameNode, flags: EvaluatorFlags): TypeResult {
|
||||
const name = node.nameToken.value;
|
||||
let type: Type | undefined;
|
||||
|
||||
// Look for the scope that contains the value definition and
|
||||
// see if it has a declared type.
|
||||
let scope: Scope | undefined = this._scope;
|
||||
let beyondLocalScope = false;
|
||||
while (scope) {
|
||||
let symbol = scope.lookUpSymbol(name);
|
||||
if (symbol) {
|
||||
let declaration = symbol.declarations ? symbol.declarations[0] : undefined;
|
||||
const symbolWithScope = this._scope.lookUpSymbolRecursive(name);
|
||||
|
||||
if (symbolWithScope) {
|
||||
const symbol = symbolWithScope.symbol;
|
||||
|
||||
let declaration = symbol.declarations ? symbol.declarations[0] : undefined;
|
||||
|
||||
if (declaration && declaration.declaredType) {
|
||||
// Was there a defined type hint?
|
||||
if (declaration && declaration.declaredType) {
|
||||
type = declaration.declaredType;
|
||||
break;
|
||||
}
|
||||
|
||||
type = declaration.declaredType;
|
||||
} else if (declaration && declaration.category !== SymbolCategory.Variable) {
|
||||
// If this is a non-variable type (e.g. a class, function, method), we
|
||||
// can assume that it's not going to be modified outside the local scope.
|
||||
if (declaration && declaration.category !== SymbolCategory.Variable) {
|
||||
type = symbol.currentType;
|
||||
break;
|
||||
}
|
||||
|
||||
type = symbol.currentType;
|
||||
} else if (symbolWithScope.isBeyondLocalScope) {
|
||||
// If we haven't already gone beyond the local scope, we can
|
||||
// trust the current type. If we've moved beyond the local
|
||||
// scope to some other outer scope (e.g. the global scope), we
|
||||
// cannot trust the current type.
|
||||
if (beyondLocalScope) {
|
||||
type = symbol.inferredType.getType();
|
||||
} else {
|
||||
type = symbol.currentType;
|
||||
}
|
||||
break;
|
||||
type = symbol.inferredType.getType();
|
||||
} else {
|
||||
type = symbol.currentType;
|
||||
}
|
||||
|
||||
if (scope.getType() !== ScopeType.Temporary) {
|
||||
beyondLocalScope = true;
|
||||
}
|
||||
scope = scope.getParent();
|
||||
}
|
||||
|
||||
if (!type) {
|
||||
return undefined;
|
||||
this._addError(`'${ name }' is not a known symbol`, node);
|
||||
type = UnknownType.create();
|
||||
}
|
||||
|
||||
type = this._convertClassToObject(type, flags);
|
||||
@ -458,6 +379,11 @@ export class ExpressionEvaluator {
|
||||
} else if (baseType instanceof PropertyType) {
|
||||
// TODO - need to come up with new strategy for properties
|
||||
type = UnknownType.create();
|
||||
} else if (baseType instanceof FunctionType) {
|
||||
if (baseType.hasCustomDecorators()) {
|
||||
// TODO - deal with custom decorators in a better way
|
||||
type = UnknownType.create();
|
||||
}
|
||||
}
|
||||
|
||||
if (!type) {
|
||||
@ -480,21 +406,26 @@ export class ExpressionEvaluator {
|
||||
|
||||
this._validateTypeArgs(typeArgs);
|
||||
|
||||
if (baseType instanceof ClassType) {
|
||||
type = this.specializeClassType(baseType, node.indexExpression, flags);
|
||||
if (baseType.isAny()) {
|
||||
type = baseType;
|
||||
} else if (baseType instanceof ClassType) {
|
||||
type = this._createSpecializeClassType(baseType, node.indexExpression, flags);
|
||||
} else if (baseType instanceof UnionType) {
|
||||
// TODO - need to implement
|
||||
type = UnknownType.create();
|
||||
} else if (baseType instanceof FunctionType) {
|
||||
// TODO - need to implement
|
||||
} else if (!baseType.isAny()) {
|
||||
if ((flags & EvaluatorFlags.ConvertClassToObject) !== 0) {
|
||||
this._addError(
|
||||
`'Unsupported type expression: indexed (${ baseType.asString() })`,
|
||||
node.baseExpression);
|
||||
}
|
||||
type = UnknownType.create();
|
||||
} else if (baseType instanceof ObjectType) {
|
||||
// TODO - need to implement
|
||||
type = UnknownType.create();
|
||||
}
|
||||
|
||||
if (!type) {
|
||||
this._addError(
|
||||
`'Unsupported expression type: indexed (${ baseType.asString() })`,
|
||||
node.baseExpression);
|
||||
|
||||
type = UnknownType.create();
|
||||
}
|
||||
|
||||
@ -1177,6 +1108,74 @@ export class ExpressionEvaluator {
|
||||
return type;
|
||||
}
|
||||
|
||||
// Specializes the specified (potentially generic) class type using
|
||||
// the specified type arguments, reporting errors as appropriate.
|
||||
// Returns the specialized type and a boolean indicating whether
|
||||
// the type indiciates a class type (true) or an object type (false).
|
||||
private _createSpecializeClassType(classType: ClassType,
|
||||
typeArgNode: ExpressionNode, flags: EvaluatorFlags): Type {
|
||||
|
||||
let typeArgs = this._getTypeArgs(typeArgNode);
|
||||
|
||||
// Handle the special-case classes that are not defined
|
||||
// in the type stubs.
|
||||
if (classType.isSpecialBuiltIn()) {
|
||||
const className = classType.getClassName();
|
||||
|
||||
switch (className) {
|
||||
case 'Callable': {
|
||||
return this._createCallableType(typeArgs);
|
||||
}
|
||||
|
||||
case 'Optional': {
|
||||
return this._createOptional(typeArgNode, typeArgs);
|
||||
}
|
||||
|
||||
case 'Type': {
|
||||
return this._createTypeType(typeArgNode, typeArgs);
|
||||
}
|
||||
|
||||
case 'ClassVar':
|
||||
case 'Deque':
|
||||
case 'List':
|
||||
case 'FrozenSet':
|
||||
case 'Set': {
|
||||
return this._createSpecialType(classType, typeArgs, flags, 1);
|
||||
}
|
||||
|
||||
case 'ChainMap':
|
||||
case 'Dict':
|
||||
case 'DefaultDict': {
|
||||
return this._createSpecialType(classType, typeArgs, flags, 2);
|
||||
}
|
||||
|
||||
case 'Protocol':
|
||||
case 'Tuple': {
|
||||
return this._createSpecialType(classType, typeArgs, flags);
|
||||
}
|
||||
|
||||
case 'Union': {
|
||||
return this._createUnionType(typeArgs);
|
||||
}
|
||||
|
||||
case 'Generic':
|
||||
if (flags & EvaluatorFlags.ConvertClassToObject) {
|
||||
this._addError(`Generic allowed only as base class`, typeArgNode);
|
||||
}
|
||||
return this._createGenericType(typeArgNode, classType, typeArgs);
|
||||
}
|
||||
}
|
||||
|
||||
if (classType === ScopeUtils.getBuiltInType(this._scope, 'type')) {
|
||||
// The built-in 'type' class isn't defined as a generic class.
|
||||
// It needs to be special-cased here.
|
||||
return this._createTypeType(typeArgNode, typeArgs);
|
||||
}
|
||||
|
||||
let specializedType = this._createSpecializedClassType(classType, typeArgs);
|
||||
return this._convertClassToObject(specializedType, flags);
|
||||
}
|
||||
|
||||
private _convertClassToObject(type: Type, flags: EvaluatorFlags): Type {
|
||||
if (flags & EvaluatorFlags.ConvertClassToObject) {
|
||||
if (type instanceof ClassType) {
|
||||
|
@ -132,7 +132,7 @@ export class Scope {
|
||||
}
|
||||
|
||||
lookUpSymbolRecursive(name: string): SymbolWithScope | undefined {
|
||||
return this._lookUpSymbolRecursiveInternal(name, false);
|
||||
return this._lookUpSymbolRecursiveInternal(name, false, false);
|
||||
}
|
||||
|
||||
// Adds a new (unbound) symbol to the scope.
|
||||
@ -351,8 +351,8 @@ export class Scope {
|
||||
this._typeConstraints.push(constraint);
|
||||
}
|
||||
|
||||
private _lookUpSymbolRecursiveInternal(name: string, isOutsideCallerModule: boolean):
|
||||
SymbolWithScope | undefined {
|
||||
private _lookUpSymbolRecursiveInternal(name: string, isOutsideCallerModule: boolean,
|
||||
isBeyondLocalScope: boolean): SymbolWithScope | undefined {
|
||||
// If we're searching outside of the original caller's module (global) scope,
|
||||
// hide any names that are not meant to be visible to importers.
|
||||
if (isOutsideCallerModule && this._hiddenNameMap[name]) {
|
||||
@ -363,6 +363,8 @@ export class Scope {
|
||||
if (symbol) {
|
||||
return {
|
||||
symbol,
|
||||
isBeyondLocalScope,
|
||||
isOutsideCallerModule,
|
||||
scope: this
|
||||
};
|
||||
}
|
||||
@ -371,7 +373,8 @@ export class Scope {
|
||||
// If our recursion is about to take us outside the scope of the current
|
||||
// module (i.e. into a built-in scope), indicate as such with the second parameter.
|
||||
return this._parent._lookUpSymbolRecursiveInternal(name,
|
||||
isOutsideCallerModule || this._scopeType === ScopeType.Global);
|
||||
isOutsideCallerModule || this._scopeType === ScopeType.Global,
|
||||
isBeyondLocalScope || this._scopeType !== ScopeType.Temporary);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
@ -380,5 +383,7 @@ export class Scope {
|
||||
|
||||
export interface SymbolWithScope {
|
||||
symbol: Symbol;
|
||||
isBeyondLocalScope: boolean;
|
||||
isOutsideCallerModule: boolean;
|
||||
scope: Scope;
|
||||
}
|
||||
|
@ -781,7 +781,6 @@ export class TypeAnalyzer extends ParseTreeWalker {
|
||||
// Set the member type for the hover provider.
|
||||
this._updateExpressionTypeForNode(node.memberName, this._getTypeOfExpression(node));
|
||||
|
||||
// Don't walk the member name.
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1709,7 +1708,7 @@ export class TypeAnalyzer extends ParseTreeWalker {
|
||||
}
|
||||
}
|
||||
|
||||
private _validateMemberAccess(baseType: Type, memberName: NameNode): boolean {
|
||||
private _validateMemberAccess(baseType: Type, memberName: NameNode): void {
|
||||
// TODO - most of this logic is now redudnant with the expression evaluation
|
||||
// logic. The only part that remains is the calls to setDeclaration. Clean
|
||||
// this up at some point.
|
||||
@ -1722,75 +1721,21 @@ export class TypeAnalyzer extends ParseTreeWalker {
|
||||
if (classMemberInfo.symbol && classMemberInfo.symbol.declarations) {
|
||||
AnalyzerNodeInfo.setDeclaration(memberName, classMemberInfo.symbol.declarations[0]);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
// See if the class has a "__getattribute__" or "__getattr__" method.
|
||||
// If so, aribrary members are supported.
|
||||
let getAttribMember = TypeUtils.lookUpClassMember(
|
||||
baseType.getClassType(), '__getattribute__');
|
||||
if (getAttribMember && getAttribMember.class) {
|
||||
const isObjectClass = getAttribMember.class.isBuiltIn() &&
|
||||
getAttribMember.class.getClassName() === 'object';
|
||||
// The built-in 'object' class, from which every class derives,
|
||||
// implements the default __getattribute__ method. We want to ignore
|
||||
// this one. If this method is overridden, we need to assume that
|
||||
// all members can be accessed.
|
||||
if (!isObjectClass) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
let getAttrMember = TypeUtils.lookUpClassMember(
|
||||
baseType.getClassType(), '__getattr__');
|
||||
if (getAttrMember) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the class has decorators, there may be additional fields
|
||||
// added that we don't know about.
|
||||
// TODO - figure out a better approach here.
|
||||
if (!baseType.getClassType().hasDecorators()) {
|
||||
this._addError(
|
||||
`'${ memberNameValue }' is not a known member of type '${ baseType.asString() }'`,
|
||||
memberName);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (baseType instanceof ModuleType) {
|
||||
let moduleMemberInfo = baseType.getFields().get(memberNameValue);
|
||||
if (!moduleMemberInfo) {
|
||||
this._addError(
|
||||
`'${ memberNameValue }' is not a known member of module`,
|
||||
memberName);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (moduleMemberInfo.declarations) {
|
||||
if (moduleMemberInfo && moduleMemberInfo.declarations) {
|
||||
AnalyzerNodeInfo.setDeclaration(memberName, moduleMemberInfo.declarations[0]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (baseType instanceof ClassType) {
|
||||
let classMemberInfo = TypeUtils.lookUpClassMember(baseType, memberNameValue, false);
|
||||
if (!classMemberInfo) {
|
||||
// If the class has decorators, there may be additional fields
|
||||
// added that we don't know about.
|
||||
// TODO - figure out a better approach here.
|
||||
if (!baseType.hasDecorators()) {
|
||||
this._addError(
|
||||
`'${ memberNameValue }' is not a known member of '${ baseType.asString() }'`,
|
||||
memberName);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (classMemberInfo.symbol && classMemberInfo.symbol.declarations) {
|
||||
if (classMemberInfo && classMemberInfo.symbol && classMemberInfo.symbol.declarations) {
|
||||
AnalyzerNodeInfo.setDeclaration(memberName, classMemberInfo.symbol.declarations[0]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (baseType instanceof UnionType) {
|
||||
@ -1799,44 +1744,12 @@ export class TypeAnalyzer extends ParseTreeWalker {
|
||||
let simplifiedType = baseType.removeOptional();
|
||||
if (simplifiedType instanceof UnionType) {
|
||||
for (let t of simplifiedType.getTypes()) {
|
||||
if (!this._validateMemberAccess(t, memberName)) {
|
||||
return false;
|
||||
}
|
||||
this._validateMemberAccess(t, memberName);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
this._validateMemberAccess(simplifiedType, memberName);
|
||||
}
|
||||
|
||||
return this._validateMemberAccess(simplifiedType, memberName);
|
||||
}
|
||||
|
||||
if (baseType instanceof UnboundType) {
|
||||
this._addError(
|
||||
`'${ memberNameValue }' cannot be accessed from unbound variable`, memberName);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (baseType instanceof PropertyType) {
|
||||
// TODO - need to implement this check
|
||||
return true;
|
||||
}
|
||||
|
||||
if (baseType instanceof FunctionType) {
|
||||
// TODO - need to implement this check
|
||||
return true;
|
||||
}
|
||||
|
||||
if (baseType instanceof TypeVarType) {
|
||||
// TODO - need to handle this check
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!baseType.isAny()) {
|
||||
this._addError(
|
||||
`'${ memberNameValue }' is not a known member of type ${ baseType.asString() }`, memberName);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private _useExpressionTypeConstraint(typeConstraints: TypeConstraintResults | undefined,
|
||||
|
@ -10,9 +10,9 @@ import * as assert from 'assert';
|
||||
|
||||
import { ParameterCategory } from '../parser/parseNodes';
|
||||
import { Symbol } from './symbol';
|
||||
import { ClassType, FunctionType, ObjectType, OverloadedFunctionType,
|
||||
TupleType, Type, TypeCategory, TypeVarType, UnboundType,
|
||||
UnionType, UnknownType } from './types';
|
||||
import { ClassType, FunctionType, ObjectType,
|
||||
OverloadedFunctionType, TupleType, Type, TypeCategory, TypeVarType,
|
||||
UnboundType, UnionType, UnknownType } from './types';
|
||||
|
||||
export interface ClassMember {
|
||||
symbol?: Symbol;
|
||||
|
Loading…
Reference in New Issue
Block a user