From 0febae2a620efc8a25bd84a2601b09e7ee174246 Mon Sep 17 00:00:00 2001 From: tserg <8017125+tserg@users.noreply.github.com> Date: Mon, 8 Jan 2024 13:52:08 +0800 Subject: [PATCH 01/13] override fields for annotation --- vyper/ast/parse.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/vyper/ast/parse.py b/vyper/ast/parse.py index b657cf2245..ea7d3a2794 100644 --- a/vyper/ast/parse.py +++ b/vyper/ast/parse.py @@ -258,6 +258,19 @@ def visit_For(self, node): self.generic_visit(node) + # 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 + for n in python_ast.iter_child_nodes(node.target.annotation): + # override the source code to show the spliced type annotation + n.node_source_code = raw_annotation + + # override the locations to show the `For` node + n.lineno = node.lineno + n.col_offset = node.col_offset + n.end_lineno = node.end_lineno + n.end_col_offset = node.end_col_offset + return node def visit_Expr(self, node): From 423107297f688088d034c6f7575e42ccd66e264e Mon Sep 17 00:00:00 2001 From: tserg <8017125+tserg@users.noreply.github.com> Date: Mon, 8 Jan 2024 13:52:12 +0800 Subject: [PATCH 02/13] add tests --- .../features/iteration/test_for_in_list.py | 29 +++++++++++++++++++ .../exceptions/test_syntax_exception.py | 12 ++++++++ tests/functional/syntax/test_for_range.py | 12 ++++++++ 3 files changed, 53 insertions(+) diff --git a/tests/functional/codegen/features/iteration/test_for_in_list.py b/tests/functional/codegen/features/iteration/test_for_in_list.py index 5c7b5c6b1b..b64fe4b626 100644 --- a/tests/functional/codegen/features/iteration/test_for_in_list.py +++ b/tests/functional/codegen/features/iteration/test_for_in_list.py @@ -11,7 +11,9 @@ NamespaceCollision, StateAccessViolation, StructureException, + SyntaxException, TypeMismatch, + UnknownType, ) BASIC_FOR_LOOP_CODE = [ @@ -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: DynArra[uint256, 3] in [1, 2, 3]: + pass + """, + UnknownType, + ), ] BAD_CODE = [code if isinstance(code, tuple) else (code, StructureException) for code in BAD_CODE] diff --git a/tests/functional/syntax/exceptions/test_syntax_exception.py b/tests/functional/syntax/exceptions/test_syntax_exception.py index 9ab9b6c677..53a9550a7d 100644 --- a/tests/functional/syntax/exceptions/test_syntax_exception.py +++ b/tests/functional/syntax/exceptions/test_syntax_exception.py @@ -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 + """, ] diff --git a/tests/functional/syntax/test_for_range.py b/tests/functional/syntax/test_for_range.py index 66981a90de..5798e5c42f 100644 --- a/tests/functional/syntax/test_for_range.py +++ b/tests/functional/syntax/test_for_range.py @@ -8,6 +8,7 @@ StateAccessViolation, StructureException, TypeMismatch, + UnknownType, ) fail_list = [ @@ -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 (.*):") From a288ebd337099fbb447171dc3521beca78855678 Mon Sep 17 00:00:00 2001 From: tserg <8017125+tserg@users.noreply.github.com> Date: Mon, 8 Jan 2024 14:00:03 +0800 Subject: [PATCH 03/13] include annotation node --- vyper/ast/parse.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/vyper/ast/parse.py b/vyper/ast/parse.py index ea7d3a2794..790cb4f692 100644 --- a/vyper/ast/parse.py +++ b/vyper/ast/parse.py @@ -1,4 +1,5 @@ import ast as python_ast +import itertools import tokenize from decimal import Decimal from typing import Any, Dict, List, Optional, Union, cast @@ -261,7 +262,8 @@ def visit_For(self, node): # 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 - for n in python_ast.iter_child_nodes(node.target.annotation): + annotation_child_nodes = python_ast.iter_child_nodes(node.target.annotation) + for n in itertools.chain(annotation_child_nodes, [node.target.annotation]): # override the source code to show the spliced type annotation n.node_source_code = raw_annotation From 66d2ed051ef14398555985e6269cdb31a2b06e9b Mon Sep 17 00:00:00 2001 From: tserg <8017125+tserg@users.noreply.github.com> Date: Mon, 8 Jan 2024 14:00:08 +0800 Subject: [PATCH 04/13] vary test --- tests/functional/codegen/features/iteration/test_for_in_list.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/codegen/features/iteration/test_for_in_list.py b/tests/functional/codegen/features/iteration/test_for_in_list.py index b64fe4b626..7f5658e485 100644 --- a/tests/functional/codegen/features/iteration/test_for_in_list.py +++ b/tests/functional/codegen/features/iteration/test_for_in_list.py @@ -827,7 +827,7 @@ def foo(): """ @external def foo(): - for i: DynArra[uint256, 3] in [1, 2, 3]: + for i: uint9 in [1, 2, 3]: pass """, UnknownType, From b181b1dc166f93878aee10ac866f31cab1ef636f Mon Sep 17 00:00:00 2001 From: tserg <8017125+tserg@users.noreply.github.com> Date: Mon, 8 Jan 2024 15:17:01 +0800 Subject: [PATCH 05/13] use copy_location --- vyper/ast/parse.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/vyper/ast/parse.py b/vyper/ast/parse.py index 790cb4f692..2d65abb710 100644 --- a/vyper/ast/parse.py +++ b/vyper/ast/parse.py @@ -268,10 +268,7 @@ def visit_For(self, node): n.node_source_code = raw_annotation # override the locations to show the `For` node - n.lineno = node.lineno - n.col_offset = node.col_offset - n.end_lineno = node.end_lineno - n.end_col_offset = node.end_col_offset + python_ast.copy_location(n, node) return node From 0cb6494ecbdcc8ad622796287e2fb18ba3cf49a5 Mon Sep 17 00:00:00 2001 From: tserg <8017125+tserg@users.noreply.github.com> Date: Mon, 8 Jan 2024 18:15:59 +0800 Subject: [PATCH 06/13] cast iterator to list --- vyper/ast/parse.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/vyper/ast/parse.py b/vyper/ast/parse.py index 2d65abb710..646e01a057 100644 --- a/vyper/ast/parse.py +++ b/vyper/ast/parse.py @@ -1,5 +1,4 @@ import ast as python_ast -import itertools import tokenize from decimal import Decimal from typing import Any, Dict, List, Optional, Union, cast @@ -263,7 +262,7 @@ def visit_For(self, node): # 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_child_nodes = python_ast.iter_child_nodes(node.target.annotation) - for n in itertools.chain(annotation_child_nodes, [node.target.annotation]): + for n in list(annotation_child_nodes) + [node.target.annotation]: # override the source code to show the spliced type annotation n.node_source_code = raw_annotation From ebf6df7f6e2029123d304cb8f11bea30ab5f65d9 Mon Sep 17 00:00:00 2001 From: tserg <8017125+tserg@users.noreply.github.com> Date: Mon, 8 Jan 2024 18:36:02 +0800 Subject: [PATCH 07/13] unpack instead of cast --- vyper/ast/parse.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vyper/ast/parse.py b/vyper/ast/parse.py index 646e01a057..1c667839db 100644 --- a/vyper/ast/parse.py +++ b/vyper/ast/parse.py @@ -262,7 +262,7 @@ def visit_For(self, node): # 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_child_nodes = python_ast.iter_child_nodes(node.target.annotation) - for n in list(annotation_child_nodes) + [node.target.annotation]: + for n in [*annotation_child_nodes, node.target.annotation]: # override the source code to show the spliced type annotation n.node_source_code = raw_annotation From 3d38a028cc211ff06ff985a8a62cb100f94661b1 Mon Sep 17 00:00:00 2001 From: tserg <8017125+tserg@users.noreply.github.com> Date: Mon, 8 Jan 2024 18:36:31 +0800 Subject: [PATCH 08/13] rename var --- vyper/ast/parse.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vyper/ast/parse.py b/vyper/ast/parse.py index 1c667839db..fbf3a99b64 100644 --- a/vyper/ast/parse.py +++ b/vyper/ast/parse.py @@ -261,8 +261,8 @@ def visit_For(self, node): # 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_child_nodes = python_ast.iter_child_nodes(node.target.annotation) - for n in [*annotation_child_nodes, node.target.annotation]: + annotation_children = python_ast.iter_child_nodes(node.target.annotation) + for n in [*annotation_children, node.target.annotation]: # override the source code to show the spliced type annotation n.node_source_code = raw_annotation From 5bf38d672f76379671fb980bf20481084cfab81d Mon Sep 17 00:00:00 2001 From: tserg <8017125+tserg@users.noreply.github.com> Date: Tue, 9 Jan 2024 22:34:01 +0800 Subject: [PATCH 09/13] wip --- vyper/ast/parse.py | 47 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/vyper/ast/parse.py b/vyper/ast/parse.py index fbf3a99b64..a00303d006 100644 --- a/vyper/ast/parse.py +++ b/vyper/ast/parse.py @@ -258,16 +258,59 @@ def visit_For(self, node): 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) for n in [*annotation_children, node.target.annotation]: # override the source code to show the spliced type annotation - n.node_source_code = raw_annotation + #n.node_source_code = raw_annotation # override the locations to show the `For` node - python_ast.copy_location(n, node) + #python_ast.copy_location(n, node) + continue return node From 0104db549578a6919ba4389741619e9905dcaaa0 Mon Sep 17 00:00:00 2001 From: Charles Cooper Date: Tue, 9 Jan 2024 12:02:31 -0500 Subject: [PATCH 10/13] wip - propagate full AnnAssign node from pre_parse.py --- vyper/ast/parse.py | 132 +++++++++++++++++++++++----------------- vyper/ast/pre_parser.py | 37 ++++++----- vyper/exceptions.py | 1 + 3 files changed, 98 insertions(+), 72 deletions(-) diff --git a/vyper/ast/parse.py b/vyper/ast/parse.py index a00303d006..fad89c45b8 100644 --- a/vyper/ast/parse.py +++ b/vyper/ast/parse.py @@ -139,6 +139,7 @@ def __init__( self.counter: int = 0 + def generic_visit(self, node): """ Annotate a node with information that simplifies Vyper node generation. @@ -150,7 +151,9 @@ def generic_visit(self, node): self.counter += 1 # Decorate every node with source end offsets - start = node.first_token.start if hasattr(node, "first_token") else (None, None) + start = (None, None) + if hasattr(node, "first_token"): + start = node.first_token.start end = (None, None) if hasattr(node, "last_token"): end = node.last_token.end @@ -224,9 +227,9 @@ def visit_For(self, node): Visit a For node, splicing in the loop variable annotation provided by the pre-parser """ - raw_annotation = self._for_loop_annotations.pop((node.lineno, node.col_offset)) + annotation_tokens = self._for_loop_annotations.pop((node.lineno, node.col_offset)) - if not raw_annotation: + if not annotation_tokens: # a common case for people migrating to 0.4.0, provide a more # specific error message than "invalid type annotation" raise SyntaxException( @@ -238,79 +241,96 @@ def visit_For(self, node): node.col_offset, ) + self.generic_visit(node) + try: - annotation = python_ast.parse(raw_annotation, mode="eval") - # annotate with token and source code information. `first_token` - # and `last_token` attributes are accessed in `generic_visit`. - tokens = asttokens.ASTTokens(raw_annotation) - tokens.mark_tokens(annotation) + from tokenize import untokenize + import string + annotation_str = untokenize(annotation_tokens).strip(string.whitespace + "\\") + print("ENTER4", annotation_str) + annotation = python_ast.parse(annotation_str) except SyntaxError as e: raise SyntaxException( "invalid type annotation", self._source_code, node.lineno, node.col_offset ) from e - assert isinstance(annotation, python_ast.Expression) - annotation = annotation.body + annotation = annotation.body[0] + print("ANN", annotation) + og_target = node.target - old_target = node.target - new_target = python_ast.AnnAssign(target=old_target, annotation=annotation, simple=1) - node.target = new_target + # annotate with token and source code information. `first_token` + # and `last_token` attributes are accessed in `generic_visit`. + tokens = asttokens.ASTTokens(annotation_str) + tokens.mark_tokens(annotation) - self.generic_visit(node) + adjustment = og_target.first_token.start[0] - 1, og_target.first_token.start[1] + def _add_pair(x, y): + return x[0] + y[0], x[1] + y[1] - start_lineno = node.lineno - 1 - end_lineno = node.end_lineno - 1 - col_offset = node.col_offset - end_col_offset = node.end_col_offset + for n in python_ast.walk(annotation): + # adjust all offsets + if hasattr(n, "first_token"): + n.first_token = n.first_token._replace( + start=_add_pair(n.first_token.start, adjustment), + end=_add_pair(n.first_token.end, adjustment), + startpos=n.first_token.startpos + og_target.first_token.startpos, + endpos=n.first_token.startpos + og_target.first_token.startpos + ) + if hasattr(n, "last_token"): + n.last_token = n.last_token._replace( + start=_add_pair(n.last_token.start, adjustment), + end=_add_pair(n.last_token.end, adjustment), + startpos=n.last_token.startpos + og_target.first_token.startpos, + endpos=n.last_token.endpos + og_target.first_token.startpos + ) - 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) + node.target = annotation - for n in python_ast.walk(node.target.annotation): - if not n.lineno: - continue + #start_lineno = node.lineno - 1 + #end_lineno = node.end_lineno - 1 + #col_offset = node.col_offset + #end_col_offset = node.end_col_offset - 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)) + try: + tmp = self.lineno_ofst, self.col_ofst + #self._source_code = annotation_str + print("ENTER", self.lineno_ofst, self.col_ofst, node) + node.target = self.generic_visit(node.target) + finally: + self.lineno_ofst, self.col_ofst = tmp + #print("OOH", self._source_code) + + for n in python_ast.walk(node): + n.full_source_code = self._source_code + print("WALK 1", n, n.full_source_code) - line_offset = node.end_lineno - node.lineno + return node - n.lineno = start_lineno + line_offset - n.end_lineno = end_lineno + line_offset + # dead code: + for_node = node + for n in python_ast.walk(node.target): + if not n.lineno: + print("CONTINUE", n) + continue + + #n.lineno += for_node.lineno + #n.end_lineno += for_node.lineno + #DIRTY_CONSTANT = 2 + #n.col_offset += for_node.col_offset + DIRTY_CONSTANT + #n.end_col_offset += for_node.end_col_offset + DIRTY_CONSTANT - node_col_offset = n.end_col_offset - n.col_offset + #n.first_token._replace(start=(0, 2)) - n.col_offset = col_offset + node_col_offset - n.end_col_offset = end_col_offset + node_col_offset + #line_offset = node.end_lineno - node.lineno - #self.generic_visit(node.target.annotation) - + #n.lineno = start_lineno + line_offset + #n.end_lineno = end_lineno + line_offset - # 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) - for n in [*annotation_children, node.target.annotation]: - # override the source code to show the spliced type annotation - #n.node_source_code = raw_annotation + #node_col_offset = n.end_col_offset - n.col_offset - # override the locations to show the `For` node - #python_ast.copy_location(n, node) - continue + #n.col_offset = col_offset + node_col_offset + #n.end_col_offset = end_col_offset + node_col_offset return node diff --git a/vyper/ast/pre_parser.py b/vyper/ast/pre_parser.py index c7e6f3698f..fc458cb9cc 100644 --- a/vyper/ast/pre_parser.py +++ b/vyper/ast/pre_parser.py @@ -65,27 +65,32 @@ def __init__(self, code): def consume(self, token): # state machine: we can start slurping tokens soon if token.type == NAME and token.string == "for": - # note: self._state should be NOT_RUNNING here, but we don't sanity - # check here as that should be an error the parser will handle. + # sanity check -- this should never really happen, but if it does, + # try to raise an exception which pinpoints the source. + if self._current_annotation is not None: + raise SyntaxException( + "for loop parse error", self._code, token.start[0], token.start[1] + ) + self._current_annotation = [] + + assert self._state == ForParserState.NOT_RUNNING self._state = ForParserState.START_SOON self._current_for_loop = token.start + return False if self._state == ForParserState.NOT_RUNNING: return False - # state machine: start slurping tokens - if token.type == OP and token.string == ":": - self._state = ForParserState.RUNNING + if self._state == ForParserState.START_SOON: + # state machine: start slurping tokens - # sanity check -- this should never really happen, but if it does, - # try to raise an exception which pinpoints the source. - if self._current_annotation is not None: - raise SyntaxException( - "for loop parse error", self._code, token.start[0], token.start[1] - ) + self._current_annotation.append(token) - self._current_annotation = [] - return True # do not add ":" to tokens. + if token.type == OP and token.string == ":": + self._state = ForParserState.RUNNING + return True # do not add ":" to global tokens. + + return False # add everything before ":" to tokens # state machine: end slurping tokens if token.type == NAME and token.string == "in": @@ -220,9 +225,9 @@ def pre_parse(code: str) -> tuple[Settings, ModificationOffsets, dict, str]: for_loop_annotations = {} for k, v in for_parser.annotations.items(): - v_source = untokenize(v) + #v_source = untokenize(v) # untokenize adds backslashes and whitespace, strip them. - v_source = v_source.replace("\\", "").strip() - for_loop_annotations[k] = v_source + #v_source = v_source.replace("\\", "").strip() + for_loop_annotations[k] = v.copy() return settings, modification_offsets, for_loop_annotations, untokenize(result).decode("utf-8") diff --git a/vyper/exceptions.py b/vyper/exceptions.py index 51f3fea14c..e42afe4710 100644 --- a/vyper/exceptions.py +++ b/vyper/exceptions.py @@ -106,6 +106,7 @@ def __str__(self): line_numbers=VYPER_ERROR_LINE_NUMBERS, ) except Exception: + print("EXC", node.full_source_code, node.node_source_code, node.lineno, node.col_offset) # necessary for certain types of syntax exceptions return self.message From 4259a9fa48b143054dbe17ec6777aab6f41150b3 Mon Sep 17 00:00:00 2001 From: tserg <8017125+tserg@users.noreply.github.com> Date: Wed, 10 Jan 2024 08:54:19 +0800 Subject: [PATCH 11/13] clean up --- vyper/ast/parse.py | 56 +++++----------------------------------------- 1 file changed, 6 insertions(+), 50 deletions(-) diff --git a/vyper/ast/parse.py b/vyper/ast/parse.py index fad89c45b8..e0bd86e352 100644 --- a/vyper/ast/parse.py +++ b/vyper/ast/parse.py @@ -1,5 +1,7 @@ import ast as python_ast +import string import tokenize + from decimal import Decimal from typing import Any, Dict, List, Optional, Union, cast @@ -244,10 +246,7 @@ def visit_For(self, node): self.generic_visit(node) try: - from tokenize import untokenize - import string - annotation_str = untokenize(annotation_tokens).strip(string.whitespace + "\\") - print("ENTER4", annotation_str) + annotation_str = tokenize.untokenize(annotation_tokens).strip(string.whitespace + "\\") annotation = python_ast.parse(annotation_str) except SyntaxError as e: raise SyntaxException( @@ -255,7 +254,6 @@ def visit_For(self, node): ) from e annotation = annotation.body[0] - print("ANN", annotation) og_target = node.target # annotate with token and source code information. `first_token` @@ -263,6 +261,8 @@ def visit_For(self, node): tokens = asttokens.ASTTokens(annotation_str) tokens.mark_tokens(annotation) + # decrease line offset by 1 because annotation is on the same line as `For` node + # but the spliced expression also starts at line 1 adjustment = og_target.first_token.start[0] - 1, og_target.first_token.start[1] def _add_pair(x, y): @@ -286,51 +286,7 @@ def _add_pair(x, y): ) node.target = annotation - - #start_lineno = node.lineno - 1 - #end_lineno = node.end_lineno - 1 - #col_offset = node.col_offset - #end_col_offset = node.end_col_offset - - try: - tmp = self.lineno_ofst, self.col_ofst - #self._source_code = annotation_str - print("ENTER", self.lineno_ofst, self.col_ofst, node) - node.target = self.generic_visit(node.target) - finally: - self.lineno_ofst, self.col_ofst = tmp - #print("OOH", self._source_code) - - for n in python_ast.walk(node): - n.full_source_code = self._source_code - print("WALK 1", n, n.full_source_code) - - return node - - # dead code: - for_node = node - for n in python_ast.walk(node.target): - if not n.lineno: - print("CONTINUE", n) - continue - - #n.lineno += for_node.lineno - #n.end_lineno += for_node.lineno - #DIRTY_CONSTANT = 2 - #n.col_offset += for_node.col_offset + DIRTY_CONSTANT - #n.end_col_offset += for_node.end_col_offset + DIRTY_CONSTANT - - #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 + node.target = self.generic_visit(node.target) return node From 01269049f985a3d58c6be7d38572600b147880a2 Mon Sep 17 00:00:00 2001 From: tserg <8017125+tserg@users.noreply.github.com> Date: Wed, 10 Jan 2024 08:57:47 +0800 Subject: [PATCH 12/13] more clean up; fix lint --- vyper/ast/parse.py | 10 ++++------ vyper/ast/pre_parser.py | 8 +++----- vyper/exceptions.py | 1 - 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/vyper/ast/parse.py b/vyper/ast/parse.py index e0bd86e352..b1b9a8d917 100644 --- a/vyper/ast/parse.py +++ b/vyper/ast/parse.py @@ -1,7 +1,6 @@ import ast as python_ast import string import tokenize - from decimal import Decimal from typing import Any, Dict, List, Optional, Union, cast @@ -141,7 +140,6 @@ def __init__( self.counter: int = 0 - def generic_visit(self, node): """ Annotate a node with information that simplifies Vyper node generation. @@ -275,14 +273,14 @@ def _add_pair(x, y): start=_add_pair(n.first_token.start, adjustment), end=_add_pair(n.first_token.end, adjustment), startpos=n.first_token.startpos + og_target.first_token.startpos, - endpos=n.first_token.startpos + og_target.first_token.startpos + endpos=n.first_token.startpos + og_target.first_token.startpos, ) if hasattr(n, "last_token"): n.last_token = n.last_token._replace( start=_add_pair(n.last_token.start, adjustment), end=_add_pair(n.last_token.end, adjustment), startpos=n.last_token.startpos + og_target.first_token.startpos, - endpos=n.last_token.endpos + og_target.first_token.startpos + endpos=n.last_token.endpos + og_target.first_token.startpos, ) node.target = annotation @@ -448,8 +446,8 @@ def annotate_python_ast( source_code : str The originating source code of the AST. loop_var_annotations: dict, optional - A mapping of line numbers of `For` nodes to the type annotation of the iterator - extracted during pre-parsing. + A mapping of line numbers of `For` nodes to the tokens of the type annotation + of the iterator extracted during pre-parsing. modification_offsets : dict, optional A mapping of class names to their original class types. diff --git a/vyper/ast/pre_parser.py b/vyper/ast/pre_parser.py index fc458cb9cc..f7d2df208a 100644 --- a/vyper/ast/pre_parser.py +++ b/vyper/ast/pre_parser.py @@ -141,8 +141,9 @@ def pre_parse(code: str) -> tuple[Settings, ModificationOffsets, dict, str]: Compilation settings based on the directives in the source code ModificationOffsets A mapping of class names to their original class types. - dict[tuple[int, int], str] - A mapping of line/column offsets of `For` nodes to the annotation of the for loop target + dict[tuple[int, int], list[TokenInfo]] + A mapping of line/column offsets of `For` nodes to the tokens of the annotation of the + for loop target str Reformatted python source string. """ @@ -225,9 +226,6 @@ def pre_parse(code: str) -> tuple[Settings, ModificationOffsets, dict, str]: for_loop_annotations = {} for k, v in for_parser.annotations.items(): - #v_source = untokenize(v) - # untokenize adds backslashes and whitespace, strip them. - #v_source = v_source.replace("\\", "").strip() for_loop_annotations[k] = v.copy() return settings, modification_offsets, for_loop_annotations, untokenize(result).decode("utf-8") diff --git a/vyper/exceptions.py b/vyper/exceptions.py index e42afe4710..51f3fea14c 100644 --- a/vyper/exceptions.py +++ b/vyper/exceptions.py @@ -106,7 +106,6 @@ def __str__(self): line_numbers=VYPER_ERROR_LINE_NUMBERS, ) except Exception: - print("EXC", node.full_source_code, node.node_source_code, node.lineno, node.col_offset) # necessary for certain types of syntax exceptions return self.message From 1f476fc2352d981d8b3751710ef07bd17e52bfce Mon Sep 17 00:00:00 2001 From: tserg <8017125+tserg@users.noreply.github.com> Date: Wed, 10 Jan 2024 12:27:56 +0800 Subject: [PATCH 13/13] fix test --- tests/functional/syntax/test_for_range.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/functional/syntax/test_for_range.py b/tests/functional/syntax/test_for_range.py index 5798e5c42f..e807e12d41 100644 --- a/tests/functional/syntax/test_for_range.py +++ b/tests/functional/syntax/test_for_range.py @@ -244,8 +244,8 @@ def foo(): pass """, UnknownType, - "No builtin or user-defined type named 'DynArra[uint256, 3]'. ", - "DynArra[uint256, 3]", + "No builtin or user-defined type named 'DynArra'. Did you mean 'DynArray'?", + "DynArra", ), ]