Type check a binary operation which maps to a method call. Return tuple (result type, inferred operator method type).
(
self,
method: str,
base_type: Type,
arg: Expression,
context: Context,
allow_reverse: bool = False,
)
| 4257 | return result |
| 4258 | |
| 4259 | def check_op( |
| 4260 | self, |
| 4261 | method: str, |
| 4262 | base_type: Type, |
| 4263 | arg: Expression, |
| 4264 | context: Context, |
| 4265 | allow_reverse: bool = False, |
| 4266 | ) -> tuple[Type, Type]: |
| 4267 | """Type check a binary operation which maps to a method call. |
| 4268 | |
| 4269 | Return tuple (result type, inferred operator method type). |
| 4270 | """ |
| 4271 | |
| 4272 | if allow_reverse: |
| 4273 | left_variants = [base_type] |
| 4274 | base_type = get_proper_type(base_type) |
| 4275 | if isinstance(base_type, UnionType): |
| 4276 | left_variants = list(flatten_nested_unions(base_type.relevant_items())) |
| 4277 | right_type = self.accept(arg) |
| 4278 | |
| 4279 | # Step 1: We first try leaving the right arguments alone and destructure |
| 4280 | # just the left ones. (Mypy can sometimes perform some more precise inference |
| 4281 | # if we leave the right operands a union -- see testOperatorWithEmptyListAndSum.) |
| 4282 | all_results = [] |
| 4283 | all_inferred = [] |
| 4284 | |
| 4285 | with self.msg.filter_errors() as local_errors: |
| 4286 | for left_possible_type in left_variants: |
| 4287 | result, inferred = self.check_op_reversible( |
| 4288 | op_name=method, |
| 4289 | left_type=left_possible_type, |
| 4290 | left_expr=TempNode(left_possible_type, context=context), |
| 4291 | right_type=right_type, |
| 4292 | right_expr=arg, |
| 4293 | context=context, |
| 4294 | ) |
| 4295 | all_results.append(result) |
| 4296 | all_inferred.append(inferred) |
| 4297 | |
| 4298 | if not local_errors.has_new_errors(): |
| 4299 | results_final = make_simplified_union(all_results) |
| 4300 | inferred_final = make_simplified_union(all_inferred) |
| 4301 | return results_final, inferred_final |
| 4302 | |
| 4303 | # Step 2: If that fails, we try again but also destructure the right argument. |
| 4304 | # This is also necessary to make certain edge cases work -- see |
| 4305 | # testOperatorDoubleUnionInterwovenUnionAdd, for example. |
| 4306 | |
| 4307 | # Note: We want to pass in the original 'arg' for 'left_expr' and 'right_expr' |
| 4308 | # whenever possible so that plugins and similar things can introspect on the original |
| 4309 | # node if possible. |
| 4310 | # |
| 4311 | # We don't do the same for the base expression because it could lead to weird |
| 4312 | # type inference errors -- e.g. see 'testOperatorDoubleUnionSum'. |
| 4313 | # TODO: Can we use `type_overrides_set()` here? |
| 4314 | right_variants = [(right_type, arg)] |
| 4315 | right_type = get_proper_type(right_type) |
| 4316 | if isinstance(right_type, UnionType): |
no test coverage detected