Skip to content

Report note about binary operation on the same line as error #8936

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 3, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions mypy/checkexpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -2680,12 +2680,16 @@ def check_op(self, method: str, base_type: Type,

if msg.is_errors():
self.msg.add_errors(msg)
# Point any notes to the same location as an existing message.
recent_context = msg.most_recent_context()
if len(left_variants) >= 2 and len(right_variants) >= 2:
self.msg.warn_both_operands_are_from_unions(context)
self.msg.warn_both_operands_are_from_unions(recent_context)
elif len(left_variants) >= 2:
self.msg.warn_operand_was_from_union("Left", base_type, context=right_expr)
self.msg.warn_operand_was_from_union(
"Left", base_type, context=recent_context)
elif len(right_variants) >= 2:
self.msg.warn_operand_was_from_union("Right", right_type, context=right_expr)
self.msg.warn_operand_was_from_union(
"Right", right_type, context=recent_context)

# See the comment in 'check_overload_call' for more details on why
# we call 'combine_function_signature' instead of just unioning the inferred
Expand Down
4 changes: 4 additions & 0 deletions mypy/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,10 @@ def is_errors_for_file(self, file: str) -> bool:
"""Are there any errors for the given file?"""
return file in self.error_info_map

def most_recent_error_location(self) -> Tuple[int, int]:
info = self.error_info_map[self.file][-1]
return info.line, info.column

def raise_error(self, use_stdout: bool = True) -> None:
"""Raise a CompileError with the generated messages.

Expand Down
10 changes: 9 additions & 1 deletion mypy/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
FuncDef, reverse_builtin_aliases,
ARG_POS, ARG_OPT, ARG_NAMED, ARG_NAMED_OPT, ARG_STAR, ARG_STAR2,
ReturnStmt, NameExpr, Var, CONTRAVARIANT, COVARIANT, SymbolNode,
CallExpr, SymbolTable
CallExpr, SymbolTable, TempNode
)
from mypy.subtypes import (
is_subtype, find_member, get_member_flags,
Expand Down Expand Up @@ -145,6 +145,14 @@ def enable_errors(self) -> None:
def is_errors(self) -> bool:
return self.errors.is_errors()

def most_recent_context(self) -> Context:
"""Return a dummy context matching the most recent generated error in current file."""
line, column = self.errors.most_recent_error_location()
node = TempNode(NoneType())
node.line = line
node.column = column
return node

def report(self,
msg: str,
context: Optional[Context],
Expand Down
12 changes: 12 additions & 0 deletions test-data/unit/check-errorcodes.test
Original file line number Diff line number Diff line change
Expand Up @@ -736,3 +736,15 @@ class C:
def __rsub__(self, x): pass

x - C() # type: ignore[operator]

[case testErrorCodeMultiLineBinaryOperatorOperand]
# flags: --strict-optional
from typing import Optional

class C: pass

def f() -> Optional[C]:
return None

f( # type: ignore[operator]
) + C()
4 changes: 2 additions & 2 deletions test-data/unit/check-isinstance.test
Original file line number Diff line number Diff line change
Expand Up @@ -974,8 +974,8 @@ def foo() -> Union[int, str, A]: pass
def bar() -> None:
x = foo()
x + 1 # E: Unsupported left operand type for + ("A") \
# E: Unsupported operand types for + ("str" and "int") \
# N: Left operand is of type "Union[int, str, A]"
# N: Left operand is of type "Union[int, str, A]" \
# E: Unsupported operand types for + ("str" and "int")
if isinstance(x, A):
x.a
else:
Expand Down
4 changes: 2 additions & 2 deletions test-data/unit/check-unions.test
Original file line number Diff line number Diff line change
Expand Up @@ -303,9 +303,9 @@ from typing import Union
class A: pass
def f(x: Union[int, str, A]):
x + object() # E: Unsupported left operand type for + ("A") \
# N: Left operand is of type "Union[int, str, A]" \
# E: Unsupported operand types for + ("int" and "object") \
# E: Unsupported operand types for + ("str" and "object") \
# N: Left operand is of type "Union[int, str, A]"
# E: Unsupported operand types for + ("str" and "object")
[builtins fixtures/primitives.pyi]

[case testNarrowingDownNamedTupleUnion]
Expand Down