feat(checker): implement lookup_member method
This commit is contained in:
@@ -11,14 +11,11 @@ from midas.checker.registry import TypesRegistry
|
||||
from midas.checker.reporter import FileReporter, Reporter
|
||||
from midas.checker.resolver import Resolver
|
||||
from midas.checker.types import (
|
||||
ComplexType,
|
||||
ExtensionType,
|
||||
Function,
|
||||
Operation,
|
||||
Type,
|
||||
UnitType,
|
||||
UnknownType,
|
||||
unfold_type,
|
||||
)
|
||||
from midas.parser.python import PythonParser
|
||||
|
||||
@@ -250,8 +247,7 @@ class PythonTyper(
|
||||
self._assign_var(location, target, value_type)
|
||||
|
||||
case p.GetExpr(object=object, name=name):
|
||||
object_type: Type = self.type_of(object)
|
||||
self._assign_attr(location, object_type, name, value_type)
|
||||
self._assign_attr(location, object, name, value_type)
|
||||
|
||||
case _:
|
||||
if not isinstance(target, p.VariableExpr):
|
||||
@@ -277,42 +273,18 @@ class PythonTyper(
|
||||
)
|
||||
|
||||
def _assign_attr(
|
||||
self, location: Location, object: Type, name: str, value_type: Type
|
||||
self, location: Location, object: p.Expr, name: str, value_type: Type
|
||||
):
|
||||
# TODO: improve recursion to have better error messages
|
||||
base_object: Type = unfold_type(object)
|
||||
match base_object:
|
||||
case ComplexType(members=members):
|
||||
if name not in members:
|
||||
self.reporter.error(location, f"Unknown member '{object}.{name}'")
|
||||
object_type: Type = self.type_of(object)
|
||||
member: Optional[Type] = self.types.lookup_member(object_type, name)
|
||||
if member is None:
|
||||
self.reporter.error(location, f"Unknown member '{name}' of {object_type}")
|
||||
return
|
||||
|
||||
member_type: Type = members[name]
|
||||
if not self.is_subtype(value_type, member_type):
|
||||
self.logger.debug(f"Member '{name}' of {object_type} has type {member}")
|
||||
if not self.is_subtype(value_type, member):
|
||||
self.reporter.error(
|
||||
location,
|
||||
f"Cannot assign {value_type} to member '{object}.{name}' of type {member_type}",
|
||||
)
|
||||
return
|
||||
|
||||
case ExtensionType(base=base, extension=ComplexType(members=members)):
|
||||
if name in members:
|
||||
member_type: Type = members[name]
|
||||
if not self.is_subtype(value_type, member_type):
|
||||
self.reporter.error(
|
||||
location,
|
||||
f"Cannot assign {value_type} to member '{object}.{name}' of type {member_type}",
|
||||
)
|
||||
return
|
||||
return self._assign_attr(location, base, name, value_type)
|
||||
|
||||
case UnknownType():
|
||||
pass
|
||||
|
||||
case _:
|
||||
self.reporter.error(
|
||||
location,
|
||||
f"Cannot assign {value_type} to unknown property '{object}.{name}'",
|
||||
f"Cannot assign {value_type} to member '{object_type}.{name}' of type {member}",
|
||||
)
|
||||
|
||||
def visit_return_stmt(self, stmt: p.ReturnStmt) -> None:
|
||||
@@ -433,39 +405,15 @@ class PythonTyper(
|
||||
|
||||
def visit_get_expr(self, expr: p.GetExpr) -> Type:
|
||||
object: Type = self.type_of(expr.object)
|
||||
member: Optional[Type] = self._get_member(object, expr.name)
|
||||
member: Optional[Type] = self.types.lookup_member(object, expr.name)
|
||||
if member is None:
|
||||
self.reporter.error(
|
||||
expr.location, f"Unknown property '{expr.name}' on {object}"
|
||||
expr.location, f"Unknown member '{expr.name}' of {object}"
|
||||
)
|
||||
return UnknownType()
|
||||
self.logger.debug(f"Property '{expr.name}' on {object} has type {member}")
|
||||
self.logger.debug(f"Member '{expr.name}' of {object} has type {member}")
|
||||
return member
|
||||
|
||||
def _get_member(self, object: Type, name: str) -> Optional[Type]:
|
||||
base_object: Type = unfold_type(object)
|
||||
match base_object:
|
||||
case ComplexType(members=members):
|
||||
if name in members:
|
||||
return members[name]
|
||||
self.logger.debug(f"No property '{name}' in {base_object}")
|
||||
return None
|
||||
|
||||
case ExtensionType(base=base, extension=ComplexType(members=members)):
|
||||
if name in members:
|
||||
return members[name]
|
||||
self.logger.debug(
|
||||
f"No property '{name}' on {base_object}, looking up in base"
|
||||
)
|
||||
return self._get_member(base, name)
|
||||
|
||||
case UnknownType():
|
||||
return UnknownType()
|
||||
|
||||
case _:
|
||||
self.logger.debug(f"Can't get property on {base_object}")
|
||||
return UnknownType()
|
||||
|
||||
def visit_literal_expr(self, expr: p.LiteralExpr) -> Type:
|
||||
match expr.value:
|
||||
case bool(): # Must be before int
|
||||
|
||||
@@ -7,11 +7,13 @@ from midas.checker.types import (
|
||||
AppliedType,
|
||||
BaseType,
|
||||
ComplexType,
|
||||
ExtensionType,
|
||||
Function,
|
||||
GenericType,
|
||||
Operation,
|
||||
OverloadedFunction,
|
||||
Type,
|
||||
UnknownType,
|
||||
substitute_typevars,
|
||||
)
|
||||
|
||||
@@ -337,3 +339,51 @@ class TypesRegistry:
|
||||
reduced = True
|
||||
break
|
||||
return [types[i] for i in keep]
|
||||
|
||||
def lookup_member(self, type: Type, member_name: str) -> Optional[Type]:
|
||||
match type:
|
||||
case AliasType(name=name, type=base):
|
||||
if name in self._members:
|
||||
if member_name in self._members[name]:
|
||||
return self._members[name][member_name]
|
||||
return self.lookup_member(base, member_name)
|
||||
|
||||
case AppliedType(name=name, body=body, args=args):
|
||||
generic: Type = self.get_type(name)
|
||||
|
||||
if not isinstance(generic, GenericType):
|
||||
raise ValueError("AppliedType not derived from a GenericType")
|
||||
|
||||
substitutions = {
|
||||
type_var.name: arg for arg, type_var in zip(args, generic.params)
|
||||
}
|
||||
if name in self._members:
|
||||
if member_name in self._members[name]:
|
||||
member_type: Type = self._members[name][member_name]
|
||||
return substitute_typevars(member_type, substitutions)
|
||||
|
||||
member_type2: Optional[Type] = self.lookup_member(body, member_name)
|
||||
if member_type2 is not None:
|
||||
member_type2 = substitute_typevars(member_type2, substitutions)
|
||||
return member_type2
|
||||
|
||||
case ComplexType(members=members):
|
||||
if member_name in members:
|
||||
return members[member_name]
|
||||
self.logger.debug(f"No member '{member_name}' in {type}")
|
||||
return None
|
||||
|
||||
case ExtensionType(base=base, extension=ComplexType(members=members)):
|
||||
if member_name in members:
|
||||
return members[member_name]
|
||||
self.logger.debug(
|
||||
f"No member '{member_name}' on {type}, looking up in base"
|
||||
)
|
||||
return self.lookup_member(base, member_name)
|
||||
|
||||
case UnknownType():
|
||||
return UnknownType()
|
||||
|
||||
case _:
|
||||
self.logger.debug(f"Can't get member on {type}")
|
||||
return None
|
||||
|
||||
Reference in New Issue
Block a user