Skip to content

Commit

Permalink
diagnostics!
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexWaygood committed Oct 18, 2024
1 parent 95a6163 commit 340ebac
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ class A:
def __init__(self):
self.__add__ = add_impl

# error: [operator]
# error: [unsupported-operator] "Operator `+` is unsupported between objects of type `A` and `A`"
# revealed: Unknown
reveal_type(A() + A())
```
Expand All @@ -275,7 +275,7 @@ reveal_type(A() + A())
```py
class A: pass

# error: [operator]
# error: [unsupported-operator]
# revealed: Unknown
reveal_type(A() + A())
```
Expand All @@ -293,11 +293,11 @@ class B:

class C: pass

# error: [operator]
# error: [unsupported-operator]
# revealed: Unknown
reveal_type(C() + A())

# error: [operator]
# error: [unsupported-operator]
# revealed: Unknown
reveal_type(B() + C())
```
Expand Down
97 changes: 58 additions & 39 deletions crates/red_knot_python_semantic/src/types/infer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2498,79 +2498,97 @@ impl<'db> TypeInferenceBuilder<'db> {
}

self.infer_binary_expression_type(left_ty, right_ty, *op)
.unwrap_or_else(|| {
self.add_diagnostic(
binary.into(),
"unsupported-operator",
format_args!(
"Operator `{op}` is unsupported between objects of type `{}` and `{}`",
left_ty.display(self.db),
right_ty.display(self.db)
),
);
Type::Unknown
})
}

fn infer_binary_expression_type(
&mut self,
left_ty: Type<'db>,
right_ty: Type<'db>,
op: ast::Operator,
) -> Type<'db> {
) -> Option<Type<'db>> {
match (left_ty, right_ty, op) {
// When interacting with Todo, Any and Unknown should propagate (as if we fix this
// `Todo` in the future, the result would then become Any or Unknown, respectively.)
(Type::Any, _, _) | (_, Type::Any, _) => Type::Any,
(Type::Unknown, _, _) | (_, Type::Unknown, _) => Type::Unknown,
(Type::Any, _, _) | (_, Type::Any, _) => Some(Type::Any),
(Type::Unknown, _, _) | (_, Type::Unknown, _) => Some(Type::Unknown),

(Type::IntLiteral(n), Type::IntLiteral(m), ast::Operator::Add) => n
.checked_add(m)
.map(Type::IntLiteral)
.unwrap_or_else(|| KnownClass::Int.to_instance(self.db)),
(Type::IntLiteral(n), Type::IntLiteral(m), ast::Operator::Add) => Some(
n.checked_add(m)
.map(Type::IntLiteral)
.unwrap_or_else(|| KnownClass::Int.to_instance(self.db)),
),

(Type::IntLiteral(n), Type::IntLiteral(m), ast::Operator::Sub) => n
.checked_sub(m)
.map(Type::IntLiteral)
.unwrap_or_else(|| KnownClass::Int.to_instance(self.db)),
(Type::IntLiteral(n), Type::IntLiteral(m), ast::Operator::Sub) => Some(
n.checked_sub(m)
.map(Type::IntLiteral)
.unwrap_or_else(|| KnownClass::Int.to_instance(self.db)),
),

(Type::IntLiteral(n), Type::IntLiteral(m), ast::Operator::Mult) => n
.checked_mul(m)
.map(Type::IntLiteral)
.unwrap_or_else(|| KnownClass::Int.to_instance(self.db)),
(Type::IntLiteral(n), Type::IntLiteral(m), ast::Operator::Mult) => Some(
n.checked_mul(m)
.map(Type::IntLiteral)
.unwrap_or_else(|| KnownClass::Int.to_instance(self.db)),
),

(Type::IntLiteral(_), Type::IntLiteral(_), ast::Operator::Div) => {
KnownClass::Float.to_instance(self.db)
Some(KnownClass::Float.to_instance(self.db))
}

(Type::IntLiteral(n), Type::IntLiteral(m), ast::Operator::FloorDiv) => n
.checked_div(m)
.map(Type::IntLiteral)
.unwrap_or_else(|| KnownClass::Int.to_instance(self.db)),
(Type::IntLiteral(n), Type::IntLiteral(m), ast::Operator::FloorDiv) => Some(
n.checked_div(m)
.map(Type::IntLiteral)
.unwrap_or_else(|| KnownClass::Int.to_instance(self.db)),
),

(Type::IntLiteral(n), Type::IntLiteral(m), ast::Operator::Mod) => n
.checked_rem(m)
.map(Type::IntLiteral)
.unwrap_or_else(|| KnownClass::Int.to_instance(self.db)),
(Type::IntLiteral(n), Type::IntLiteral(m), ast::Operator::Mod) => Some(
n.checked_rem(m)
.map(Type::IntLiteral)
.unwrap_or_else(|| KnownClass::Int.to_instance(self.db)),
),

(Type::BytesLiteral(lhs), Type::BytesLiteral(rhs), ast::Operator::Add) => {
Type::BytesLiteral(BytesLiteralType::new(
Some(Type::BytesLiteral(BytesLiteralType::new(
self.db,
[lhs.value(self.db).as_ref(), rhs.value(self.db).as_ref()]
.concat()
.into_boxed_slice(),
))
)))
}

(Type::StringLiteral(lhs), Type::StringLiteral(rhs), ast::Operator::Add) => {
let lhs_value = lhs.value(self.db).to_string();
let rhs_value = rhs.value(self.db).as_ref();
if lhs_value.len() + rhs_value.len() <= Self::MAX_STRING_LITERAL_SIZE {
let ty = if lhs_value.len() + rhs_value.len() <= Self::MAX_STRING_LITERAL_SIZE {
Type::StringLiteral(StringLiteralType::new(self.db, {
(lhs_value + rhs_value).into_boxed_str()
}))
} else {
Type::LiteralString
}
};
Some(ty)
}

(
Type::StringLiteral(_) | Type::LiteralString,
Type::StringLiteral(_) | Type::LiteralString,
ast::Operator::Add,
) => Type::LiteralString,
) => Some(Type::LiteralString),

