Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: allow functions to be named after builtins and reserved keywords #3307

Merged
merged 23 commits into from
Apr 8, 2023
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 0 additions & 11 deletions tests/parser/syntax/test_send.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,17 +137,6 @@ def foo():
send(0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe, 5)
""",
"""
# Test custom send method
@internal
def send(a: address, w: uint256):
send(0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe, 1)

@external
@payable
def foo():
self.send(msg.sender, msg.value)
""",
"""
#Test send gas stipend
@external
def foo():
Expand Down
55 changes: 30 additions & 25 deletions tests/parser/syntax/utils/test_function_names.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,13 @@
from vyper import compiler
from vyper.exceptions import NamespaceCollision, StructureException

fail_list = [ # noqa: E122
fail_list = [
"""
@external
def ő1qwerty(i: int128) -> int128:
temp_var : int128 = i
return temp_var
""",
"""
@external
def int128(i: int128) -> int128:
temp_var : int128 = i
return temp_var
""",
"""
@external
def decimal(i: int128) -> int128:
temp_var : int128 = i
return temp_var
""",
"""
@external
def wei(i: int128) -> int128:
temp_var : int128 = i
return temp_var
""",
"""
@external
def false(i: int128) -> int128:
temp_var : int128 = i
return temp_var
""",
]


Expand Down Expand Up @@ -63,6 +39,35 @@ def first1(i: int128) -> int128:
_var123 : int128 = i
return _var123
""",
"""
@external
def int128(i: int128) -> int128:
temp_var : int128 = i
return temp_var
""",
"""
@external
def decimal(i: int128) -> int128:
temp_var : int128 = i
return temp_var
""",
"""
@external
def false(i: int128) -> int128:
temp_var : int128 = i
return temp_var
""",
"""
@external
def wei(i: int128) -> int128:
temp_var : int128 = i
return temp_var
""",
"""
@external
def floor():
pass
""",
]


Expand Down
1 change: 0 additions & 1 deletion vyper/semantics/analysis/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,6 @@ def visit_FunctionDef(self, node):
func = ContractFunctionT.from_FunctionDef(node)

try:
# TODO sketchy elision of namespace validation
charles-cooper marked this conversation as resolved.
Show resolved Hide resolved
self.namespace["self"].typ.add_member(func.name, func, skip_namespace_validation=True)
node._metadata["type"] = func
except VyperException as exc:
Expand Down
8 changes: 6 additions & 2 deletions vyper/semantics/namespace.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,8 @@ def clear(self):
self.__init__()

def validate_assignment(self, attr):
validate_identifier(attr)
validate_identifier_name(attr)
validate_identifier_with_namespace(attr)
if attr in self:
obj = super().__getitem__(attr)
raise NamespaceCollision(f"'{attr}' has already been declared as a {obj}")
Expand Down Expand Up @@ -119,12 +120,15 @@ def override_global_namespace(ns):
_namespace = tmp


def validate_identifier(attr):
def validate_identifier_with_namespace(attr):
tserg marked this conversation as resolved.
Show resolved Hide resolved
namespace = get_namespace()
if attr in namespace and attr not in [x for i in namespace._scopes for x in i]:
raise NamespaceCollision(f"Cannot assign to '{attr}', it is a builtin")
if attr.lower() in RESERVED_KEYWORDS or attr.upper() in OPCODES:
raise StructureException(f"'{attr}' is a reserved keyword")


def validate_identifier_name(attr):
Copy link
Member

Choose a reason for hiding this comment

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

this can stay as validate_identifier

if not re.match("^[_a-zA-Z][a-zA-Z0-9_]*$", attr):
raise StructureException(f"'{attr}' contains invalid character(s)")

Expand Down
11 changes: 7 additions & 4 deletions vyper/semantics/types/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
UnknownAttribute,
)
from vyper.semantics.analysis.levenshtein_utils import get_levenshtein_error_suggestions
from vyper.semantics.namespace import validate_identifier
from vyper.semantics.namespace import validate_identifier_with_namespace, validate_identifier_name


# Some fake type with an overridden `compare_type` which accepts any RHS
Expand Down Expand Up @@ -57,7 +57,9 @@ class VyperType:

size_in_bytes = 32 # default; override for larger types

def __init__(self, members: Optional[Dict] = None) -> None:
def __init__(
self, members: Optional[Dict] = None, skip_namespace_validation: bool = False
) -> None:
self.members: Dict = {}

# add members that are on the class instance.
Expand All @@ -69,7 +71,7 @@ def __init__(self, members: Optional[Dict] = None) -> None:

members = members or {}
for k, v in members.items():
self.add_member(k, v)
self.add_member(k, v, skip_namespace_validation=skip_namespace_validation)

def _get_equality_attrs(self):
return tuple(getattr(self, attr) for attr in self._equality_attrs)
Expand Down Expand Up @@ -283,7 +285,8 @@ def add_member(
# skip_namespace_validation provides a way of bypassing validate_identifier, which
# introduces a dependency cycle with the builtin_functions module
if not skip_namespace_validation:
validate_identifier(name)
validate_identifier_with_namespace(name)
validate_identifier_name(name)
if name in self.members:
raise NamespaceCollision(f"Member '{name}' already exists in {self}")
self.members[name] = type_
Expand Down
2 changes: 1 addition & 1 deletion vyper/semantics/types/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ class InterfaceT(_UserType):

def __init__(self, _id: str, members: dict, events: dict) -> None:
validate_unique_method_ids(list(members.values())) # explicit list cast for mypy
super().__init__(members)
super().__init__(members, skip_namespace_validation=True)

self._id = _id
self.events = events
Expand Down