Add errors for duplicate properties and blocks

This commit is contained in:
James Westman 2022-05-27 13:27:18 -05:00
parent f18c8b7a2d
commit 824476bda1
No known key found for this signature in database
GPG key ID: CE2DBA0ADB654EA6
12 changed files with 122 additions and 0 deletions

View file

@ -131,6 +131,16 @@ class AstNode:
yield from child.iterate_children_recursive() yield from child.iterate_children_recursive()
def validate_unique_in_parent(self, error, check=None):
for child in self.parent.children:
if child is self:
break
if type(child) is type(self):
if check is None or check(child):
raise CompileError(error)
def validate(token_name=None, end_token_name=None, skip_incomplete=False): def validate(token_name=None, end_token_name=None, skip_incomplete=False):
""" Decorator for functions that validate an AST node. Exceptions raised """ Decorator for functions that validate an AST node. Exceptions raised
during validation are marked with range information from the tokens. """ during validation are marked with range information from the tokens. """

View file

@ -112,6 +112,14 @@ class Property(AstNode):
) )
@validate("name")
def unique_in_parent(self):
self.validate_unique_in_parent(
f"Duplicate property '{self.tokens['name']}'",
check=lambda child: child.tokens["name"] == self.tokens["name"]
)
@docs("name") @docs("name")
def property_docs(self): def property_docs(self):
if self.gir_property is not None: if self.gir_property is not None:

View file

@ -139,6 +139,13 @@ class A11yProperty(BaseTypedAttribute):
did_you_mean=(self.tokens["name"], types.keys()), did_you_mean=(self.tokens["name"], types.keys()),
) )
@validate("name")
def unique_in_parent(self):
self.validate_unique_in_parent(
f"Duplicate accessibility attribute '{self.tokens['name']}'",
check=lambda child: child.tokens["name"] == self.tokens["name"],
)
@docs("name") @docs("name")
def prop_docs(self): def prop_docs(self):
if self.tokens["name"] in get_types(self.root.gir): if self.tokens["name"] in get_types(self.root.gir):
@ -156,6 +163,9 @@ class A11y(AstNode):
def container_is_widget(self): def container_is_widget(self):
validate_parent_type(self, "Gtk", "Widget", "accessibility properties") validate_parent_type(self, "Gtk", "Widget", "accessibility properties")
@validate("accessibility")
def unique_in_parent(self):
self.validate_unique_in_parent("Duplicate accessibility block")
def emit_xml(self, xml: XmlEmitter): def emit_xml(self, xml: XmlEmitter):
xml.start_tag("accessibility") xml.start_tag("accessibility")

View file

@ -56,6 +56,9 @@ class Items(AstNode):
def container_is_combo_box_text(self): def container_is_combo_box_text(self):
validate_parent_type(self, "Gtk", "ComboBoxText", "combo box items") validate_parent_type(self, "Gtk", "ComboBoxText", "combo box items")
@validate("items")
def unique_in_parent(self):
self.validate_unique_in_parent("Duplicate items block")
def emit_xml(self, xml: XmlEmitter): def emit_xml(self, xml: XmlEmitter):
xml.start_tag("items") xml.start_tag("items")

View file

@ -27,6 +27,18 @@ class Filters(AstNode):
def container_is_file_filter(self): def container_is_file_filter(self):
validate_parent_type(self, "Gtk", "FileFilter", "file filter properties") validate_parent_type(self, "Gtk", "FileFilter", "file filter properties")
@validate()
def unique_in_parent(self):
# The token argument to validate() needs to be calculated based on
# the instance, hence wrapping it like this.
@validate(self.tokens["tag_name"])
def wrapped_validator(self):
self.validate_unique_in_parent(
f"Duplicate {self.tokens['tag_name']} block",
check=lambda child: child.tokens["tag_name"] == self.tokens["tag_name"],
)
wrapped_validator(self)
def emit_xml(self, xml: XmlEmitter): def emit_xml(self, xml: XmlEmitter):
xml.start_tag(self.tokens["tag_name"]) xml.start_tag(self.tokens["tag_name"])
for child in self.children: for child in self.children:

View file

