From 99e428d93c17070699d1f21a0436c606b74df2c9 Mon Sep 17 00:00:00 2001 From: James Westman Date: Thu, 28 Apr 2022 23:07:32 -0500 Subject: [PATCH] validation: Disallow instantiating abstract classes --- blueprintcompiler/gir.py | 4 ++++ blueprintcompiler/language/gobject_object.py | 8 ++++++++ blueprintcompiler/language/gtk_menu.py | 2 +- blueprintcompiler/language/gtkbuilder_template.py | 4 ++++ tests/sample_errors/a11y_prop_dne.blp | 2 +- tests/sample_errors/a11y_prop_obj_dne.blp | 2 +- tests/sample_errors/a11y_prop_type.blp | 2 +- tests/sample_errors/abstract_class.blp | 4 ++++ tests/sample_errors/abstract_class.err | 1 + tests/sample_errors/assign_inline_menu.err | 2 +- tests/sample_errors/filters_in_non_file_filter.blp | 2 +- tests/sample_errors/filters_in_non_file_filter.err | 6 +++--- tests/sample_errors/obj_in_string_list.blp | 2 +- tests/sample_errors/obj_in_string_list.err | 2 +- tests/sample_errors/widgets_in_non_size_group.blp | 2 +- tests/sample_errors/widgets_in_non_size_group.err | 2 +- tests/test_samples.py | 1 + 17 files changed, 35 insertions(+), 13 deletions(-) create mode 100644 tests/sample_errors/abstract_class.blp create mode 100644 tests/sample_errors/abstract_class.err diff --git a/blueprintcompiler/gir.py b/blueprintcompiler/gir.py index 973ccbb..6234077 100644 --- a/blueprintcompiler/gir.py +++ b/blueprintcompiler/gir.py @@ -236,6 +236,10 @@ class Class(GirNode, GirType): self.own_properties = {child["name"]: Property(self, child) for child in xml.get_elements("property")} self.own_signals = {child["name"]: Signal(self, child) for child in xml.get_elements("glib:signal")} + @property + def abstract(self): + return self.xml["abstract"] == "1" + @property def signature(self): result = f"class {self.container.name}.{self.name}" diff --git a/blueprintcompiler/language/gobject_object.py b/blueprintcompiler/language/gobject_object.py index 6d1aa66..f2d6535 100644 --- a/blueprintcompiler/language/gobject_object.py +++ b/blueprintcompiler/language/gobject_object.py @@ -61,6 +61,14 @@ class Object(AstNode): if self.tokens["class_name"] and not self.tokens["ignore_gir"] and self.gir_ns is not None: self.root.gir.validate_class(self.tokens["class_name"], self.tokens["namespace"]) + @validate("namespace", "class_name") + def not_abstract(self): + if self.gir_class is not None and self.gir_class.abstract: + raise CompileError( + f"{self.gir_class.full_name} can't be instantiated because it's abstract", + hints=[f"did you mean to use a subclass of {self.gir_class.full_name}?"] + ) + @property def gir_ns(self): if not self.tokens["ignore_gir"]: diff --git a/blueprintcompiler/language/gtk_menu.py b/blueprintcompiler/language/gtk_menu.py index 2932e9a..b63233c 100644 --- a/blueprintcompiler/language/gtk_menu.py +++ b/blueprintcompiler/language/gtk_menu.py @@ -33,7 +33,7 @@ class Menu(Object): @property def gir_class(self): - return self.root.gir.namespaces["Gtk"].lookup_type("Gio.MenuModel") + return self.root.gir.namespaces["Gtk"].lookup_type("Gio.Menu") class MenuAttribute(BaseAttribute): diff --git a/blueprintcompiler/language/gtkbuilder_template.py b/blueprintcompiler/language/gtkbuilder_template.py index 66e01c5..4cd5e75 100644 --- a/blueprintcompiler/language/gtkbuilder_template.py +++ b/blueprintcompiler/language/gtkbuilder_template.py @@ -33,6 +33,10 @@ class Template(Object): ObjectContent, ] + @validate() + def not_abstract(self): + pass # does not apply to templates + def emit_xml(self, xml: XmlEmitter): if self.gir_class: parent = self.gir_class.glib_type_name diff --git a/tests/sample_errors/a11y_prop_dne.blp b/tests/sample_errors/a11y_prop_dne.blp index b17c507..ad444f0 100644 --- a/tests/sample_errors/a11y_prop_dne.blp +++ b/tests/sample_errors/a11y_prop_dne.blp @@ -1,6 +1,6 @@ using Gtk 4.0; -Widget { +Button { accessibility { not_a_prop: "Hello, world!"; } diff --git a/tests/sample_errors/a11y_prop_obj_dne.blp b/tests/sample_errors/a11y_prop_obj_dne.blp index e0285ff..79bba2e 100644 --- a/tests/sample_errors/a11y_prop_obj_dne.blp +++ b/tests/sample_errors/a11y_prop_obj_dne.blp @@ -1,6 +1,6 @@ using Gtk 4.0; -Widget { +Button { accessibility { labelled-by: not_an_object; } diff --git a/tests/sample_errors/a11y_prop_type.blp b/tests/sample_errors/a11y_prop_type.blp index 52eb37b..7fae738 100644 --- a/tests/sample_errors/a11y_prop_type.blp +++ b/tests/sample_errors/a11y_prop_type.blp @@ -1,6 +1,6 @@ using Gtk 4.0; -Widget { +Button { accessibility { orientation: 1; } diff --git a/tests/sample_errors/abstract_class.blp b/tests/sample_errors/abstract_class.blp new file mode 100644 index 0000000..c6ea8b9 --- /dev/null +++ b/tests/sample_errors/abstract_class.blp @@ -0,0 +1,4 @@ +using Gtk 4.0; + +template MyWidget : Gtk.Widget {} +Gtk.Widget {} diff --git a/tests/sample_errors/abstract_class.err b/tests/sample_errors/abstract_class.err new file mode 100644 index 0000000..1765fc4 --- /dev/null +++ b/tests/sample_errors/abstract_class.err @@ -0,0 +1 @@ +4,1,10,Gtk.Widget can't be instantiated because it's abstract diff --git a/tests/sample_errors/assign_inline_menu.err b/tests/sample_errors/assign_inline_menu.err index aec5ce9..5200603 100644 --- a/tests/sample_errors/assign_inline_menu.err +++ b/tests/sample_errors/assign_inline_menu.err @@ -1 +1 @@ -4,3,15,Cannot assign Gio.MenuModel to string +4,3,15,Cannot assign Gio.Menu to string diff --git a/tests/sample_errors/filters_in_non_file_filter.blp b/tests/sample_errors/filters_in_non_file_filter.blp index 2c08e6f..eeab6a7 100644 --- a/tests/sample_errors/filters_in_non_file_filter.blp +++ b/tests/sample_errors/filters_in_non_file_filter.blp @@ -1,6 +1,6 @@ using Gtk 4.0; -Widget { +Button { mime-types [] patterns [] suffixes [] diff --git a/tests/sample_errors/filters_in_non_file_filter.err b/tests/sample_errors/filters_in_non_file_filter.err index 4bb0619..e069b5f 100644 --- a/tests/sample_errors/filters_in_non_file_filter.err +++ b/tests/sample_errors/filters_in_non_file_filter.err @@ -1,3 +1,3 @@ -4,3,13,Gtk.Widget is not a Gtk.FileFilter, so it doesn't have file filter properties -5,3,11,Gtk.Widget is not a Gtk.FileFilter, so it doesn't have file filter properties -6,3,11,Gtk.Widget is not a Gtk.FileFilter, so it doesn't have file filter properties +4,3,13,Gtk.Button is not a Gtk.FileFilter, so it doesn't have file filter properties +5,3,11,Gtk.Button is not a Gtk.FileFilter, so it doesn't have file filter properties +6,3,11,Gtk.Button is not a Gtk.FileFilter, so it doesn't have file filter properties diff --git a/tests/sample_errors/obj_in_string_list.blp b/tests/sample_errors/obj_in_string_list.blp index 18cac9e..e3f70ef 100644 --- a/tests/sample_errors/obj_in_string_list.blp +++ b/tests/sample_errors/obj_in_string_list.blp @@ -6,4 +6,4 @@ StringList { ] } -Widget id {} +Button id {} diff --git a/tests/sample_errors/obj_in_string_list.err b/tests/sample_errors/obj_in_string_list.err index aacead5..67628a3 100644 --- a/tests/sample_errors/obj_in_string_list.err +++ b/tests/sample_errors/obj_in_string_list.err @@ -1 +1 @@ -5,5,2,Cannot assign Gtk.Widget to string +5,5,2,Cannot assign Gtk.Button to string diff --git a/tests/sample_errors/widgets_in_non_size_group.blp b/tests/sample_errors/widgets_in_non_size_group.blp index c73204d..76fd42c 100644 --- a/tests/sample_errors/widgets_in_non_size_group.blp +++ b/tests/sample_errors/widgets_in_non_size_group.blp @@ -1,5 +1,5 @@ using Gtk 4.0; -Widget { +Button { widgets [] } diff --git a/tests/sample_errors/widgets_in_non_size_group.err b/tests/sample_errors/widgets_in_non_size_group.err index 70ce8fb..c57bbdf 100644 --- a/tests/sample_errors/widgets_in_non_size_group.err +++ b/tests/sample_errors/widgets_in_non_size_group.err @@ -1 +1 @@ -4,3,7,Gtk.Widget is not a Gtk.SizeGroup, so it doesn't have size group properties +4,3,7,Gtk.Button is not a Gtk.SizeGroup, so it doesn't have size group properties diff --git a/tests/test_samples.py b/tests/test_samples.py index c490388..c4a00a8 100644 --- a/tests/test_samples.py +++ b/tests/test_samples.py @@ -164,6 +164,7 @@ class TestSamples(unittest.TestCase): self.assert_sample_error("a11y_prop_dne") self.assert_sample_error("a11y_prop_obj_dne") self.assert_sample_error("a11y_prop_type") + self.assert_sample_error("abstract_class") self.assert_sample_error("assign_inline_menu") self.assert_sample_error("action_widget_float_response") self.assert_sample_error("action_widget_have_no_id")