in src/Analysis/Ast/Impl/Analyzer/Evaluation/ExpressionEval.Operators.cs [61:200]
private IMember GetValueFromBinaryOp(Expression expr, LookupOptions lookupOptions) {
if (expr is AndExpression a) {
GetValueFromExpression(a.Left, lookupOptions);
GetValueFromExpression(a.Right, lookupOptions);
return Interpreter.GetBuiltinType(BuiltinTypeId.Bool);
}
if (expr is OrExpression orexp) {
// Consider 'self.__params = types.MappingProxyType(params or {})'
var leftSide = GetValueFromExpression(orexp.Left, lookupOptions);
// Do evaluate both sides in order to correctly track references
var rightSide = GetValueFromExpression(orexp.Right, lookupOptions);
if (!leftSide.IsUnknown()) {
return leftSide;
}
return rightSide.IsUnknown() ? Interpreter.GetBuiltinType(BuiltinTypeId.Bool) : rightSide;
}
if (!(expr is BinaryExpression binop) || binop.Left == null) {
return null;
}
var op = binop.Operator;
var left = GetValueFromExpression(binop.Left, lookupOptions) ?? UnknownType;
var right = GetValueFromExpression(binop.Right, lookupOptions) ?? UnknownType;
if (left.IsUnknown() && right.IsUnknown()) {
// Fast path for when nothing below will give any results.
if (op.IsComparison()) {
return Interpreter.GetBuiltinType(BuiltinTypeId.Bool);
}
return UnknownType;
}
var leftValue = left is IVariable v1 ? v1.Value : left;
var rightValue = right is IVariable v2 ? v2.Value : right;
var isInstance = leftValue is IPythonInstance || rightValue is IPythonInstance;
var leftType = left.GetPythonType();
var rightType = right.GetPythonType();
var leftTypeId = leftType.TypeId;
var rightTypeId = rightType.TypeId;
if (op == PythonOperator.Add
&& leftTypeId == rightTypeId
&& left is IPythonCollection lc && right is IPythonCollection rc) {
switch (leftTypeId) {
case BuiltinTypeId.List:
return PythonCollectionType.CreateConcatenatedList(Module, lc, rc);
case BuiltinTypeId.Tuple:
return PythonCollectionType.CreateConcatenatedTuple(Module, lc, rc);
}
}
// Mod-style string formatting; don't bother looking at the right side.
if (op == PythonOperator.Mod && (leftTypeId == BuiltinTypeId.Str || leftTypeId == BuiltinTypeId.Unicode)) {
return Interpreter.GetBuiltinType(leftTypeId);
}
var leftIsSupported = IsSupportedBinopBuiltin(leftTypeId);
var rightIsSupported = IsSupportedBinopBuiltin(rightTypeId);
if (leftIsSupported && rightIsSupported) {
if (TryGetValueFromBuiltinBinaryOp(op, leftTypeId, rightTypeId, Interpreter.LanguageVersion.Is3x(), out var member)) {
return isInstance ? new PythonInstance(member.GetPythonType()) : member;
}
}
if (leftIsSupported) {
IMember ret;
if (op.IsComparison()) {
// If the op is a comparison, and the thing on the left is the builtin,
// flip the operation and call it instead.
ret = CallOperator(op.InvertComparison(), right, rightType, left, leftType, expr, tryRight: false);
} else {
ret = CallOperator(op, left, leftType, right, rightType, expr, tryLeft: false);
}
if (ret.IsUnknown()) {
ret = op.IsComparison() ? Interpreter.GetBuiltinType(BuiltinTypeId.Bool) : left;
}
return isInstance ? new PythonInstance(ret.GetPythonType()) : ret;
}
if (rightIsSupported) {
// Try calling the function on the left side, otherwise just return right.
var ret = CallOperator(op, left, leftType, right, rightType, expr, tryRight: false);
if (!ret.IsUnknown()) {
return ret;
}
return op.IsComparison() ? Interpreter.GetBuiltinType(BuiltinTypeId.Bool) : right;
}
var callRet = CallOperator(op, left, leftType, right, rightType, expr);
if (!callRet.IsUnknown()) {
return callRet;
}
if (op.IsComparison()) {
callRet = CallOperator(op.InvertComparison(), right, rightType, left, leftType, expr);
if (!callRet.IsUnknown()) {
return callRet;
}
}
// TODO: Specific parsing
// TODO: warn about incompatible types like 'str' + 1
switch (op) {
case PythonOperator.Equal:
case PythonOperator.GreaterThan:
case PythonOperator.GreaterThanOrEqual:
case PythonOperator.In:
case PythonOperator.Is:
case PythonOperator.IsNot:
case PythonOperator.LessThan:
case PythonOperator.LessThanOrEqual:
case PythonOperator.Not:
case PythonOperator.NotEqual:
case PythonOperator.NotIn:
// Assume all of these return True/False
return Interpreter.GetBuiltinType(BuiltinTypeId.Bool);
case PythonOperator.Divide:
case PythonOperator.TrueDivide:
if (Interpreter.LanguageVersion.Is3x()) {
return Interpreter.GetBuiltinType(BuiltinTypeId.Float);
}
break;
}
return left.IsUnknown() ? right : left;
}