From 52f7d790bde21dcb92910c0cd8c22c557f0a764f Mon Sep 17 00:00:00 2001 From: James Westman Date: Sat, 30 Apr 2022 16:13:40 -0500 Subject: [PATCH] tests: Test custom types in lookup expressions --- blueprintcompiler/language/expression.py | 30 ++++++++++++++++++-- blueprintcompiler/language/gobject_object.py | 14 +++++---- blueprintcompiler/language/lambdas.py | 4 +++ blueprintcompiler/language/types.py | 14 +++++---- tests/sample_errors/abstract_class.err | 2 +- tests/sample_errors/not_a_class.blp | 2 ++ tests/sample_errors/not_a_class.err | 4 ++- tests/samples/expr_custom_types.blp | 8 ++++++ tests/samples/expr_custom_types.ui | 17 +++++++++++ tests/test_samples.py | 1 + 10 files changed, 82 insertions(+), 14 deletions(-) create mode 100644 tests/samples/expr_custom_types.blp create mode 100644 tests/samples/expr_custom_types.ui diff --git a/blueprintcompiler/language/expression.py b/blueprintcompiler/language/expression.py index 3958907..084d31a 100644 --- a/blueprintcompiler/language/expression.py +++ b/blueprintcompiler/language/expression.py @@ -32,6 +32,10 @@ class Expr(AstNode): def gir_type(self): return self.children[-1].gir_type + @property + def glib_type_name(self): + return self.children[-1].glib_type_name + def emit_xml(self, xml: XmlEmitter): self.children[-1].emit_xml(xml) @@ -71,6 +75,15 @@ class IdentExpr(AstNode): elif self.tokens["ident"] in scope.get_objects(): return scope.get_objects()[self.tokens["ident"]].gir_class + @property + def glib_type_name(self): + scope = self.parent_by_type(Scope) + + if self.is_this: + return scope.this_type_glib_name + elif self.tokens["ident"] in scope.get_objects(): + return scope.get_objects()[self.tokens["ident"]].glib_type_name + def emit_xml(self, xml: XmlEmitter): if self.is_this: raise CompilerBugError() @@ -97,6 +110,10 @@ class ClosureExpr(AstNode): def gir_type(self): return self.parent.parent.gir_type + @property + def glib_type_name(self): + return self.parent.parent.glib_type_name + def emit_xml(self, xml: XmlEmitter): xml.start_tag("closure", function=self.tokens["function"], type=self.gir_type) for child in self.children[Expr]: @@ -113,6 +130,11 @@ class LookupOp(InfixExpr): if prop := parent_type.properties.get(self.tokens["property"]): return prop.type + @property + def glib_type_name(self): + if self.gir_type is not None: + return self.gir_type.glib_type_name + @validate("property") def property_exists(self): if parent_type := self.lhs.gir_type: @@ -127,9 +149,9 @@ class LookupOp(InfixExpr): def emit_xml(self, xml: XmlEmitter): if isinstance(self.lhs, IdentExpr) and self.lhs.is_this: - xml.put_self_closing("lookup", name=self.tokens["property"], type=self.parent_by_type(Scope).this_type) + xml.put_self_closing("lookup", name=self.tokens["property"], type=self.parent_by_type(Scope).this_type_glib_name) else: - xml.start_tag("lookup", name=self.tokens["property"], type=self.lhs.gir_type) + xml.start_tag("lookup", name=self.tokens["property"], type=self.lhs.glib_type_name) self.lhs.emit_xml(xml) xml.end_tag() @@ -141,6 +163,10 @@ class CastExpr(AstNode): def gir_type(self): return self.children[TypeName][0].gir_type + @property + def glib_type_name(self): + return self.children[TypeName][0].glib_type_name + def emit_xml(self, xml: XmlEmitter): self.children[Expr][0].emit_xml(xml) diff --git a/blueprintcompiler/language/gobject_object.py b/blueprintcompiler/language/gobject_object.py index 45c3f40..9190560 100644 --- a/blueprintcompiler/language/gobject_object.py +++ b/blueprintcompiler/language/gobject_object.py @@ -21,6 +21,7 @@ import typing as T from functools import cached_property +from ..gir import Class from .types import ConcreteClassName, ClassName from .common import * from .response_id import ResponseId @@ -60,10 +61,13 @@ class Object(AstNode): @property def gir_class(self): class_names = self.children[ClassName] - if len(class_names) == 0: - return None - else: - return class_names[0].gir_type + if len(class_names) > 0: + if isinstance(class_names[0].gir_type, Class): + return class_names[0].gir_type + + @property + def glib_type_name(self) -> str: + return self.children[ClassName][0].glib_type_name @docs("namespace") def namespace_docs(self): @@ -94,7 +98,7 @@ class Object(AstNode): from .gtkbuilder_child import Child xml.start_tag("object", **{ - "class": self.children[ClassName][0].glib_type_name, + "class": self.glib_type_name, "id": self.tokens["id"], }) for child in self.children: diff --git a/blueprintcompiler/language/lambdas.py b/blueprintcompiler/language/lambdas.py index 0eef4d3..2fb9dfe 100644 --- a/blueprintcompiler/language/lambdas.py +++ b/blueprintcompiler/language/lambdas.py @@ -51,3 +51,7 @@ class Lambda(Value, Scope): @property def this_type(self) -> str: return self.children[TypeName][0].gir_type + + @property + def this_type_glib_name(self) -> str: + return self.children[TypeName][0].glib_type_name diff --git a/blueprintcompiler/language/types.py b/blueprintcompiler/language/types.py index ad48c3e..3a81b5c 100644 --- a/blueprintcompiler/language/types.py +++ b/blueprintcompiler/language/types.py @@ -20,6 +20,7 @@ import typing as T from .common import * +from ..gir import Class, Interface class TypeName(AstNode): @@ -80,16 +81,19 @@ class TypeName(AstNode): class ClassName(TypeName): - @validate("class_name") - def gir_type_is_class(self): - if self.gir_type is not None and not isinstance(self.gir_type, gir.Class): - raise CompileError(f"{self.gir_type.full_name} is not a class") + @validate("namespace", "class_name") + def gir_class_exists(self): + if self.gir_type is not None and not isinstance(self.gir_type, Class): + if isinstance(self.gir_type, Interface): + raise CompileError(f"{self.gir_type.full_name} is an interface, not a class") + else: + raise CompileError(f"{self.gir_type.full_name} is not a class") class ConcreteClassName(ClassName): @validate("namespace", "class_name") def not_abstract(self): - if self.gir_type is not None and self.gir_type.abstract: + if isinstance(self.gir_type, Class) and self.gir_type.abstract: raise CompileError( f"{self.gir_type.full_name} can't be instantiated because it's abstract", hints=[f"did you mean to use a subclass of {self.gir_type.full_name}?"] diff --git a/tests/sample_errors/abstract_class.err b/tests/sample_errors/abstract_class.err index 1765fc4..f682ead 100644 --- a/tests/sample_errors/abstract_class.err +++ b/tests/sample_errors/abstract_class.err @@ -1 +1 @@ -4,1,10,Gtk.Widget can't be instantiated because it's abstract +4,1,10,Gtk.Widget can't be instantiated because it's abstract \ No newline at end of file diff --git a/tests/sample_errors/not_a_class.blp b/tests/sample_errors/not_a_class.blp index 0383154..bdc510b 100644 --- a/tests/sample_errors/not_a_class.blp +++ b/tests/sample_errors/not_a_class.blp @@ -1,3 +1,5 @@ using Gtk 4.0; template TestTemplate : Gtk.Orientable {} +Gtk.Orientable {} +int {} diff --git a/tests/sample_errors/not_a_class.err b/tests/sample_errors/not_a_class.err index d0a7260..a181722 100644 --- a/tests/sample_errors/not_a_class.err +++ b/tests/sample_errors/not_a_class.err @@ -1 +1,3 @@ -3,29,10,Gtk.Orientable is not a class +3,25,14,Gtk.Orientable is an interface, not a class +4,1,14,Gtk.Orientable is an interface, not a class +5,1,3,int is not a class \ No newline at end of file diff --git a/tests/samples/expr_custom_types.blp b/tests/samples/expr_custom_types.blp new file mode 100644 index 0000000..308977a --- /dev/null +++ b/tests/samples/expr_custom_types.blp @@ -0,0 +1,8 @@ +using Gtk 4.0; + +.MyWidget widget {} + +.MyOtherWidget other_widget { + property: bind ((.MyObject) widget.something).other; + expr-prop: (.MyObject obj) => obj.other; +} diff --git a/tests/samples/expr_custom_types.ui b/tests/samples/expr_custom_types.ui new file mode 100644 index 0000000..0407fda --- /dev/null +++ b/tests/samples/expr_custom_types.ui @@ -0,0 +1,17 @@ + + + + + + + + + widget + + + + + + + + diff --git a/tests/test_samples.py b/tests/test_samples.py index 7f7adfb..d398195 100644 --- a/tests/test_samples.py +++ b/tests/test_samples.py @@ -137,6 +137,7 @@ class TestSamples(unittest.TestCase): self.assert_sample("comments") self.assert_sample("enum") self.assert_sample("expr_closure") + self.assert_sample("expr_custom_types") self.assert_sample("expr_lookup") self.assert_sample("file_filter") self.assert_sample("flags")