@ -31,6 +31,13 @@ class LayoutProperty(BaseAttribute):
# there isn't really a way to validate these # there isn't really a way to validate these
return None return None
@validate("name")
def unique_in_parent(self):
self.validate_unique_in_parent(
f"Duplicate layout property '{self.name}'",
check=lambda child: child.name == self.name,
)
layout_prop = Group( layout_prop = Group(
LayoutProperty, LayoutProperty,
@ -53,6 +60,9 @@ class Layout(AstNode):
def container_is_widget(self): def container_is_widget(self):
validate_parent_type(self, "Gtk", "Widget", "layout properties") validate_parent_type(self, "Gtk", "Widget", "layout properties")
@validate("layout")
def unique_in_parent(self):
self.validate_unique_in_parent("Duplicate layout block")
def emit_xml(self, xml: XmlEmitter): def emit_xml(self, xml: XmlEmitter):
xml.start_tag("layout") xml.start_tag("layout")

View file

@ -55,6 +55,10 @@ class Widgets(AstNode):
def container_is_size_group(self): def container_is_size_group(self):
validate_parent_type(self, "Gtk", "SizeGroup", "size group properties") validate_parent_type(self, "Gtk", "SizeGroup", "size group properties")
@validate("widgets")
def unique_in_parent(self):
self.validate_unique_in_parent("Duplicate widgets block")
def emit_xml(self, xml: XmlEmitter): def emit_xml(self, xml: XmlEmitter):
xml.start_tag("widgets") xml.start_tag("widgets")
for child in self.children: for child in self.children:

View file

@ -51,6 +51,9 @@ class Strings(AstNode):
def container_is_string_list(self): def container_is_string_list(self):
validate_parent_type(self, "Gtk", "StringList", "StringList items") validate_parent_type(self, "Gtk", "StringList", "StringList items")
@validate("strings")
def unique_in_parent(self):
self.validate_unique_in_parent("Duplicate strings block")
def emit_xml(self, xml: XmlEmitter): def emit_xml(self, xml: XmlEmitter):
xml.start_tag("items") xml.start_tag("items")

View file

@ -41,6 +41,10 @@ class Styles(AstNode):
def container_is_widget(self): def container_is_widget(self):
validate_parent_type(self, "Gtk", "Widget", "style classes") validate_parent_type(self, "Gtk", "Widget", "style classes")
@validate("styles")
def unique_in_parent(self):
self.validate_unique_in_parent("Duplicate styles block")
def emit_xml(self, xml: XmlEmitter): def emit_xml(self, xml: XmlEmitter):
xml.start_tag("style") xml.start_tag("style")
for child in self.children: for child in self.children:

View file

@ -0,0 +1,45 @@
using Gtk 4.0;
Label {
visible: true;
visible: false;
styles [""]
styles [""]
accessibility {
label: "label";
label: "label";
}
accessibility {}
}
FileFilter {
suffixes []
patterns []
mime-types []
suffixes []
patterns []
mime-types []
}
ComboBoxText {
layout {
orientation: vertical;
orientation: vertical;
}
layout {}
items []
items []
}
SizeGroup {
widgets []
widgets []
}
StringList {
strings []
strings []
}

View file

@ -0,0 +1,12 @@
5,3,7,Duplicate property 'visible'
8,3,6,Duplicate styles block
12,5,5,Duplicate accessibility attribute 'label'
14,3,13,Duplicate accessibility block
21,3,8,Duplicate suffixes block
22,3,8,Duplicate patterns block
23,3,10,Duplicate mime-types block
29,5,11,Duplicate layout property 'orientation'
31,3,6,Duplicate layout block
34,3,5,Duplicate items block
39,3,7,Duplicate widgets block
44,3,7,Duplicate strings block

View file

@ -179,6 +179,7 @@ class TestSamples(unittest.TestCase):
self.assert_sample_error("consecutive_unexpected_tokens") self.assert_sample_error("consecutive_unexpected_tokens")
self.assert_sample_error("does_not_implement") self.assert_sample_error("does_not_implement")
self.assert_sample_error("duplicate_obj_id") self.assert_sample_error("duplicate_obj_id")
self.assert_sample_error("duplicates")
self.assert_sample_error("enum_member_dne") self.assert_sample_error("enum_member_dne")
self.assert_sample_error("filters_in_non_file_filter") self.assert_sample_error("filters_in_non_file_filter")
self.assert_sample_error("gtk_3") self.assert_sample_error("gtk_3")