diff --git a/tpParser/shared/src/main/scala/tigerpython/utilities/scopes/AstWalker.scala b/tpParser/shared/src/main/scala/tigerpython/utilities/scopes/AstWalker.scala index bed334f..fbfc6f2 100644 --- a/tpParser/shared/src/main/scala/tigerpython/utilities/scopes/AstWalker.scala +++ b/tpParser/shared/src/main/scala/tigerpython/utilities/scopes/AstWalker.scala @@ -555,7 +555,15 @@ class AstWalker(val scope: Scope) { case AstNode.NameParameter(_, name, annotation) => val dataType = if (annotation != null) - getType(annotation) + // A bare class (or a container built from one, e.g. `list[Widget]`) resolves + // via getType to the raw ClassType/PrimitiveType itself - its real members + // live in getInstanceFields, which only an Instance wrapper exposes (see + // Instance.apply's docs). Every other call-site that produces a value of a + // user's own type (constructor calls, function-call return values, `for` + // targets) already goes through this same wrapping; a param annotation was + // the one place that didn't, silently losing all member completion for any + // annotated parameter. + Instance(getType(annotation)) else if (i >= delta) { getType(params.defaults(i - delta)._1) match { case BuiltinTypes.NONE_TYPE => BuiltinTypes.ANY_TYPE diff --git a/tpParser/shared/src/main/scala/tigerpython/utilities/types/TypeAstWalker.scala b/tpParser/shared/src/main/scala/tigerpython/utilities/types/TypeAstWalker.scala index fc37f51..f8e7920 100644 --- a/tpParser/shared/src/main/scala/tigerpython/utilities/types/TypeAstWalker.scala +++ b/tpParser/shared/src/main/scala/tigerpython/utilities/types/TypeAstWalker.scala @@ -68,8 +68,14 @@ class TypeAstWalker { (getType(subscript.base), subscript.slice) match { case (BuiltinTypes.LIST_TYPE, Index(_, value)) => ListType(getType(value)) + case (BuiltinTypes.SET_TYPE, Index(_, value)) => + SetType(getType(value)) case (BuiltinTypes.DICT_TYPE, MultiSlice(_, Array(Index(_, key), Index(_, value)))) => new DictType(getType(key), getType(value)) + // `tuple[X, ...]`: a homogeneous, variable-length tuple (as opposed to `tuple[X, Y]`, + // a fixed-arity tuple where each position has its own type). + case (BuiltinTypes.TUPLE_TYPE, MultiSlice(_, Array(Index(_, elt), Index(_, _: AstNode.Ellipsis)))) => + new VarTupleType(getType(elt)) case (BuiltinTypes.TUPLE_TYPE, MultiSlice(_, elements)) if elements.forall { case Index(_, _) => true case _ => false diff --git a/tpParser/shared/src/test/programs/completer/completer_annotation_generic_set.py b/tpParser/shared/src/test/programs/completer/completer_annotation_generic_set.py new file mode 100644 index 0000000..ed25d3c --- /dev/null +++ b/tpParser/shared/src/test/programs/completer/completer_annotation_generic_set.py @@ -0,0 +1,9 @@ +# 107 +# draw +class Widget: + def draw(self): + pass + +def foo(items: set[Widget]): + for w in items: + w. diff --git a/tpParser/shared/src/test/programs/completer/completer_annotation_generic_tuple_ellipsis.py b/tpParser/shared/src/test/programs/completer/completer_annotation_generic_tuple_ellipsis.py new file mode 100644 index 0000000..b5a98ce --- /dev/null +++ b/tpParser/shared/src/test/programs/completer/completer_annotation_generic_tuple_ellipsis.py @@ -0,0 +1,9 @@ +# 114 +# draw +class Widget: + def draw(self): + pass + +def foo(items: tuple[Widget, ...]): + for w in items: + w. diff --git a/tpParser/shared/src/test/programs/completer/completer_annotation_param_class.py b/tpParser/shared/src/test/programs/completer/completer_annotation_param_class.py new file mode 100644 index 0000000..405486f --- /dev/null +++ b/tpParser/shared/src/test/programs/completer/completer_annotation_param_class.py @@ -0,0 +1,8 @@ +# 74 +# draw +class Widget: + def draw(self): + pass + +def foo(w: Widget): + w. diff --git a/tpParser/shared/src/test/programs/completer/completer_pyi_generic_list_paraminfer.py b/tpParser/shared/src/test/programs/completer/completer_pyi_generic_list_paraminfer.py new file mode 100644 index 0000000..ad222bf --- /dev/null +++ b/tpParser/shared/src/test/programs/completer/completer_pyi_generic_list_paraminfer.py @@ -0,0 +1,15 @@ +# pyi:pyi.generic_list_paraminfer +# class Actor: +# def is_at_edge(self) -> None: ... +# def remove(self) -> None: ... +# def get_x(self) -> None: ... +# def get_actors() -> list[Actor]: ... +# 76 +# get_x;is_at_edge;remove +from pyi.generic_list_paraminfer import * + +def move(particle): + particle. + +for p in get_actors(): + move(p) diff --git a/tpParser/shared/src/test/programs/completer/completer_pyi_generic_set_paraminfer.py b/tpParser/shared/src/test/programs/completer/completer_pyi_generic_set_paraminfer.py new file mode 100644 index 0000000..a3de6b5 --- /dev/null +++ b/tpParser/shared/src/test/programs/completer/completer_pyi_generic_set_paraminfer.py @@ -0,0 +1,15 @@ +# pyi:pyi.generic_set_paraminfer +# class Actor: +# def is_at_edge(self) -> None: ... +# def remove(self) -> None: ... +# def get_x(self) -> None: ... +# def get_actors() -> set[Actor]: ... +# 75 +# get_x;is_at_edge;remove +from pyi.generic_set_paraminfer import * + +def move(particle): + particle. + +for p in get_actors(): + move(p) diff --git a/tpParser/shared/src/test/programs/completer/completer_pyi_generic_tuple_paraminfer.py b/tpParser/shared/src/test/programs/completer/completer_pyi_generic_tuple_paraminfer.py new file mode 100644 index 0000000..57a8d56 --- /dev/null +++ b/tpParser/shared/src/test/programs/completer/completer_pyi_generic_tuple_paraminfer.py @@ -0,0 +1,15 @@ +# pyi:pyi.generic_tuple_paraminfer +# class Actor: +# def is_at_edge(self) -> None: ... +# def remove(self) -> None: ... +# def get_x(self) -> None: ... +# def get_actors() -> tuple[Actor, ...]: ... +# 77 +# get_x;is_at_edge;remove +from pyi.generic_tuple_paraminfer import * + +def move(particle): + particle. + +for p in get_actors(): + move(p)