We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Disallow `++a` unary operations
## Simple Summary

Disallow the (pre-increment) `++a` expression in Vyper for all signed and unsigned types.

```vy
x: uint256 = 0
y: uint256 = 0

# throws
y = x++
# does currently not throw
y = ++x
```

## Motivation

Currently, the (post-increment) expression `a++` throws correctly as it complies with the Python syntax. Unfortunately, Python allows for the expression `++a` which is parsed as `+(+(a)) = a` and therefore is an identity operation on the integer (`+` is here a unary operator). Vyper currently supports this Syntax as well and I think it's more ergonomic & consistent with other languages to disallow it (many non-Python engineers will get confused if they are not aware of the semantics of the syntax). This is one of the design decisions of Python that am not happy about and would recommend not implementing in Vyper either.

## Specification

The compiler should throw an error for any `++a`-like expression for all signed and unsigned types.

## Backwards Compatibility

Will break any previous code that uses a `++a` syntax.

## Dependencies

N/A.

## References

N/A.

## Copyright

Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/)


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `vyper/ast/annotation.py`
Content:
```
1 import ast as python_ast
2 import tokenize
3 from decimal import Decimal
4 from typing import Optional, cast
5 
6 import asttokens
7 
8 from vyper.exceptions import CompilerPanic, SyntaxException
9 from vyper.typing import ModificationOffsets
10 
11 
12 class AnnotatingVisitor(python_ast.NodeTransformer):
13     _source_code: str
14     _modification_offsets: ModificationOffsets
15 
16     def __init__(
17         self,
18         source_code: str,
19         modification_offsets: Optional[ModificationOffsets],
20         tokens: asttokens.ASTTokens,
21         source_id: int,
22         contract_name: Optional[str],
23     ):
24         self._tokens = tokens
25         self._source_id = source_id
26         self._contract_name = contract_name
27         self._source_code: str = source_code
28         self.counter: int = 0
29         self._modification_offsets = {}
30         if modification_offsets is not None:
31             self._modification_offsets = modification_offsets
32 
33     def generic_visit(self, node):
34         """
35         Annotate a node with information that simplifies Vyper node generation.
36         """
37         # Decorate every node with the original source code to allow pretty-printing errors
38         node.full_source_code = self._source_code
39         node.node_id = self.counter
40         node.ast_type = node.__class__.__name__
41         self.counter += 1
42 
43         # Decorate every node with source end offsets
44         start = node.first_token.start if hasattr(node, "first_token") else (None, None)
45         end = (None, None)
46         if hasattr(node, "last_token"):
47             end = node.last_token.end
48             if node.last_token.type == 4:
49                 # token type 4 is a `\n`, some nodes include a trailing newline
50                 # here we ignore it when building the node offsets
51                 end = (end[0], end[1] - 1)
52 
53         node.lineno = start[0]
54         node.col_offset = start[1]
55         node.end_lineno = end[0]
56         node.end_col_offset = end[1]
57 
58         if hasattr(node, "last_token"):
59             start_pos = node.first_token.startpos
60             end_pos = node.last_token.endpos
61             if node.last_token.type == 4:
62                 # ignore trailing newline once more
63                 end_pos -= 1
64             node.src = f"{start_pos}:{end_pos-start_pos}:{self._source_id}"
65             node.node_source_code = self._source_code[start_pos:end_pos]
66 
67         return super().generic_visit(node)
68 
69     def _visit_docstring(self, node):
70         """
71         Move a node docstring from body to `doc_string` and annotate it as `DocStr`.
72         """
73         self.generic_visit(node)
74 
75         if node.body:
76             n = node.body[0]
77             if isinstance(n, python_ast.Expr) and isinstance(n.value, python_ast.Str):
78                 self.generic_visit(n.value)
79                 n.value.ast_type = "DocStr"
80                 del node.body[0]
81                 node.doc_string = n.value
82 
83         return node
84 
85     def visit_Module(self, node):
86         node.name = self._contract_name
87         return self._visit_docstring(node)
88 
89     def visit_FunctionDef(self, node):
90         if node.decorator_list:
91             # start the source highlight at `def` to improve annotation readability
92             decorator_token = node.decorator_list[-1].last_token
93             def_token = self._tokens.find_token(decorator_token, tokenize.NAME, tok_str="def")
94             node.first_token = def_token
95 
96         return self._visit_docstring(node)
97 
98     def visit_ClassDef(self, node):
99         """
100         Convert the `ClassDef` node into a Vyper-specific node type.
101 
102         Vyper uses `struct` and `interface` in place of `class`, however these
103         values must be substituted out to create parseable Python. The Python
104         node is annotated with the desired Vyper type via the `ast_type` member.
105         """
106         self.generic_visit(node)
107 
108         node.ast_type = self._modification_offsets[(node.lineno, node.col_offset)]
109         return node
110 
111     def visit_Expr(self, node):
112         """
113         Convert the `Yield` node into a Vyper-specific node type.
114 
115         Vyper substitutes `yield` for non-pythonic statement such as `log`. Prior
116         to generating Vyper AST, we must annotate `Yield` nodes with their original
117         value.
118 
119         Because `Yield` is an expression-statement, we also remove it from it's
120         enclosing `Expr` node.
121         """
122         self.generic_visit(node)
123 
124         if isinstance(node.value, python_ast.Yield):
125             node = node.value
126             node.ast_type = self._modification_offsets[(node.lineno, node.col_offset)]
127 
128         return node
129 
130     def visit_Subscript(self, node):
131         """
132         Maintain consistency of `Subscript.slice` across python versions.
133 
134         Starting from python 3.9, the `Index` node type has been deprecated,
135         and made impossible to instantiate via regular means. Here we do awful
136         hacky black magic to create an `Index` node. We need our own parser.
137         """
138         self.generic_visit(node)
139 
140         if not isinstance(node.slice, python_ast.Index):
141             index = python_ast.Constant(value=node.slice, ast_type="Index")
142             index.__class__ = python_ast.Index
143             self.generic_visit(index)
144             node.slice = index
145 
146         return node
147 
148     def visit_Constant(self, node):
149         """
150         Handle `Constant` when using Python >=3.8
151 
152         In Python 3.8, `NameConstant`, `Num`, `Str`, and `Bytes` are deprecated
153         in favor of `Constant`. To maintain consistency across versions, `ast_type`
154         is modified to create the <=3.7 node classes.
155         """
156         if not isinstance(node.value, bool) and isinstance(node.value, (int, float)):
157             return self.visit_Num(node)
158 
159         self.generic_visit(node)
160         if node.value is None or isinstance(node.value, bool):
161             node.ast_type = "NameConstant"
162         elif isinstance(node.value, str):
163             node.ast_type = "Str"
164         elif isinstance(node.value, bytes):
165             node.ast_type = "Bytes"
166         else:
167             raise SyntaxException(
168                 "Invalid syntax (unsupported Python Constant AST node).",
169                 self._source_code,
170                 node.lineno,
171                 node.col_offset,
172             )
173 
174         return node
175 
176     def visit_Num(self, node):
177         """
178         Adjust numeric node class based on the value type.
179 
180         Python uses `Num` to represent floats and integers. Integers may also
181         be given in binary, octal, decimal, or hexadecimal format. This method
182         modifies `ast_type` to separate `Num` into more granular Vyper node
183         classes.
184         """
185         # modify vyper AST type according to the format of the literal value
186         self.generic_visit(node)
187         value = node.node_source_code
188 
189         # deduce non base-10 types based on prefix
190         if value.lower()[:2] == "0x":
191             if len(value) % 2:
192                 raise SyntaxException(
193                     "Hex notation requires an even number of digits",
194                     self._source_code,
195                     node.lineno,
196                     node.col_offset,
197                 )
198             node.ast_type = "Hex"
199             node.n = value
200 
201         elif value.lower()[:2] == "0b":
202             node.ast_type = "Bytes"
203             mod = (len(value) - 2) % 8
204             if mod:
205                 raise SyntaxException(
206                     f"Bit notation requires a multiple of 8 bits. {8-mod} bit(s) are missing.",
207                     self._source_code,
208                     node.lineno,
209                     node.col_offset,
210                 )
211             node.value = int(value, 2).to_bytes(len(value) // 8, "big")
212 
213         elif isinstance(node.n, float):
214             node.ast_type = "Decimal"
215             node.n = Decimal(value)
216 
217         elif isinstance(node.n, int):
218             node.ast_type = "Int"
219 
220         else:
221             raise CompilerPanic(f"Unexpected type for Constant value: {type(node.n).__name__}")
222 
223         return node
224 
225     def visit_UnaryOp(self, node):
226         """
227         Adjust operand value and discard unary operations, where possible.
228 
229         This is done so that negative decimal literals are accurately represented.
230         """
231         self.generic_visit(node)
232 
233         # TODO once grammar is updated, remove this
234         # UAdd has no effect on the value of it's operand, so it is discarded
235         if isinstance(node.op, python_ast.UAdd):
236             return node.operand
237 
238         is_sub = isinstance(node.op, python_ast.USub)
239         is_num = (
240             hasattr(node.operand, "n")
241             and not isinstance(node.operand.n, bool)
242             and isinstance(node.operand.n, (int, Decimal))
243         )
244         if is_sub and is_num:
245             node.operand.n = 0 - node.operand.n
246             node.operand.col_offset = node.col_offset
247             node.operand.node_source_code = node.node_source_code
248             return node.operand
249         else:
250             return node
251 
252 
253 def annotate_python_ast(
254     parsed_ast: python_ast.AST,
255     source_code: str,
256     modification_offsets: Optional[ModificationOffsets] = None,
257     source_id: int = 0,
258     contract_name: Optional[str] = None,
259 ) -> python_ast.AST:
260     """
261     Annotate and optimize a Python AST in preparation conversion to a Vyper AST.
262 
263     Parameters
264     ----------
265     parsed_ast : AST
266         The AST to be annotated and optimized.
267     source_code : str
268         The originating source code of the AST.
269     modification_offsets : dict, optional
270         A mapping of class names to their original class types.
271 
272     Returns
273     -------
274         The annotated and optimized AST.
275     """
276 
277     tokens = asttokens.ASTTokens(source_code, tree=cast(Optional[python_ast.Module], parsed_ast))
278     visitor = AnnotatingVisitor(source_code, modification_offsets, tokens, source_id, contract_name)
279     visitor.visit(parsed_ast)
280 
281     return parsed_ast
282
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/vyper/ast/annotation.py b/vyper/ast/annotation.py
--- a/vyper/ast/annotation.py
+++ b/vyper/ast/annotation.py
@@ -230,11 +230,6 @@
         """
         self.generic_visit(node)
 
-        # TODO once grammar is updated, remove this
-        # UAdd has no effect on the value of it's operand, so it is discarded
-        if isinstance(node.op, python_ast.UAdd):
-            return node.operand
-
         is_sub = isinstance(node.op, python_ast.USub)
         is_num = (
             hasattr(node.operand, "n")

{"golden_diff": "diff --git a/vyper/ast/annotation.py b/vyper/ast/annotation.py\n--- a/vyper/ast/annotation.py\n+++ b/vyper/ast/annotation.py\n@@ -230,11 +230,6 @@\n         \"\"\"\n         self.generic_visit(node)\n \n-        # TODO once grammar is updated, remove this\n-        # UAdd has no effect on the value of it's operand, so it is discarded\n-        if isinstance(node.op, python_ast.UAdd):\n-            return node.operand\n-\n         is_sub = isinstance(node.op, python_ast.USub)\n         is_num = (\n             hasattr(node.operand, \"n\")\n", "issue": "Disallow `++a` unary operations\n## Simple Summary\r\n\r\nDisallow the (pre-increment) `++a` expression in Vyper for all signed and unsigned types.\r\n\r\n```vy\r\nx: uint256 = 0\r\ny: uint256 = 0\r\n\r\n# throws\r\ny = x++\r\n# does currently not throw\r\ny = ++x\r\n```\r\n\r\n## Motivation\r\n\r\nCurrently, the (post-increment) expression `a++` throws correctly as it complies with the Python syntax. Unfortunately, Python allows for the expression `++a` which is parsed as `+(+(a)) = a` and therefore is an identity operation on the integer (`+` is here a unary operator). Vyper currently supports this Syntax as well and I think it's more ergonomic & consistent with other languages to disallow it (many non-Python engineers will get confused if they are not aware of the semantics of the syntax). This is one of the design decisions of Python that am not happy about and would recommend not implementing in Vyper either.\r\n\r\n## Specification\r\n\r\nThe compiler should throw an error for any `++a`-like expression for all signed and unsigned types.\r\n\r\n## Backwards Compatibility\r\n\r\nWill break any previous code that uses a `++a` syntax.\r\n\r\n## Dependencies\r\n\r\nN/A.\r\n\r\n## References\r\n\r\nN/A.\r\n\r\n## Copyright\r\n\r\nCopyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/)\r\n\n", "before_files": [{"content": "import ast as python_ast\nimport tokenize\nfrom decimal import Decimal\nfrom typing import Optional, cast\n\nimport asttokens\n\nfrom vyper.exceptions import CompilerPanic, SyntaxException\nfrom vyper.typing import ModificationOffsets\n\n\nclass AnnotatingVisitor(python_ast.NodeTransformer):\n    _source_code: str\n    _modification_offsets: ModificationOffsets\n\n    def __init__(\n        self,\n        source_code: str,\n        modification_offsets: Optional[ModificationOffsets],\n        tokens: asttokens.ASTTokens,\n        source_id: int,\n        contract_name: Optional[str],\n    ):\n        self._tokens = tokens\n        self._source_id = source_id\n        self._contract_name = contract_name\n        self._source_code: str = source_code\n        self.counter: int = 0\n        self._modification_offsets = {}\n        if modification_offsets is not None:\n            self._modification_offsets = modification_offsets\n\n    def generic_visit(self, node):\n        \"\"\"\n        Annotate a node with information that simplifies Vyper node generation.\n        \"\"\"\n        # Decorate every node with the original source code to allow pretty-printing errors\n        node.full_source_code = self._source_code\n        node.node_id = self.counter\n        node.ast_type = node.__class__.__name__\n        self.counter += 1\n\n        # Decorate every node with source end offsets\n        start = node.first_token.start if hasattr(node, \"first_token\") else (None, None)\n        end = (None, None)\n        if hasattr(node, \"last_token\"):\n            end = node.last_token.end\n            if node.last_token.type == 4:\n                # token type 4 is a `\\n`, some nodes include a trailing newline\n                # here we ignore it when building the node offsets\n                end = (end[0], end[1] - 1)\n\n        node.lineno = start[0]\n        node.col_offset = start[1]\n        node.end_lineno = end[0]\n        node.end_col_offset = end[1]\n\n        if hasattr(node, \"last_token\"):\n            start_pos = node.first_token.startpos\n            end_pos = node.last_token.endpos\n            if node.last_token.type == 4:\n                # ignore trailing newline once more\n                end_pos -= 1\n            node.src = f\"{start_pos}:{end_pos-start_pos}:{self._source_id}\"\n            node.node_source_code = self._source_code[start_pos:end_pos]\n\n        return super().generic_visit(node)\n\n    def _visit_docstring(self, node):\n        \"\"\"\n        Move a node docstring from body to `doc_string` and annotate it as `DocStr`.\n        \"\"\"\n        self.generic_visit(node)\n\n        if node.body:\n            n = node.body[0]\n            if isinstance(n, python_ast.Expr) and isinstance(n.value, python_ast.Str):\n                self.generic_visit(n.value)\n                n.value.ast_type = \"DocStr\"\n                del node.body[0]\n                node.doc_string = n.value\n\n        return node\n\n    def visit_Module(self, node):\n        node.name = self._contract_name\n        return self._visit_docstring(node)\n\n    def visit_FunctionDef(self, node):\n        if node.decorator_list:\n            # start the source highlight at `def` to improve annotation readability\n            decorator_token = node.decorator_list[-1].last_token\n            def_token = self._tokens.find_token(decorator_token, tokenize.NAME, tok_str=\"def\")\n            node.first_token = def_token\n\n        return self._visit_docstring(node)\n\n    def visit_ClassDef(self, node):\n        \"\"\"\n        Convert the `ClassDef` node into a Vyper-specific node type.\n\n        Vyper uses `struct` and `interface` in place of `class`, however these\n        values must be substituted out to create parseable Python. The Python\n        node is annotated with the desired Vyper type via the `ast_type` member.\n        \"\"\"\n        self.generic_visit(node)\n\n        node.ast_type = self._modification_offsets[(node.lineno, node.col_offset)]\n        return node\n\n    def visit_Expr(self, node):\n        \"\"\"\n        Convert the `Yield` node into a Vyper-specific node type.\n\n        Vyper substitutes `yield` for non-pythonic statement such as `log`. Prior\n        to generating Vyper AST, we must annotate `Yield` nodes with their original\n        value.\n\n        Because `Yield` is an expression-statement, we also remove it from it's\n        enclosing `Expr` node.\n        \"\"\"\n        self.generic_visit(node)\n\n        if isinstance(node.value, python_ast.Yield):\n            node = node.value\n            node.ast_type = self._modification_offsets[(node.lineno, node.col_offset)]\n\n        return node\n\n    def visit_Subscript(self, node):\n        \"\"\"\n        Maintain consistency of `Subscript.slice` across python versions.\n\n        Starting from python 3.9, the `Index` node type has been deprecated,\n        and made impossible to instantiate via regular means. Here we do awful\n        hacky black magic to create an `Index` node. We need our own parser.\n        \"\"\"\n        self.generic_visit(node)\n\n        if not isinstance(node.slice, python_ast.Index):\n            index = python_ast.Constant(value=node.slice, ast_type=\"Index\")\n            index.__class__ = python_ast.Index\n            self.generic_visit(index)\n            node.slice = index\n\n        return node\n\n    def visit_Constant(self, node):\n        \"\"\"\n        Handle `Constant` when using Python >=3.8\n\n        In Python 3.8, `NameConstant`, `Num`, `Str`, and `Bytes` are deprecated\n        in favor of `Constant`. To maintain consistency across versions, `ast_type`\n        is modified to create the <=3.7 node classes.\n        \"\"\"\n        if not isinstance(node.value, bool) and isinstance(node.value, (int, float)):\n            return self.visit_Num(node)\n\n        self.generic_visit(node)\n        if node.value is None or isinstance(node.value, bool):\n            node.ast_type = \"NameConstant\"\n        elif isinstance(node.value, str):\n            node.ast_type = \"Str\"\n        elif isinstance(node.value, bytes):\n            node.ast_type = \"Bytes\"\n        else:\n            raise SyntaxException(\n                \"Invalid syntax (unsupported Python Constant AST node).\",\n                self._source_code,\n                node.lineno,\n                node.col_offset,\n            )\n\n        return node\n\n    def visit_Num(self, node):\n        \"\"\"\n        Adjust numeric node class based on the value type.\n\n        Python uses `Num` to represent floats and integers. Integers may also\n        be given in binary, octal, decimal, or hexadecimal format. This method\n        modifies `ast_type` to separate `Num` into more granular Vyper node\n        classes.\n        \"\"\"\n        # modify vyper AST type according to the format of the literal value\n        self.generic_visit(node)\n        value = node.node_source_code\n\n        # deduce non base-10 types based on prefix\n        if value.lower()[:2] == \"0x\":\n            if len(value) % 2:\n                raise SyntaxException(\n                    \"Hex notation requires an even number of digits\",\n                    self._source_code,\n                    node.lineno,\n                    node.col_offset,\n                )\n            node.ast_type = \"Hex\"\n            node.n = value\n\n        elif value.lower()[:2] == \"0b\":\n            node.ast_type = \"Bytes\"\n            mod = (len(value) - 2) % 8\n            if mod:\n                raise SyntaxException(\n                    f\"Bit notation requires a multiple of 8 bits. {8-mod} bit(s) are missing.\",\n                    self._source_code,\n                    node.lineno,\n                    node.col_offset,\n                )\n            node.value = int(value, 2).to_bytes(len(value) // 8, \"big\")\n\n        elif isinstance(node.n, float):\n            node.ast_type = \"Decimal\"\n            node.n = Decimal(value)\n\n        elif isinstance(node.n, int):\n            node.ast_type = \"Int\"\n\n        else:\n            raise CompilerPanic(f\"Unexpected type for Constant value: {type(node.n).__name__}\")\n\n        return node\n\n    def visit_UnaryOp(self, node):\n        \"\"\"\n        Adjust operand value and discard unary operations, where possible.\n\n        This is done so that negative decimal literals are accurately represented.\n        \"\"\"\n        self.generic_visit(node)\n\n        # TODO once grammar is updated, remove this\n        # UAdd has no effect on the value of it's operand, so it is discarded\n        if isinstance(node.op, python_ast.UAdd):\n            return node.operand\n\n        is_sub = isinstance(node.op, python_ast.USub)\n        is_num = (\n            hasattr(node.operand, \"n\")\n            and not isinstance(node.operand.n, bool)\n            and isinstance(node.operand.n, (int, Decimal))\n        )\n        if is_sub and is_num:\n            node.operand.n = 0 - node.operand.n\n            node.operand.col_offset = node.col_offset\n            node.operand.node_source_code = node.node_source_code\n            return node.operand\n        else:\n            return node\n\n\ndef annotate_python_ast(\n    parsed_ast: python_ast.AST,\n    source_code: str,\n    modification_offsets: Optional[ModificationOffsets] = None,\n    source_id: int = 0,\n    contract_name: Optional[str] = None,\n) -> python_ast.AST:\n    \"\"\"\n    Annotate and optimize a Python AST in preparation conversion to a Vyper AST.\n\n    Parameters\n    ----------\n    parsed_ast : AST\n        The AST to be annotated and optimized.\n    source_code : str\n        The originating source code of the AST.\n    modification_offsets : dict, optional\n        A mapping of class names to their original class types.\n\n    Returns\n    -------\n        The annotated and optimized AST.\n    \"\"\"\n\n    tokens = asttokens.ASTTokens(source_code, tree=cast(Optional[python_ast.Module], parsed_ast))\n    visitor = AnnotatingVisitor(source_code, modification_offsets, tokens, source_id, contract_name)\n    visitor.visit(parsed_ast)\n\n    return parsed_ast\n", "path": "vyper/ast/annotation.py"}], "after_files": [{"content": "import ast as python_ast\nimport tokenize\nfrom decimal import Decimal\nfrom typing import Optional, cast\n\nimport asttokens\n\nfrom vyper.exceptions import CompilerPanic, SyntaxException\nfrom vyper.typing import ModificationOffsets\n\n\nclass AnnotatingVisitor(python_ast.NodeTransformer):\n    _source_code: str\n    _modification_offsets: ModificationOffsets\n\n    def __init__(\n        self,\n        source_code: str,\n        modification_offsets: Optional[ModificationOffsets],\n        tokens: asttokens.ASTTokens,\n        source_id: int,\n        contract_name: Optional[str],\n    ):\n        self._tokens = tokens\n        self._source_id = source_id\n        self._contract_name = contract_name\n        self._source_code: str = source_code\n        self.counter: int = 0\n        self._modification_offsets = {}\n        if modification_offsets is not None:\n            self._modification_offsets = modification_offsets\n\n    def generic_visit(self, node):\n        \"\"\"\n        Annotate a node with information that simplifies Vyper node generation.\n        \"\"\"\n        # Decorate every node with the original source code to allow pretty-printing errors\n        node.full_source_code = self._source_code\n        node.node_id = self.counter\n        node.ast_type = node.__class__.__name__\n        self.counter += 1\n\n        # Decorate every node with source end offsets\n        start = node.first_token.start if hasattr(node, \"first_token\") else (None, None)\n        end = (None, None)\n        if hasattr(node, \"last_token\"):\n            end = node.last_token.end\n            if node.last_token.type == 4:\n                # token type 4 is a `\\n`, some nodes include a trailing newline\n                # here we ignore it when building the node offsets\n                end = (end[0], end[1] - 1)\n\n        node.lineno = start[0]\n        node.col_offset = start[1]\n        node.end_lineno = end[0]\n        node.end_col_offset = end[1]\n\n        if hasattr(node, \"last_token\"):\n            start_pos = node.first_token.startpos\n            end_pos = node.last_token.endpos\n            if node.last_token.type == 4:\n                # ignore trailing newline once more\n                end_pos -= 1\n            node.src = f\"{start_pos}:{end_pos-start_pos}:{self._source_id}\"\n            node.node_source_code = self._source_code[start_pos:end_pos]\n\n        return super().generic_visit(node)\n\n    def _visit_docstring(self, node):\n        \"\"\"\n        Move a node docstring from body to `doc_string` and annotate it as `DocStr`.\n        \"\"\"\n        self.generic_visit(node)\n\n        if node.body:\n            n = node.body[0]\n            if isinstance(n, python_ast.Expr) and isinstance(n.value, python_ast.Str):\n                self.generic_visit(n.value)\n                n.value.ast_type = \"DocStr\"\n                del node.body[0]\n                node.doc_string = n.value\n\n        return node\n\n    def visit_Module(self, node):\n        node.name = self._contract_name\n        return self._visit_docstring(node)\n\n    def visit_FunctionDef(self, node):\n        if node.decorator_list:\n            # start the source highlight at `def` to improve annotation readability\n            decorator_token = node.decorator_list[-1].last_token\n            def_token = self._tokens.find_token(decorator_token, tokenize.NAME, tok_str=\"def\")\n            node.first_token = def_token\n\n        return self._visit_docstring(node)\n\n    def visit_ClassDef(self, node):\n        \"\"\"\n        Convert the `ClassDef` node into a Vyper-specific node type.\n\n        Vyper uses `struct` and `interface` in place of `class`, however these\n        values must be substituted out to create parseable Python. The Python\n        node is annotated with the desired Vyper type via the `ast_type` member.\n        \"\"\"\n        self.generic_visit(node)\n\n        node.ast_type = self._modification_offsets[(node.lineno, node.col_offset)]\n        return node\n\n    def visit_Expr(self, node):\n        \"\"\"\n        Convert the `Yield` node into a Vyper-specific node type.\n\n        Vyper substitutes `yield` for non-pythonic statement such as `log`. Prior\n        to generating Vyper AST, we must annotate `Yield` nodes with their original\n        value.\n\n        Because `Yield` is an expression-statement, we also remove it from it's\n        enclosing `Expr` node.\n        \"\"\"\n        self.generic_visit(node)\n\n        if isinstance(node.value, python_ast.Yield):\n            node = node.value\n            node.ast_type = self._modification_offsets[(node.lineno, node.col_offset)]\n\n        return node\n\n    def visit_Subscript(self, node):\n        \"\"\"\n        Maintain consistency of `Subscript.slice` across python versions.\n\n        Starting from python 3.9, the `Index` node type has been deprecated,\n        and made impossible to instantiate via regular means. Here we do awful\n        hacky black magic to create an `Index` node. We need our own parser.\n        \"\"\"\n        self.generic_visit(node)\n\n        if not isinstance(node.slice, python_ast.Index):\n            index = python_ast.Constant(value=node.slice, ast_type=\"Index\")\n            index.__class__ = python_ast.Index\n            self.generic_visit(index)\n            node.slice = index\n\n        return node\n\n    def visit_Constant(self, node):\n        \"\"\"\n        Handle `Constant` when using Python >=3.8\n\n        In Python 3.8, `NameConstant`, `Num`, `Str`, and `Bytes` are deprecated\n        in favor of `Constant`. To maintain consistency across versions, `ast_type`\n        is modified to create the <=3.7 node classes.\n        \"\"\"\n        if not isinstance(node.value, bool) and isinstance(node.value, (int, float)):\n            return self.visit_Num(node)\n\n        self.generic_visit(node)\n        if node.value is None or isinstance(node.value, bool):\n            node.ast_type = \"NameConstant\"\n        elif isinstance(node.value, str):\n            node.ast_type = \"Str\"\n        elif isinstance(node.value, bytes):\n            node.ast_type = \"Bytes\"\n        else:\n            raise SyntaxException(\n                \"Invalid syntax (unsupported Python Constant AST node).\",\n                self._source_code,\n                node.lineno,\n                node.col_offset,\n            )\n\n        return node\n\n    def visit_Num(self, node):\n        \"\"\"\n        Adjust numeric node class based on the value type.\n\n        Python uses `Num` to represent floats and integers. Integers may also\n        be given in binary, octal, decimal, or hexadecimal format. This method\n        modifies `ast_type` to separate `Num` into more granular Vyper node\n        classes.\n        \"\"\"\n        # modify vyper AST type according to the format of the literal value\n        self.generic_visit(node)\n        value = node.node_source_code\n\n        # deduce non base-10 types based on prefix\n        if value.lower()[:2] == \"0x\":\n            if len(value) % 2:\n                raise SyntaxException(\n                    \"Hex notation requires an even number of digits\",\n                    self._source_code,\n                    node.lineno,\n                    node.col_offset,\n                )\n            node.ast_type = \"Hex\"\n            node.n = value\n\n        elif value.lower()[:2] == \"0b\":\n            node.ast_type = \"Bytes\"\n            mod = (len(value) - 2) % 8\n            if mod:\n                raise SyntaxException(\n                    f\"Bit notation requires a multiple of 8 bits. {8-mod} bit(s) are missing.\",\n                    self._source_code,\n                    node.lineno,\n                    node.col_offset,\n                )\n            node.value = int(value, 2).to_bytes(len(value) // 8, \"big\")\n\n        elif isinstance(node.n, float):\n            node.ast_type = \"Decimal\"\n            node.n = Decimal(value)\n\n        elif isinstance(node.n, int):\n            node.ast_type = \"Int\"\n\n        else:\n            raise CompilerPanic(f\"Unexpected type for Constant value: {type(node.n).__name__}\")\n\n        return node\n\n    def visit_UnaryOp(self, node):\n        \"\"\"\n        Adjust operand value and discard unary operations, where possible.\n\n        This is done so that negative decimal literals are accurately represented.\n        \"\"\"\n        self.generic_visit(node)\n\n        is_sub = isinstance(node.op, python_ast.USub)\n        is_num = (\n            hasattr(node.operand, \"n\")\n            and not isinstance(node.operand.n, bool)\n            and isinstance(node.operand.n, (int, Decimal))\n        )\n        if is_sub and is_num:\n            node.operand.n = 0 - node.operand.n\n            node.operand.col_offset = node.col_offset\n            node.operand.node_source_code = node.node_source_code\n            return node.operand\n        else:\n            return node\n\n\ndef annotate_python_ast(\n    parsed_ast: python_ast.AST,\n    source_code: str,\n    modification_offsets: Optional[ModificationOffsets] = None,\n    source_id: int = 0,\n    contract_name: Optional[str] = None,\n) -> python_ast.AST:\n    \"\"\"\n    Annotate and optimize a Python AST in preparation conversion to a Vyper AST.\n\n    Parameters\n    ----------\n    parsed_ast : AST\n        The AST to be annotated and optimized.\n    source_code : str\n        The originating source code of the AST.\n    modification_offsets : dict, optional\n        A mapping of class names to their original class types.\n\n    Returns\n    -------\n        The annotated and optimized AST.\n    \"\"\"\n\n    tokens = asttokens.ASTTokens(source_code, tree=cast(Optional[python_ast.Module], parsed_ast))\n    visitor = AnnotatingVisitor(source_code, modification_offsets, tokens, source_id, contract_name)\n    visitor.visit(parsed_ast)\n\n    return parsed_ast\n", "path": "vyper/ast/annotation.py"}]}