(Type::StringLiteral(s), Type::IntLiteral(n), ast::Operator::Mult)
| (Type::IntLiteral(n), Type::StringLiteral(s), ast::Operator::Mult) => {
if n < 1 {
let ty = if n < 1 {
Type::StringLiteral(StringLiteralType::new(self.db, ""))
} else if let Ok(n) = usize::try_from(n) {
if n.checked_mul(s.value(self.db).len())
Expand All @@ -2586,16 +2604,18 @@ impl<'db> TypeInferenceBuilder<'db> {
}
} else {
Type::LiteralString
}
};
Some(ty)
}

(Type::LiteralString, Type::IntLiteral(n), ast::Operator::Mult)
| (Type::IntLiteral(n), Type::LiteralString, ast::Operator::Mult) => {
if n < 1 {
let ty = if n < 1 {
Type::StringLiteral(StringLiteralType::new(self.db, ""))
} else {
Type::LiteralString
}
};
Some(ty)
}

(Type::Instance(_), Type::IntLiteral(_), op) => {
Expand Down Expand Up @@ -2644,9 +2664,10 @@ impl<'db> TypeInferenceBuilder<'db> {

(Type::Instance(left_class), Type::Instance(right_class), op) => {
if left_class != right_class && right_ty.is_assignable_to(self.db, left_ty) {
let rhs_reflected = right_class.class_member(self.db, op.reflected_dunder());
let reflected_dunder = op.reflected_dunder();
let rhs_reflected = right_class.class_member(self.db, reflected_dunder);
if !rhs_reflected.is_unbound()
&& rhs_reflected != left_class.class_member(self.db, op.reflected_dunder())
&& rhs_reflected != left_class.class_member(self.db, reflected_dunder)
{
return rhs_reflected
.call(self.db, &[right_ty, left_ty])
Expand All @@ -2656,8 +2677,7 @@ impl<'db> TypeInferenceBuilder<'db> {
.class_member(self.db, op.dunder())
.call(self.db, &[left_ty, right_ty])
.return_ty(self.db)
})
.unwrap_or(Type::Unknown);
});
}
}
left_class
Expand All @@ -2670,10 +2690,9 @@ impl<'db> TypeInferenceBuilder<'db> {
.call(self.db, &[right_ty, left_ty])
.return_ty(self.db)
})
.unwrap_or(Type::Unknown)
}

_ => Type::Todo, // TODO
_ => Some(Type::Todo), // TODO
}
}

Expand Down

0 comments on commit 340ebac

Please sign in to comment.