From 4425feb0697ca3364102bf079b5ab82c9b460e80 Mon Sep 17 00:00:00 2001 From: Sand Bubbles Date: Fri, 1 Nov 2024 11:15:29 +0000 Subject: [PATCH 01/34] add path info to syntax exception --- vyper/ast/parse.py | 14 +++++++++++++- vyper/exceptions.py | 4 ++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/vyper/ast/parse.py b/vyper/ast/parse.py index 1e88241186..0ba4f4c1f3 100644 --- a/vyper/ast/parse.py +++ b/vyper/ast/parse.py @@ -16,13 +16,25 @@ def parse_to_ast(*args: Any, **kwargs: Any) -> vy_ast.Module: _settings, ast = parse_to_ast_with_settings(*args, **kwargs) return ast - def parse_to_ast_with_settings( vyper_source: str, source_id: int = 0, module_path: Optional[str] = None, resolved_path: Optional[str] = None, add_fn_node: Optional[str] = None, +) -> tuple[Settings, vy_ast.Module]: + try: + return _parse_to_ast_with_settings(vyper_source, source_id, module_path, resolved_path, add_fn_node) + except SyntaxException as e: + e.path = resolved_path + raise e + +def _parse_to_ast_with_settings( + vyper_source: str, + source_id: int = 0, + module_path: Optional[str] = None, + resolved_path: Optional[str] = None, + add_fn_node: Optional[str] = None, ) -> tuple[Settings, vy_ast.Module]: """ Parses a Vyper source string and generates basic Vyper AST nodes. diff --git a/vyper/exceptions.py b/vyper/exceptions.py index c69163b561..976023e45d 100644 --- a/vyper/exceptions.py +++ b/vyper/exceptions.py @@ -134,6 +134,10 @@ def format_annotation(self, value): if fn_node: node_msg = f'{node_msg}function "{fn_node.name}", ' + path = getattr(self, "path", None) + if path is not None: + node_msg = f'{node_msg}contract "{path}", ' + col_offset_str = "" if node.col_offset is None else str(node.col_offset) node_msg = f"{node_msg}line {node.lineno}:{col_offset_str} \n{source_annotation}\n" From 3316865a5df8b1f81dcec5b7189b0af3ecc46a43 Mon Sep 17 00:00:00 2001 From: Sand Bubbles Date: Fri, 1 Nov 2024 19:32:06 +0000 Subject: [PATCH 02/34] set path for natspec exceptions --- vyper/compiler/phases.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/vyper/compiler/phases.py b/vyper/compiler/phases.py index d9b6b13b48..4c6d98bd62 100644 --- a/vyper/compiler/phases.py +++ b/vyper/compiler/phases.py @@ -19,6 +19,7 @@ from vyper.typing import StorageLayout from vyper.utils import ERC5202_PREFIX, vyper_warn from vyper.venom import generate_assembly_experimental, generate_ir +from vyper.exceptions import SyntaxException DEFAULT_CONTRACT_PATH = PurePath("VyperContract.vy") @@ -174,7 +175,11 @@ def resolved_imports(self): def _annotate(self) -> tuple[natspec.NatspecOutput, vy_ast.Module]: module = self._resolve_imports[0] analyze_module(module) - nspec = natspec.parse_natspec(module) + try: + nspec = natspec.parse_natspec(module) + except SyntaxException as e: + e.path = module.resolved_path + raise e return nspec, module @cached_property From 90723e18eddeb8baced2f1b0a1e500153b1ee42c Mon Sep 17 00:00:00 2001 From: Sand Bubbles Date: Sat, 2 Nov 2024 08:13:52 +0000 Subject: [PATCH 03/34] lint --- vyper/ast/parse.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/vyper/ast/parse.py b/vyper/ast/parse.py index 0ba4f4c1f3..55e0cc8c18 100644 --- a/vyper/ast/parse.py +++ b/vyper/ast/parse.py @@ -16,6 +16,7 @@ def parse_to_ast(*args: Any, **kwargs: Any) -> vy_ast.Module: _settings, ast = parse_to_ast_with_settings(*args, **kwargs) return ast + def parse_to_ast_with_settings( vyper_source: str, source_id: int = 0, @@ -24,11 +25,14 @@ def parse_to_ast_with_settings( add_fn_node: Optional[str] = None, ) -> tuple[Settings, vy_ast.Module]: try: - return _parse_to_ast_with_settings(vyper_source, source_id, module_path, resolved_path, add_fn_node) + return _parse_to_ast_with_settings( + vyper_source, source_id, module_path, resolved_path, add_fn_node + ) except SyntaxException as e: e.path = resolved_path raise e + def _parse_to_ast_with_settings( vyper_source: str, source_id: int = 0, From bdf5a42d79d656268dfe8dc7a407adfa8c1a5c34 Mon Sep 17 00:00:00 2001 From: Sand Bubbles Date: Sat, 2 Nov 2024 08:21:30 +0000 Subject: [PATCH 04/34] rewrite the path attribute --- vyper/exceptions.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vyper/exceptions.py b/vyper/exceptions.py index 976023e45d..a92b10170d 100644 --- a/vyper/exceptions.py +++ b/vyper/exceptions.py @@ -54,6 +54,7 @@ def __init__(self, message="Error Message not found.", *items, hint=None, prev_d self.lineno = None self.col_offset = None self.annotations = None + self.path = None if len(items) == 1 and isinstance(items[0], tuple) and isinstance(items[0][0], int): # support older exceptions that don't annotate - remove this in the future! @@ -134,9 +135,8 @@ def format_annotation(self, value): if fn_node: node_msg = f'{node_msg}function "{fn_node.name}", ' - path = getattr(self, "path", None) - if path is not None: - node_msg = f'{node_msg}contract "{path}", ' + if self.path is not None: + node_msg = f'{node_msg}contract "{self.path}", ' col_offset_str = "" if node.col_offset is None else str(node.col_offset) node_msg = f"{node_msg}line {node.lineno}:{col_offset_str} \n{source_annotation}\n" From 363cf1d130877e19d48edad9e0761211fc8e4c94 Mon Sep 17 00:00:00 2001 From: Sand Bubbles Date: Sat, 2 Nov 2024 08:43:01 +0000 Subject: [PATCH 05/34] test natspec exception contains file --- tests/unit/ast/test_natspec.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/unit/ast/test_natspec.py b/tests/unit/ast/test_natspec.py index 710b7a9312..c29e4127ff 100644 --- a/tests/unit/ast/test_natspec.py +++ b/tests/unit/ast/test_natspec.py @@ -436,3 +436,19 @@ def test_natspec_parsed_implicitly(): # anything beyond ast is blocked with pytest.raises(NatSpecSyntaxException): compile_code(code, output_formats=["annotated_ast_dict"]) + + +def test_natspec_exception_contains_file_path(): + code = """ +@external +def foo() -> (int128,uint256): + ''' + @return int128 + @return uint256 + @return this should fail + ''' + return 1, 2 + """ + + with pytest.raises(NatSpecSyntaxException, match=r'contract ".*\.vy"'): + parse_natspec(code) From 241511b7f48ffe70a3b339af89f248e7aa6ee00f Mon Sep 17 00:00:00 2001 From: Sand Bubbles Date: Sat, 2 Nov 2024 09:12:39 +0000 Subject: [PATCH 06/34] test version exception contains filename --- tests/unit/ast/test_pre_parser.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/unit/ast/test_pre_parser.py b/tests/unit/ast/test_pre_parser.py index 4190725f7e..51d31e520e 100644 --- a/tests/unit/ast/test_pre_parser.py +++ b/tests/unit/ast/test_pre_parser.py @@ -1,4 +1,5 @@ import pytest +from pathlib import Path from vyper import compile_code from vyper.ast.pre_parser import pre_parse, validate_version_pragma @@ -56,6 +57,12 @@ def test_invalid_version_pragma(file_version, mock_version): validate_version_pragma(f"{file_version}", file_version, (SRC_LINE)) +def test_invalid_version_contains_file(mock_version): + mock_version(COMPILER_VERSION) + with pytest.raises(VersionException, match=r'contract ".*\.vy"'): + compile_code("# pragma version ^0.3.10", resolved_path=Path("mock.vy")) + + prerelease_valid_versions = [ "<0.1.1-beta.9", "<0.1.1b9", From b3c8ef51bc9f6f0b619e0c7076b14cc1aff2f651 Mon Sep 17 00:00:00 2001 From: Sand Bubbles Date: Sat, 2 Nov 2024 09:25:15 +0000 Subject: [PATCH 07/34] test imported invalid compiler version is attributed correctly --- tests/unit/ast/test_pre_parser.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/unit/ast/test_pre_parser.py b/tests/unit/ast/test_pre_parser.py index 51d31e520e..58b8c4f050 100644 --- a/tests/unit/ast/test_pre_parser.py +++ b/tests/unit/ast/test_pre_parser.py @@ -63,6 +63,16 @@ def test_invalid_version_contains_file(mock_version): compile_code("# pragma version ^0.3.10", resolved_path=Path("mock.vy")) +def test_imported_invalid_version_contains_correct_file(mock_version, make_input_bundle): + code_a = "# pragma version ^0.3.10" + code_b = "import A" + input_bundle = make_input_bundle({"A.vy": code_a, "B.vy": code_b}) + mock_version(COMPILER_VERSION) + + with pytest.raises(VersionException, match=r'contract ".*\/A\.vy"'): + compile_code(code_b, input_bundle=input_bundle) + + prerelease_valid_versions = [ "<0.1.1-beta.9", "<0.1.1b9", From 02c57652541809d0c1846637efc6d2132be63464 Mon Sep 17 00:00:00 2001 From: Sand Bubbles Date: Sat, 2 Nov 2024 09:40:10 +0000 Subject: [PATCH 08/34] check syntax exception in json contains filename --- tests/unit/cli/vyper_json/test_compile_json.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/cli/vyper_json/test_compile_json.py b/tests/unit/cli/vyper_json/test_compile_json.py index 5da98cf20f..565360c704 100644 --- a/tests/unit/cli/vyper_json/test_compile_json.py +++ b/tests/unit/cli/vyper_json/test_compile_json.py @@ -237,7 +237,7 @@ def test_wrong_language(): def test_exc_handler_raises_syntax(input_json): input_json["sources"]["badcode.vy"] = {"content": BAD_SYNTAX_CODE} - with pytest.raises(SyntaxException): + with pytest.raises(SyntaxException, match=r'contract ".*\.vy"'): compile_json(input_json) From 548d583f477bcc47ee791378a1ebc093d08b3541 Mon Sep 17 00:00:00 2001 From: Sand Bubbles Date: Sat, 2 Nov 2024 09:46:53 +0000 Subject: [PATCH 09/34] check the exception is attributed to the correct file --- tests/unit/ast/test_pre_parser.py | 2 +- tests/unit/cli/vyper_json/test_compile_json.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/ast/test_pre_parser.py b/tests/unit/ast/test_pre_parser.py index 58b8c4f050..512bbcca0c 100644 --- a/tests/unit/ast/test_pre_parser.py +++ b/tests/unit/ast/test_pre_parser.py @@ -69,7 +69,7 @@ def test_imported_invalid_version_contains_correct_file(mock_version, make_input input_bundle = make_input_bundle({"A.vy": code_a, "B.vy": code_b}) mock_version(COMPILER_VERSION) - with pytest.raises(VersionException, match=r'contract ".*\/A\.vy"'): + with pytest.raises(VersionException, match=r'contract ".*A\.vy"'): compile_code(code_b, input_bundle=input_bundle) diff --git a/tests/unit/cli/vyper_json/test_compile_json.py b/tests/unit/cli/vyper_json/test_compile_json.py index 565360c704..55fdc4a6b3 100644 --- a/tests/unit/cli/vyper_json/test_compile_json.py +++ b/tests/unit/cli/vyper_json/test_compile_json.py @@ -237,7 +237,7 @@ def test_wrong_language(): def test_exc_handler_raises_syntax(input_json): input_json["sources"]["badcode.vy"] = {"content": BAD_SYNTAX_CODE} - with pytest.raises(SyntaxException, match=r'contract ".*\.vy"'): + with pytest.raises(SyntaxException, match=r'contract ".*badcode\.vy"'): compile_json(input_json) From 67dcfb85f64ef319855b768fa6d6c0ee90a8990f Mon Sep 17 00:00:00 2001 From: Sand Bubbles Date: Sat, 2 Nov 2024 10:04:15 +0000 Subject: [PATCH 10/34] test syntax exceptions report file correctly --- .../exceptions/test_vyper_exception_pos.py | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/tests/functional/syntax/exceptions/test_vyper_exception_pos.py b/tests/functional/syntax/exceptions/test_vyper_exception_pos.py index 9e0767cb83..2e47d83866 100644 --- a/tests/functional/syntax/exceptions/test_vyper_exception_pos.py +++ b/tests/functional/syntax/exceptions/test_vyper_exception_pos.py @@ -1,6 +1,7 @@ from pytest import raises -from vyper.exceptions import VyperException +from vyper import compile_code +from vyper.exceptions import SyntaxException, VyperException def test_type_exception_pos(): @@ -29,3 +30,21 @@ def __init__(): """ assert_compile_failed(lambda: get_contract(code), VyperException) + + +def test_exception_contains_file(make_input_bundle): + code = """ +def bar()>: + """ + input_bundle = make_input_bundle({"code.vy": code}) + with raises(SyntaxException, match="contract"): + compile_code(code, input_bundle=input_bundle) + + +def test_exception_reports_correct_file(make_input_bundle): + code_a = "def bar()>:" + code_b = "import A" + input_bundle = make_input_bundle({"A.vy": code_a, "B.vy": code_b}) + + with raises(SyntaxException, match=r'contract ".*A\.vy"'): + compile_code(code_b, input_bundle=input_bundle) From 2dded1a17567b3f4541897b9545474ae754bebd6 Mon Sep 17 00:00:00 2001 From: Sand Bubbles Date: Mon, 4 Nov 2024 11:46:01 +0000 Subject: [PATCH 11/34] fix isort errors --- tests/unit/ast/test_pre_parser.py | 3 ++- vyper/compiler/phases.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/unit/ast/test_pre_parser.py b/tests/unit/ast/test_pre_parser.py index 512bbcca0c..3dcf48e9a4 100644 --- a/tests/unit/ast/test_pre_parser.py +++ b/tests/unit/ast/test_pre_parser.py @@ -1,6 +1,7 @@ -import pytest from pathlib import Path +import pytest + from vyper import compile_code from vyper.ast.pre_parser import pre_parse, validate_version_pragma from vyper.compiler.phases import CompilerData diff --git a/vyper/compiler/phases.py b/vyper/compiler/phases.py index 4c6d98bd62..65c17aa09c 100644 --- a/vyper/compiler/phases.py +++ b/vyper/compiler/phases.py @@ -10,6 +10,7 @@ from vyper.codegen.ir_node import IRnode from vyper.compiler.input_bundle import FileInput, FilesystemInputBundle, InputBundle from vyper.compiler.settings import OptimizationLevel, Settings, anchor_settings, merge_settings +from vyper.exceptions import SyntaxException from vyper.ir import compile_ir, optimizer from vyper.semantics import analyze_module, set_data_positions, validate_compilation_target from vyper.semantics.analysis.data_positions import generate_layout_export @@ -19,7 +20,6 @@ from vyper.typing import StorageLayout from vyper.utils import ERC5202_PREFIX, vyper_warn from vyper.venom import generate_assembly_experimental, generate_ir -from vyper.exceptions import SyntaxException DEFAULT_CONTRACT_PATH = PurePath("VyperContract.vy") From 2dfa03393d0802a22d15b2975ebbe32a54e1b5dd Mon Sep 17 00:00:00 2001 From: sandbubbles <160503471+sandbubbles@users.noreply.github.com> Date: Tue, 5 Nov 2024 14:14:07 +0100 Subject: [PATCH 12/34] refactor[ux]: add `venom` as `experimental-codegen` alias (#4337) Make "venom" an alternative alias to the "experimental_codegen" flag. Add the alias to cli, pre-parser, and json parser. --- tests/unit/ast/test_pre_parser.py | 28 +++++++++++++++ .../unit/cli/vyper_json/test_compile_json.py | 36 +++++++++++++++++++ vyper/ast/pre_parser.py | 4 +-- vyper/cli/vyper_compile.py | 1 + vyper/cli/vyper_json.py | 6 ++++ vyper/compiler/settings.py | 2 +- 6 files changed, 74 insertions(+), 3 deletions(-) diff --git a/tests/unit/ast/test_pre_parser.py b/tests/unit/ast/test_pre_parser.py index 3dcf48e9a4..fd24586a46 100644 --- a/tests/unit/ast/test_pre_parser.py +++ b/tests/unit/ast/test_pre_parser.py @@ -209,6 +209,25 @@ def test_parse_pragmas(code, pre_parse_settings, compiler_data_settings, mock_ve assert compiler_data.settings == compiler_data_settings +pragma_venom = [ + """ + #pragma venom + """, + """ + #pragma experimental-codegen + """, +] + + +@pytest.mark.parametrize("code", pragma_venom) +def test_parse_venom_pragma(code): + pre_parse_result = pre_parse(code) + assert pre_parse_result.settings.experimental_codegen is True + + compiler_data = CompilerData(code) + assert compiler_data.settings.experimental_codegen is True + + invalid_pragmas = [ # evm-versionnn """ @@ -236,6 +255,15 @@ def test_parse_pragmas(code, pre_parse_settings, compiler_data_settings, mock_ve # pragma evm-version cancun # pragma evm-version shanghai """, + # duplicate setting of venom + """ + #pragma venom + #pragma experimental-codegen + """, + """ + #pragma venom + #pragma venom + """, ] diff --git a/tests/unit/cli/vyper_json/test_compile_json.py b/tests/unit/cli/vyper_json/test_compile_json.py index 55fdc4a6b3..d3386b567c 100644 --- a/tests/unit/cli/vyper_json/test_compile_json.py +++ b/tests/unit/cli/vyper_json/test_compile_json.py @@ -9,6 +9,7 @@ compile_json, exc_handler_to_dict, get_inputs, + get_settings, ) from vyper.compiler import OUTPUT_FORMATS, compile_code, compile_from_file_input from vyper.compiler.input_bundle import JSONInputBundle @@ -319,3 +320,38 @@ def test_compile_json_with_abi_top(make_input_bundle): """ input_bundle = make_input_bundle({"stream.json": stream, "code.vy": code}) vyper.compiler.compile_code(code, input_bundle=input_bundle) + + +def test_compile_json_with_experimental_codegen(): + code = { + "language": "Vyper", + "sources": {"foo.vy": {"content": "@external\ndef foo() -> bool:\n return True"}}, + "settings": { + "evmVersion": "cancun", + "optimize": "gas", + "venom": True, + "search_paths": [], + "outputSelection": {"*": ["ast"]}, + }, + } + + settings = get_settings(code) + assert settings.experimental_codegen is True + + +def test_compile_json_with_both_venom_aliases(): + code = { + "language": "Vyper", + "sources": {"foo.vy": {"content": ""}}, + "settings": { + "evmVersion": "cancun", + "optimize": "gas", + "experimentalCodegen": False, + "venom": False, + "search_paths": [], + "outputSelection": {"*": ["ast"]}, + }, + } + with pytest.raises(JSONError) as e: + get_settings(code) + assert e.value.args[0] == "both experimentalCodegen and venom cannot be set" diff --git a/vyper/ast/pre_parser.py b/vyper/ast/pre_parser.py index 07ba1d2d0d..5d2abcf645 100644 --- a/vyper/ast/pre_parser.py +++ b/vyper/ast/pre_parser.py @@ -265,10 +265,10 @@ def pre_parse(code: str) -> PreParseResult: if evm_version not in EVM_VERSIONS: raise StructureException(f"Invalid evm version: `{evm_version}`", start) settings.evm_version = evm_version - elif pragma.startswith("experimental-codegen"): + elif pragma.startswith("experimental-codegen") or pragma.startswith("venom"): if settings.experimental_codegen is not None: raise StructureException( - "pragma experimental-codegen specified twice!", start + "pragma experimental-codegen/venom specified twice!", start ) settings.experimental_codegen = True elif pragma.startswith("enable-decimals"): diff --git a/vyper/cli/vyper_compile.py b/vyper/cli/vyper_compile.py index 5999aed178..fde35f781e 100755 --- a/vyper/cli/vyper_compile.py +++ b/vyper/cli/vyper_compile.py @@ -176,6 +176,7 @@ def _parse_args(argv): parser.add_argument("-o", help="Set the output path", dest="output_path") parser.add_argument( "--experimental-codegen", + "--venom", help="The compiler use the new IR codegen. This is an experimental feature.", action="store_true", dest="experimental_codegen", diff --git a/vyper/cli/vyper_json.py b/vyper/cli/vyper_json.py index 7d0e6064af..9fcdf27baf 100755 --- a/vyper/cli/vyper_json.py +++ b/vyper/cli/vyper_json.py @@ -253,7 +253,13 @@ def get_settings(input_dict: dict) -> Settings: evm_version = get_evm_version(input_dict) optimize = input_dict["settings"].get("optimize") + experimental_codegen = input_dict["settings"].get("experimentalCodegen") + if experimental_codegen is None: + experimental_codegen = input_dict["settings"].get("venom") + elif input_dict["settings"].get("venom") is not None: + raise JSONError("both experimentalCodegen and venom cannot be set") + if isinstance(optimize, bool): # bool optimization level for backwards compatibility warnings.warn( diff --git a/vyper/compiler/settings.py b/vyper/compiler/settings.py index 7c20e03906..a8e28c1ed1 100644 --- a/vyper/compiler/settings.py +++ b/vyper/compiler/settings.py @@ -77,7 +77,7 @@ def as_cli(self): if self.optimize is not None: ret.append(" --optimize " + str(self.optimize)) if self.experimental_codegen is True: - ret.append(" --experimental-codegen") + ret.append(" --venom") if self.evm_version is not None: ret.append(" --evm-version " + self.evm_version) if self.debug is True: From 5637913695d73d398d5cc6b70344d42daef614cb Mon Sep 17 00:00:00 2001 From: Charles Cooper Date: Tue, 5 Nov 2024 09:17:02 -0500 Subject: [PATCH 13/34] fix[ci]: fix README encoding in `setup.py` (#4348) the call to `setup()` would fail on windows when there are unicode characters in `README.md`, because files are apparently opened with encoding `cp1252` by default on windows. this commit ensures the file is opened with `utf-8` encoding. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 6e48129cba..5d6bd1db3a 100644 --- a/setup.py +++ b/setup.py @@ -36,7 +36,7 @@ extras_require["dev"] = extras_require["dev"] + extras_require["test"] + extras_require["lint"] -with open("README.md", "r") as f: +with open("README.md", "r", encoding="utf-8") as f: long_description = f.read() From b6591b45276e03c3ddc460b807136dd614abc497 Mon Sep 17 00:00:00 2001 From: Rafael Abuawad Date: Tue, 5 Nov 2024 10:34:52 -0400 Subject: [PATCH 14/34] feat[docs]: add Telegram badge to README.md (#4342) --------- Co-authored-by: Charles Cooper --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index bcaa50b570..84c2948ceb 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ [![Build Status](https://github.com/vyperlang/vyper/workflows/Test/badge.svg)](https://github.com/vyperlang/vyper/actions/workflows/test.yml) [![Documentation Status](https://readthedocs.org/projects/vyper/badge/?version=latest)](http://docs.vyperlang.org/en/latest/?badge=latest "ReadTheDocs") [![Discord](https://img.shields.io/discord/969926564286459934.svg?label=%23vyper)](https://discord.gg/6tw7PTM7C2) +[![Telegram](https://img.shields.io/badge/Vyperholics🐍-Telegram-blue)](https://t.me/vyperlang) [![PyPI](https://badge.fury.io/py/vyper.svg)](https://pypi.org/project/vyper "PyPI") [![Docker](https://img.shields.io/docker/cloud/build/vyperlang/vyper)](https://hub.docker.com/r/vyperlang/vyper "DockerHub") From 94504284b27bc347e521541757d9a4c166e1d0e7 Mon Sep 17 00:00:00 2001 From: Sand Bubbles Date: Thu, 7 Nov 2024 11:38:12 +0000 Subject: [PATCH 15/34] extract adding contract info into a function --- vyper/exceptions.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/vyper/exceptions.py b/vyper/exceptions.py index a92b10170d..cbef12e19b 100644 --- a/vyper/exceptions.py +++ b/vyper/exceptions.py @@ -129,14 +129,14 @@ def format_annotation(self, value): # TODO: handle cases where module is None or vy_ast.Module if module_node.get("path") not in (None, ""): - node_msg = f'{node_msg}contract "{module_node.path}:{node.lineno}", ' + node_msg = self._append_contract(node_msg, module_node.path, node.lineno) fn_node = node.get_ancestor(vy_ast.FunctionDef) if fn_node: node_msg = f'{node_msg}function "{fn_node.name}", ' - if self.path is not None: - node_msg = f'{node_msg}contract "{self.path}", ' + elif self.path is not None: + node_msg = self._append_contract(node_msg, self.path, node.lineno) col_offset_str = "" if node.col_offset is None else str(node.col_offset) node_msg = f"{node_msg}line {node.lineno}:{col_offset_str} \n{source_annotation}\n" @@ -155,6 +155,9 @@ def _add_hint(self, msg): return msg return msg + f"\n (hint: {self.hint})" + def _append_contract(self, msg, path, lineno): + return f'{msg}contract "{path}:{lineno}", ' + def __str__(self): return self._add_hint(self._str_helper()) From 97b3abe6c3067aa42eb0d4d09044f38efb01b7ff Mon Sep 17 00:00:00 2001 From: Sand Bubbles Date: Thu, 7 Nov 2024 11:41:29 +0000 Subject: [PATCH 16/34] update error message verification to include line number --- .../functional/syntax/exceptions/test_vyper_exception_pos.py | 2 +- tests/unit/ast/test_natspec.py | 2 +- tests/unit/ast/test_pre_parser.py | 4 ++-- tests/unit/cli/vyper_json/test_compile_json.py | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/functional/syntax/exceptions/test_vyper_exception_pos.py b/tests/functional/syntax/exceptions/test_vyper_exception_pos.py index 2e47d83866..dac125001b 100644 --- a/tests/functional/syntax/exceptions/test_vyper_exception_pos.py +++ b/tests/functional/syntax/exceptions/test_vyper_exception_pos.py @@ -46,5 +46,5 @@ def test_exception_reports_correct_file(make_input_bundle): code_b = "import A" input_bundle = make_input_bundle({"A.vy": code_a, "B.vy": code_b}) - with raises(SyntaxException, match=r'contract ".*A\.vy"'): + with raises(SyntaxException, match=r'contract ".*A\.vy:\d+"'): compile_code(code_b, input_bundle=input_bundle) diff --git a/tests/unit/ast/test_natspec.py b/tests/unit/ast/test_natspec.py index c29e4127ff..3c819cc908 100644 --- a/tests/unit/ast/test_natspec.py +++ b/tests/unit/ast/test_natspec.py @@ -450,5 +450,5 @@ def foo() -> (int128,uint256): return 1, 2 """ - with pytest.raises(NatSpecSyntaxException, match=r'contract ".*\.vy"'): + with pytest.raises(NatSpecSyntaxException, match=r'contract ".*\.vy:\d+"'): parse_natspec(code) diff --git a/tests/unit/ast/test_pre_parser.py b/tests/unit/ast/test_pre_parser.py index fd24586a46..3242963dbb 100644 --- a/tests/unit/ast/test_pre_parser.py +++ b/tests/unit/ast/test_pre_parser.py @@ -60,7 +60,7 @@ def test_invalid_version_pragma(file_version, mock_version): def test_invalid_version_contains_file(mock_version): mock_version(COMPILER_VERSION) - with pytest.raises(VersionException, match=r'contract ".*\.vy"'): + with pytest.raises(VersionException, match=r'contract ".*\.vy:\d+"'): compile_code("# pragma version ^0.3.10", resolved_path=Path("mock.vy")) @@ -70,7 +70,7 @@ def test_imported_invalid_version_contains_correct_file(mock_version, make_input input_bundle = make_input_bundle({"A.vy": code_a, "B.vy": code_b}) mock_version(COMPILER_VERSION) - with pytest.raises(VersionException, match=r'contract ".*A\.vy"'): + with pytest.raises(VersionException, match=r'contract ".*A\.vy:\d+"'): compile_code(code_b, input_bundle=input_bundle) diff --git a/tests/unit/cli/vyper_json/test_compile_json.py b/tests/unit/cli/vyper_json/test_compile_json.py index d3386b567c..3479d33579 100644 --- a/tests/unit/cli/vyper_json/test_compile_json.py +++ b/tests/unit/cli/vyper_json/test_compile_json.py @@ -238,7 +238,7 @@ def test_wrong_language(): def test_exc_handler_raises_syntax(input_json): input_json["sources"]["badcode.vy"] = {"content": BAD_SYNTAX_CODE} - with pytest.raises(SyntaxException, match=r'contract ".*badcode\.vy"'): + with pytest.raises(SyntaxException, match=r'contract ".*badcode\.vy:\d+"'): compile_json(input_json) From 05e24d6a24f25c16cb64ee1195cf05df2418c0b9 Mon Sep 17 00:00:00 2001 From: Sand Bubbles Date: Thu, 21 Nov 2024 11:23:35 +0000 Subject: [PATCH 17/34] wrap parse_natspec in a try catch to add path --- vyper/ast/natspec.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/vyper/ast/natspec.py b/vyper/ast/natspec.py index 48fc9134dd..d2fe147258 100644 --- a/vyper/ast/natspec.py +++ b/vyper/ast/natspec.py @@ -17,8 +17,15 @@ class NatspecOutput: userdoc: dict devdoc: dict - def parse_natspec(annotated_vyper_module: vy_ast.Module) -> NatspecOutput: + try: + return _parse_natspec(annotated_vyper_module) + except NatSpecSyntaxException as e: + e.path = annotated_vyper_module.resolved_path + raise e + pass + +def _parse_natspec(annotated_vyper_module: vy_ast.Module) -> NatspecOutput: """ Parses NatSpec documentation from a contract. From 5101a2724341db90104d4ce7345dc421106b7915 Mon Sep 17 00:00:00 2001 From: Sand Bubbles Date: Thu, 21 Nov 2024 11:24:30 +0000 Subject: [PATCH 18/34] remove changes to _annotate functions --- vyper/compiler/phases.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/vyper/compiler/phases.py b/vyper/compiler/phases.py index 65c17aa09c..d9b6b13b48 100644 --- a/vyper/compiler/phases.py +++ b/vyper/compiler/phases.py @@ -10,7 +10,6 @@ from vyper.codegen.ir_node import IRnode from vyper.compiler.input_bundle import FileInput, FilesystemInputBundle, InputBundle from vyper.compiler.settings import OptimizationLevel, Settings, anchor_settings, merge_settings -from vyper.exceptions import SyntaxException from vyper.ir import compile_ir, optimizer from vyper.semantics import analyze_module, set_data_positions, validate_compilation_target from vyper.semantics.analysis.data_positions import generate_layout_export @@ -175,11 +174,7 @@ def resolved_imports(self): def _annotate(self) -> tuple[natspec.NatspecOutput, vy_ast.Module]: module = self._resolve_imports[0] analyze_module(module) - try: - nspec = natspec.parse_natspec(module) - except SyntaxException as e: - e.path = module.resolved_path - raise e + nspec = natspec.parse_natspec(module) return nspec, module @cached_property From 7f44b10baf136fbf419b663afc56b693b1ae3767 Mon Sep 17 00:00:00 2001 From: Sand Bubbles Date: Thu, 21 Nov 2024 11:25:34 +0000 Subject: [PATCH 19/34] rename method to "_format_contract_details" --- vyper/exceptions.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vyper/exceptions.py b/vyper/exceptions.py index cbef12e19b..5060b76e7d 100644 --- a/vyper/exceptions.py +++ b/vyper/exceptions.py @@ -129,14 +129,14 @@ def format_annotation(self, value): # TODO: handle cases where module is None or vy_ast.Module if module_node.get("path") not in (None, ""): - node_msg = self._append_contract(node_msg, module_node.path, node.lineno) + node_msg = self._format_contract_details(node_msg, module_node.path, node.lineno) fn_node = node.get_ancestor(vy_ast.FunctionDef) if fn_node: node_msg = f'{node_msg}function "{fn_node.name}", ' elif self.path is not None: - node_msg = self._append_contract(node_msg, self.path, node.lineno) + node_msg = self._format_contract_details(node_msg, self.path, node.lineno) col_offset_str = "" if node.col_offset is None else str(node.col_offset) node_msg = f"{node_msg}line {node.lineno}:{col_offset_str} \n{source_annotation}\n" @@ -155,7 +155,7 @@ def _add_hint(self, msg): return msg return msg + f"\n (hint: {self.hint})" - def _append_contract(self, msg, path, lineno): + def _format_contract_details(self, msg, path, lineno): return f'{msg}contract "{path}:{lineno}", ' def __str__(self): From 88174e746ab9ebc507d0e52e4bab290a7b87cf12 Mon Sep 17 00:00:00 2001 From: Sand Bubbles Date: Thu, 21 Nov 2024 11:26:04 +0000 Subject: [PATCH 20/34] lint --- vyper/ast/natspec.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/vyper/ast/natspec.py b/vyper/ast/natspec.py index d2fe147258..812a6e8642 100644 --- a/vyper/ast/natspec.py +++ b/vyper/ast/natspec.py @@ -17,6 +17,7 @@ class NatspecOutput: userdoc: dict devdoc: dict + def parse_natspec(annotated_vyper_module: vy_ast.Module) -> NatspecOutput: try: return _parse_natspec(annotated_vyper_module) @@ -25,6 +26,7 @@ def parse_natspec(annotated_vyper_module: vy_ast.Module) -> NatspecOutput: raise e pass + def _parse_natspec(annotated_vyper_module: vy_ast.Module) -> NatspecOutput: """ Parses NatSpec documentation from a contract. From e66da98285834336e6c4c9f39eca58454dc1b5a0 Mon Sep 17 00:00:00 2001 From: Sand Bubbles Date: Thu, 21 Nov 2024 11:51:52 +0000 Subject: [PATCH 21/34] remove redundant "pass" --- vyper/ast/natspec.py | 1 - 1 file changed, 1 deletion(-) diff --git a/vyper/ast/natspec.py b/vyper/ast/natspec.py index 812a6e8642..40efe54532 100644 --- a/vyper/ast/natspec.py +++ b/vyper/ast/natspec.py @@ -24,7 +24,6 @@ def parse_natspec(annotated_vyper_module: vy_ast.Module) -> NatspecOutput: except NatSpecSyntaxException as e: e.path = annotated_vyper_module.resolved_path raise e - pass def _parse_natspec(annotated_vyper_module: vy_ast.Module) -> NatspecOutput: From bf3ee5bfa75e91e9a912fcdef08ec3c1a8c57dc7 Mon Sep 17 00:00:00 2001 From: Sand Bubbles Date: Thu, 21 Nov 2024 12:45:11 +0000 Subject: [PATCH 22/34] change path to resolved_path in exceptions --- vyper/exceptions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vyper/exceptions.py b/vyper/exceptions.py index 5060b76e7d..ea522fe6c0 100644 --- a/vyper/exceptions.py +++ b/vyper/exceptions.py @@ -129,7 +129,7 @@ def format_annotation(self, value): # TODO: handle cases where module is None or vy_ast.Module if module_node.get("path") not in (None, ""): - node_msg = self._format_contract_details(node_msg, module_node.path, node.lineno) + node_msg = self._format_contract_details(node_msg, module_node.resolved_path, node.lineno) fn_node = node.get_ancestor(vy_ast.FunctionDef) if fn_node: From bd0d2d32616e3a0e8afc5ea79b00fbf468cad102 Mon Sep 17 00:00:00 2001 From: Sand Bubbles Date: Thu, 21 Nov 2024 12:45:38 +0000 Subject: [PATCH 23/34] wrap path in safe_relpath --- vyper/exceptions.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/vyper/exceptions.py b/vyper/exceptions.py index ea522fe6c0..a8b8b6be82 100644 --- a/vyper/exceptions.py +++ b/vyper/exceptions.py @@ -156,7 +156,8 @@ def _add_hint(self, msg): return msg + f"\n (hint: {self.hint})" def _format_contract_details(self, msg, path, lineno): - return f'{msg}contract "{path}:{lineno}", ' + from vyper.utils import safe_relpath + return f'{msg}contract "{safe_relpath(path)}:{lineno}", ' def __str__(self): return self._add_hint(self._str_helper()) From b6adc0b857c872f47e811688c13dd13d5d383308 Mon Sep 17 00:00:00 2001 From: Sand Bubbles Date: Thu, 21 Nov 2024 12:46:13 +0000 Subject: [PATCH 24/34] lint --- vyper/exceptions.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/vyper/exceptions.py b/vyper/exceptions.py index a8b8b6be82..15832a193f 100644 --- a/vyper/exceptions.py +++ b/vyper/exceptions.py @@ -129,7 +129,9 @@ def format_annotation(self, value): # TODO: handle cases where module is None or vy_ast.Module if module_node.get("path") not in (None, ""): - node_msg = self._format_contract_details(node_msg, module_node.resolved_path, node.lineno) + node_msg = self._format_contract_details( + node_msg, module_node.resolved_path, node.lineno + ) fn_node = node.get_ancestor(vy_ast.FunctionDef) if fn_node: @@ -157,6 +159,7 @@ def _add_hint(self, msg): def _format_contract_details(self, msg, path, lineno): from vyper.utils import safe_relpath + return f'{msg}contract "{safe_relpath(path)}:{lineno}", ' def __str__(self): From 43edf77de7598cfe8e064585b75cea48352c512d Mon Sep 17 00:00:00 2001 From: Sand Bubbles Date: Thu, 21 Nov 2024 14:27:53 +0000 Subject: [PATCH 25/34] add chdir_tmp_path to test fixtures and remove wildcards --- .../syntax/exceptions/test_vyper_exception_pos.py | 4 ++-- tests/unit/ast/test_natspec.py | 4 ++-- tests/unit/ast/test_pre_parser.py | 10 ++++++---- tests/unit/cli/vyper_json/test_compile_json.py | 4 ++-- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/tests/functional/syntax/exceptions/test_vyper_exception_pos.py b/tests/functional/syntax/exceptions/test_vyper_exception_pos.py index dac125001b..e3a82c711f 100644 --- a/tests/functional/syntax/exceptions/test_vyper_exception_pos.py +++ b/tests/functional/syntax/exceptions/test_vyper_exception_pos.py @@ -41,10 +41,10 @@ def bar()>: compile_code(code, input_bundle=input_bundle) -def test_exception_reports_correct_file(make_input_bundle): +def test_exception_reports_correct_file(make_input_bundle, chdir_tmp_path): code_a = "def bar()>:" code_b = "import A" input_bundle = make_input_bundle({"A.vy": code_a, "B.vy": code_b}) - with raises(SyntaxException, match=r'contract ".*A\.vy:\d+"'): + with raises(SyntaxException, match=r'contract "A\.vy:\d+"'): compile_code(code_b, input_bundle=input_bundle) diff --git a/tests/unit/ast/test_natspec.py b/tests/unit/ast/test_natspec.py index 3c819cc908..c5a2ded292 100644 --- a/tests/unit/ast/test_natspec.py +++ b/tests/unit/ast/test_natspec.py @@ -438,7 +438,7 @@ def test_natspec_parsed_implicitly(): compile_code(code, output_formats=["annotated_ast_dict"]) -def test_natspec_exception_contains_file_path(): +def test_natspec_exception_contains_file_path(chdir_tmp_path): code = """ @external def foo() -> (int128,uint256): @@ -450,5 +450,5 @@ def foo() -> (int128,uint256): return 1, 2 """ - with pytest.raises(NatSpecSyntaxException, match=r'contract ".*\.vy:\d+"'): + with pytest.raises(NatSpecSyntaxException, match=r'contract "VyperContract\.vy:\d+"'): parse_natspec(code) diff --git a/tests/unit/ast/test_pre_parser.py b/tests/unit/ast/test_pre_parser.py index 3242963dbb..082b0f9b52 100644 --- a/tests/unit/ast/test_pre_parser.py +++ b/tests/unit/ast/test_pre_parser.py @@ -58,19 +58,21 @@ def test_invalid_version_pragma(file_version, mock_version): validate_version_pragma(f"{file_version}", file_version, (SRC_LINE)) -def test_invalid_version_contains_file(mock_version): +def test_invalid_version_contains_file(mock_version, chdir_tmp_path): mock_version(COMPILER_VERSION) - with pytest.raises(VersionException, match=r'contract ".*\.vy:\d+"'): + with pytest.raises(VersionException, match=r'contract "mock\.vy:\d+"'): compile_code("# pragma version ^0.3.10", resolved_path=Path("mock.vy")) -def test_imported_invalid_version_contains_correct_file(mock_version, make_input_bundle): +def test_imported_invalid_version_contains_correct_file( + mock_version, make_input_bundle, chdir_tmp_path +): code_a = "# pragma version ^0.3.10" code_b = "import A" input_bundle = make_input_bundle({"A.vy": code_a, "B.vy": code_b}) mock_version(COMPILER_VERSION) - with pytest.raises(VersionException, match=r'contract ".*A\.vy:\d+"'): + with pytest.raises(VersionException, match=r'contract "A\.vy:\d+"'): compile_code(code_b, input_bundle=input_bundle) diff --git a/tests/unit/cli/vyper_json/test_compile_json.py b/tests/unit/cli/vyper_json/test_compile_json.py index 3479d33579..c5980123ad 100644 --- a/tests/unit/cli/vyper_json/test_compile_json.py +++ b/tests/unit/cli/vyper_json/test_compile_json.py @@ -236,9 +236,9 @@ def test_wrong_language(): compile_json({"language": "Solidity"}) -def test_exc_handler_raises_syntax(input_json): +def test_exc_handler_raises_syntax(input_json, chdir_tmp_path): input_json["sources"]["badcode.vy"] = {"content": BAD_SYNTAX_CODE} - with pytest.raises(SyntaxException, match=r'contract ".*badcode\.vy:\d+"'): + with pytest.raises(SyntaxException, match=r'contract "badcode\.vy:\d+"'): compile_json(input_json) From 96bac14e54034b38bd96da878d0d2cef3fd30ff5 Mon Sep 17 00:00:00 2001 From: Sand Bubbles Date: Thu, 21 Nov 2024 16:00:16 +0000 Subject: [PATCH 26/34] remove chdir_tmp_path when make_input_bundle is not used --- tests/unit/ast/test_natspec.py | 2 +- tests/unit/ast/test_pre_parser.py | 2 +- tests/unit/cli/vyper_json/test_compile_json.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/unit/ast/test_natspec.py b/tests/unit/ast/test_natspec.py index c5a2ded292..37120d2978 100644 --- a/tests/unit/ast/test_natspec.py +++ b/tests/unit/ast/test_natspec.py @@ -438,7 +438,7 @@ def test_natspec_parsed_implicitly(): compile_code(code, output_formats=["annotated_ast_dict"]) -def test_natspec_exception_contains_file_path(chdir_tmp_path): +def test_natspec_exception_contains_file_path(): code = """ @external def foo() -> (int128,uint256): diff --git a/tests/unit/ast/test_pre_parser.py b/tests/unit/ast/test_pre_parser.py index 082b0f9b52..ed3b5ddc6b 100644 --- a/tests/unit/ast/test_pre_parser.py +++ b/tests/unit/ast/test_pre_parser.py @@ -58,7 +58,7 @@ def test_invalid_version_pragma(file_version, mock_version): validate_version_pragma(f"{file_version}", file_version, (SRC_LINE)) -def test_invalid_version_contains_file(mock_version, chdir_tmp_path): +def test_invalid_version_contains_file(mock_version): mock_version(COMPILER_VERSION) with pytest.raises(VersionException, match=r'contract "mock\.vy:\d+"'): compile_code("# pragma version ^0.3.10", resolved_path=Path("mock.vy")) diff --git a/tests/unit/cli/vyper_json/test_compile_json.py b/tests/unit/cli/vyper_json/test_compile_json.py index c5980123ad..8cd9be128f 100644 --- a/tests/unit/cli/vyper_json/test_compile_json.py +++ b/tests/unit/cli/vyper_json/test_compile_json.py @@ -236,7 +236,7 @@ def test_wrong_language(): compile_json({"language": "Solidity"}) -def test_exc_handler_raises_syntax(input_json, chdir_tmp_path): +def test_exc_handler_raises_syntax(input_json): input_json["sources"]["badcode.vy"] = {"content": BAD_SYNTAX_CODE} with pytest.raises(SyntaxException, match=r'contract "badcode\.vy:\d+"'): compile_json(input_json) From af391a653a955a477ca762b9439c874a6ed88ca8 Mon Sep 17 00:00:00 2001 From: Sand Bubbles Date: Sat, 7 Dec 2024 16:54:01 +0100 Subject: [PATCH 27/34] check resolved_path is not unknown --- vyper/exceptions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vyper/exceptions.py b/vyper/exceptions.py index 15832a193f..ada2e02109 100644 --- a/vyper/exceptions.py +++ b/vyper/exceptions.py @@ -128,7 +128,7 @@ def format_annotation(self, value): module_node = node.module_node # TODO: handle cases where module is None or vy_ast.Module - if module_node.get("path") not in (None, ""): + if module_node.get("resolved_path") not in (None, ""): node_msg = self._format_contract_details( node_msg, module_node.resolved_path, node.lineno ) From c09dc650ea7fa77eb34e9af371ba82ba0f7f3d9a Mon Sep 17 00:00:00 2001 From: Sand Bubbles Date: Sat, 7 Dec 2024 17:21:03 +0100 Subject: [PATCH 28/34] use only the msg of SyntaxError --- 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 55e0cc8c18..8482c6e2bc 100644 --- a/vyper/ast/parse.py +++ b/vyper/ast/parse.py @@ -75,7 +75,7 @@ def _parse_to_ast_with_settings( py_ast = python_ast.parse(pre_parse_result.reformatted_code) except SyntaxError as e: # TODO: Ensure 1-to-1 match of source_code:reformatted_code SyntaxErrors - raise SyntaxException(str(e), vyper_source, e.lineno, e.offset) from None + raise SyntaxException(str(e.msg), vyper_source, e.lineno, e.offset) from None # Add dummy function node to ensure local variables are treated as `AnnAssign` # instead of state variables (`VariableDecl`) From fd8d78d81394ec1339ebafa07a7b3982c54b33e5 Mon Sep 17 00:00:00 2001 From: Sand Bubbles Date: Sat, 7 Dec 2024 19:10:45 +0100 Subject: [PATCH 29/34] adjust the offset by 1 --- 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 35da5ff14d..c9eeb255d6 100644 --- a/vyper/ast/parse.py +++ b/vyper/ast/parse.py @@ -76,7 +76,7 @@ def _parse_to_ast_with_settings( py_ast = python_ast.parse(pre_parser.reformatted_code) except SyntaxError as e: # TODO: Ensure 1-to-1 match of source_code:reformatted_code SyntaxErrors - raise SyntaxException(str(e.msg), vyper_source, e.lineno, e.offset) from None + raise SyntaxException(str(e.msg), vyper_source, e.lineno, e.offset - 1) from None # Add dummy function node to ensure local variables are treated as `AnnAssign` # instead of state variables (`VariableDecl`) From 78362e6bb56f13c044fff7db3b7c36d400790ddf Mon Sep 17 00:00:00 2001 From: Sand Bubbles Date: Sat, 7 Dec 2024 19:32:32 +0100 Subject: [PATCH 30/34] test offset points to correct place --- .../syntax/exceptions/test_vyper_exception_pos.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/functional/syntax/exceptions/test_vyper_exception_pos.py b/tests/functional/syntax/exceptions/test_vyper_exception_pos.py index e3a82c711f..0c712eb035 100644 --- a/tests/functional/syntax/exceptions/test_vyper_exception_pos.py +++ b/tests/functional/syntax/exceptions/test_vyper_exception_pos.py @@ -48,3 +48,14 @@ def test_exception_reports_correct_file(make_input_bundle, chdir_tmp_path): with raises(SyntaxException, match=r'contract "A\.vy:\d+"'): compile_code(code_b, input_bundle=input_bundle) + + +def test_syntax_exception_reports_correct_offset(make_input_bundle): + code = """ +def foo(): + uint256 a = pass + """ + input_bundle = make_input_bundle({"code.vy": code}) + + with raises(SyntaxException, match="line \d+:12"): + compile_code(code, input_bundle=input_bundle) From 5ea4e2c81526eaa7ba62ae2135eae7bea4e34e75 Mon Sep 17 00:00:00 2001 From: Sand Bubbles Date: Sat, 7 Dec 2024 19:32:47 +0100 Subject: [PATCH 31/34] add comment --- vyper/ast/parse.py | 1 + 1 file changed, 1 insertion(+) diff --git a/vyper/ast/parse.py b/vyper/ast/parse.py index c9eeb255d6..aa328544d1 100644 --- a/vyper/ast/parse.py +++ b/vyper/ast/parse.py @@ -76,6 +76,7 @@ def _parse_to_ast_with_settings( py_ast = python_ast.parse(pre_parser.reformatted_code) except SyntaxError as e: # TODO: Ensure 1-to-1 match of source_code:reformatted_code SyntaxErrors + # SyntaxError offset is 1-based, not 0-based raise SyntaxException(str(e.msg), vyper_source, e.lineno, e.offset - 1) from None # Add dummy function node to ensure local variables are treated as `AnnAssign` From 5022cfd3bb55ba2c7239c9ec1774c9a7332046a2 Mon Sep 17 00:00:00 2001 From: Charles Cooper Date: Fri, 13 Dec 2024 11:27:40 -0500 Subject: [PATCH 32/34] nits --- vyper/ast/natspec.py | 2 +- vyper/ast/parse.py | 5 +++-- vyper/exceptions.py | 6 +++--- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/vyper/ast/natspec.py b/vyper/ast/natspec.py index 40efe54532..f65a361338 100644 --- a/vyper/ast/natspec.py +++ b/vyper/ast/natspec.py @@ -22,7 +22,7 @@ def parse_natspec(annotated_vyper_module: vy_ast.Module) -> NatspecOutput: try: return _parse_natspec(annotated_vyper_module) except NatSpecSyntaxException as e: - e.path = annotated_vyper_module.resolved_path + e.resolved_path = annotated_vyper_module.resolved_path raise e diff --git a/vyper/ast/parse.py b/vyper/ast/parse.py index aa328544d1..97ad5d3c1b 100644 --- a/vyper/ast/parse.py +++ b/vyper/ast/parse.py @@ -29,7 +29,7 @@ def parse_to_ast_with_settings( vyper_source, source_id, module_path, resolved_path, add_fn_node ) except SyntaxException as e: - e.path = resolved_path + e.resolved_path = resolved_path raise e @@ -76,7 +76,8 @@ def _parse_to_ast_with_settings( py_ast = python_ast.parse(pre_parser.reformatted_code) except SyntaxError as e: # TODO: Ensure 1-to-1 match of source_code:reformatted_code SyntaxErrors - # SyntaxError offset is 1-based, not 0-based + # SyntaxError offset is 1-based, not 0-based (see: + # https://docs.python.org/3/library/exceptions.html#SyntaxError.offset) raise SyntaxException(str(e.msg), vyper_source, e.lineno, e.offset - 1) from None # Add dummy function node to ensure local variables are treated as `AnnAssign` diff --git a/vyper/exceptions.py b/vyper/exceptions.py index ada2e02109..990dbf7953 100644 --- a/vyper/exceptions.py +++ b/vyper/exceptions.py @@ -54,7 +54,7 @@ def __init__(self, message="Error Message not found.", *items, hint=None, prev_d self.lineno = None self.col_offset = None self.annotations = None - self.path = None + self.resolved_path = None if len(items) == 1 and isinstance(items[0], tuple) and isinstance(items[0][0], int): # support older exceptions that don't annotate - remove this in the future! @@ -137,8 +137,8 @@ def format_annotation(self, value): if fn_node: node_msg = f'{node_msg}function "{fn_node.name}", ' - elif self.path is not None: - node_msg = self._format_contract_details(node_msg, self.path, node.lineno) + elif self.resolved_path is not None: + node_msg = self._format_contract_details(node_msg, self.resolved_path, node.lineno) col_offset_str = "" if node.col_offset is None else str(node.col_offset) node_msg = f"{node_msg}line {node.lineno}:{col_offset_str} \n{source_annotation}\n" From 7f0b3f4e1d02dedd49ae581f75046ed9fff43d8d Mon Sep 17 00:00:00 2001 From: Charles Cooper Date: Fri, 13 Dec 2024 11:29:18 -0500 Subject: [PATCH 33/34] fix lint --- .../functional/syntax/exceptions/test_vyper_exception_pos.py | 2 +- vyper/ast/parse.py | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/functional/syntax/exceptions/test_vyper_exception_pos.py b/tests/functional/syntax/exceptions/test_vyper_exception_pos.py index 0c712eb035..17bd4de1cd 100644 --- a/tests/functional/syntax/exceptions/test_vyper_exception_pos.py +++ b/tests/functional/syntax/exceptions/test_vyper_exception_pos.py @@ -57,5 +57,5 @@ def foo(): """ input_bundle = make_input_bundle({"code.vy": code}) - with raises(SyntaxException, match="line \d+:12"): + with raises(SyntaxException, match=r"line \d+:12"): compile_code(code, input_bundle=input_bundle) diff --git a/vyper/ast/parse.py b/vyper/ast/parse.py index 97ad5d3c1b..3e818f3136 100644 --- a/vyper/ast/parse.py +++ b/vyper/ast/parse.py @@ -78,7 +78,10 @@ def _parse_to_ast_with_settings( # TODO: Ensure 1-to-1 match of source_code:reformatted_code SyntaxErrors # SyntaxError offset is 1-based, not 0-based (see: # https://docs.python.org/3/library/exceptions.html#SyntaxError.offset) - raise SyntaxException(str(e.msg), vyper_source, e.lineno, e.offset - 1) from None + offset = e.offset + if offset is not None: + offset -= 1 + raise SyntaxException(str(e.msg), vyper_source, e.lineno, offset) from None # Add dummy function node to ensure local variables are treated as `AnnAssign` # instead of state variables (`VariableDecl`) From 531cdca55dbbeb70cbc26b524da617ca142445e2 Mon Sep 17 00:00:00 2001 From: Charles Cooper Date: Fri, 13 Dec 2024 11:32:42 -0500 Subject: [PATCH 34/34] move comment --- 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 3e818f3136..d975aafac4 100644 --- a/vyper/ast/parse.py +++ b/vyper/ast/parse.py @@ -76,10 +76,10 @@ def _parse_to_ast_with_settings( py_ast = python_ast.parse(pre_parser.reformatted_code) except SyntaxError as e: # TODO: Ensure 1-to-1 match of source_code:reformatted_code SyntaxErrors - # SyntaxError offset is 1-based, not 0-based (see: - # https://docs.python.org/3/library/exceptions.html#SyntaxError.offset) offset = e.offset if offset is not None: + # SyntaxError offset is 1-based, not 0-based (see: + # https://docs.python.org/3/library/exceptions.html#SyntaxError.offset) offset -= 1 raise SyntaxException(str(e.msg), vyper_source, e.lineno, offset) from None