diff --git a/blueprintcompiler/language/expression.py b/blueprintcompiler/language/expression.py index 558392c..ae9c399 100644 --- a/blueprintcompiler/language/expression.py +++ b/blueprintcompiler/language/expression.py @@ -171,6 +171,24 @@ class LookupOp(InfixExpr): did_you_mean=(self.property_name, self.lhs.type.properties.keys()), ) + @validate("property") + def property_deprecated(self): + if self.lhs.type is None or not ( + isinstance(self.lhs.type, gir.Class) + or isinstance(self.lhs.type, gir.Interface) + ): + return + + if property := self.lhs.type.properties.get(self.property_name): + if property.deprecated: + hints = [] + if property.deprecated_doc: + hints.append(property.deprecated_doc) + raise DeprecatedWarning( + f"{property.signature} is deprecated", + hints=hints, + ) + class CastExpr(InfixExpr): grammar = [ diff --git a/tests/sample_errors/deprecations.blp b/tests/sample_errors/deprecations.blp deleted file mode 100644 index f67f002..0000000 --- a/tests/sample_errors/deprecations.blp +++ /dev/null @@ -1,9 +0,0 @@ -using Gtk 4.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 deleted file mode 100644 index e3abd61..0000000 --- a/tests/sample_errors/deprecations.err +++ /dev/null @@ -1 +0,0 @@ -3,1,6,Gtk.Dialog is deprecated \ No newline at end of file diff --git a/tests/test_deprecations.py b/tests/test_deprecations.py new file mode 100644 index 0000000..0ffe726 --- /dev/null +++ b/tests/test_deprecations.py @@ -0,0 +1,110 @@ +# test_samples.py +# +# Copyright 2024 James Westman +# +# This file is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This file is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this program. If not, see . +# +# SPDX-License-Identifier: LGPL-3.0-or-later + + +import unittest + +import gi + +gi.require_version("Gtk", "4.0") +from gi.repository import Gtk + +from blueprintcompiler import parser, tokenizer +from blueprintcompiler.errors import DeprecatedWarning, PrintableError + +# Testing deprecation warnings requires special handling because libraries can add deprecations with new versions, +# causing tests to break if we're not careful. + + +class TestDeprecations(unittest.TestCase): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + self.gtkVersion = f"{Gtk.get_major_version()}.{Gtk.get_minor_version()}.{Gtk.get_micro_version()}" + + def assertDeprecation(self, blueprint: str, message: str): + try: + tokens = tokenizer.tokenize(blueprint) + _ast, errors, warnings = parser.parse(tokens) + + self.assertIsNone(errors) + self.assertEqual(len(warnings), 1) + self.assertIsInstance(warnings[0], DeprecatedWarning) + self.assertEqual(warnings[0].message, message) + except PrintableError as e: # pragma: no cover + e.pretty_print("", blueprint) + raise AssertionError() + + def test_class_deprecation(self): + if Gtk.check_version(4, 10, 0) is not None: + self.skipTest(f"Gtk.Dialog is not deprecated in GTK {self.gtkVersion}") + + blueprint = """ + using Gtk 4.0; + + Dialog { + use-header-bar: 1; + } + """ + message = "Gtk.Dialog is deprecated" + + self.assertDeprecation(blueprint, message) + + def test_property_deprecation(self): + self.skipTest( + "gobject-introspection does not currently write property deprecations to the typelib. See ." + ) + + if Gtk.check_version(4, 4, 0) is not None: + self.skipTest( + f"Gtk.DropTarget:drop is not deprecated in GTK {self.gtkVersion}" + ) + + blueprint = """ + using Gtk 4.0; + + $MyObject { + a: bind drop_target.drop; + } + + DropTarget drop_target { + } + """ + + message = "Gtk.DropTarget:drop is deprecated" + + self.assertDeprecation(blueprint, message) + + def test_signal_deprecation(self): + if Gtk.check_version(4, 10, 0) is not None: + self.skipTest( + f"Gtk.Window::keys-changed is not deprecated in GTK {self.gtkVersion}" + ) + + blueprint = """ + using Gtk 4.0; + + Window { + keys-changed => $handler(); + } + """ + + message = "signal Gtk.Window::keys-changed () is deprecated" + + self.assertDeprecation(blueprint, message)