diff --git a/gobject/gobject.c b/gobject/gobject.c index f743e589a..18ec2368b 100644 --- a/gobject/gobject.c +++ b/gobject/gobject.c @@ -1766,7 +1766,8 @@ static inline void object_set_property (GObject *object, GParamSpec *pspec, const GValue *value, - GObjectNotifyQueue *nqueue) + GObjectNotifyQueue *nqueue, + gboolean user_specified) { GTypeInstance *inst = (GTypeInstance *) object; GObjectClass *class; @@ -1782,7 +1783,8 @@ object_set_property (GObject *object, param_spec_follow_override (&pspec); - consider_issuing_property_deprecation_warning (pspec); + if (user_specified) + consider_issuing_property_deprecation_warning (pspec); pclass = G_PARAM_SPEC_GET_CLASS (pspec); if (g_value_type_compatible (G_VALUE_TYPE (value), pspec->value_type) && @@ -2202,7 +2204,7 @@ g_object_new_with_custom_constructor (GObjectClass *class, /* set remaining properties */ for (i = 0; i < n_params; i++) if (!(params[i].pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY))) - object_set_property (object, params[i].pspec, params[i].value, nqueue); + object_set_property (object, params[i].pspec, params[i].value, nqueue, TRUE); /* If nqueue is non-NULL then we are frozen. Thaw it. */ if (nqueue) @@ -2252,6 +2254,7 @@ g_object_new_internal (GObjectClass *class, const GValue *value; GParamSpec *pspec; guint j; + gboolean user_specified = FALSE; pspec = node->data; value = NULL; /* to silence gcc... */ @@ -2260,13 +2263,14 @@ g_object_new_internal (GObjectClass *class, if (params[j].pspec == pspec) { value = params[j].value; + user_specified = TRUE; break; } if (value == NULL) value = g_param_spec_get_default_value (pspec); - object_set_property (object, pspec, value, nqueue); + object_set_property (object, pspec, value, nqueue, user_specified); } } @@ -2279,7 +2283,7 @@ g_object_new_internal (GObjectClass *class, */ for (i = 0; i < n_params; i++) if (!(params[i].pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY))) - object_set_property (object, params[i].pspec, params[i].value, nqueue); + object_set_property (object, params[i].pspec, params[i].value, nqueue, TRUE); if (nqueue) g_object_notify_queue_thaw (object, nqueue); @@ -2610,7 +2614,7 @@ g_object_constructor (GType type, GParamSpec *pspec = construct_params->pspec; construct_params++; - object_set_property (object, pspec, value, nqueue); + object_set_property (object, pspec, value, nqueue, TRUE); } g_object_notify_queue_thaw (object, nqueue); /* the notification queue is still frozen from g_object_init(), so @@ -2698,7 +2702,7 @@ g_object_setv (GObject *object, if (!g_object_set_is_valid_property (object, pspec, names[i])) break; - object_set_property (object, pspec, &values[i], nqueue); + object_set_property (object, pspec, &values[i], nqueue, TRUE); } if (nqueue) @@ -2756,7 +2760,7 @@ g_object_set_valist (GObject *object, break; } - object_set_property (object, pspec, &value, nqueue); + object_set_property (object, pspec, &value, nqueue, TRUE); /* We open-code g_value_unset() here to avoid the * cost of looking up the GTypeValueTable again. diff --git a/gobject/tests/deprecated-properties.c b/gobject/tests/deprecated-properties.c new file mode 100644 index 000000000..fd9cb0a45 --- /dev/null +++ b/gobject/tests/deprecated-properties.c @@ -0,0 +1,257 @@ +/* + * Copyright 2022 Simon McVittie + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include +#include + +typedef struct +{ + GObject parent; + int normal; + int normal_construct; + int deprecated; + int deprecated_construct; +} MyObject; + +typedef struct +{ + GObjectClass parent; +} MyObjectClass; + +typedef enum +{ + PROP_0, + PROP_NORMAL, + PROP_NORMAL_CONSTRUCT, + PROP_DEPRECATED, + PROP_DEPRECATED_CONSTRUCT, + N_PROPS +} Property; + +static GParamSpec *props[N_PROPS] = { NULL }; + +static GType my_object_get_type (void); + +G_DEFINE_TYPE (MyObject, my_object, G_TYPE_OBJECT); + +static void +my_object_init (MyObject *self) +{ +} + +static void +my_object_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *param_spec) +{ + MyObject *self = (MyObject *) object; + + switch ((Property) prop_id) + { + case PROP_NORMAL: + self->normal = g_value_get_int (value); + break; + + case PROP_NORMAL_CONSTRUCT: + self->normal_construct = g_value_get_int (value); + break; + + case PROP_DEPRECATED: + self->deprecated = g_value_get_int (value); + break; + + case PROP_DEPRECATED_CONSTRUCT: + self->deprecated_construct = g_value_get_int (value); + break; + + case PROP_0: + case N_PROPS: + default: + g_assert_not_reached (); + } +} + +static void +my_object_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *param_spec) +{ + MyObject *self = (MyObject *) object; + + switch ((Property) prop_id) + { + case PROP_NORMAL: + g_value_set_int (value, self->normal); + break; + + case PROP_NORMAL_CONSTRUCT: + g_value_set_int (value, self->normal_construct); + break; + + case PROP_DEPRECATED: + g_value_set_int (value, self->deprecated); + break; + + case PROP_DEPRECATED_CONSTRUCT: + g_value_set_int (value, self->deprecated_construct); + break; + + case PROP_0: + case N_PROPS: + default: + g_assert_not_reached (); + } +} + +static void +my_object_class_init (MyObjectClass *cls) +{ + GObjectClass *object_class = G_OBJECT_CLASS (cls); + + props[PROP_NORMAL] = g_param_spec_int ("normal", NULL, NULL, + G_MININT, G_MAXINT, -1, + (G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + props[PROP_NORMAL_CONSTRUCT] = g_param_spec_int ("normal-construct", NULL, NULL, + G_MININT, G_MAXINT, -1, + (G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS | + G_PARAM_CONSTRUCT)); + props[PROP_DEPRECATED] = g_param_spec_int ("deprecated", NULL, NULL, + G_MININT, G_MAXINT, -1, + (G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS | + G_PARAM_DEPRECATED)); + props[PROP_DEPRECATED_CONSTRUCT] = g_param_spec_int ("deprecated-construct", NULL, NULL, + G_MININT, G_MAXINT, -1, + (G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS | + G_PARAM_CONSTRUCT | + G_PARAM_DEPRECATED)); + object_class->get_property = my_object_get_property; + object_class->set_property = my_object_set_property; + g_object_class_install_properties (object_class, N_PROPS, props); +} + +static void +test_construct (void) +{ + if (g_test_subprocess ()) + { + MyObject *o; + + /* Don't crash on deprecation warnings, so we can see all of them */ + g_log_set_always_fatal (G_LOG_FATAL_MASK); + g_log_set_fatal_mask ("GLib-GObject", G_LOG_FATAL_MASK); + + o = g_object_new (my_object_get_type (), + "normal", 1, + "normal-construct", 2, + "deprecated", 3, + "deprecated-construct", 4, + NULL); + g_printerr ("Constructed object"); + g_assert_cmpint (o->normal, ==, 1); + g_assert_cmpint (o->normal_construct, ==, 2); + g_assert_cmpint (o->deprecated, ==, 3); + g_assert_cmpint (o->deprecated_construct, ==, 4); + g_clear_object (&o); + return; + } + + g_test_trap_subprocess (NULL, 0, G_TEST_SUBPROCESS_DEFAULT); + g_test_trap_assert_stderr ("*The property MyObject:deprecated-construct is deprecated*"); + g_test_trap_assert_stderr ("*The property MyObject:deprecated is deprecated*"); + g_test_trap_assert_stderr_unmatched ("*The property MyObject:normal*"); + g_test_trap_assert_passed (); +} + +static void +test_def_construct (void) +{ + if (g_test_subprocess ()) + { + MyObject *o; + + /* Don't crash on deprecation warnings, so we can see all of them */ + g_log_set_always_fatal (G_LOG_FATAL_MASK); + g_log_set_fatal_mask ("GLib-GObject", G_LOG_FATAL_MASK); + + o = g_object_new (my_object_get_type (), + NULL); + g_printerr ("Constructed object"); + g_assert_cmpint (o->normal, ==, 0); + g_assert_cmpint (o->normal_construct, ==, -1); + g_assert_cmpint (o->deprecated, ==, 0); + g_assert_cmpint (o->deprecated_construct, ==, -1); + g_clear_object (&o); + return; + } + + g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/2748"); + g_test_trap_subprocess (NULL, 0, G_TEST_SUBPROCESS_DEFAULT); + g_test_trap_assert_stderr_unmatched ("*The property MyObject:deprecated*"); + g_test_trap_assert_stderr_unmatched ("*The property MyObject:normal*"); + g_test_trap_assert_passed (); +} + +static void +test_set (void) +{ + if (g_test_subprocess ()) + { + MyObject *o; + + /* Don't crash on deprecation warnings, so we can see all of them */ + g_log_set_always_fatal (G_LOG_FATAL_MASK); + g_log_set_fatal_mask ("GLib-GObject", G_LOG_FATAL_MASK); + + o = g_object_new (my_object_get_type (), + NULL); + g_printerr ("Constructed object"); + g_assert_cmpint (o->normal, ==, 0); + g_assert_cmpint (o->normal_construct, ==, -1); + g_assert_cmpint (o->deprecated, ==, 0); + g_assert_cmpint (o->deprecated_construct, ==, -1); + + g_object_set (o, + "normal", 1, + "normal-construct", 2, + "deprecated", 3, + "deprecated-construct", 4, + NULL); + g_printerr ("Set properties"); + g_assert_cmpint (o->normal, ==, 1); + g_assert_cmpint (o->normal_construct, ==, 2); + g_assert_cmpint (o->deprecated, ==, 3); + g_assert_cmpint (o->deprecated_construct, ==, 4); + + g_clear_object (&o); + return; + } + + g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/2748"); + g_test_trap_subprocess (NULL, 0, G_TEST_SUBPROCESS_DEFAULT); + g_test_trap_assert_stderr ("*The property MyObject:deprecated-construct is deprecated*"); + g_test_trap_assert_stderr ("*The property MyObject:deprecated is deprecated*"); + g_test_trap_assert_stderr_unmatched ("*The property MyObject:normal*"); + g_test_trap_assert_passed (); +} + +int +main (int argc, char *argv[]) +{ + g_test_init (&argc, &argv, NULL); + + g_setenv ("G_ENABLE_DIAGNOSTIC", "1", TRUE); + + g_test_set_nonfatal_assertions (); + g_test_add_func ("/deprecated-properties/construct", test_construct); + g_test_add_func ("/deprecated-properties/default-construct", test_def_construct); + g_test_add_func ("/deprecated-properties/set", test_set); + return g_test_run (); +} diff --git a/gobject/tests/meson.build b/gobject/tests/meson.build index 58cf4ab1f..c3fc29f6b 100644 --- a/gobject/tests/meson.build +++ b/gobject/tests/meson.build @@ -42,6 +42,7 @@ gobject_tests = { 'source' : ['defaultiface.c', 'testmodule.c'], }, 'deftype' : {}, + 'deprecated-properties' : {}, 'dynamictype' : { 'source' : ['dynamictype.c', 'testmodule.c'], },