diff --git a/tests/functional/codegen/types/numbers/test_unsigned_ints.py b/tests/functional/codegen/types/numbers/test_unsigned_ints.py index 42619a8bd5..2bd3184ec0 100644 --- a/tests/functional/codegen/types/numbers/test_unsigned_ints.py +++ b/tests/functional/codegen/types/numbers/test_unsigned_ints.py @@ -269,13 +269,65 @@ def foo(): compile_code(code) -def test_invalid_div(): - code = """ +div_code_with_hint = [ + ( + """ @external def foo(): a: uint256 = 5 / 9 - """ + """, + "did you mean `5 // 9`?", + ), + ( + """ +@external +def foo(): + a: uint256 = 10 + a /= (3 + 10) // (2 + 3) + """, + "did you mean `a //= (3 + 10) // (2 + 3)`?", + ), + ( + """ +@external +def foo(a: uint256, b:uint256, c: uint256) -> uint256: + return (a + b) / c + """, + "did you mean `(a + b) // c`?", + ), + ( + """ +@external +def foo(a: uint256, b:uint256, c: uint256) -> uint256: + return (a + b) / (a + c) + """, + "did you mean `(a + b) // (a + c)`?", + ), + ( + """ +@external +def foo(a: uint256, b:uint256, c: uint256) -> uint256: + return (a + (c + b)) / (a + c) + """, + "did you mean `(a + (c + b)) // (a + c)`?", + ), + ( + """ +interface Foo: + def foo() -> uint256: view + +@external +def foo(a: uint256, b:uint256, c: uint256) -> uint256: + return (a + b) / staticcall Foo(self).foo() + """, + "did you mean `(a + b) // staticcall Foo(self).foo()`?", + ), +] + + +@pytest.mark.parametrize("code, expected_hint", div_code_with_hint) +def test_invalid_div(code, expected_hint): with pytest.raises(InvalidOperation) as e: compile_code(code) - assert e.value._hint == "did you mean `5 // 9`?" + assert e.value._hint == expected_hint diff --git a/vyper/semantics/types/primitives.py b/vyper/semantics/types/primitives.py index eea58c6c68..5c0362e662 100644 --- a/vyper/semantics/types/primitives.py +++ b/vyper/semantics/types/primitives.py @@ -211,9 +211,16 @@ def _add_div_hint(node, e): else: return e + def _get_source(node): + source = node.node_source_code + if isinstance(node, vy_ast.BinOp): + # parenthesize, to preserve precedence + return f"({source})" + return source + if isinstance(node, vy_ast.BinOp): - e._hint = f"did you mean `{node.left.node_source_code} " - e._hint += f"{suggested} {node.right.node_source_code}`?" + e._hint = f"did you mean `{_get_source(node.left)} " + e._hint += f"{suggested} {_get_source(node.right)}`?" elif isinstance(node, vy_ast.AugAssign): e._hint = f"did you mean `{node.target.node_source_code} " e._hint += f"{suggested}= {node.value.node_source_code}`?"