vala: Add consts/methods to retrieve and check library version
[vala-gnome.git] / vala / valaclass.vala
blob2167e4e4b67765f7519c7613ded76fefe9155e36
1 /* valaclass.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>
23 using GLib;
25 /**
26 * Represents a class declaration in the source code.
28 public class Vala.Class : ObjectTypeSymbol {
29 /**
30 * Specifies the base class.
32 public Class base_class { get; set; }
34 /**
35 * Specifies whether this class is abstract. Abstract classes may not be
36 * instantiated.
38 public bool is_abstract { get; set; }
40 /**
41 * Instances of compact classes are fast to create and have a
42 * compact memory layout. Compact classes don't support runtime
43 * type information or virtual methods.
45 public bool is_compact {
46 get {
47 if (_is_compact == null) {
48 if (base_class != null) {
49 _is_compact = base_class.is_compact;
50 } else {
51 _is_compact = get_attribute ("Compact") != null;
54 return _is_compact;
56 set {
57 _is_compact = value;
58 set_attribute ("Compact", value);
62 /**
63 * Instances of immutable classes are immutable after construction.
65 public bool is_immutable {
66 get {
67 if (_is_immutable == null) {
68 if (base_class != null) {
69 _is_immutable = base_class.is_immutable;
70 } else {
71 _is_immutable = get_attribute ("Immutable") != null;
74 return _is_immutable;
76 set {
77 _is_immutable = value;
78 set_attribute ("Immutable", value);
82 /**
83 * Specifies whether this class has private fields.
85 public bool has_private_fields { get; set; }
87 /**
88 * Specifies whether this class has class fields.
90 public bool has_class_private_fields { get; private set; }
92 private bool? _is_compact;
93 private bool? _is_immutable;
95 private List<DataType> base_types = new ArrayList<DataType> ();
97 /**
98 * Specifies the default construction method.
100 public CreationMethod default_construction_method { get; set; }
103 * Specifies the instance constructor.
105 public Constructor constructor { get; set; }
108 * Specifies the class constructor.
110 public Constructor class_constructor { get; set; }
113 * Specifies the static class constructor.
115 public Constructor static_constructor { get; set; }
118 * Specifies the instance destructor.
120 public Destructor? destructor {
121 get { return _destructor; }
122 set {
123 _destructor = value;
124 if (_destructor != null) {
125 if (_destructor.this_parameter != null) {
126 _destructor.scope.remove (_destructor.this_parameter.name);
128 _destructor.this_parameter = new Parameter ("this", get_this_type ());
129 _destructor.scope.add (_destructor.this_parameter.name, _destructor.this_parameter);
135 * Specifies the class destructor.
137 public Destructor? static_destructor { get; set; }
140 * Specifies the class destructor.
142 public Destructor? class_destructor { get; set; }
145 * Specifies whether this class denotes an error base.
147 public bool is_error_base {
148 get {
149 return get_attribute ("ErrorBase") != null;
153 Destructor? _destructor;
156 * Creates a new class.
158 * @param name type name
159 * @param source_reference reference to source code
160 * @param comment class documentation
161 * @return newly created class
163 public Class (string name, SourceReference? source_reference = null, Comment? comment = null) {
164 base (name, source_reference, comment);
168 * Adds the specified class or interface to the list of base types of
169 * this class.
171 * @param type a class or interface reference
173 public void add_base_type (DataType type) {
174 base_types.add (type);
175 type.parent_node = this;
179 * Returns a copy of the base type list.
181 * @return list of base types
183 public List<DataType> get_base_types () {
184 return base_types;
188 * Adds the specified field as a member to this class.
190 * @param f a field
192 public override void add_field (Field f) {
193 base.add_field (f);
195 if (f.access == SymbolAccessibility.PRIVATE && f.binding == MemberBinding.INSTANCE) {
196 has_private_fields = true;
197 } else if (f.access == SymbolAccessibility.PRIVATE && f.binding == MemberBinding.CLASS) {
198 has_class_private_fields = true;
203 * Adds the specified method as a member to this class.
205 * @param m a method
207 public override void add_method (Method m) {
208 if (m.binding == MemberBinding.INSTANCE || m is CreationMethod) {
209 if (m.this_parameter != null) {
210 m.scope.remove (m.this_parameter.name);
212 m.this_parameter = new Parameter ("this", get_this_type ());
213 m.scope.add (m.this_parameter.name, m.this_parameter);
215 if (!(m.return_type is VoidType) && m.get_postconditions ().size > 0) {
216 if (m.result_var != null) {
217 m.scope.remove (m.result_var.name);
219 m.result_var = new LocalVariable (m.return_type.copy (), "result", null, source_reference);
220 m.result_var.is_result = true;
222 if (m is CreationMethod) {
223 if (m.name == null) {
224 default_construction_method = (CreationMethod) m;
225 m.name = ".new";
228 var cm = (CreationMethod) m;
229 if (cm.class_name != null && cm.class_name != name) {
230 // class_name is null for constructors generated by GIdlParser
231 Report.error (m.source_reference, "missing return type in method `%s.%s´".printf (get_full_name (), cm.class_name));
232 m.error = true;
233 return;
237 base.add_method (m);
238 // explicit interface method implementation
239 if (m.base_interface_type != null) {
240 scope.remove (m.name);
241 scope.add (null, m);
246 * Adds the specified property as a member to this class.
248 * @param prop a property
250 public override void add_property (Property prop) {
251 base.add_property (prop);
253 prop.this_parameter = new Parameter ("this", get_this_type ());
254 prop.scope.add (prop.this_parameter.name, prop.this_parameter);
256 if (prop.field != null) {
257 add_field (prop.field);
261 public override void add_constructor (Constructor c) {
262 if (c.binding == MemberBinding.INSTANCE) {
263 if (constructor != null) {
264 Report.error (c.source_reference, "class already contains a constructor");
266 constructor = c;
267 } else if (c.binding == MemberBinding.CLASS) {
268 if (class_constructor != null) {
269 Report.error (c.source_reference, "class already contains a class constructor");
271 class_constructor = c;
272 } else {
273 if (static_constructor != null) {
274 Report.error (c.source_reference, "class already contains a static constructor");
276 static_constructor = c;
280 public override void add_destructor (Destructor d) {
281 if (d.binding == MemberBinding.INSTANCE) {
282 if (destructor != null) {
283 Report.error (d.source_reference, "class already contains a destructor");
285 destructor = d;
286 } else if (d.binding == MemberBinding.CLASS) {
287 if (class_destructor != null) {
288 Report.error (d.source_reference, "class already contains a class destructor");
290 class_destructor = d;
291 } else {
292 if (static_destructor != null) {
293 Report.error (d.source_reference, "class already contains a static destructor");
295 static_destructor = d;
299 public override void accept (CodeVisitor visitor) {
300 visitor.visit_class (this);
303 public override void accept_children (CodeVisitor visitor) {
304 foreach (DataType type in base_types) {
305 type.accept (visitor);
308 foreach (TypeParameter p in get_type_parameters ()) {
309 p.accept (visitor);
312 /* process enums first to avoid order problems in C code */
313 foreach (Enum en in get_enums ()) {
314 en.accept (visitor);
317 foreach (Field f in get_fields ()) {
318 f.accept (visitor);
321 foreach (Constant c in get_constants ()) {
322 c.accept (visitor);
325 foreach (Method m in get_methods ()) {
326 m.accept (visitor);
329 foreach (Property prop in get_properties ()) {
330 prop.accept (visitor);
333 foreach (Signal sig in get_signals ()) {
334 sig.accept (visitor);
337 if (constructor != null) {
338 constructor.accept (visitor);
341 if (class_constructor != null) {
342 class_constructor.accept (visitor);
345 if (static_constructor != null) {
346 static_constructor.accept (visitor);
349 if (destructor != null) {
350 destructor.accept (visitor);
353 if (static_destructor != null) {
354 static_destructor.accept (visitor);
357 if (class_destructor != null) {
358 class_destructor.accept (visitor);
361 foreach (Class cl in get_classes ()) {
362 cl.accept (visitor);
365 foreach (Struct st in get_structs ()) {
366 st.accept (visitor);
369 foreach (Delegate d in get_delegates ()) {
370 d.accept (visitor);
374 public override bool is_reference_type () {
375 return true;
378 public bool is_fundamental () {
379 if (!is_compact && base_class == null) {
380 return true;
382 return false;
385 public override bool is_subtype_of (TypeSymbol t) {
386 if (this == t) {
387 return true;
390 foreach (DataType base_type in base_types) {
391 if (base_type.data_type != null && base_type.data_type.is_subtype_of (t)) {
392 return true;
396 return false;
399 public override void replace_type (DataType old_type, DataType new_type) {
400 for (int i = 0; i < base_types.size; i++) {
401 if (base_types[i] == old_type) {
402 base_types[i] = new_type;
403 new_type.parent_node = this;
404 return;
409 private void get_all_prerequisites (Interface iface, List<TypeSymbol> list) {
410 foreach (DataType prereq in iface.get_prerequisites ()) {
411 TypeSymbol type = prereq.data_type;
412 /* skip on previous errors */
413 if (type == null) {
414 continue;
417 list.add (type);
418 if (type is Interface) {
419 get_all_prerequisites ((Interface) type, list);
425 private bool class_is_a (Class cl, TypeSymbol t) {
426 if (cl == t) {
427 return true;
430 foreach (DataType base_type in cl.get_base_types ()) {
431 if (base_type.data_type is Class) {
432 if (class_is_a ((Class) base_type.data_type, t)) {
433 return true;
435 } else if (base_type.data_type == t) {
436 return true;
440 return false;
443 public override bool check (CodeContext context) {
444 if (checked) {
445 return !error;
448 checked = true;
450 var old_source_file = context.analyzer.current_source_file;
451 var old_symbol = context.analyzer.current_symbol;
453 if (source_reference != null) {
454 context.analyzer.current_source_file = source_reference.file;
456 context.analyzer.current_symbol = this;
458 foreach (DataType base_type_reference in get_base_types ()) {
459 if (!base_type_reference.check (context)) {
460 error = true;
461 return false;
464 if (!(base_type_reference is ObjectType)) {
465 error = true;
466 Report.error (source_reference, "base type `%s' of class `%s' is not an object type".printf (base_type_reference.to_string (), get_full_name ()));
467 return false;
470 // check whether base type is at least as accessible as the class
471 if (!context.analyzer.is_type_accessible (this, base_type_reference)) {
472 error = true;
473 Report.error (source_reference, "base type `%s' is less accessible than class `%s'".printf (base_type_reference.to_string (), get_full_name ()));
474 return false;
477 int n_type_args = base_type_reference.get_type_arguments ().size;
478 int n_type_params = ((ObjectTypeSymbol) base_type_reference.data_type).get_type_parameters ().size;
479 if (n_type_args < n_type_params) {
480 error = true;
481 Report.error (base_type_reference.source_reference, "too few type arguments");
482 return false;
483 } else if (n_type_args > n_type_params) {
484 error = true;
485 Report.error (base_type_reference.source_reference, "too many type arguments");
486 return false;
490 foreach (DataType type in base_types) {
491 type.check (context);
494 foreach (TypeParameter p in get_type_parameters ()) {
495 p.check (context);
498 /* process enums first to avoid order problems in C code */
499 foreach (Enum en in get_enums ()) {
500 en.check (context);
503 foreach (Field f in get_fields ()) {
504 f.check (context);
507 foreach (Constant c in get_constants ()) {
508 c.check (context);
511 foreach (Method m in get_methods ()) {
512 m.check (context);
515 foreach (Property prop in get_properties ()) {
516 if (prop.get_attribute ("NoAccessorMethod") != null && !is_subtype_of (context.analyzer.object_type)) {
517 error = true;
518 Report.error (prop.source_reference, "NoAccessorMethod is only allowed for properties in classes derived from GLib.Object");
519 return false;
521 prop.check (context);
524 foreach (Signal sig in get_signals ()) {
525 sig.check (context);
528 if (constructor != null) {
529 constructor.check (context);
532 if (class_constructor != null) {
533 class_constructor.check (context);
536 if (static_constructor != null) {
537 static_constructor.check (context);
540 if (destructor != null) {
541 destructor.check (context);
544 if (static_destructor != null) {
545 static_destructor.check (context);
548 if (class_destructor != null) {
549 class_destructor.check (context);
552 foreach (Class cl in get_classes ()) {
553 cl.check (context);
556 foreach (Struct st in get_structs ()) {
557 st.check (context);
560 foreach (Delegate d in get_delegates ()) {
561 d.check (context);
564 /* compact classes cannot implement interfaces */
565 if (is_compact) {
566 foreach (DataType base_type in get_base_types ()) {
567 if (base_type.data_type is Interface) {
568 error = true;
569 Report.error (source_reference, "compact classes `%s' may not implement interfaces".printf (get_full_name ()));
573 if (!external && !external_package && base_class != null && base_class != context.analyzer.gsource_type) {
574 foreach (Field f in get_fields ()) {
575 if (f.binding == MemberBinding.INSTANCE) {
576 error = true;
577 Report.error (source_reference, "derived compact classes may not have instance fields");
578 break;
584 /* gather all prerequisites */
585 List<TypeSymbol> prerequisites = new ArrayList<TypeSymbol> ();
586 foreach (DataType base_type in get_base_types ()) {
587 if (base_type.data_type is Interface) {
588 get_all_prerequisites ((Interface) base_type.data_type, prerequisites);
591 /* check whether all prerequisites are met */
592 List<string> missing_prereqs = new ArrayList<string> ();
593 foreach (TypeSymbol prereq in prerequisites) {
594 if (!class_is_a (this, prereq)) {
595 missing_prereqs.insert (0, prereq.get_full_name ());
598 /* report any missing prerequisites */
599 if (missing_prereqs.size > 0) {
600 error = true;
602 string error_string = "%s: some prerequisites (".printf (get_full_name ());
603 bool first = true;
604 foreach (string s in missing_prereqs) {
605 if (first) {
606 error_string = "%s`%s'".printf (error_string, s);
607 first = false;
608 } else {
609 error_string = "%s, `%s'".printf (error_string, s);
612 error_string += ") are not met";
613 Report.error (source_reference, error_string);
616 /* VAPI classes don't have to specify overridden methods */
617 if (source_type == SourceFileType.SOURCE) {
618 /* all abstract symbols defined in base types have to be at least defined (or implemented) also in this type */
619 foreach (DataType base_type in get_base_types ()) {
620 if (base_type.data_type is Interface) {
621 Interface iface = (Interface) base_type.data_type;
623 if (base_class != null && base_class.is_subtype_of (iface)) {
624 // reimplementation of interface, class is not required to reimplement all methods
625 break;
628 /* We do not need to do expensive equality checking here since this is done
629 * already. We only need to guarantee the symbols are present.
632 /* check methods */
633 foreach (Method m in iface.get_methods ()) {
634 if (m.is_abstract) {
635 var implemented = false;
636 var base_class = this;
637 while (base_class != null) {
638 foreach (var impl in base_class.get_methods ()) {
639 if (impl.name == m.name && (impl.base_interface_type == null || impl.base_interface_type.data_type == iface)) {
640 // method is used as interface implementation, so it is not unused
641 impl.version.check (source_reference);
642 impl.used = true;
643 implemented = true;
644 break;
647 base_class = base_class.base_class;
649 if (!implemented) {
650 error = true;
651 Report.error (source_reference, "`%s' does not implement interface method `%s'".printf (get_full_name (), m.get_full_name ()));
656 /* check properties */
657 foreach (Property prop in iface.get_properties ()) {
658 if (prop.is_abstract) {
659 Symbol sym = null;
660 var base_class = this;
661 while (base_class != null && !(sym is Property)) {
662 sym = base_class.scope.lookup (prop.name);
663 base_class = base_class.base_class;
665 if (sym is Property) {
666 var base_prop = (Property) sym;
667 string? invalid_match = null;
668 // No check at all for "new" classified properties, really?
669 if (!base_prop.hides && !base_prop.compatible (prop, out invalid_match)) {
670 error = true;
671 Report.error (source_reference, "Type and/or accessors of inherited properties `%s' and `%s' do not match: %s.".printf (prop.get_full_name (), base_prop.get_full_name (), invalid_match));
673 // property is used as interface implementation, so it is not unused
674 sym.version.check (source_reference);
675 sym.used = true;
676 } else {
677 error = true;
678 Report.error (source_reference, "`%s' does not implement interface property `%s'".printf (get_full_name (), prop.get_full_name ()));
685 /* all abstract symbols defined in base classes have to be implemented in non-abstract classes */
686 if (!is_abstract) {
687 var base_class = base_class;
688 while (base_class != null && base_class.is_abstract) {
689 foreach (Method base_method in base_class.get_methods ()) {
690 if (base_method.is_abstract) {
691 var override_method = SemanticAnalyzer.symbol_lookup_inherited (this, base_method.name) as Method;
692 if (override_method == null || !override_method.overrides) {
693 error = true;
694 Report.error (source_reference, "`%s' does not implement abstract method `%s'".printf (get_full_name (), base_method.get_full_name ()));
698 foreach (Property base_property in base_class.get_properties ()) {
699 if (base_property.is_abstract) {
700 var override_property = SemanticAnalyzer.symbol_lookup_inherited (this, base_property.name) as Property;
701 if (override_property == null || !override_property.overrides) {
702 error = true;
703 Report.error (source_reference, "`%s' does not implement abstract property `%s'".printf (get_full_name (), base_property.get_full_name ()));
707 base_class = base_class.base_class;
712 context.analyzer.current_source_file = old_source_file;
713 context.analyzer.current_symbol = old_symbol;
715 return !error;
719 // vim:sw=8 noet