Skip to content
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

chore: improve diagnostics for invalid for loop annotation #3721

Merged
merged 13 commits into from
Jan 11, 2024
29 changes: 29 additions & 0 deletions tests/functional/codegen/features/iteration/test_for_in_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
NamespaceCollision,
StateAccessViolation,
StructureException,
SyntaxException,
TypeMismatch,
UnknownType,
)

BASIC_FOR_LOOP_CODE = [
Expand Down Expand Up @@ -803,6 +805,33 @@ def test_for() -> int128:
""",
TypeMismatch,
),
(
"""
@external
def foo():
for i in [1, 2, 3]:
pass
""",
SyntaxException,
),
(
"""
@external
def foo():
for i: $$$ in [1, 2, 3]:
pass
""",
SyntaxException,
),
(
"""
@external
def foo():
for i: uint9 in [1, 2, 3]:
pass
""",
UnknownType,
),
]

BAD_CODE = [code if isinstance(code, tuple) else (code, StructureException) for code in BAD_CODE]
Expand Down
12 changes: 12 additions & 0 deletions tests/functional/syntax/exceptions/test_syntax_exception.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,18 @@ def f(a:uint256,/): # test posonlyargs blocked
def g():
self.f()
""",
"""
@external
def foo():
for i in range(0, 10):
pass
""",
"""
@external
def foo():
for i: $$$ in range(0, 10):
pass
""",
]


Expand Down
12 changes: 12 additions & 0 deletions tests/functional/syntax/test_for_range.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
StateAccessViolation,
StructureException,
TypeMismatch,
UnknownType,
)

fail_list = [
Expand Down Expand Up @@ -235,6 +236,17 @@ def foo():
"Bound must be at least 1",
"FOO",
),
(
"""
@external
def foo():
for i: DynArra[uint256, 3] in [1, 2, 3]:
pass
""",
UnknownType,
"No builtin or user-defined type named 'DynArra[uint256, 3]'. ",
"DynArra[uint256, 3]",
),
]

for_code_regex = re.compile(r"for .+ in (.*):")
Expand Down
54 changes: 54 additions & 0 deletions vyper/ast/parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,60 @@

self.generic_visit(node)


start_lineno = node.lineno - 1
end_lineno = node.end_lineno - 1
col_offset = node.col_offset
end_col_offset = node.end_col_offset

print("for node token deets")
print(node.first_token.string)
print(node.first_token.start)
print(node.first_token.end)
print(node.first_token.line)
print(node.first_token.startpos)
print(node.first_token.endpos)

for n in python_ast.walk(node.target.annotation):
if not n.lineno:
continue

print("annotation node token deets")
print(n.first_token.string)
print(n.first_token.start)
print(n.first_token.end)
print(n.first_token.line)
print(n.first_token.startpos)
print(n.first_token.endpos)

print("type(token.start): ", type(n.first_token.start))
#n.first_token._replace(start=(0, 2))

line_offset = node.end_lineno - node.lineno

n.lineno = start_lineno + line_offset
n.end_lineno = end_lineno + line_offset

node_col_offset = n.end_col_offset - n.col_offset

n.col_offset = col_offset + node_col_offset
n.end_col_offset = end_col_offset + node_col_offset

#self.generic_visit(node.target.annotation)


# this step improves the diagnostics during semantics analysis as otherwise
# the code displayed in the error message would be incorrectly based on the
# full source code with the location of the type annotation as an expression
annotation_children = python_ast.iter_child_nodes(node.target.annotation)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think we can use python_ast.walk here

for n in [*annotation_children, node.target.annotation]:
Fixed Show fixed Hide fixed
# override the source code to show the spliced type annotation
#n.node_source_code = raw_annotation

# override the locations to show the `For` node
#python_ast.copy_location(n, node)
continue

return node

def visit_Expr(self, node):
Expand Down
Loading