diff --git a/blueprintcompiler/gir.py b/blueprintcompiler/gir.py index 6dab652..3642bd6 100644 --- a/blueprintcompiler/gir.py +++ b/blueprintcompiler/gir.py @@ -136,6 +136,14 @@ class GirType: def incomplete(self) -> bool: return False + @property + def deprecated(self) -> bool: + return False + + @property + def deprecated_doc(self) -> T.Optional[str]: + return None + class ExternType(GirType): def __init__(self, name: str) -> None: @@ -330,6 +338,13 @@ class GirNode: def type(self) -> GirType: raise NotImplementedError() + @property + def deprecated_doc(self) -> T.Optional[str]: + try: + return self.xml.get_elements("doc-deprecated")[0].cdata.strip() + except: + return None + class Property(GirNode): xml_tag = "property" @@ -365,6 +380,10 @@ class Property(GirNode): else: return None + @property + def deprecated(self) -> bool: + return self.tl.PROP_DEPRECATED == 1 + class Argument(GirNode): def __init__(self, container: GirNode, tl: typelib.Typelib) -> None: @@ -427,6 +446,10 @@ class Signal(GirNode): else: return None + @property + def deprecated(self) -> bool: + return self.tl.SIGNAL_DEPRECATED == 1 + class Interface(GirNode, GirType): xml_tag = "interface" @@ -488,6 +511,10 @@ class Interface(GirNode, GirType): else: return None + @property + def deprecated(self) -> bool: + return self.tl.INTERFACE_DEPRECATED == 1 + class Class(GirNode, GirType): xml_tag = "class" @@ -609,6 +636,10 @@ class Class(GirNode, GirType): else: return None + @property + def deprecated(self) -> bool: + return self.tl.OBJ_DEPRECATED == 1 + class TemplateType(GirType): def __init__(self, name: str, parent: T.Optional[GirType]): @@ -722,6 +753,10 @@ class Enumeration(GirNode, GirType): else: return None + @property + def deprecated(self) -> bool: + return self.tl.ENUM_DEPRECATED == 1 + class Boxed(GirNode, GirType): xml_tag = "glib:boxed" @@ -743,6 +778,10 @@ class Boxed(GirNode, GirType): else: return None + @property + def deprecated(self) -> bool: + return self.tl.STRUCT_DEPRECATED == 1 + class Bitfield(Enumeration): xml_tag = "bitfield" diff --git a/blueprintcompiler/language/gobject_property.py b/blueprintcompiler/language/gobject_property.py index b24cd07..526b308 100644 --- a/blueprintcompiler/language/gobject_property.py +++ b/blueprintcompiler/language/gobject_property.py @@ -41,9 +41,11 @@ class Property(AstNode): return self.parent.parent.gir_class @property - def gir_property(self): + def gir_property(self) -> T.Optional[gir.Property]: if self.gir_class is not None and not isinstance(self.gir_class, ExternType): return self.gir_class.properties.get(self.tokens["name"]) + else: + return None @validate() def binding_valid(self): @@ -91,6 +93,17 @@ class Property(AstNode): check=lambda child: child.tokens["name"] == self.tokens["name"], ) + @validate("name") + def deprecated(self) -> None: + if self.gir_property is not None and self.gir_property.deprecated: + hints = [] + if self.gir_property.deprecated_doc: + hints.append(self.gir_property.deprecated_doc) + raise CompileWarning( + f"{self.gir_property.signature} is deprecated", + hints=hints, + ) + @docs("name") def property_docs(self): if self.gir_property is not None: diff --git a/blueprintcompiler/language/gobject_signal.py b/blueprintcompiler/language/gobject_signal.py index 2fc4699..1c60bbf 100644 --- a/blueprintcompiler/language/gobject_signal.py +++ b/blueprintcompiler/language/gobject_signal.py @@ -95,9 +95,11 @@ class Signal(AstNode): return any(x.flag == "after" for x in self.flags) @property - def gir_signal(self): + def gir_signal(self) -> T.Optional[gir.Signal]: if self.gir_class is not None and not isinstance(self.gir_class, ExternType): return self.gir_class.signals.get(self.tokens["name"]) + else: + return None @property def gir_class(self): @@ -134,6 +136,17 @@ class Signal(AstNode): if self.context[ScopeCtx].objects.get(object_id) is None: raise CompileError(f"Could not find object with ID '{object_id}'") + @validate("name") + def deprecated(self) -> None: + if self.gir_signal is not None and self.gir_signal.deprecated: + hints = [] + if self.gir_signal.deprecated_doc: + hints.append(self.gir_signal.deprecated_doc) + raise CompileWarning( + f"{self.gir_signal.signature} is deprecated", + hints=hints, + ) + @docs("name") def signal_docs(self): if self.gir_signal is not None: diff --git a/blueprintcompiler/language/types.py b/blueprintcompiler/language/types.py index e75ebdc..1d9911b 100644 --- a/blueprintcompiler/language/types.py +++ b/blueprintcompiler/language/types.py @@ -57,6 +57,17 @@ class TypeName(AstNode): if not self.tokens["extern"]: self.root.gir.validate_ns(self.tokens["namespace"]) + @validate() + def deprecated(self) -> None: + if self.gir_type and self.gir_type.deprecated: + hints = [] + if self.gir_type.deprecated_doc: + hints.append(self.gir_type.deprecated_doc) + raise CompileWarning( + f"{self.gir_type.full_name} is deprecated", + hints=hints, + ) + @property def gir_ns(self): if not self.tokens["extern"]: diff --git a/blueprintcompiler/typelib.py b/blueprintcompiler/typelib.py index 2ce3e32..c8a3ff3 100644 --- a/blueprintcompiler/typelib.py +++ b/blueprintcompiler/typelib.py @@ -150,11 +150,15 @@ class Typelib: BLOB_NAME = Field(0x4, "string") + STRUCT_DEPRECATED = Field(0x2, "u16", 0, 1) + + ENUM_DEPRECATED = Field(0x2, "u16", 0, 1) ENUM_GTYPE_NAME = Field(0x8, "string") ENUM_N_VALUES = Field(0x10, "u16") ENUM_N_METHODS = Field(0x12, "u16") ENUM_VALUES = Field(0x18, "offset") + INTERFACE_DEPRECATED = Field(0x2, "u16", 0, 1) INTERFACE_GTYPE_NAME = Field(0x8, "string") INTERFACE_N_PREREQUISITES = Field(0x12, "u16") INTERFACE_N_PROPERTIES = Field(0x14, "u16") diff --git a/tests/sample_errors/deprecations.blp b/tests/sample_errors/deprecations.blp new file mode 100644 index 0000000..1554a0b --- /dev/null +++ b/tests/sample_errors/deprecations.blp @@ -0,0 +1,10 @@ +using Gtk 4.0; +using Gio 2.0; + +Dialog { + use-header-bar: 1; +} + +Window { + keys-changed => $on_window_keys_changed(); +} diff --git a/tests/sample_errors/deprecations.err b/tests/sample_errors/deprecations.err new file mode 100644 index 0000000..6059412 --- /dev/null +++ b/tests/sample_errors/deprecations.err @@ -0,0 +1 @@ +4,1,6,Gtk.Dialog is deprecated \ No newline at end of file diff --git a/tests/sample_errors/legacy_template.err b/tests/sample_errors/legacy_template.err index 876b0cf..79ad519 100644 --- a/tests/sample_errors/legacy_template.err +++ b/tests/sample_errors/legacy_template.err @@ -1,2 +1,3 @@ 3,10,12,Use type syntax here (introduced in blueprint 0.8.0) +8,1,6,Gtk.Dialog is deprecated 9,18,12,Use 'template' instead of the class name (introduced in 0.8.0) \ No newline at end of file diff --git a/tests/test_samples.py b/tests/test_samples.py index 7fe7941..1273805 100644 --- a/tests/test_samples.py +++ b/tests/test_samples.py @@ -54,6 +54,14 @@ class TestSamples(unittest.TestCase): tokens = tokenizer.tokenize(blueprint) ast, errors, warnings = parser.parse(tokens) + # Ignore deprecation warnings because some of the things we're testing + # are deprecated + warnings = [ + warning + for warning in warnings + if "is deprecated" not in warning.message + ] + if errors: raise errors if len(warnings):