зеркало из https://github.com/microsoft/pyright.git
Added type checking for unary operations (not, -, +, ~).
This commit is contained in:
Родитель
705cc767de
Коммит
de035a2f1b
|
@ -207,9 +207,7 @@ export class ExpressionEvaluator {
|
|||
} else if (node instanceof EllipsisNode) {
|
||||
typeResult = { type: AnyType.create(), node };
|
||||
} else if (node instanceof UnaryExpressionNode) {
|
||||
// TODO - need to implement
|
||||
this._getTypeFromExpression(node.expression, flags);
|
||||
typeResult = { type: UnknownType.create(), node };
|
||||
typeResult = this._getTypeFromUnaryExpression(node, flags);
|
||||
} else if (node instanceof BinaryExpressionNode) {
|
||||
typeResult = this._getTypeFromBinaryExpression(node, flags);
|
||||
} else if (node instanceof ListNode) {
|
||||
|
@ -237,7 +235,7 @@ export class ExpressionEvaluator {
|
|||
elseType = this._getTypeFromExpression(node.elseExpression, flags);
|
||||
});
|
||||
|
||||
let type = TypeUtils.combineTypes(ifType!.type, elseType!.type);
|
||||
let type = TypeUtils.combineTypes([ifType!.type, elseType!.type]);
|
||||
typeResult = { type, node };
|
||||
} else if (node instanceof ListComprehensionNode) {
|
||||
// TODO - need to implement
|
||||
|
@ -389,7 +387,7 @@ export class ExpressionEvaluator {
|
|||
});
|
||||
|
||||
if (returnTypes.length > 0) {
|
||||
type = TypeUtils.combineTypesArray(returnTypes);
|
||||
type = TypeUtils.combineTypes(returnTypes);
|
||||
}
|
||||
} else if (baseType instanceof PropertyType) {
|
||||
// TODO - need to come up with new strategy for properties
|
||||
|
@ -736,7 +734,7 @@ export class ExpressionEvaluator {
|
|||
});
|
||||
|
||||
if (returnTypes.length > 0) {
|
||||
type = TypeUtils.combineTypesArray(returnTypes);
|
||||
type = TypeUtils.combineTypes(returnTypes);
|
||||
}
|
||||
} else if (callType.isAny()) {
|
||||
type = UnknownType.create();
|
||||
|
@ -874,7 +872,7 @@ export class ExpressionEvaluator {
|
|||
}
|
||||
|
||||
if (returnTypes.length > 0) {
|
||||
returnType = TypeUtils.combineTypesArray(returnTypes);
|
||||
returnType = TypeUtils.combineTypes(returnTypes);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1180,41 +1178,110 @@ export class ExpressionEvaluator {
|
|||
classFields.set('__class__', new Symbol(classType, DefaultTypeSourceId));
|
||||
const instanceFields = classType.getInstanceFields();
|
||||
|
||||
let tupleType = new TupleType(ScopeUtils.getBuiltInType(this._scope, 'tuple') as ClassType);
|
||||
let constructorType = new FunctionType(FunctionTypeFlags.ClassMethod);
|
||||
constructorType.setDeclaredReturnType(new ObjectType(classType));
|
||||
constructorType.addParameter({
|
||||
category: ParameterCategory.Simple,
|
||||
name: 'cls',
|
||||
type: classType
|
||||
});
|
||||
let builtInTupleType = ScopeUtils.getBuiltInType(this._scope, 'tuple');
|
||||
if (builtInTupleType instanceof ClassType) {
|
||||
let tupleType = new TupleType(builtInTupleType);
|
||||
let constructorType = new FunctionType(FunctionTypeFlags.ClassMethod);
|
||||
constructorType.setDeclaredReturnType(new ObjectType(classType));
|
||||
constructorType.addParameter({
|
||||
category: ParameterCategory.Simple,
|
||||
name: 'cls',
|
||||
type: classType
|
||||
});
|
||||
|
||||
let initType = new FunctionType(FunctionTypeFlags.InstanceMethod);
|
||||
const selfParameter: FunctionParameter = {
|
||||
category: ParameterCategory.Simple,
|
||||
name: 'self',
|
||||
type: new ObjectType(classType)
|
||||
};
|
||||
initType.setDeclaredReturnType(NoneType.create());
|
||||
initType.addParameter(selfParameter);
|
||||
let initType = new FunctionType(FunctionTypeFlags.InstanceMethod);
|
||||
const selfParameter: FunctionParameter = {
|
||||
category: ParameterCategory.Simple,
|
||||
name: 'self',
|
||||
type: new ObjectType(classType)
|
||||
};
|
||||
initType.setDeclaredReturnType(NoneType.create());
|
||||
initType.addParameter(selfParameter);
|
||||
|
||||
let addGenericGetAttribute = false;
|
||||
let addGenericGetAttribute = false;
|
||||
|
||||
if (node.arguments.length < 2) {
|
||||
this._addError('Expected named tuple entry list as second parameter',
|
||||
node.leftExpression);
|
||||
addGenericGetAttribute = true;
|
||||
} else {
|
||||
const entriesArg = node.arguments[1];
|
||||
if (entriesArg.argumentCategory !== ArgumentCategory.Simple) {
|
||||
if (node.arguments.length < 2) {
|
||||
this._addError('Expected named tuple entry list as second parameter',
|
||||
node.leftExpression);
|
||||
addGenericGetAttribute = true;
|
||||
} else {
|
||||
if (!includesTypes && entriesArg.valueExpression instanceof StringNode) {
|
||||
let entries = entriesArg.valueExpression.getValue().split(' ');
|
||||
entries.forEach(entryName => {
|
||||
entryName = entryName.trim();
|
||||
if (entryName) {
|
||||
let entryType = UnknownType.create();
|
||||
const entriesArg = node.arguments[1];
|
||||
if (entriesArg.argumentCategory !== ArgumentCategory.Simple) {
|
||||
addGenericGetAttribute = true;
|
||||
} else {
|
||||
if (!includesTypes && entriesArg.valueExpression instanceof StringNode) {
|
||||
let entries = entriesArg.valueExpression.getValue().split(' ');
|
||||
entries.forEach(entryName => {
|
||||
entryName = entryName.trim();
|
||||
if (entryName) {
|
||||
let entryType = UnknownType.create();
|
||||
tupleType.addEntryType(entryType);
|
||||
const paramInfo: FunctionParameter = {
|
||||
category: ParameterCategory.Simple,
|
||||
name: entryName,
|
||||
type: entryType
|
||||
};
|
||||
|
||||
constructorType.addParameter(paramInfo);
|
||||
initType.addParameter(paramInfo);
|
||||
|
||||
instanceFields.set(entryName, new Symbol(entryType, DefaultTypeSourceId));
|
||||
}
|
||||
});
|
||||
} else if (entriesArg.valueExpression instanceof ListNode) {
|
||||
const entryList = entriesArg.valueExpression;
|
||||
let entryMap: { [name: string]: string } = {};
|
||||
|
||||
entryList.entries.forEach((entry, index) => {
|
||||
let entryType: Type | undefined;
|
||||
let entryNameNode: ExpressionNode | undefined;
|
||||
let entryName = '';
|
||||
|
||||
if (includesTypes) {
|
||||
// Handle the variant that includes name/type tuples.
|
||||
if (entry instanceof TupleExpressionNode && entry.expressions.length === 2) {
|
||||
entryNameNode = entry.expressions[0];
|
||||
let entryTypeInfo = this._getTypeFromExpression(entry.expressions[1],
|
||||
EvaluatorFlags.ConvertClassToObject);
|
||||
if (entryTypeInfo) {
|
||||
entryType = entryTypeInfo.type;
|
||||
}
|
||||
} else {
|
||||
this._addError(
|
||||
'Expected two-entry tuple specifying entry name and type', entry);
|
||||
}
|
||||
} else {
|
||||
entryNameNode = entry;
|
||||
entryType = UnknownType.create();
|
||||
}
|
||||
|
||||
if (entryNameNode instanceof StringNode) {
|
||||
entryName = entryNameNode.getValue();
|
||||
if (!entryName) {
|
||||
this._addError(
|
||||
'Names within a named tuple cannot be empty', entryNameNode);
|
||||
}
|
||||
} else {
|
||||
this._addError(
|
||||
'Expected string literal for entry name', entryNameNode || entry);
|
||||
}
|
||||
|
||||
if (!entryName) {
|
||||
entryName = `_${ index.toString() }`;
|
||||
}
|
||||
|
||||
if (entryMap[entryName]) {
|
||||
this._addError(
|
||||
'Names within a named tuple must be unique', entryNameNode || entry);
|
||||
}
|
||||
|
||||
// Record names in a map to detect duplicates.
|
||||
entryMap[entryName] = entryName;
|
||||
|
||||
if (!entryType) {
|
||||
entryType = UnknownType.create();
|
||||
}
|
||||
|
||||
tupleType.addEntryType(entryType);
|
||||
const paramInfo: FunctionParameter = {
|
||||
category: ParameterCategory.Simple,
|
||||
|
@ -1226,111 +1293,45 @@ export class ExpressionEvaluator {
|
|||
initType.addParameter(paramInfo);
|
||||
|
||||
instanceFields.set(entryName, new Symbol(entryType, DefaultTypeSourceId));
|
||||
}
|
||||
});
|
||||
} else if (entriesArg.valueExpression instanceof ListNode) {
|
||||
const entryList = entriesArg.valueExpression;
|
||||
let entryMap: { [name: string]: string } = {};
|
||||
|
||||
entryList.entries.forEach((entry, index) => {
|
||||
let entryType: Type | undefined;
|
||||
let entryNameNode: ExpressionNode | undefined;
|
||||
let entryName = '';
|
||||
|
||||
if (includesTypes) {
|
||||
// Handle the variant that includes name/type tuples.
|
||||
if (entry instanceof TupleExpressionNode && entry.expressions.length === 2) {
|
||||
entryNameNode = entry.expressions[0];
|
||||
let entryTypeInfo = this._getTypeFromExpression(entry.expressions[1],
|
||||
EvaluatorFlags.ConvertClassToObject);
|
||||
if (entryTypeInfo) {
|
||||
entryType = entryTypeInfo.type;
|
||||
}
|
||||
} else {
|
||||
this._addError(
|
||||
'Expected two-entry tuple specifying entry name and type', entry);
|
||||
}
|
||||
} else {
|
||||
entryNameNode = entry;
|
||||
entryType = UnknownType.create();
|
||||
}
|
||||
|
||||
if (entryNameNode instanceof StringNode) {
|
||||
entryName = entryNameNode.getValue();
|
||||
if (!entryName) {
|
||||
this._addError(
|
||||
'Names within a named tuple cannot be empty', entryNameNode);
|
||||
}
|
||||
} else {
|
||||
this._addError(
|
||||
'Expected string literal for entry name', entryNameNode || entry);
|
||||
}
|
||||
|
||||
if (!entryName) {
|
||||
entryName = `_${ index.toString() }`;
|
||||
}
|
||||
|
||||
if (entryMap[entryName]) {
|
||||
this._addError(
|
||||
'Names within a named tuple must be unique', entryNameNode || entry);
|
||||
}
|
||||
|
||||
// Record names in a map to detect duplicates.
|
||||
entryMap[entryName] = entryName;
|
||||
|
||||
if (!entryType) {
|
||||
entryType = UnknownType.create();
|
||||
}
|
||||
|
||||
tupleType.addEntryType(entryType);
|
||||
const paramInfo: FunctionParameter = {
|
||||
category: ParameterCategory.Simple,
|
||||
name: entryName,
|
||||
type: entryType
|
||||
};
|
||||
|
||||
constructorType.addParameter(paramInfo);
|
||||
initType.addParameter(paramInfo);
|
||||
|
||||
instanceFields.set(entryName, new Symbol(entryType, DefaultTypeSourceId));
|
||||
});
|
||||
} else {
|
||||
// A dynamic expression was used, so we can't evaluate
|
||||
// the named tuple statically.
|
||||
addGenericGetAttribute = true;
|
||||
});
|
||||
} else {
|
||||
// A dynamic expression was used, so we can't evaluate
|
||||
// the named tuple statically.
|
||||
addGenericGetAttribute = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (addGenericGetAttribute) {
|
||||
TypeUtils.addDefaultFunctionParameters(constructorType);
|
||||
TypeUtils.addDefaultFunctionParameters(initType);
|
||||
}
|
||||
if (addGenericGetAttribute) {
|
||||
TypeUtils.addDefaultFunctionParameters(constructorType);
|
||||
TypeUtils.addDefaultFunctionParameters(initType);
|
||||
}
|
||||
|
||||
classFields.set('__new__', new Symbol(constructorType, DefaultTypeSourceId));
|
||||
classFields.set('__init__', new Symbol(initType, DefaultTypeSourceId));
|
||||
classFields.set('__new__', new Symbol(constructorType, DefaultTypeSourceId));
|
||||
classFields.set('__init__', new Symbol(initType, DefaultTypeSourceId));
|
||||
|
||||
let keysItemType = new FunctionType(FunctionTypeFlags.None);
|
||||
keysItemType.setDeclaredReturnType(ScopeUtils.getBuiltInObject(this._scope, 'list',
|
||||
[ScopeUtils.getBuiltInObject(this._scope, 'str')]));
|
||||
classFields.set('keys', new Symbol(keysItemType, DefaultTypeSourceId));
|
||||
classFields.set('items', new Symbol(keysItemType, DefaultTypeSourceId));
|
||||
let keysItemType = new FunctionType(FunctionTypeFlags.None);
|
||||
keysItemType.setDeclaredReturnType(ScopeUtils.getBuiltInObject(this._scope, 'list',
|
||||
[ScopeUtils.getBuiltInObject(this._scope, 'str')]));
|
||||
classFields.set('keys', new Symbol(keysItemType, DefaultTypeSourceId));
|
||||
classFields.set('items', new Symbol(keysItemType, DefaultTypeSourceId));
|
||||
|
||||
let lenType = new FunctionType(FunctionTypeFlags.InstanceMethod);
|
||||
lenType.setDeclaredReturnType(ScopeUtils.getBuiltInObject(this._scope, 'int'));
|
||||
lenType.addParameter(selfParameter);
|
||||
classFields.set('__len__', new Symbol(lenType, DefaultTypeSourceId));
|
||||
let lenType = new FunctionType(FunctionTypeFlags.InstanceMethod);
|
||||
lenType.setDeclaredReturnType(ScopeUtils.getBuiltInObject(this._scope, 'int'));
|
||||
lenType.addParameter(selfParameter);
|
||||
classFields.set('__len__', new Symbol(lenType, DefaultTypeSourceId));
|
||||
|
||||
if (addGenericGetAttribute) {
|
||||
let getAttribType = new FunctionType(FunctionTypeFlags.InstanceMethod);
|
||||
getAttribType.setDeclaredReturnType(AnyType.create());
|
||||
getAttribType.addParameter(selfParameter);
|
||||
getAttribType.addParameter({
|
||||
category: ParameterCategory.Simple,
|
||||
name: 'name',
|
||||
type: ScopeUtils.getBuiltInObject(this._scope, 'str')
|
||||
});
|
||||
classFields.set('__getattribute__', new Symbol(getAttribType, DefaultTypeSourceId));
|
||||
if (addGenericGetAttribute) {
|
||||
let getAttribType = new FunctionType(FunctionTypeFlags.InstanceMethod);
|
||||
getAttribType.setDeclaredReturnType(AnyType.create());
|
||||
getAttribType.addParameter(selfParameter);
|
||||
getAttribType.addParameter({
|
||||
category: ParameterCategory.Simple,
|
||||
name: 'name',
|
||||
type: ScopeUtils.getBuiltInObject(this._scope, 'str')
|
||||
});
|
||||
classFields.set('__getattribute__', new Symbol(getAttribType, DefaultTypeSourceId));
|
||||
}
|
||||
}
|
||||
|
||||
return classType;
|
||||
|
@ -1356,9 +1357,56 @@ export class ExpressionEvaluator {
|
|||
return { type, node };
|
||||
}
|
||||
|
||||
private _getTypeFromUnaryExpression(node: UnaryExpressionNode, flags: EvaluatorFlags): TypeResult | undefined {
|
||||
let exprType = this._getTypeFromExpression(node.expression, flags).type;
|
||||
|
||||
let type: Type;
|
||||
if (exprType.isAny()) {
|
||||
type = exprType;
|
||||
} else if (exprType instanceof ObjectType) {
|
||||
if (node.operator === OperatorType.Not) {
|
||||
// The "not" operator always returns a boolean.
|
||||
type = ScopeUtils.getBuiltInObject(this._scope, 'bool');
|
||||
} else if (node.operator === OperatorType.BitwiseInvert) {
|
||||
const intObjType = ScopeUtils.getBuiltInObject(this._scope, 'int');
|
||||
|
||||
if (intObjType.isSame(exprType)) {
|
||||
type = intObjType;
|
||||
} else {
|
||||
// TODO - need to handle generic case.
|
||||
type = UnknownType.create();
|
||||
}
|
||||
} else if (node.operator === OperatorType.Add || node.operator === OperatorType.Subtract) {
|
||||
const intType = ScopeUtils.getBuiltInObject(this._scope, 'int');
|
||||
const floatType = ScopeUtils.getBuiltInObject(this._scope, 'float');
|
||||
const complexType = ScopeUtils.getBuiltInObject(this._scope, 'complex');
|
||||
|
||||
if (intType.isSame(exprType)) {
|
||||
type = intType;
|
||||
} else if (floatType.isSame(exprType)) {
|
||||
type = floatType;
|
||||
} else if (complexType.isSame(exprType)) {
|
||||
type = complexType;
|
||||
} else {
|
||||
// TODO - need to handle generic case.
|
||||
type = UnknownType.create();
|
||||
}
|
||||
} else {
|
||||
// We should never get here.
|
||||
this._addError('Unexpected unary operator', node);
|
||||
type = UnknownType.create();
|
||||
}
|
||||
} else {
|
||||
// TODO - need to handle additional types.
|
||||
type = UnknownType.create();
|
||||
}
|
||||
|
||||
return { type, node };
|
||||
}
|
||||
|
||||
private _getTypeFromBinaryExpression(node: BinaryExpressionNode, flags: EvaluatorFlags): TypeResult | undefined {
|
||||
let leftType = this._getTypeFromExpression(node.leftExpression, flags);
|
||||
let rightType = this._getTypeFromExpression(node.rightExpression, flags);
|
||||
let leftType = this._getTypeFromExpression(node.leftExpression, flags).type;
|
||||
let rightType = this._getTypeFromExpression(node.rightExpression, flags).type;
|
||||
|
||||
// Is this an AND operator? If so, we can assume that the
|
||||
// rightExpression won't be evaluated at runtime unless the
|
||||
|
@ -1411,9 +1459,9 @@ export class ExpressionEvaluator {
|
|||
let type: Type;
|
||||
|
||||
if (arithmeticOperatorMap[node.operator]) {
|
||||
if (leftType.type.isAny() || rightType.type.isAny()) {
|
||||
if (leftType.isAny() || rightType.isAny()) {
|
||||
type = UnknownType.create();
|
||||
} else if (leftType.type instanceof ObjectType && rightType.type instanceof ObjectType) {
|
||||
} else if (leftType instanceof ObjectType && rightType instanceof ObjectType) {
|
||||
const builtInClassTypes = this._getBuiltInClassTypes(['int', 'float', 'complex']);
|
||||
const getTypeMatch = (classType: ClassType): boolean[] => {
|
||||
let foundMatch = false;
|
||||
|
@ -1424,8 +1472,8 @@ export class ExpressionEvaluator {
|
|||
return foundMatch;
|
||||
});
|
||||
};
|
||||
const leftClassMatches = getTypeMatch(leftType.type.getClassType());
|
||||
const rightClassMatches = getTypeMatch(rightType.type.getClassType());
|
||||
const leftClassMatches = getTypeMatch(leftType.getClassType());
|
||||
const rightClassMatches = getTypeMatch(rightType.getClassType());
|
||||
|
||||
if (leftClassMatches[0] && rightClassMatches[0]) {
|
||||
// If they're both int types, the result is an int.
|
||||
|
@ -1449,15 +1497,17 @@ export class ExpressionEvaluator {
|
|||
type = UnknownType.create();
|
||||
}
|
||||
} else if (bitwiseOperatorMap[node.operator]) {
|
||||
if (leftType.type.isAny() || rightType.type.isAny()) {
|
||||
if (leftType.isAny() || rightType.isAny()) {
|
||||
type = UnknownType.create();
|
||||
} else if (leftType.type instanceof ObjectType && rightType.type instanceof ObjectType) {
|
||||
const intType = ScopeUtils.getBuiltInType(this._scope, 'int') as ClassType;
|
||||
const leftIsInt = intType && leftType.type.getClassType().isSameGenericClass(intType);
|
||||
const rightIsInt = intType && rightType.type.getClassType().isSameGenericClass(intType);
|
||||
} else if (leftType instanceof ObjectType && rightType instanceof ObjectType) {
|
||||
const intType = ScopeUtils.getBuiltInType(this._scope, 'int');
|
||||
const leftIsInt = intType instanceof ClassType &&
|
||||
leftType.getClassType().isSameGenericClass(intType);
|
||||
const rightIsInt = intType instanceof ClassType &&
|
||||
rightType.getClassType().isSameGenericClass(intType);
|
||||
|
||||
if (leftIsInt && rightIsInt) {
|
||||
type = new ObjectType(intType);
|
||||
type = new ObjectType(intType as ClassType);
|
||||
} else {
|
||||
// In all other cases, we need to look at the magic methods
|
||||
// on the two types.
|
||||
|
@ -1469,21 +1519,19 @@ export class ExpressionEvaluator {
|
|||
type = UnknownType.create();
|
||||
}
|
||||
} else if (comparisonOperatorMap[node.operator]) {
|
||||
const boolType = ScopeUtils.getBuiltInType(this._scope, 'bool') as ClassType;
|
||||
type = boolType ? new ObjectType(boolType) : UnknownType.create();
|
||||
type = ScopeUtils.getBuiltInObject(this._scope, 'bool');
|
||||
} else if (booleanOperatorMap[node.operator]) {
|
||||
if (node.operator === OperatorType.And) {
|
||||
// If the operator is an AND or OR, we need to combine the two types.
|
||||
type = TypeUtils.combineTypesArray([
|
||||
TypeUtils.removeTruthinessFromType(leftType.type), rightType.type]);
|
||||
type = TypeUtils.combineTypes([
|
||||
TypeUtils.removeTruthinessFromType(leftType), rightType]);
|
||||
} else if (node.operator === OperatorType.Or) {
|
||||
type = TypeUtils.combineTypesArray([
|
||||
TypeUtils.removeFalsinessFromType(leftType.type), rightType.type]);
|
||||
type = TypeUtils.combineTypes([
|
||||
TypeUtils.removeFalsinessFromType(leftType), rightType]);
|
||||
} else {
|
||||
// The other boolean operators always return a bool value.
|
||||
// TODO - validate inputs for "is", "is not", "in" and "not in" operators.
|
||||
const boolType = ScopeUtils.getBuiltInType(this._scope, 'bool') as ClassType;
|
||||
type = boolType ? new ObjectType(boolType) : UnknownType.create();
|
||||
type = ScopeUtils.getBuiltInObject(this._scope, 'bool');
|
||||
}
|
||||
} else {
|
||||
// We should never get here.
|
||||
|
@ -1496,7 +1544,8 @@ export class ExpressionEvaluator {
|
|||
|
||||
private _getBuiltInClassTypes(names: string[]): (ClassType | undefined)[] {
|
||||
return names.map(name => {
|
||||
return ScopeUtils.getBuiltInType(this._scope, name) as ClassType;
|
||||
let classType = ScopeUtils.getBuiltInType(this._scope, name);
|
||||
return classType instanceof ClassType ? classType : undefined;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1606,7 +1655,7 @@ export class ExpressionEvaluator {
|
|||
return UnknownType.create();
|
||||
}
|
||||
|
||||
return TypeUtils.combineTypes(typeArgs[0].type, NoneType.create());
|
||||
return TypeUtils.combineTypes([typeArgs[0].type, NoneType.create()]);
|
||||
}
|
||||
|
||||
private _createClassVarType(typeArgs: TypeResult[], flags: EvaluatorFlags): Type {
|
||||
|
@ -1646,7 +1695,7 @@ export class ExpressionEvaluator {
|
|||
}
|
||||
|
||||
if (types.length > 0) {
|
||||
return TypeUtils.combineTypesArray(types);
|
||||
return TypeUtils.combineTypes(types);
|
||||
}
|
||||
|
||||
return NoneType.create();
|
||||
|
|
|
@ -70,7 +70,7 @@ export class InferredType {
|
|||
if (!newCombinedType) {
|
||||
newCombinedType = source.type;
|
||||
} else {
|
||||
newCombinedType = TypeUtils.combineTypes(newCombinedType, source.type);
|
||||
newCombinedType = TypeUtils.combineTypes([newCombinedType, source.type]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -217,14 +217,14 @@ export class Scope {
|
|||
// is properly merged later.
|
||||
const targetSymbol = targetScope._symbolTable.get(name);
|
||||
if (targetSymbol) {
|
||||
newType = TypeUtils.combineTypes(newType, targetSymbol.currentType);
|
||||
newType = TypeUtils.combineTypes([newType, targetSymbol.currentType]);
|
||||
} else {
|
||||
markTypeConditional = true;
|
||||
}
|
||||
} else {
|
||||
let existingBinding = targetScope.lookUpSymbolRecursive(name);
|
||||
if (existingBinding) {
|
||||
newType = TypeUtils.combineTypes(newType, existingBinding.symbol.currentType);
|
||||
newType = TypeUtils.combineTypes([newType, existingBinding.symbol.currentType]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -286,7 +286,7 @@ export class Scope {
|
|||
|
||||
if (targetSymbol) {
|
||||
this.setSymbolCurrentType(name,
|
||||
TypeUtils.combineTypes(targetSymbol.currentType, sourceSymbol.currentType),
|
||||
TypeUtils.combineTypes([targetSymbol.currentType, sourceSymbol.currentType]),
|
||||
sourceSymbol.inferredType.getPrimarySourceId());
|
||||
|
||||
if (sourceSymbol.declarations) {
|
||||
|
|
|
@ -224,7 +224,8 @@ export class TypeAnalyzer extends ParseTreeWalker {
|
|||
// TODO - tighten this up, perhaps using a config flag
|
||||
if (param.defaultValue instanceof ConstantNode) {
|
||||
if (param.defaultValue.token.keywordType === KeywordType.None) {
|
||||
annotatedType = TypeUtils.combineTypes(annotatedType, NoneType.create());
|
||||
annotatedType = TypeUtils.combineTypes(
|
||||
[annotatedType, NoneType.create()]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1271,7 +1272,8 @@ export class TypeAnalyzer extends ParseTreeWalker {
|
|||
// name, but there's also now an instance variable introduced. Combine the
|
||||
// type of the class variable with that of the new instance variable.
|
||||
if (memberInfo.symbol && !memberInfo.isInstanceMember && isInstanceMember) {
|
||||
typeOfExpr = TypeUtils.combineTypes(typeOfExpr, TypeUtils.getEffectiveTypeOfMember(memberInfo));
|
||||
typeOfExpr = TypeUtils.combineTypes(
|
||||
[typeOfExpr, TypeUtils.getEffectiveTypeOfMember(memberInfo)]);
|
||||
}
|
||||
addNewMemberToLocalClass = true;
|
||||
}
|
||||
|
|
|
@ -121,14 +121,7 @@ export class TruthyTypeConstraint extends TypeConstraint {
|
|||
types = types.filter(t => TypeUtils.canBeFalsy(t));
|
||||
}
|
||||
|
||||
if (types.length === 0) {
|
||||
// Use a "Never" type (which is a special form
|
||||
// of None) to indicate that the condition will
|
||||
// always evaluate to false.
|
||||
return NeverType.create();
|
||||
} else {
|
||||
return TypeUtils.combineTypesArray(types);
|
||||
}
|
||||
return TypeUtils.combineTypes(types);
|
||||
}
|
||||
|
||||
// Return the original type.
|
||||
|
@ -159,14 +152,7 @@ export class IsNoneTypeConstraint extends TypeConstraint {
|
|||
return (t instanceof NoneType) === this.isPositiveTest();
|
||||
});
|
||||
|
||||
if (remainingTypes.length === 0) {
|
||||
// Use a "Never" type (which is a special form
|
||||
// of None) to indicate that the condition will
|
||||
// always evaluate to false.
|
||||
return NeverType.create();
|
||||
}
|
||||
|
||||
return TypeUtils.combineTypesArray(remainingTypes);
|
||||
return TypeUtils.combineTypes(remainingTypes);
|
||||
} else if (type instanceof NoneType) {
|
||||
if (!this.isPositiveTest()) {
|
||||
// Use a "Never" type (which is a special form
|
||||
|
@ -237,14 +223,7 @@ export class IsInstanceTypeConstraint extends TypeConstraint {
|
|||
};
|
||||
|
||||
const finalizeFilteredTypeList = (types: Type[]): Type => {
|
||||
if (types.length === 0) {
|
||||
// Use a "Never" type (which is a special form
|
||||
// of None) to indicate that the condition will
|
||||
// always evaluate to false.
|
||||
return NeverType.create();
|
||||
}
|
||||
|
||||
return TypeUtils.combineTypesArray(types);
|
||||
return TypeUtils.combineTypes(types);
|
||||
};
|
||||
|
||||
if (type instanceof ObjectType) {
|
||||
|
|
|
@ -24,38 +24,21 @@ export interface ClassMember {
|
|||
}
|
||||
|
||||
export class TypeUtils {
|
||||
// Combines two types into a single type. If the types are
|
||||
// Combines multiple types into a single type. If the types are
|
||||
// the same, only one is returned. If they differ, they
|
||||
// are combined into a UnionType.
|
||||
static combineTypes(type1: Type, type2: Type): Type {
|
||||
if (type1.isSame(type2)) {
|
||||
return type1;
|
||||
// are combined into a UnionType. NeverTypes are filtered out.
|
||||
// If no types remain in the end, a NeverType is returned.
|
||||
static combineTypes(types: Type[]): Type {
|
||||
// Filter out any "Never" types.
|
||||
types = types.filter(type => type.category !== TypeCategory.Never);
|
||||
if (types.length === 0) {
|
||||
return NeverType.create();
|
||||
}
|
||||
|
||||
let unionType = new UnionType();
|
||||
|
||||
if (type1 instanceof UnionType) {
|
||||
unionType.addTypes(type1.getTypes());
|
||||
} else {
|
||||
unionType.addTypes([type1]);
|
||||
}
|
||||
|
||||
if (type2 instanceof UnionType) {
|
||||
unionType.addTypes(type2.getTypes());
|
||||
} else {
|
||||
unionType.addTypes([type2]);
|
||||
}
|
||||
|
||||
return unionType;
|
||||
}
|
||||
|
||||
static combineTypesArray(types: Type[]): Type {
|
||||
assert(types.length > 0);
|
||||
|
||||
let resultingType = types[0];
|
||||
types.forEach((t, index) => {
|
||||
if (index > 0) {
|
||||
resultingType = this.combineTypes(resultingType, t);
|
||||
resultingType = this._combineTwoTypes(resultingType, t);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -596,7 +579,7 @@ export class TypeUtils {
|
|||
recursionLevel + 1));
|
||||
});
|
||||
|
||||
return TypeUtils.combineTypesArray(subtypes);
|
||||
return TypeUtils.combineTypes(subtypes);
|
||||
}
|
||||
|
||||
if (type instanceof ObjectType) {
|
||||
|
@ -690,7 +673,7 @@ export class TypeUtils {
|
|||
this.specializeType(constraint, undefined, recursionLevel + 1)
|
||||
);
|
||||
|
||||
return TypeUtils.combineTypesArray(concreteTypes);
|
||||
return TypeUtils.combineTypes(concreteTypes);
|
||||
}
|
||||
|
||||
private static _specializeFunctionType(functionType: FunctionType,
|
||||
|
@ -954,7 +937,7 @@ export class TypeUtils {
|
|||
return AnyType.create();
|
||||
}
|
||||
|
||||
return TypeUtils.combineTypesArray(subtypes);
|
||||
return TypeUtils.combineTypes(subtypes);
|
||||
}
|
||||
|
||||
static cloneTypeVarMap(typeVarMap: TypeVarMap): TypeVarMap {
|
||||
|
@ -1018,7 +1001,7 @@ export class TypeUtils {
|
|||
return NeverType.create();
|
||||
}
|
||||
|
||||
return this.combineTypesArray(remainingTypes);
|
||||
return this.combineTypes(remainingTypes);
|
||||
}
|
||||
|
||||
// Filters a type such that that it is guaranteed not to
|
||||
|
@ -1059,6 +1042,28 @@ export class TypeUtils {
|
|||
return NeverType.create();
|
||||
}
|
||||
|
||||
return this.combineTypesArray(remainingTypes);
|
||||
return this.combineTypes(remainingTypes);
|
||||
}
|
||||
|
||||
private static _combineTwoTypes(type1: Type, type2: Type): Type {
|
||||
if (type1.isSame(type2)) {
|
||||
return type1;
|
||||
}
|
||||
|
||||
let unionType = new UnionType();
|
||||
|
||||
if (type1 instanceof UnionType) {
|
||||
unionType.addTypes(type1.getTypes());
|
||||
} else {
|
||||
unionType.addTypes([type1]);
|
||||
}
|
||||
|
||||
if (type2 instanceof UnionType) {
|
||||
unionType.addTypes(type2.getTypes());
|
||||
} else {
|
||||
unionType.addTypes([type2]);
|
||||
}
|
||||
|
||||
return unionType;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -966,6 +966,7 @@ export class UnionType extends Type {
|
|||
|
||||
addType(type1: Type) {
|
||||
assert(type1.category !== TypeCategory.Union);
|
||||
assert(type1.category !== TypeCategory.Never);
|
||||
|
||||
this._types.push(type1);
|
||||
}
|
||||
|
@ -974,6 +975,7 @@ export class UnionType extends Type {
|
|||
// Add any types that are unique to the union.
|
||||
for (let newType of types) {
|
||||
assert(newType.category !== TypeCategory.Union);
|
||||
assert(newType.category !== TypeCategory.Never);
|
||||
if (!this._types.find(t => t.isSame(newType))) {
|
||||
this._types.push(newType);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
# This sample tests various unary expressions.
|
||||
|
||||
def returnsFloat1() -> float:
|
||||
a = 1
|
||||
b = not a
|
||||
|
||||
# This should generate an error because bool
|
||||
# cannot be assigned to a float.
|
||||
return b
|
||||
|
||||
def returnsInt1() -> int:
|
||||
a = 1
|
||||
b = -a
|
||||
return b
|
||||
|
||||
def returnsInt2() -> int:
|
||||
a = 1
|
||||
b = +a
|
||||
return b
|
||||
|
||||
def returnsInt3() -> int:
|
||||
a = 4
|
||||
b = ~a
|
||||
return b
|
||||
|
||||
|
|
@ -138,3 +138,10 @@ test('Expressions2', () => {
|
|||
assert.equal(analysisResults.length, 1);
|
||||
assert.equal(analysisResults[0].errors.length, 1);
|
||||
});
|
||||
|
||||
test('Expressions3', () => {
|
||||
let analysisResults = TestUtils.typeAnalyzeSampleFiles(['expressions3.py']);
|
||||
|
||||
assert.equal(analysisResults.length, 1);
|
||||
assert.equal(analysisResults[0].errors.length, 1);
|
||||
});
|
||||
|
|
Загрузка…
Ссылка в новой задаче