def get_possible_types_from_node(self, node, only_definitions=True): """ Find all possible types for a given node. Arguments --------- node : VyperNode The vyper AST node to find a type for. only_definitions: bool, optional If True, raises when the return value is not a type definition e.g a primitive, meta type, or function call Returns ------- List A list of type objects """ fn = self._find_fn(node) types_list = fn(node) if only_definitions: invalid = next( (i for i in types_list if not isinstance(i, BaseTypeDefinition)), None) if invalid: if isinstance(invalid, type) and types.BasePrimitive in invalid.mro(): raise InvalidReference( f"'{invalid._id}' is a type - expected a literal or variable", node) else: raise InvalidReference("Expected a literal or variable", node) return types_list
def get_possible_types_from_node(self, node, only_definitions=True): """ Find all possible types for a given node. If the node's metadata contains type information propagated from constant folding, then that type is returned. Arguments --------- node : VyperNode The vyper AST node to find a type for. only_definitions: bool, optional If True, raises when the return value is not a type definition e.g a primitive, meta type, or function call Returns ------- List A list of type objects """ # Early termination if typedef is propagated in metadata if "type" in node._metadata: return [node._metadata["type"]] fn = self._find_fn(node) types_list = fn(node) if only_definitions: invalid = next( (i for i in types_list if not isinstance(i, BaseTypeDefinition)), None) if invalid: if isinstance(invalid, type) and types.BasePrimitive in invalid.mro(): raise InvalidReference( f"'{invalid._id}' is a type - expected a literal or variable", node) else: raise InvalidReference("Expected a literal or variable", node) if all(isinstance(i, IntegerAbstractType) for i in types_list): # for numeric types, sort according by number of bits descending # we do this to ensure literals are cast with the largest possible type return sorted(types_list, key=lambda k: (k._bits, not k._is_signed), reverse=True) return types_list
def types_from_Name(self, node): # variable name, e.g. `foo` name = node.id if name not in self.namespace and name in self.namespace["self"].members: raise InvalidReference( f"'{name}' is a storage variable, access it as self.{name}", node, ) try: return [self.namespace[node.id]] except VyperException as exc: raise exc.with_annotation(node) from None
def types_from_Attribute(self, node): # variable attribute, e.g. `foo.bar` var = self.get_exact_type_from_node(node.value) name = node.attr try: return [var.get_member(name, node)] except UnknownAttribute: if node.get("value.id") != "self": raise if name in self.namespace: raise InvalidReference( f"'{name}' is not a storage variable, it should not be prepended with self", node, ) from None raise UndeclaredDefinition( f"Storage variable '{name}' has not been declared", node) from None
def types_from_Attribute(self, node): # variable attribute, e.g. `foo.bar` var = self.get_exact_type_from_node(node.value, only_definitions=False) name = node.attr try: return [var.get_member(name, node)] except UnknownAttribute: if node.get("value.id") != "self": raise if name in self.namespace: raise InvalidReference( f"'{name}' is not a storage variable, it should not be prepended with self", node, ) from None suggestions_str = get_levenshtein_error_suggestions( name, var.members, 0.4) raise UndeclaredDefinition( f"Storage variable '{name}' has not been declared. {suggestions_str}", node) from None
def validate_expected_type(node, expected_type): """ Validate that the given node matches the expected type(s) Raises if the node does not match one of the expected types. Arguments --------- node : VyperNode Vyper ast node. expected_type : Tuple | BaseType A type object, or tuple of type objects Returns ------- None """ given_types = _ExprTypeChecker().get_possible_types_from_node(node) if not isinstance(expected_type, tuple): expected_type = (expected_type,) if isinstance(node, (vy_ast.List, vy_ast.Tuple)): # special case - for literal arrays or tuples we individually validate each item for expected in (i for i in expected_type if isinstance(i, ArrayDefinition)): if _validate_literal_array(node, expected): return else: for given, expected in itertools.product(given_types, expected_type): if expected.compare_type(given): return # validation failed, prepare a meaningful error message if len(expected_type) > 1: expected_str = f"one of {', '.join(str(i) for i in expected_type)}" else: expected_str = expected_type[0] if len(given_types) == 1 and getattr(given_types[0], "_is_callable", False): raise StructureException( f"{given_types[0]} cannot be referenced directly, it must be called", node ) if not isinstance(node, (vy_ast.List, vy_ast.Tuple)) and node.get_descendants( vy_ast.Name, include_self=True ): given = given_types[0] if isinstance(given, type) and types.BasePrimitive in given.mro(): raise InvalidReference( f"'{given._id}' is a type - expected a literal or variable", node ) raise TypeMismatch(f"Given reference has type {given}, expected {expected_str}", node) else: if len(given_types) == 1: given_str = str(given_types[0]) else: types_str = sorted(str(i) for i in given_types) given_str = f"{', '.join(types_str[:1])} or {types_str[-1]}" raise InvalidType( f"Expected {expected_str} but literal can only be cast as {given_str}", node )