mirror of
https://github.com/microsoft/pyright.git
synced 2024-10-05 12:27:30 +03:00
Removed redundant expression evaluation code from type analyzer.
This commit is contained in:
parent
cee369f31b
commit
b518f36694
@ -230,10 +230,12 @@ export class ExpressionEvaluator {
|
||||
} else if (node instanceof UnaryExpressionNode) {
|
||||
// TODO - need to implement
|
||||
this._getTypeFromExpression(node.expression, flags);
|
||||
typeResult = { type: UnknownType.create(), node };
|
||||
} else if (node instanceof BinaryExpressionNode) {
|
||||
// TODO - need to implement
|
||||
this._getTypeFromExpression(node.leftExpression, flags);
|
||||
this._getTypeFromExpression(node.rightExpression, flags);
|
||||
typeResult = { type: UnknownType.create(), node };
|
||||
} else if (node instanceof ListNode) {
|
||||
typeResult = this._getListExpression(node, flags);
|
||||
} else if (node instanceof SliceExpressionNode) {
|
||||
@ -352,9 +354,15 @@ export class ExpressionEvaluator {
|
||||
}
|
||||
|
||||
private _getTypeFromMemberAccessExpression(node: MemberAccessExpressionNode,
|
||||
flags: EvaluatorFlags): TypeResult | undefined {
|
||||
flags: EvaluatorFlags): TypeResult {
|
||||
|
||||
const baseTypeResult = this._getTypeFromExpression(node.leftExpression, EvaluatorFlags.None);
|
||||
return this._getTypeFromMemberAccessExpressionWithBaseType(node, baseTypeResult, flags);
|
||||
}
|
||||
|
||||
private _getTypeFromMemberAccessExpressionWithBaseType(node: MemberAccessExpressionNode,
|
||||
baseTypeResult: TypeResult, flags: EvaluatorFlags): TypeResult {
|
||||
|
||||
const baseType = baseTypeResult.type;
|
||||
const memberName = node.memberName.nameToken.value;
|
||||
const getTypeFromClass = (classType: ClassType, includeInstanceMembers: boolean) => {
|
||||
@ -397,10 +405,34 @@ export class ExpressionEvaluator {
|
||||
this._addError(`'${ memberName }' is not a known member of module`, node.memberName);
|
||||
type = UnknownType.create();
|
||||
}
|
||||
} else if (baseType instanceof UnionType) {
|
||||
let returnTypes: Type[] = [];
|
||||
baseType.getTypes().forEach(typeEntry => {
|
||||
if (typeEntry instanceof NoneType) {
|
||||
// TODO - ignore None for now.
|
||||
} else {
|
||||
let typeResult = this._getTypeFromMemberAccessExpressionWithBaseType(node,
|
||||
{ type: typeEntry, isClassOrObjectMember: baseTypeResult.isClassOrObjectMember, node },
|
||||
EvaluatorFlags.None);
|
||||
if (typeResult) {
|
||||
returnTypes.push(typeResult.type);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (returnTypes.length > 0) {
|
||||
type = TypeUtils.combineTypesArray(returnTypes);
|
||||
}
|
||||
} else if (baseType instanceof PropertyType) {
|
||||
// TODO - need to come up with new strategy for properties
|
||||
type = UnknownType.create();
|
||||
}
|
||||
|
||||
if (!type) {
|
||||
return undefined;
|
||||
this._addError(
|
||||
`'${ memberName }' is not a known member of '${ baseType.asString() }'`,
|
||||
node.memberName);
|
||||
type = UnknownType.create();
|
||||
}
|
||||
|
||||
type = this._convertClassToObject(type, flags);
|
||||
@ -474,7 +506,7 @@ export class ExpressionEvaluator {
|
||||
return typeResult;
|
||||
}
|
||||
|
||||
private _getTupleExpression(node: TupleExpressionNode, flags: EvaluatorFlags): TypeResult | undefined {
|
||||
private _getTupleExpression(node: TupleExpressionNode, flags: EvaluatorFlags): TypeResult {
|
||||
let tupleType = new TupleType(ScopeUtils.getBuiltInType(this._scope, 'tuple') as ClassType);
|
||||
|
||||
node.expressions.forEach(expr => {
|
||||
@ -488,9 +520,16 @@ export class ExpressionEvaluator {
|
||||
};
|
||||
}
|
||||
|
||||
private _getCallExpression(node: CallExpressionNode, flags: EvaluatorFlags): TypeResult | undefined {
|
||||
let type: Type | undefined;
|
||||
private _getCallExpression(node: CallExpressionNode, flags: EvaluatorFlags): TypeResult {
|
||||
const baseTypeResult = this._getTypeFromExpression(node.leftExpression, EvaluatorFlags.None);
|
||||
|
||||
return this._getCallExpressionWithBaseType(node, baseTypeResult, flags);
|
||||
}
|
||||
|
||||
private _getCallExpressionWithBaseType(node: CallExpressionNode, baseTypeResult: TypeResult,
|
||||
flags: EvaluatorFlags): TypeResult {
|
||||
|
||||
let type: Type | undefined;
|
||||
const callType = baseTypeResult.type;
|
||||
|
||||
if (callType instanceof ClassType) {
|
||||
@ -535,7 +574,7 @@ export class ExpressionEvaluator {
|
||||
}
|
||||
} else if (callType instanceof OverloadedFunctionType) {
|
||||
// Determine which of the overloads (if any) match.
|
||||
let skipFirstMethodParam = !!baseTypeResult.isClassOrObjectMember;
|
||||
// let skipFirstMethodParam = !!baseTypeResult.isClassOrObjectMember;
|
||||
|
||||
// TODO - need to figure out what to do here.
|
||||
// let functionType = this._findOverloadedFunctionType(
|
||||
@ -543,13 +582,16 @@ export class ExpressionEvaluator {
|
||||
// if (functionType) {
|
||||
// type = functionType.getEffectiveReturnType();
|
||||
// }
|
||||
type = UnknownType.create();
|
||||
} else if (callType instanceof UnionType) {
|
||||
let returnTypes: Type[] = [];
|
||||
callType.getTypes().forEach(typeEntry => {
|
||||
if (typeEntry instanceof NoneType) {
|
||||
// TODO - ignore None for now.
|
||||
} else {
|
||||
let typeResult = this._getCallExpression(node, EvaluatorFlags.None);
|
||||
let typeResult = this._getCallExpressionWithBaseType(node,
|
||||
{ type: typeEntry, isClassOrObjectMember: baseTypeResult.isClassOrObjectMember, node },
|
||||
EvaluatorFlags.None);
|
||||
if (typeResult) {
|
||||
returnTypes.push(typeResult.type);
|
||||
}
|
||||
@ -565,7 +607,7 @@ export class ExpressionEvaluator {
|
||||
|
||||
if (type === undefined) {
|
||||
this._addError(`'Unsupported type expression: call`, node);
|
||||
return undefined;
|
||||
type = UnknownType.create();
|
||||
}
|
||||
|
||||
type = this._convertClassToObject(type, flags);
|
||||
|
@ -15,8 +15,6 @@ import { ParseResults } from '../parser/parser';
|
||||
import { AnalyzerNodeInfo } from './analyzerNodeInfo';
|
||||
import { ParseTreeUtils } from './parseTreeUtils';
|
||||
import { SymbolCategory } from './symbol';
|
||||
import { ClassType } from './types';
|
||||
import { TypeUtils } from './typeUtils';
|
||||
|
||||
export class HoverProvider {
|
||||
static getHoverForPosition(parseResults: ParseResults,
|
||||
|
@ -14,16 +14,15 @@ import { TextRangeDiagnosticSink } from '../common/diagnosticSink';
|
||||
import { convertOffsetsToRange } from '../common/positionUtils';
|
||||
import StringMap from '../common/stringMap';
|
||||
import { TextRange } from '../common/textRange';
|
||||
import { ArgumentCategory, AssignmentNode, AwaitExpressionNode, BinaryExpressionNode, CallExpressionNode,
|
||||
ClassNode, ConditionalExpressionNode, ConstantNode, DictionaryNode, EllipsisNode, ExceptNode,
|
||||
import { ArgumentCategory, AssignmentNode, BinaryExpressionNode, CallExpressionNode,
|
||||
ClassNode, ConditionalExpressionNode, ConstantNode, ExceptNode,
|
||||
ExpressionNode, ForNode, FunctionNode, IfNode, ImportAsNode, ImportFromNode,
|
||||
IndexExpressionNode, LambdaNode, ListComprehensionForNode, ListComprehensionNode,
|
||||
ListNode, MemberAccessExpressionNode, ModuleNode, NameNode, NumberNode, ParameterCategory,
|
||||
ParseNode, RaiseNode, ReturnNode, SetNode, SliceExpressionNode,
|
||||
StarExpressionNode, StringNode, TryNode, TupleExpressionNode,
|
||||
TypeAnnotationExpressionNode, UnaryExpressionNode, WithNode,
|
||||
YieldExpressionNode, YieldFromExpressionNode } from '../parser/parseNodes';
|
||||
import { KeywordType, OperatorType, QuoteTypeFlags } from '../parser/tokenizerTypes';
|
||||
LambdaNode, ListComprehensionForNode, ListComprehensionNode,
|
||||
MemberAccessExpressionNode, ModuleNode, NameNode, ParameterCategory,
|
||||
ParseNode, RaiseNode, ReturnNode, StarExpressionNode, TryNode, TupleExpressionNode,
|
||||
TypeAnnotationExpressionNode, WithNode, YieldExpressionNode,
|
||||
YieldFromExpressionNode } from '../parser/parseNodes';
|
||||
import { KeywordType, OperatorType } from '../parser/tokenizerTypes';
|
||||
import { ScopeUtils } from '../scopeUtils';
|
||||
import { AnalyzerFileInfo } from './analyzerFileInfo';
|
||||
import { AnalyzerNodeInfo } from './analyzerNodeInfo';
|
||||
@ -108,14 +107,11 @@ export class TypeAnalyzer extends ParseTreeWalker {
|
||||
// We should have already resolved most of the base class
|
||||
// parameters in the semantic analyzer, but if these parameters
|
||||
// are variables, they may not have been resolved at that time.
|
||||
this.walkMultiple(node.arguments);
|
||||
let classType = AnalyzerNodeInfo.getExpressionType(node) as ClassType;
|
||||
assert(classType instanceof ClassType);
|
||||
|
||||
let evaluator = this._getEvaluator();
|
||||
|
||||
node.arguments.forEach((arg, index) => {
|
||||
let argType = evaluator.getType(arg.valueExpression, EvaluatorFlags.None);
|
||||
let argType = this._getTypeOfExpression(arg.valueExpression);
|
||||
|
||||
// In some stub files, classes are conditionally defined (e.g. based
|
||||
// on platform type). We'll assume that the conditional logic is correct
|
||||
@ -133,6 +129,8 @@ export class TypeAnalyzer extends ParseTreeWalker {
|
||||
}
|
||||
});
|
||||
|
||||
this.walkMultiple(node.arguments);
|
||||
|
||||
// Update the type parameters for the class.
|
||||
let typeParameters: TypeVarType[] = [];
|
||||
classType.getBaseClasses().forEach(baseClass => {
|
||||
@ -171,14 +169,11 @@ export class TypeAnalyzer extends ParseTreeWalker {
|
||||
functionType.setSpecialBuiltInName(node.name.nameToken.value);
|
||||
}
|
||||
|
||||
let evaluator = this._getEvaluator();
|
||||
|
||||
const functionParams = functionType.getParameters();
|
||||
node.parameters.forEach((param, index) => {
|
||||
let annotatedType: Type | undefined;
|
||||
if (param.typeAnnotation) {
|
||||
annotatedType = evaluator.getType(param.typeAnnotation.expression,
|
||||
EvaluatorFlags.ConvertClassToObject);
|
||||
annotatedType = this._getTypeOfAnnotation(param.typeAnnotation.expression);
|
||||
|
||||
// PEP 484 indicates that if a parameter has a default value of 'None'
|
||||
// the type checker should assume that the type is optional (i.e. a union
|
||||
@ -194,7 +189,7 @@ export class TypeAnalyzer extends ParseTreeWalker {
|
||||
|
||||
if (param.defaultValue) {
|
||||
// Verify that the default value matches the type annotation.
|
||||
let defaultValueType = this._getTypeOfExpressionWithTypeConstraints(param.defaultValue);
|
||||
let defaultValueType = this._getTypeOfExpression(param.defaultValue);
|
||||
if (annotatedType && !TypeUtils.canAssignType(annotatedType, defaultValueType)) {
|
||||
this._addError(
|
||||
`Value of type '${ defaultValueType.asString() }' cannot` +
|
||||
@ -220,8 +215,7 @@ export class TypeAnalyzer extends ParseTreeWalker {
|
||||
});
|
||||
|
||||
if (node.returnTypeAnnotation) {
|
||||
const returnType = evaluator.getType(node.returnTypeAnnotation.expression,
|
||||
EvaluatorFlags.ConvertClassToObject);
|
||||
const returnType = this._getTypeOfAnnotation(node.returnTypeAnnotation.expression);
|
||||
if (functionType.setDeclaredReturnType(returnType)) {
|
||||
this._setAnalysisChanged();
|
||||
}
|
||||
@ -331,6 +325,7 @@ export class TypeAnalyzer extends ParseTreeWalker {
|
||||
|
||||
// Handle overload decorators specially.
|
||||
let overloadedType: OverloadedFunctionType | undefined;
|
||||
let evaluator = this._getEvaluator();
|
||||
[overloadedType] = evaluator.getOverloadedFunctionType(node, functionType);
|
||||
if (overloadedType) {
|
||||
decoratedType = overloadedType;
|
||||
@ -364,7 +359,7 @@ export class TypeAnalyzer extends ParseTreeWalker {
|
||||
}
|
||||
|
||||
visitCall(node: CallExpressionNode): boolean {
|
||||
let callType = this._getTypeOfExpressionWithTypeConstraints(node.leftExpression);
|
||||
let callType = this._getTypeOfExpression(node.leftExpression);
|
||||
|
||||
if (callType instanceof ClassType && callType.isGeneric()) {
|
||||
// TODO - need to infer types. For now, just assume "any" type.
|
||||
@ -387,7 +382,7 @@ export class TypeAnalyzer extends ParseTreeWalker {
|
||||
visitFor(node: ForNode): boolean {
|
||||
this.walk(node.sequenceExpression);
|
||||
|
||||
let exprType = this._getTypeOfExpressionWithTypeConstraints(node.sequenceExpression);
|
||||
let exprType = this._getTypeOfExpression(node.sequenceExpression);
|
||||
if (exprType.category === TypeCategory.Unbound) {
|
||||
exprType = UnknownType.create();
|
||||
} else {
|
||||
@ -418,7 +413,7 @@ export class TypeAnalyzer extends ParseTreeWalker {
|
||||
this.walk(compr.sequenceExpression);
|
||||
|
||||
// TODO - need to figure out right type for target expression.
|
||||
// let exprType = this._getTypeOfExpressionWithTypeConstraints(compr.sequenceExpression);
|
||||
// let exprType = this._getTypeOfExpression(compr.sequenceExpression);
|
||||
let exprType = UnknownType.create();
|
||||
|
||||
this._assignTypeToPossibleTuple(compr.targetExpression, exprType);
|
||||
@ -536,7 +531,7 @@ export class TypeAnalyzer extends ParseTreeWalker {
|
||||
|
||||
node.withItems.forEach(item => {
|
||||
if (item.target) {
|
||||
let exprType = this._getTypeOfExpressionWithTypeConstraints(item.expression);
|
||||
let exprType = this._getTypeOfExpression(item.expression);
|
||||
|
||||
// If the type has an "__enter__" method, it can return
|
||||
// a type other than its own type.
|
||||
@ -592,7 +587,7 @@ export class TypeAnalyzer extends ParseTreeWalker {
|
||||
}
|
||||
|
||||
if (node.returnExpression) {
|
||||
returnType = this._getTypeOfExpressionWithTypeConstraints(node.returnExpression);
|
||||
returnType = this._getTypeOfExpression(node.returnExpression);
|
||||
typeSourceId = AnalyzerNodeInfo.getTypeSourceId(node);
|
||||
} else {
|
||||
// There is no return expression, so "None" is assumed.
|
||||
@ -614,7 +609,7 @@ export class TypeAnalyzer extends ParseTreeWalker {
|
||||
}
|
||||
|
||||
visitYield(node: YieldExpressionNode) {
|
||||
let yieldType = this._getTypeOfExpressionWithTypeConstraints(node.expression);
|
||||
let yieldType = this._getTypeOfExpression(node.expression);
|
||||
let typeSourceId = AnalyzerNodeInfo.getTypeSourceId(node.expression);
|
||||
this._currentScope.getReturnType().addSource(yieldType, typeSourceId);
|
||||
|
||||
@ -644,8 +639,7 @@ export class TypeAnalyzer extends ParseTreeWalker {
|
||||
|
||||
if (node.typeExpression && node.name) {
|
||||
this._currentScope.addUnboundSymbol(node.name.nameToken.value);
|
||||
let evaluator = this._getEvaluator();
|
||||
let exceptionType = evaluator.getType(node.typeExpression, EvaluatorFlags.None);
|
||||
let exceptionType = this._getTypeOfExpression(node.typeExpression);
|
||||
|
||||
// If more than one type was specified for the exception,
|
||||
// handle that here.
|
||||
@ -748,7 +742,7 @@ export class TypeAnalyzer extends ParseTreeWalker {
|
||||
}
|
||||
}
|
||||
|
||||
let typeOfExpr = this._getTypeOfExpressionWithTypeConstraints(node.rightExpression);
|
||||
let typeOfExpr = this._getTypeOfExpression(node.rightExpression);
|
||||
|
||||
if (!(node.leftExpression instanceof NameNode) ||
|
||||
!this._assignTypeForPossibleEnumeration(node.leftExpression, typeOfExpr)) {
|
||||
@ -781,7 +775,7 @@ export class TypeAnalyzer extends ParseTreeWalker {
|
||||
visitMemberAccess(node: MemberAccessExpressionNode) {
|
||||
this.walk(node.leftExpression);
|
||||
|
||||
let leftType = this._getTypeOfExpressionWithTypeConstraints(node.leftExpression);
|
||||
let leftType = this._getTypeOfExpression(node.leftExpression);
|
||||
this._validateMemberAccess(leftType, node.memberName);
|
||||
|
||||
// Set the member type for the hover provider.
|
||||
@ -1001,9 +995,7 @@ export class TypeAnalyzer extends ParseTreeWalker {
|
||||
}
|
||||
}
|
||||
|
||||
let evaluator = this._getEvaluator();
|
||||
let typeHint = evaluator.getType(node.typeAnnotation.expression,
|
||||
EvaluatorFlags.ConvertClassToObject);
|
||||
let typeHint = this._getTypeOfAnnotation(node.typeAnnotation.expression);
|
||||
if (typeHint) {
|
||||
if (!(node.valueExpression instanceof NameNode) ||
|
||||
!this._assignTypeForPossibleEnumeration(node.valueExpression, typeHint)) {
|
||||
@ -1025,7 +1017,7 @@ export class TypeAnalyzer extends ParseTreeWalker {
|
||||
private _isCallOnObjectOrClass(node: CallExpressionNode): boolean {
|
||||
let skipFirstMethodParam = false;
|
||||
if (node.leftExpression instanceof MemberAccessExpressionNode) {
|
||||
let leftType = this._getTypeOfExpressionWithTypeConstraints(
|
||||
let leftType = this._getTypeOfExpression(
|
||||
node.leftExpression.leftExpression);
|
||||
|
||||
// TODO - what should we do about UnionType here?
|
||||
@ -1251,319 +1243,14 @@ export class TypeAnalyzer extends ParseTreeWalker {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private _getTypeOfExpressionWithTypeConstraints(node: ExpressionNode): Type {
|
||||
let type = this._getTypeOfExpression(node);
|
||||
return this._applyTypeConstraint(node, type);
|
||||
private _getTypeOfAnnotation(node: ExpressionNode): Type {
|
||||
let evaluator = this._getEvaluator();
|
||||
return evaluator.getType(node, EvaluatorFlags.ConvertClassToObject);
|
||||
}
|
||||
|
||||
// Returns the type of the expression for the node and caches it
|
||||
// for subsequent calls. Generic class and function types are not
|
||||
// specialized unless explicit type arguments are provided. It
|
||||
// is up to the caller to determine inferred type specializations.
|
||||
private _getTypeOfExpression(node: ExpressionNode): Type {
|
||||
// If there's an up-to-date cached version, return it.
|
||||
let cachedType = this._readTypeFromNodeCache(node);
|
||||
if (cachedType) {
|
||||
return cachedType;
|
||||
}
|
||||
|
||||
let exprType: Type | undefined;
|
||||
|
||||
if (node instanceof NameNode) {
|
||||
exprType = this._getTypeOfName(node.nameToken.value);
|
||||
} else if (node instanceof StringNode) {
|
||||
if (node.tokens[0].quoteTypeFlags & QuoteTypeFlags.Byte) {
|
||||
exprType = ScopeUtils.getBuiltInObject(this._currentScope, 'byte');
|
||||
} else {
|
||||
exprType = ScopeUtils.getBuiltInObject(this._currentScope, 'str');
|
||||
}
|
||||
} else if (node instanceof NumberNode) {
|
||||
if (node.token.isInteger) {
|
||||
exprType = ScopeUtils.getBuiltInObject(this._currentScope, 'int');
|
||||
} else {
|
||||
exprType = ScopeUtils.getBuiltInObject(this._currentScope, 'float');
|
||||
}
|
||||
} else if (node instanceof ConstantNode) {
|
||||
if (node.token.keywordType === KeywordType.True ||
|
||||
node.token.keywordType === KeywordType.False ||
|
||||
node.token.keywordType === KeywordType.Debug) {
|
||||
exprType = ScopeUtils.getBuiltInObject(this._currentScope, 'bool');
|
||||
} else {
|
||||
assert(node.token.keywordType === KeywordType.None);
|
||||
exprType = NoneType.create();
|
||||
}
|
||||
} else if (node instanceof EllipsisNode) {
|
||||
exprType = AnyType.create();
|
||||
} else if (node instanceof MemberAccessExpressionNode) {
|
||||
exprType = this._getTypeOfMemberAccessNode(node);
|
||||
} else if (node instanceof CallExpressionNode) {
|
||||
let callType = this._getTypeOfExpression(node.leftExpression);
|
||||
exprType = this._getTypeOfCallNode(node, callType);
|
||||
} else if (node instanceof UnaryExpressionNode) {
|
||||
// TODO - need to implement
|
||||
this._getTypeOfExpression(node.expression);
|
||||
} else if (node instanceof BinaryExpressionNode) {
|
||||
// TODO - need to implement
|
||||
this._getTypeOfExpression(node.leftExpression);
|
||||
this._getTypeOfExpression(node.rightExpression);
|
||||
} else if (node instanceof TupleExpressionNode) {
|
||||
let tupleType = new TupleType(
|
||||
ScopeUtils.getBuiltInType(this._currentScope, 'tuple') as ClassType);
|
||||
node.expressions.forEach(expr => {
|
||||
tupleType.addEntryType(this._getTypeOfExpression(expr));
|
||||
});
|
||||
exprType = tupleType;
|
||||
} else if (node instanceof IndexExpressionNode) {
|
||||
exprType = this._getTypeOfIndexExpression(node);
|
||||
} else if (node instanceof ListNode) {
|
||||
node.entries.forEach(expr => {
|
||||
this._getTypeOfExpression(expr);
|
||||
});
|
||||
// TODO - infer list type
|
||||
exprType = ScopeUtils.getBuiltInObject(
|
||||
this._currentScope, 'list', []);
|
||||
} else if (node instanceof SliceExpressionNode) {
|
||||
// TODO - need to implement
|
||||
if (node.startValue) {
|
||||
this._getTypeOfExpression(node.startValue);
|
||||
}
|
||||
if (node.endValue) {
|
||||
this._getTypeOfExpression(node.endValue);
|
||||
}
|
||||
if (node.stepValue) {
|
||||
this._getTypeOfExpression(node.stepValue);
|
||||
}
|
||||
// TODO - infer set type
|
||||
exprType = ScopeUtils.getBuiltInObject(
|
||||
this._currentScope, 'set', []);
|
||||
} else if (node instanceof AwaitExpressionNode) {
|
||||
// TODO - need to implement
|
||||
exprType = this._getTypeOfExpression(node.expression);
|
||||
} else if (node instanceof ConditionalExpressionNode) {
|
||||
this._getTypeOfExpression(node.ifExpression);
|
||||
|
||||
let leftType = this._getTypeOfExpression(node.leftExpression);
|
||||
let rightType = this._getTypeOfExpression(node.elseExpression);
|
||||
exprType = TypeUtils.combineTypes(leftType, rightType);
|
||||
} else if (node instanceof ListComprehensionNode) {
|
||||
// TODO - infer list type
|
||||
// this._getTypeOfExpression(node.baseExpression);
|
||||
exprType = ScopeUtils.getBuiltInObject(
|
||||
this._currentScope, 'list', []);
|
||||
} else if (node instanceof DictionaryNode) {
|
||||
// TODO - infer dict type
|
||||
exprType = ScopeUtils.getBuiltInObject(
|
||||
this._currentScope, 'dict', []);
|
||||
} else if (node instanceof LambdaNode) {
|
||||
exprType = AnalyzerNodeInfo.getExpressionType(node);
|
||||
} else if (node instanceof SetNode) {
|
||||
node.entries.forEach(expr => {
|
||||
this._getTypeOfExpression(expr);
|
||||
});
|
||||
// TODO - infer set type
|
||||
exprType = ScopeUtils.getBuiltInObject(
|
||||
this._currentScope, 'set', []);
|
||||
} else if (node instanceof AssignmentNode) {
|
||||
this._getTypeOfExpression(node.rightExpression);
|
||||
exprType = this._getTypeOfExpression(node.leftExpression);
|
||||
} else if (node instanceof YieldExpressionNode) {
|
||||
exprType = this._getTypeOfExpression(node.expression);
|
||||
// TODO - need to handle futures
|
||||
exprType = UnknownType.create();
|
||||
} else {
|
||||
// TODO - need to finish
|
||||
this._addError(`Unsupported expression type'`, node);
|
||||
}
|
||||
|
||||
if (!exprType) {
|
||||
exprType = UnknownType.create();
|
||||
}
|
||||
|
||||
// Cache the type so we don't need to compute it again.
|
||||
this._updateExpressionTypeForNode(node, exprType);
|
||||
|
||||
return exprType;
|
||||
}
|
||||
|
||||
private _getTypeOfIndexExpression(node: IndexExpressionNode): Type | undefined {
|
||||
let baseType = this._getTypeOfExpression(node.baseExpression);
|
||||
|
||||
// Determine if this is a generic class or function.
|
||||
if (baseType instanceof ClassType) {
|
||||
if (baseType.isGeneric() || baseType.isSpecialBuiltIn()) {
|
||||
let evaluator = this._getEvaluator();
|
||||
baseType = evaluator.specializeClassType(baseType, node.indexExpression,
|
||||
EvaluatorFlags.None);
|
||||
}
|
||||
|
||||
return baseType;
|
||||
}
|
||||
|
||||
// Force the index expression to be evaluated.
|
||||
this._getTypeOfExpression(node.indexExpression);
|
||||
|
||||
// TODO - need to implement
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private _getTypeOfMemberAccessNode(node: MemberAccessExpressionNode): Type | undefined {
|
||||
let exprType: Type | undefined;
|
||||
let leftType = this._getTypeOfExpression(node.leftExpression);
|
||||
let memberName = node.memberName.nameToken.value;
|
||||
|
||||
if (!leftType.isAny()) {
|
||||
if (leftType instanceof ObjectType) {
|
||||
let classMemberType = TypeUtils.lookUpClassMember(
|
||||
leftType.getClassType(), memberName, true);
|
||||
if (classMemberType) {
|
||||
exprType = TypeUtils.getEffectiveTypeOfMember(classMemberType);
|
||||
if (exprType instanceof PropertyType) {
|
||||
exprType = exprType.getEffectiveReturnType();
|
||||
}
|
||||
}
|
||||
} else if (leftType instanceof ClassType) {
|
||||
let classMemberType = TypeUtils.lookUpClassMember(leftType, memberName);
|
||||
if (classMemberType) {
|
||||
exprType = TypeUtils.getEffectiveTypeOfMember(classMemberType);
|
||||
if (exprType instanceof PropertyType) {
|
||||
exprType = exprType.getEffectiveReturnType();
|
||||
}
|
||||
}
|
||||
} else if (leftType instanceof ModuleType) {
|
||||
const moduleFields = leftType.getFields();
|
||||
let fieldInfo = moduleFields.get(memberName);
|
||||
if (fieldInfo) {
|
||||
exprType = fieldInfo.currentType;
|
||||
}
|
||||
} else if (leftType instanceof PropertyType) {
|
||||
// TODO - handle this case properly. This is hit
|
||||
// when we encounter the pattern "@prop.setter" for
|
||||
// defining setters.
|
||||
exprType = new FunctionType(FunctionTypeFlags.None);
|
||||
}
|
||||
}
|
||||
|
||||
return exprType;
|
||||
}
|
||||
|
||||
private _getTypeOfCallNode(node: CallExpressionNode, callType: Type): Type | undefined {
|
||||
let exprType: Type | undefined;
|
||||
|
||||
if (callType.isAny()) {
|
||||
exprType = UnknownType.create();
|
||||
} else {
|
||||
let skipFirstMethodParam = this._isCallOnObjectOrClass(node);
|
||||
if (callType instanceof FunctionType) {
|
||||
// The stdlib collections.pyi stub file defines namedtuple as a function
|
||||
// rather than a class, so we need to check for it here.
|
||||
if (callType.getSpecialBuiltInName() === 'namedtuple') {
|
||||
let evaluator = this._getEvaluator();
|
||||
exprType = evaluator.createNamedTupleType(node, false);
|
||||
} else {
|
||||
exprType = callType.getEffectiveReturnType();
|
||||
}
|
||||
} else if (callType instanceof OverloadedFunctionType) {
|
||||
// Determine which of the overloads (if any) match.
|
||||
let functionType = this._findOverloadedFunctionType(
|
||||
callType, node, skipFirstMethodParam);
|
||||
if (functionType) {
|
||||
exprType = functionType.getEffectiveReturnType();
|
||||
}
|
||||
} else if (callType instanceof ClassType) {
|
||||
if (callType.isBuiltIn()) {
|
||||
const className = callType.getClassName();
|
||||
if (className === 'type') {
|
||||
// Handle the 'type' call specially.
|
||||
if (node.arguments.length >= 1) {
|
||||
let argType = this._getTypeOfExpression(
|
||||
node.arguments[0].valueExpression);
|
||||
if (argType instanceof ObjectType) {
|
||||
exprType = argType.getClassType();
|
||||
}
|
||||
}
|
||||
|
||||
// If the parameter to type() is not statically known,
|
||||
// fall back to unknown.
|
||||
if (!exprType) {
|
||||
exprType = UnknownType.create();
|
||||
}
|
||||
} else if (className === 'TypeVar') {
|
||||
let evaluator = this._getEvaluator();
|
||||
exprType = evaluator.createTypeVarType(node);
|
||||
} else if (className === 'NamedTuple') {
|
||||
// Handle the NamedTuple case specially because it's a class factory.
|
||||
let evaluator = this._getEvaluator();
|
||||
exprType = evaluator.createNamedTupleType(node, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (!exprType) {
|
||||
exprType = new ObjectType(callType);
|
||||
}
|
||||
} else if (callType instanceof UnionType) {
|
||||
let returnTypes: Type[] = [];
|
||||
callType.getTypes().forEach(typeEntry => {
|
||||
if (typeEntry instanceof NoneType) {
|
||||
// TODO - ignore None for now.
|
||||
} else {
|
||||
let returnType = this._getTypeOfCallNode(node, typeEntry);
|
||||
if (returnType) {
|
||||
returnTypes.push(returnType);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (returnTypes.length > 0) {
|
||||
exprType = TypeUtils.combineTypesArray(returnTypes);
|
||||
}
|
||||
} else {
|
||||
// TODO - report error
|
||||
}
|
||||
}
|
||||
|
||||
return exprType;
|
||||
}
|
||||
|
||||
private _getTypeOfName(name: string): Type | undefined {
|
||||
let beyondLocalScope = false;
|
||||
|
||||
// Look for the scope that contains the value definition and
|
||||
// see if it has a declared type.
|
||||
let scope: Scope | undefined = this._currentScope;
|
||||
while (scope) {
|
||||
let symbol = scope.lookUpSymbol(name);
|
||||
if (symbol) {
|
||||
let declaration = symbol.declarations ? symbol.declarations[0] : undefined;
|
||||
|
||||
// Was there a defined type hint?
|
||||
if (declaration && declaration.declaredType) {
|
||||
return declaration.declaredType;
|
||||
}
|
||||
|
||||
// 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) {
|
||||
return symbol.currentType;
|
||||
}
|
||||
|
||||
// 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) {
|
||||
return symbol.inferredType.getType();
|
||||
} else {
|
||||
return symbol.currentType;
|
||||
}
|
||||
}
|
||||
|
||||
if (scope.getType() !== ScopeType.Temporary) {
|
||||
beyondLocalScope = true;
|
||||
}
|
||||
scope = scope.getParent();
|
||||
}
|
||||
|
||||
return undefined;
|
||||
let evaluator = this._getEvaluator();
|
||||
return evaluator.getType(node, EvaluatorFlags.None);
|
||||
}
|
||||
|
||||
private _updateExpressionTypeForNode(node: ParseNode, exprType: Type) {
|
||||
@ -1614,9 +1301,7 @@ export class TypeAnalyzer extends ParseTreeWalker {
|
||||
});
|
||||
}
|
||||
} else if (target instanceof TypeAnnotationExpressionNode) {
|
||||
let evaluator = this._getEvaluator();
|
||||
let typeHint = evaluator.getType(target.typeAnnotation.expression,
|
||||
EvaluatorFlags.ConvertClassToObject);
|
||||
let typeHint = this._getTypeOfAnnotation(target.typeAnnotation.expression);
|
||||
|
||||
if (!TypeUtils.canAssignType(typeHint, type)) {
|
||||
this._addError(
|
||||
@ -1832,7 +1517,7 @@ export class TypeAnalyzer extends ParseTreeWalker {
|
||||
// The expression type will be cached in the node so we don't re-evaluate
|
||||
// it below.
|
||||
node.arguments.forEach(arg => {
|
||||
this._getTypeOfExpressionWithTypeConstraints(arg.valueExpression);
|
||||
this._getTypeOfExpression(arg.valueExpression);
|
||||
});
|
||||
|
||||
// If the function has decorators, we need to back off because the decorator
|
||||
@ -1982,7 +1667,7 @@ export class TypeAnalyzer extends ParseTreeWalker {
|
||||
}
|
||||
|
||||
private _validateArgType(paramType: Type, argExpression: ExpressionNode) {
|
||||
let argType = this._getTypeOfExpressionWithTypeConstraints(argExpression);
|
||||
let argType = this._getTypeOfExpression(argExpression);
|
||||
if (!TypeUtils.canAssignType(paramType, argType)) {
|
||||
this._addError(
|
||||
`Argument of type '${ argType.asString() }'` +
|
||||
@ -2175,40 +1860,7 @@ export class TypeAnalyzer extends ParseTreeWalker {
|
||||
|
||||
private _buildTypeConstraints(node: ExpressionNode) {
|
||||
return TypeConstraintBuilder.buildTypeConstraints(node,
|
||||
(node: ExpressionNode) => this._getTypeOfExpressionWithTypeConstraints(node));
|
||||
}
|
||||
|
||||
private _applyTypeConstraint(node: ExpressionNode, unconstrainedType: Type): Type {
|
||||
// Apply constraints associated with the expression we're
|
||||
// currently walking.
|
||||
let constrainedType = unconstrainedType;
|
||||
this._expressionTypeConstraints.forEach(constraint => {
|
||||
constrainedType = constraint.applyToType(node, constrainedType);
|
||||
});
|
||||
|
||||
// Apply constraints from the current scope and its outer scopes.
|
||||
return this._applyScopeTypeConstraintRecursive(node, constrainedType);
|
||||
}
|
||||
|
||||
private _applyScopeTypeConstraintRecursive(node: ExpressionNode, type: Type,
|
||||
scope = this._currentScope): Type {
|
||||
// If we've hit a permanent scope, don't recurse any further.
|
||||
if (scope.getType() !== ScopeType.Temporary) {
|
||||
return type;
|
||||
}
|
||||
|
||||
// Recursively allow the parent scopes to apply their type constraints.
|
||||
const parentScope = scope.getParent();
|
||||
if (parentScope) {
|
||||
type = this._applyScopeTypeConstraintRecursive(node, type, parentScope);
|
||||
}
|
||||
|
||||
// Apply the constraints within the current scope.
|
||||
scope.getTypeConstraints().forEach(constraint => {
|
||||
type = constraint.applyToType(node, type);
|
||||
});
|
||||
|
||||
return type;
|
||||
(node: ExpressionNode) => this._getTypeOfExpression(node));
|
||||
}
|
||||
|
||||
private _enterTemporaryScope(callback: () => void, isConditional ? : boolean,
|
||||
|
@ -523,7 +523,7 @@ export class FunctionType extends Type {
|
||||
// specialized will have type arguments that correspond to
|
||||
// some or all of the type parameters. Unspecified type
|
||||
// parameters are undefined.
|
||||
private _typeArguments?: (Type | undefined)[];
|
||||
// private _typeArguments?: (Type | undefined)[];
|
||||
|
||||
constructor(flags: FunctionTypeFlags) {
|
||||
super();
|
||||
|
Loading…
Reference in New Issue
Block a user