Skip to content

Commit

Permalink
add -Werror and -Wnone options
Browse files Browse the repository at this point in the history
which promote warnings to errors and ignore them, respectively.

also some small refactoring for warning handling to simplify the
implementation (allow us to use `warnings.simplefilter`).
  • Loading branch information
charles-cooper committed Jan 12, 2025
1 parent db8dcc7 commit 3728f5b
Show file tree
Hide file tree
Showing 14 changed files with 92 additions and 60 deletions.
2 changes: 1 addition & 1 deletion tests/unit/compiler/test_compile_code.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def a() -> bool:
q: Bytes[24577] = {huge_bytestring}
return True
"""
with pytest.warns(vyper.warnings.ContractSizeLimitWarning):
with pytest.warns(vyper.warnings.ContractSizeLimit):
vyper.compile_code(code, output_formats=["bytecode_runtime"])


Expand Down
11 changes: 3 additions & 8 deletions vyper/ast/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import operator
import pickle
import sys
import warnings
from typing import Any, Optional, Union

from vyper.ast.metadata import NodeMetadata
Expand All @@ -23,7 +22,6 @@
TypeMismatch,
UnfoldableNode,
VariableDeclarationException,
VyperException,
ZeroDivisionException,
)
from vyper.utils import (
Expand All @@ -34,6 +32,7 @@
quantize,
sha256sum,
)
from vyper.warnings import EnumUsage, vyper_warn

NODE_BASE_ATTRIBUTES = (
"_children",
Expand Down Expand Up @@ -133,13 +132,9 @@ def get_node(

node = vy_class(parent=parent, **ast_struct)

# TODO: Putting this after node creation to pretty print, remove after enum deprecation
if enum_warn:
# TODO: hack to pretty print, logic should be factored out of exception
pretty_printed_node = str(VyperException("", node))
warnings.warn(
f"enum will be deprecated in a future release, use flag instead. {pretty_printed_node}",
stacklevel=2,
vyper_warn(
EnumUsage("enum will be deprecated in a future release, use flag instead.", node)
)

node.validate()
Expand Down
11 changes: 5 additions & 6 deletions vyper/ast/parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
from vyper.ast.pre_parser import PreParser
from vyper.compiler.settings import Settings
from vyper.exceptions import CompilerPanic, ParserException, SyntaxException
from vyper.utils import sha256sum, vyper_warn
from vyper.utils import sha256sum
from vyper.warnings import Deprecation, vyper_warn


def parse_to_ast(*args: Any, **kwargs: Any) -> vy_ast.Module:
Expand Down Expand Up @@ -434,15 +435,15 @@ def visit_Await(self, node):
return node

def visit_Call(self, node):
self.generic_visit(node)

# Convert structs declared as `Dict` node for vyper < 0.4.0 to kwargs
if len(node.args) == 1 and isinstance(node.args[0], python_ast.Dict):
msg = "Instantiating a struct using a dictionary is deprecated "
msg += "as of v0.4.0 and will be disallowed in a future release. "
msg += "Use kwargs instead e.g. Foo(a=1, b=2)"

# add full_source_code so that str(VyperException(msg, node)) works
node.full_source_code = self._source_code
vyper_warn(msg, node)
vyper_warn(Deprecation(msg, node))

dict_ = node.args[0]
kw_list = []
Expand All @@ -458,8 +459,6 @@ def visit_Call(self, node):
node.args = []
node.keywords = kw_list

self.generic_visit(node)

return node

def visit_Constant(self, node):
Expand Down
2 changes: 1 addition & 1 deletion vyper/builtins/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,8 @@
keccak256,
method_id,
method_id_int,
vyper_warn,
)
from vyper.warnings import vyper_warn

from ._convert import convert
from ._signatures import BuiltinFunctionT, process_inputs
Expand Down
15 changes: 15 additions & 0 deletions vyper/cli/vyper_compile.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from vyper.compiler.input_bundle import FileInput, FilesystemInputBundle
from vyper.compiler.settings import VYPER_TRACEBACK_LIMIT, OptimizationLevel, Settings
from vyper.typing import ContractPath, OutputFormats
from vyper.warnings import VyperWarning

T = TypeVar("T")

Expand Down Expand Up @@ -187,6 +188,10 @@ def _parse_args(argv):
)
parser.add_argument("--enable-decimals", help="Enable decimals", action="store_true")

parser.add_argument(
"-W", help="Control warnings", dest="warnings_control", choices=["error", "none"]
)

args = parser.parse_args(argv)

if args.traceback_limit is not None:
Expand All @@ -201,6 +206,16 @@ def _parse_args(argv):
# an error occurred in a Vyper source file.
sys.tracebacklimit = 0

if args.warnings_control == "error":
warnings_filter = "error"
elif args.warnings_control == "none":
warnings_filter = "ignore"
else:
assert args.warnings_control is None # sanity
warnings_filter = "default"

warnings.simplefilter(warnings_filter, category=VyperWarning)

if args.hex_ir:
ir_node.AS_HEX_DEFAULT = True

Expand Down
8 changes: 5 additions & 3 deletions vyper/cli/vyper_json.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from vyper.exceptions import JSONError
from vyper.typing import StorageLayout
from vyper.utils import OrderedSet, keccak256
from vyper.warnings import Deprecation, vyper_warn

TRANSLATE_MAP = {
"abi": "abi",
Expand Down Expand Up @@ -276,9 +277,10 @@ def get_settings(input_dict: dict) -> Settings:

if isinstance(optimize, bool):
# bool optimization level for backwards compatibility
warnings.warn(
"optimize: <bool> is deprecated! please use one of 'gas', 'codesize', 'none'.",
stacklevel=2,
vyper_warn(
Deprecation(
"optimize: <bool> is deprecated! please use one of 'gas', 'codesize', 'none'."
)
)
optimize = OptimizationLevel.default() if optimize else OptimizationLevel.NONE
elif isinstance(optimize, str):
Expand Down
7 changes: 4 additions & 3 deletions vyper/codegen/expr.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@
from vyper.semantics.types.bytestrings import _BytestringT
from vyper.semantics.types.function import ContractFunctionT, MemberFunctionT
from vyper.semantics.types.shortcuts import BYTES32_T, UINT256_T
from vyper.utils import DECIMAL_DIVISOR, bytes_to_int, is_checksum_encoded, vyper_warn
from vyper.utils import DECIMAL_DIVISOR, bytes_to_int, is_checksum_encoded
from vyper.warnings import VyperWarning, vyper_warn

ENVIRONMENT_VARIABLES = {"block", "msg", "tx", "chain"}

Expand Down Expand Up @@ -274,13 +275,13 @@ def parse_Attribute(self):
if not version_check(begin="paris"):
warning = "tried to use block.prevrandao in pre-Paris "
warning += "environment! Suggest using block.difficulty instead."
vyper_warn(warning, self.expr)
vyper_warn(VyperWarning(warning, self.expr))
return IRnode.from_list(["prevrandao"], typ=BYTES32_T)
elif key == "block.difficulty":
if version_check(begin="paris"):
warning = "tried to use block.difficulty in post-Paris "
warning += "environment! Suggest using block.prevrandao instead."
vyper_warn(warning, self.expr)
vyper_warn(VyperWarning(warning, self.expr))
return IRnode.from_list(["difficulty"], typ=UINT256_T)
elif key == "block.timestamp":
return IRnode.from_list(["timestamp"], typ=UINT256_T)
Expand Down
17 changes: 8 additions & 9 deletions vyper/compiler/output.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import base64
import warnings
from collections import deque
from pathlib import PurePath

Expand All @@ -16,8 +15,8 @@
from vyper.semantics.types.function import ContractFunctionT, FunctionVisibility, StateMutability
from vyper.semantics.types.module import InterfaceT
from vyper.typing import StorageLayout
from vyper.utils import safe_relpath, vyper_warn
from vyper.warnings import ContractSizeLimitWarning
from vyper.utils import safe_relpath
from vyper.warnings import ContractSizeLimit, vyper_warn


def build_ast_dict(compiler_data: CompilerData) -> dict:
Expand Down Expand Up @@ -441,12 +440,12 @@ def build_blueprint_bytecode_output(compiler_data: CompilerData) -> str:
def build_bytecode_runtime_output(compiler_data: CompilerData) -> str:
compiled_bytecode_runtime_length = len(compiler_data.bytecode_runtime)
if compiled_bytecode_runtime_length > EIP170_CONTRACT_SIZE_LIMIT:
warnings.warn(
f"Length of compiled bytecode is bigger than Ethereum contract size limit "
"(see EIP-170: https://eips.ethereum.org/EIPS/eip-170): "
f"{compiled_bytecode_runtime_length}b > {EIP170_CONTRACT_SIZE_LIMIT}b",
ContractSizeLimitWarning,
stacklevel=2,
vyper_warn(
ContractSizeLimit(
f"Length of compiled bytecode is bigger than Ethereum contract size limit "
"(see EIP-170: https://eips.ethereum.org/EIPS/eip-170): "
f"{compiled_bytecode_runtime_length}b > {EIP170_CONTRACT_SIZE_LIMIT}b"
)
)
return f"0x{compiler_data.bytecode_runtime.hex()}"

Expand Down
13 changes: 7 additions & 6 deletions vyper/compiler/phases.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import copy
import json
import warnings
from functools import cached_property
from pathlib import Path, PurePath
from typing import Any, Optional
Expand All @@ -20,8 +19,9 @@
from vyper.semantics.types.function import ContractFunctionT
from vyper.semantics.types.module import ModuleT
from vyper.typing import StorageLayout
from vyper.utils import ERC5202_PREFIX, sha256sum, vyper_warn
from vyper.utils import ERC5202_PREFIX, sha256sum
from vyper.venom import generate_assembly_experimental, generate_ir
from vyper.warnings import VyperWarning, vyper_warn

DEFAULT_CONTRACT_PATH = PurePath("VyperContract.vy")

Expand Down Expand Up @@ -352,10 +352,11 @@ def generate_assembly(ir_nodes: IRnode, optimize: Optional[OptimizationLevel] =
assembly = compile_ir.compile_to_assembly(ir_nodes, optimize=optimize)

if _find_nested_opcode(assembly, "DEBUG"):
warnings.warn(
"This code contains DEBUG opcodes! The DEBUG opcode will only work in "
"a supported EVM! It will FAIL on all other nodes!",
stacklevel=2,
vyper_warn(
VyperWarning(
"This code contains DEBUG opcodes! The DEBUG opcode will only work in "
"a supported EVM! It will FAIL on all other nodes!"
)
)
return assembly

Expand Down
8 changes: 0 additions & 8 deletions vyper/semantics/types/function.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import re
import warnings
from dataclasses import dataclass
from functools import cached_property
from typing import Any, Dict, List, Optional, Tuple
Expand Down Expand Up @@ -766,13 +765,6 @@ def _parse_decorators(
state_mutability = StateMutability(decorator.id)

else:
if decorator.id == "constant":
warnings.warn(
"'@constant' decorator has been removed (see VIP2040). "
"Use `@view` instead.",
DeprecationWarning,
stacklevel=2,
)
raise FunctionDeclarationException(f"Unknown decorator: {decorator.id}", decorator)

else:
Expand Down
4 changes: 2 additions & 2 deletions vyper/semantics/types/subscriptable.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import warnings
from typing import Any, Dict, Optional, Tuple

from vyper import ast as vy_ast
Expand All @@ -9,6 +8,7 @@
from vyper.semantics.types.primitives import IntegerT
from vyper.semantics.types.shortcuts import UINT256_T
from vyper.semantics.types.utils import get_index_value, type_from_annotation
from vyper.warnings import VyperWarning, vyper_warn


class _SubscriptableT(VyperType):
Expand Down Expand Up @@ -113,7 +113,7 @@ def __init__(self, value_type: VyperType, length: int):
raise InvalidType("Array length is invalid")

if length >= 2**64:
warnings.warn("Use of large arrays can be unsafe!", stacklevel=2)
vyper_warn(VyperWarning("Use of large arrays can be unsafe!"))

super().__init__(UINT256_T, value_type)
self.length = length
Expand Down
5 changes: 3 additions & 2 deletions vyper/semantics/types/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
from vyper.semantics.types.base import VyperType
from vyper.semantics.types.subscriptable import HashMapT
from vyper.semantics.types.utils import type_from_abi, type_from_annotation
from vyper.utils import keccak256, vyper_warn
from vyper.utils import keccak256
from vyper.warnings import Deprecation, vyper_warn


# user defined type
Expand Down Expand Up @@ -303,7 +304,7 @@ def _ctor_call_return(self, node: vy_ast.Call) -> None:
msg += "in a future release. Use kwargs instead eg. "
msg += "Foo(a=1, b=2)"

vyper_warn(msg, node)
vyper_warn(Deprecation(msg, node))

validate_call_args(node, len(self.arguments))
for arg, expected in zip(node.args, self.arguments.values()):
Expand Down
10 changes: 1 addition & 9 deletions vyper/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import warnings
from typing import Generic, List, TypeVar, Union

from vyper.exceptions import CompilerPanic, DecimalOverrideException, VyperException
from vyper.exceptions import CompilerPanic, DecimalOverrideException

_T = TypeVar("_T")

Expand Down Expand Up @@ -284,14 +284,6 @@ def trace(n=5, out=sys.stderr):
print("END TRACE", file=out)


# print a warning
def vyper_warn(msg, node=None):
if node is not None:
# use VyperException for its formatting abilities
msg = str(VyperException(msg, node))
warnings.warn(msg, stacklevel=2)


# converts a signature like Func(bool,uint256,address) to its 4 byte method ID
# TODO replace manual calculations in codebase with this
def method_id_int(method_sig: str) -> int:
Expand Down
39 changes: 37 additions & 2 deletions vyper/warnings.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,38 @@
# TODO: Create VyperWarning class similarly to what is being done with exceptinos?
class ContractSizeLimitWarning(Warning):
import warnings

from vyper.exceptions import _BaseVyperException


class VyperWarning(_BaseVyperException, Warning):
pass


# print a warning
def vyper_warn(warning: VyperWarning | str, node=None):
if isinstance(warning, str):
warning = VyperWarning(warning, node)
warnings.warn(warning, stacklevel=2)


class ContractSizeLimit(VyperWarning):
"""
Warn if past the EIP-170 size limit
"""

pass


class EnumUsage(VyperWarning):
"""
Warn about using `enum` instead of `flag
"""

pass


class Deprecation(VyperWarning):
"""
General deprecation warning
"""

pass

0 comments on commit 3728f5b

Please sign in to comment.