mirror of
https://gitlab.gnome.org/jwestman/blueprint-compiler.git
synced 2025-05-04 15:59:08 -04:00
expressions: Validate lookup expressions
This commit is contained in:
parent
3f7688a563
commit
c7c32cbb4a
8 changed files with 50 additions and 9 deletions
|
@ -50,12 +50,26 @@ class IdentExpr(AstNode):
|
||||||
def is_this(self):
|
def is_this(self):
|
||||||
return self.parent_by_type(Scope).this_name == self.tokens["ident"]
|
return self.parent_by_type(Scope).this_name == self.tokens["ident"]
|
||||||
|
|
||||||
|
@validate()
|
||||||
|
def exists(self):
|
||||||
|
if self.is_this:
|
||||||
|
return
|
||||||
|
|
||||||
|
scope = self.parent_by_type(Scope)
|
||||||
|
if self.tokens["ident"] not in scope.get_objects():
|
||||||
|
raise CompileError(
|
||||||
|
f"Could not find object with ID '{self.tokens['ident']}'",
|
||||||
|
did_you_mean=(self.tokens['ident'], scope.get_objects().keys()),
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def gir_type(self):
|
def gir_type(self):
|
||||||
|
scope = self.parent_by_type(Scope)
|
||||||
|
|
||||||
if self.is_this:
|
if self.is_this:
|
||||||
return self.parent_by_type(Scope).this_type
|
return scope.this_type
|
||||||
else:
|
elif self.tokens["ident"] in scope.get_objects():
|
||||||
return None
|
return scope.get_objects()[self.tokens["ident"]].gir_class
|
||||||
|
|
||||||
def emit_xml(self, xml: XmlEmitter):
|
def emit_xml(self, xml: XmlEmitter):
|
||||||
if self.is_this:
|
if self.is_this:
|
||||||
|
@ -95,7 +109,21 @@ class LookupOp(InfixExpr):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def gir_type(self):
|
def gir_type(self):
|
||||||
return None
|
if parent_type := self.lhs.gir_type:
|
||||||
|
if prop := parent_type.properties.get(self.tokens["property"]):
|
||||||
|
return prop.type
|
||||||
|
|
||||||
|
@validate("property")
|
||||||
|
def property_exists(self):
|
||||||
|
if parent_type := self.lhs.gir_type:
|
||||||
|
if not (isinstance(parent_type, gir.Class) or isinstance(parent_type, gir.Interface)):
|
||||||
|
raise CompileError(f"Type {parent_type.full_name} does not have properties")
|
||||||
|
elif self.tokens["property"] not in parent_type.properties:
|
||||||
|
raise CompileError(
|
||||||
|
f"{parent_type.full_name} does not have a property called {self.tokens['property']}",
|
||||||
|
hints=["Do you need to cast the previous expression?"],
|
||||||
|
did_you_mean=(self.tokens['property'], parent_type.properties.keys()),
|
||||||
|
)
|
||||||
|
|
||||||
def emit_xml(self, xml: XmlEmitter):
|
def emit_xml(self, xml: XmlEmitter):
|
||||||
if isinstance(self.lhs, IdentExpr) and self.lhs.is_this:
|
if isinstance(self.lhs, IdentExpr) and self.lhs.is_this:
|
||||||
|
|
9
tests/sample_errors/expr_lookup_prop.blp
Normal file
9
tests/sample_errors/expr_lookup_prop.blp
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
using Gtk 4.0;
|
||||||
|
|
||||||
|
Box {
|
||||||
|
Label label {
|
||||||
|
visible: bind label.parent.something;
|
||||||
|
margin-start: bind label.margin-start.other;
|
||||||
|
label: bind nothing.parent.label;
|
||||||
|
}
|
||||||
|
}
|
3
tests/sample_errors/expr_lookup_prop.err
Normal file
3
tests/sample_errors/expr_lookup_prop.err
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
5,32,9,Gtk.Widget does not have a property called something
|
||||||
|
6,43,5,Type int does not have properties
|
||||||
|
7,17,7,Could not find object with ID 'nothing'
|
|
@ -10,7 +10,7 @@
|
||||||
<binding name="visible-child">
|
<binding name="visible-child">
|
||||||
<lookup name="parent" type="GtkButton">
|
<lookup name="parent" type="GtkButton">
|
||||||
<closure function="my_closure" type="GtkButton">
|
<closure function="my_closure" type="GtkButton">
|
||||||
<lookup name="parent">
|
<lookup name="parent" type="GtkBox">
|
||||||
<constant>box</constant>
|
<constant>box</constant>
|
||||||
</lookup>
|
</lookup>
|
||||||
</closure>
|
</closure>
|
||||||
|
|
|
@ -5,5 +5,5 @@ Overlay {
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
label: bind ((Label) label.parent.child).label;
|
label: bind ((Label) ((Overlay) label.parent).child).label;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,8 +9,8 @@
|
||||||
<object class="GtkLabel">
|
<object class="GtkLabel">
|
||||||
<binding name="label">
|
<binding name="label">
|
||||||
<lookup name="label" type="GtkLabel">
|
<lookup name="label" type="GtkLabel">
|
||||||
<lookup name="child">
|
<lookup name="child" type="GtkOverlay">
|
||||||
<lookup name="parent">
|
<lookup name="parent" type="GtkLabel">
|
||||||
<constant>label</constant>
|
<constant>label</constant>
|
||||||
</lookup>
|
</lookup>
|
||||||
</lookup>
|
</lookup>
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
</object>
|
</object>
|
||||||
<object class="GtkBoolFilter">
|
<object class="GtkBoolFilter">
|
||||||
<property name="expression">
|
<property name="expression">
|
||||||
<lookup name="invert">
|
<lookup name="invert" type="GtkBoolFilter">
|
||||||
<constant>filter</constant>
|
<constant>filter</constant>
|
||||||
</lookup>
|
</lookup>
|
||||||
</property>
|
</property>
|
||||||
|
|
|
@ -185,6 +185,7 @@ class TestSamples(unittest.TestCase):
|
||||||
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("enum_member_dne")
|
self.assert_sample_error("enum_member_dne")
|
||||||
|
self.assert_sample_error("expr_lookup_prop")
|
||||||
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")
|
||||||
self.assert_sample_error("gtk_exact_version")
|
self.assert_sample_error("gtk_exact_version")
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue