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
20 * Jürg Billeter <j@bitron.ch>
21 * Raffaele Sandrini <raffaele@sandrini.ch>
27 * Represents a property declaration in the source code.
29 public class Vala
.Property
: Symbol
, Lockable
{
33 public DataType? property_type
{
34 get { return _data_type
; }
38 _data_type
.parent_node
= this
;
44 * The get accessor of this property if available.
46 public PropertyAccessor? get_accessor
{
47 get { return _get_accessor
; }
49 _get_accessor
= value
;
57 * The set/construct accessor of this property if available.
59 public PropertyAccessor? set_accessor
{
60 get { return _set_accessor
; }
62 _set_accessor
= value
;
70 * Represents the generated `this` parameter in this property.
72 public Parameter this_parameter
{ get; set; }
75 * Specifies whether automatic accessor code generation should be
78 public bool interface_only
{ get; set; }
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; }
88 * Specifies whether this property is virtual. Virtual properties may be
89 * overridden by derived classes.
91 public bool is_virtual
{ get; set; }
94 * Specifies whether this property overrides a virtual or abstract
95 * property of a base type.
97 public bool overrides
{ get; set; }
100 * Reference the Field that holds this property
102 public Field? field
{
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
)) {
112 Report
.error (source_reference
, "Property getter must have a body");
114 if (get_has_body
&& (has_set
&& !set_has_body
)) {
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;
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
148 _nick
= get_attribute_string ("Description", "nick");
150 _nick
= name
.replace ("_", "-");
158 * The blurb of this property
160 public string blurb
{
162 if (_blurb
== null) {
163 _blurb
= get_attribute_string ("Description", "blurb");
164 if (_blurb
== null) {
165 _blurb
= name
.replace ("_", "-");
173 * Specifies whether this a property triggers a notify.
177 if (_notify
== null) {
178 _notify
= get_attribute_bool ("CCode", "notify", true);
185 * Specifies the virtual or abstract property this property overrides.
186 * Reference must be weak as virtual properties set base_property to
189 public Property base_property
{
191 find_base_properties ();
192 return _base_property
;
197 * Specifies the abstract interface property this property implements.
199 public Property base_interface_property
{
201 find_base_properties ();
202 return _base_interface_property
;
207 * Specifies the default value of this property.
209 public Expression initializer
{
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";
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";
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";
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";
314 if (set_accessor
.writable
!= base_property
.set_accessor
.writable
) {
315 invalid_match
= "incompatible set accessor";
318 if (set_accessor
.construction
!= base_property
.set_accessor
.construction
) {
319 invalid_match
= "incompatible set accessor";
324 invalid_match
= null;
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
) {
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
)) {
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
));
371 _base_property
= base_property
;
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
)) {
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
));
396 _base_interface_property
= base_property
;
404 public override bool check (CodeContext context
) {
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) {
415 Report
.error (source_reference
, "Abstract and virtual properties may not be declared in derived compact classes");
421 if (parent_symbol is Class
) {
422 var cl
= (Class
) parent_symbol
;
423 if (!cl
.is_abstract
) {
425 Report
.error (source_reference
, "Abstract properties may not be declared in non-abstract classes");
428 } else if (!(parent_symbol is Interface
)) {
430 Report
.error (source_reference
, "Abstract properties may not be declared outside of classes and interfaces");
433 } else if (is_virtual
) {
434 if (!(parent_symbol is Class
) && !(parent_symbol is Interface
)) {
436 Report
.error (source_reference
, "Virtual properties may not be declared outside of classes and interfaces");
439 } else if (overrides
) {
440 if (!(parent_symbol is Class
)) {
442 Report
.error (source_reference
, "Properties may not be overridden outside of classes");
445 } else if (access
== SymbolAccessibility
.PROTECTED
) {
446 if (!(parent_symbol is Class
) && !(parent_symbol is Interface
)) {
448 Report
.error (source_reference
, "Protected properties may not be declared outside of classes and interfaces");
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
) {
463 Report
.error (source_reference
, "'void' not supported as property type");
467 property_type
.check (context
);
469 if (get_accessor
== null && set_accessor
== null) {
471 Report
.error (source_reference
, "Property `%s' must have a `get' accessor and/or a `set' mutator".printf (get_full_name ()));
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
) {
484 Report
.error (source_reference
, "Property `%s' with custom `get' accessor and/or `set' mutator cannot have `default' value".printf (get_full_name ()));
488 if (initializer
!= null) {
489 initializer
.check (context
);
492 // check whether property type is at least as accessible as the property
493 if (!context
.analyzer
.is_type_accessible (this
, property_type
)) {
495 Report
.error (source_reference
, "property type `%s` is less accessible than property `%s`".printf (property_type
.to_string (), get_full_name ()));
498 if (overrides
&& base_property
== null) {
499 Report
.error (source_reference
, "%s: no suitable property found to override".printf (get_full_name ()));
502 if (!external_package
&& !overrides
&& !hides
&& get_hidden_member () != null) {
503 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 ()));
506 /* construct properties must be public */
507 if (set_accessor
!= null && set_accessor
.construction
) {
508 if (access
!= SymbolAccessibility
.PUBLIC
) {
510 Report
.error (source_reference
, "%s: construct properties must be public".printf (get_full_name ()));
514 if (initializer
!= null && !initializer
.error
&& initializer
.value_type
!= null && !(initializer
.value_type
.compatible (property_type
))) {
516 Report
.error (initializer
.source_reference
, "Expected initializer of type `%s' but got `%s'".printf (property_type
.to_string (), initializer
.value_type
.to_string ()));
519 context
.analyzer
.current_source_file
= old_source_file
;
520 context
.analyzer
.current_symbol
= old_symbol
;