Release 0.41.92
[vala-gnome.git] / vala / valaproperty.vala
blobcd1bcaff54692fd041759f090779cae2a2abbe86
1 /* valaproperty.vala
3 * Copyright (C) 2006-2012 Jürg Billeter
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 * Author:
20 * Jürg Billeter <j@bitron.ch>
21 * Raffaele Sandrini <raffaele@sandrini.ch>
24 using GLib;
26 /**
27 * Represents a property declaration in the source code.
29 public class Vala.Property : Symbol, Lockable {
30 /**
31 * The property type.
33 public DataType? property_type {
34 get { return _data_type; }
35 set {
36 _data_type = value;
37 if (value != null) {
38 _data_type.parent_node = this;
43 /**
44 * The get accessor of this property if available.
46 public PropertyAccessor? get_accessor {
47 get { return _get_accessor; }
48 set {
49 _get_accessor = value;
50 if (value != null) {
51 value.owner = scope;
56 /**
57 * The set/construct accessor of this property if available.
59 public PropertyAccessor? set_accessor {
60 get { return _set_accessor; }
61 set {
62 _set_accessor = value;
63 if (value != null) {
64 value.owner = scope;
69 /**
70 * Represents the generated `this` parameter in this property.
72 public Parameter this_parameter { get; set; }
74 /**
75 * Specifies whether automatic accessor code generation should be
76 * disabled.
78 public bool interface_only { get; set; }
80 /**
81 * Specifies whether this property is abstract. Abstract properties have
82 * no accessor bodies, may only be specified within abstract classes and
83 * interfaces, and must be overriden by derived non-abstract classes.
85 public bool is_abstract { get; set; }
87 /**
88 * Specifies whether this property is virtual. Virtual properties may be
89 * overridden by derived classes.
91 public bool is_virtual { get; set; }
93 /**
94 * Specifies whether this property overrides a virtual or abstract
95 * property of a base type.
97 public bool overrides { get; set; }
99 /**
100 * Reference the Field that holds this property
102 public Field? field {
103 get {
104 if (!_field_checked) {
105 if (!is_abstract && source_type == SourceFileType.SOURCE) {
106 bool has_get = (get_accessor != null);
107 bool get_has_body = (has_get && get_accessor.body != null);
108 bool has_set = (set_accessor != null);
109 bool set_has_body = (has_set && set_accessor.body != null);
110 if (set_has_body && (has_get && !get_has_body)) {
111 error = true;
112 Report.error (source_reference, "Property getter must have a body");
114 if (get_has_body && (has_set && !set_has_body)) {
115 error = true;
116 Report.error (source_reference, "Property setter must have a body");
118 if (!get_has_body && !set_has_body) {
119 /* automatic property accessor body generation */
120 _field = new Field ("_%s".printf (name), property_type.copy (), initializer, source_reference);
121 _field.access = SymbolAccessibility.PRIVATE;
122 _field.binding = binding;
123 // apply gtk-child attribute to backing field for gtk-template support
124 if (get_attribute ("GtkChild") != null) {
125 _field.set_attribute_string ("GtkChild", "name", get_attribute_string ("GtkChild", "name", name));
126 _field.set_attribute_bool ("GtkChild", "internal", get_attribute_bool ("GtkChild", "internal"));
130 _field_checked = true;
132 return _field;
137 * Specifies whether this field may only be accessed with an instance of
138 * the contained type.
140 public MemberBinding binding { get; set; default = MemberBinding.INSTANCE; }
143 * The nick of this property
145 public string nick {
146 get {
147 if (_nick == null) {
148 _nick = get_attribute_string ("Description", "nick");
149 if (_nick == null) {
150 _nick = name.replace ("_", "-");
153 return _nick;
158 * The blurb of this property
160 public string blurb {
161 get {
162 if (_blurb == null) {
163 _blurb = get_attribute_string ("Description", "blurb");
164 if (_blurb == null) {
165 _blurb = name.replace ("_", "-");
168 return _blurb;
173 * Specifies whether this a property triggers a notify.
175 public bool notify {
176 get {
177 if (_notify == null) {
178 _notify = get_attribute_bool ("CCode", "notify", true);
180 return _notify;
185 * Specifies the virtual or abstract property this property overrides.
186 * Reference must be weak as virtual properties set base_property to
187 * themselves.
189 public Property base_property {
190 get {
191 find_base_properties ();
192 return _base_property;
197 * Specifies the abstract interface property this property implements.
199 public Property base_interface_property {
200 get {
201 find_base_properties ();
202 return _base_interface_property;
207 * Specifies the default value of this property.
209 public Expression initializer {
210 get {
211 return _initializer;
213 set {
214 _initializer = value;
215 _initializer.parent_node = this;
219 public bool lock_used { get; set; }
221 private Expression _initializer;
223 private DataType _data_type;
225 private weak Property _base_property;
226 private Property _base_interface_property;
227 private bool base_properties_valid;
228 PropertyAccessor? _get_accessor;
229 PropertyAccessor? _set_accessor;
230 private string? _nick;
231 private string? _blurb;
232 private bool? _notify;
233 private Field? _field;
234 private bool _field_checked;
237 * Creates a new property.
239 * @param name property name
240 * @param property_type property type
241 * @param get_accessor get accessor
242 * @param set_accessor set/construct accessor
243 * @param source_reference reference to source code
244 * @return newly created property
246 public Property (string name, DataType? property_type, PropertyAccessor? get_accessor, PropertyAccessor? set_accessor, SourceReference? source_reference = null, Comment? comment = null) {
247 base (name, source_reference, comment);
248 this.property_type = property_type;
249 this.get_accessor = get_accessor;
250 this.set_accessor = set_accessor;
253 public override void accept (CodeVisitor visitor) {
254 visitor.visit_property (this);
257 public override void accept_children (CodeVisitor visitor) {
258 property_type.accept (visitor);
260 if (get_accessor != null) {
261 get_accessor.accept (visitor);
263 if (set_accessor != null) {
264 set_accessor.accept (visitor);
267 if (initializer != null) {
268 initializer.accept (visitor);
273 * Checks whether the accessors of this property are compatible
274 * with the specified base property.
276 * @param base_property a property
277 * @param invalid_match error string about which check failed
278 * @return true if the specified property is compatible to this property
280 public bool compatible (Property base_property, out string? invalid_match) {
281 if ((get_accessor == null && base_property.get_accessor != null) ||
282 (get_accessor != null && base_property.get_accessor == null)) {
283 invalid_match = "incompatible get accessor";
284 return false;
287 if ((set_accessor == null && base_property.set_accessor != null) ||
288 (set_accessor != null && base_property.set_accessor == null)) {
289 invalid_match = "incompatible set accessor";
290 return false;
293 var object_type = SemanticAnalyzer.get_data_type_for_symbol (parent_symbol);
295 if (get_accessor != null) {
296 // check accessor value_type instead of property_type
297 // due to possible ownership differences
298 var actual_base_type = base_property.get_accessor.value_type.get_actual_type (object_type, null, this);
299 if (!actual_base_type.equals (get_accessor.value_type)) {
300 invalid_match = "incompatible get accessor type";
301 return false;
305 if (set_accessor != null) {
306 // check accessor value_type instead of property_type
307 // due to possible ownership differences
308 var actual_base_type = base_property.set_accessor.value_type.get_actual_type (object_type, null, this);
309 if (!actual_base_type.equals (set_accessor.value_type)) {
310 invalid_match = "incompatible set accessor type";
311 return false;
314 if (set_accessor.writable != base_property.set_accessor.writable) {
315 invalid_match = "incompatible set accessor";
316 return false;
318 if (set_accessor.construction != base_property.set_accessor.construction) {
319 invalid_match = "incompatible set accessor";
320 return false;
324 invalid_match = null;
325 return true;
328 public override void replace_type (DataType old_type, DataType new_type) {
329 if (property_type == old_type) {
330 property_type = new_type;
334 public override void replace_expression (Expression old_node, Expression new_node) {
335 if (initializer == old_node) {
336 initializer = new_node;
340 private void find_base_properties () {
341 if (base_properties_valid) {
342 return;
345 if (parent_symbol is Class) {
346 find_base_interface_property ((Class) parent_symbol);
347 if (is_virtual || overrides) {
348 find_base_class_property ((Class) parent_symbol);
350 } else if (parent_symbol is Interface) {
351 if (is_virtual || is_abstract) {
352 _base_interface_property = this;
356 base_properties_valid = true;
359 private void find_base_class_property (Class cl) {
360 var sym = cl.scope.lookup (name);
361 if (sym is Property) {
362 var base_property = (Property) sym;
363 if (base_property.is_abstract || base_property.is_virtual) {
364 string invalid_match;
365 if (!compatible (base_property, out invalid_match)) {
366 error = true;
367 Report.error (source_reference, "Type and/or accessors of overriding property `%s' do not match overridden property `%s': %s.".printf (get_full_name (), base_property.get_full_name (), invalid_match));
368 return;
371 _base_property = base_property;
372 return;
376 if (cl.base_class != null) {
377 find_base_class_property (cl.base_class);
381 private void find_base_interface_property (Class cl) {
382 // FIXME report error if multiple possible base properties are found
383 foreach (DataType type in cl.get_base_types ()) {
384 if (type.data_type is Interface) {
385 var sym = type.data_type.scope.lookup (name);
386 if (sym is Property) {
387 var base_property = (Property) sym;
388 if (base_property.is_abstract || base_property.is_virtual) {
389 string invalid_match;
390 if (!compatible (base_property, out invalid_match)) {
391 error = true;
392 Report.error (source_reference, "Type and/or accessors of overriding property `%s' do not match overridden property `%s': %s.".printf (get_full_name (), base_property.get_full_name (), invalid_match));
393 return;
396 _base_interface_property = base_property;
397 return;
404 public override bool check (CodeContext context) {
405 if (checked) {
406 return !error;
409 checked = true;
411 if (parent_symbol is Class && (is_abstract || is_virtual)) {
412 var cl = (Class) parent_symbol;
413 if (cl.is_compact && cl.base_class != null) {
414 error = true;
415 Report.error (source_reference, "Abstract and virtual properties may not be declared in derived compact classes");
416 return false;
420 if (is_abstract) {
421 if (parent_symbol is Class) {
422 var cl = (Class) parent_symbol;
423 if (!cl.is_abstract) {
424 error = true;
425 Report.error (source_reference, "Abstract properties may not be declared in non-abstract classes");
426 return false;
428 } else if (!(parent_symbol is Interface)) {
429 error = true;
430 Report.error (source_reference, "Abstract properties may not be declared outside of classes and interfaces");
431 return false;
433 } else if (is_virtual) {
434 if (!(parent_symbol is Class) && !(parent_symbol is Interface)) {
435 error = true;
436 Report.error (source_reference, "Virtual properties may not be declared outside of classes and interfaces");
437 return false;
439 } else if (overrides) {
440 if (!(parent_symbol is Class)) {
441 error = true;
442 Report.error (source_reference, "Properties may not be overridden outside of classes");
443 return false;
445 } else if (access == SymbolAccessibility.PROTECTED) {
446 if (!(parent_symbol is Class) && !(parent_symbol is Interface)) {
447 error = true;
448 Report.error (source_reference, "Protected properties may not be declared outside of classes and interfaces");
449 return false;
453 var old_source_file = context.analyzer.current_source_file;
454 var old_symbol = context.analyzer.current_symbol;
456 if (source_reference != null) {
457 context.analyzer.current_source_file = source_reference.file;
459 context.analyzer.current_symbol = this;
461 if (property_type is VoidType) {
462 error = true;
463 Report.error (source_reference, "'void' not supported as property type");
464 return false;
467 property_type.check (context);
469 if (get_accessor == null && set_accessor == null) {
470 error = true;
471 Report.error (source_reference, "Property `%s' must have a `get' accessor and/or a `set' mutator".printf (get_full_name ()));
472 return false;
475 if (get_accessor != null) {
476 get_accessor.check (context);
478 if (set_accessor != null) {
479 set_accessor.check (context);
482 if (initializer != null && field == null && !is_abstract) {
483 Report.error (source_reference, "Property `%s' with custom `get' accessor and/or `set' mutator cannot have `default' value".printf (get_full_name ()));
486 if (initializer != null) {
487 initializer.check (context);
490 // check whether property type is at least as accessible as the property
491 if (!context.analyzer.is_type_accessible (this, property_type)) {
492 error = true;
493 Report.error (source_reference, "property type `%s' is less accessible than property `%s'".printf (property_type.to_string (), get_full_name ()));
496 if (overrides && base_property == null) {
497 Report.error (source_reference, "%s: no suitable property found to override".printf (get_full_name ()));
500 if (!external_package && !overrides && !hides && get_hidden_member () != null) {
501 Report.warning (source_reference, "%s hides inherited property `%s'. Use the `new' keyword if hiding was intentional".printf (get_full_name (), get_hidden_member ().get_full_name ()));
504 /* construct properties must be public */
505 if (set_accessor != null && set_accessor.construction) {
506 if (access != SymbolAccessibility.PUBLIC) {
507 error = true;
508 Report.error (source_reference, "%s: construct properties must be public".printf (get_full_name ()));
512 if (initializer != null && !initializer.error && initializer.value_type != null && !(initializer.value_type.compatible (property_type))) {
513 error = true;
514 Report.error (initializer.source_reference, "Expected initializer of type `%s' but got `%s'".printf (property_type.to_string (), initializer.value_type.to_string ()));
517 context.analyzer.current_source_file = old_source_file;
518 context.analyzer.current_symbol = old_symbol;
520 return !error;