tests: Add "while (false)" test to increase coverage
[vala-gnome.git] / vala / valaclass.vala
blobfdac092e4dd4c63dd51cc3ef2bff666ebac50153
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 if (_is_compact == null) {
55 _is_compact = get_attribute ("Compact") != null;
57 return _is_compact;
59 set {
60 _is_compact = value;
61 set_attribute ("Compact", value);
65 /**
66 * Instances of immutable classes are immutable after construction.
68 public bool is_immutable {
69 get {
70 if (_is_immutable == null) {
71 if (base_class != null) {
72 _is_immutable = base_class.is_immutable;
73 } else {
74 _is_immutable = get_attribute ("Immutable") != null;
77 if (_is_immutable == null) {
78 _is_immutable = get_attribute ("Immutable") != null;
80 return _is_immutable;
82 set {
83 _is_immutable = value;
84 set_attribute ("Immutable", value);
88 /**
89 * Specifies whether this class has private fields.
91 public bool has_private_fields { get; set; }
93 /**
94 * Specifies whether this class has class fields.
96 public bool has_class_private_fields { get; private set; }
98 private bool? _is_compact;
99 private bool? _is_immutable;
101 private List<DataType> base_types = new ArrayList<DataType> ();
103 private List<Constant> constants = new ArrayList<Constant> ();
104 private List<Field> fields = new ArrayList<Field> ();
105 private List<Method> methods = new ArrayList<Method> ();
106 private List<Property> properties = new ArrayList<Property> ();
107 private List<Signal> signals = new ArrayList<Signal> ();
109 // inner types
110 private List<Class> classes = new ArrayList<Class> ();
111 private List<Struct> structs = new ArrayList<Struct> ();
112 private List<Enum> enums = new ArrayList<Enum> ();
113 private List<Delegate> delegates = new ArrayList<Delegate> ();
116 * Returns a copy of the list of classes.
118 * @return list of classes
120 public List<Class> get_classes () {
121 return classes;
125 * Returns a copy of the list of structs.
127 * @return list of structs
129 public List<Struct> get_structs () {
130 return structs;
134 * Returns a copy of the list of enums.
136 * @return list of enums
138 public List<Enum> get_enums () {
139 return enums;
143 * Returns a copy of the list of delegates.
145 * @return list of delegates
147 public List<Delegate> get_delegates () {
148 return delegates;
152 * Specifies the default construction method.
154 public CreationMethod default_construction_method { get; set; }
157 * Specifies the instance constructor.
159 public Constructor constructor { get; set; }
162 * Specifies the class constructor.
164 public Constructor class_constructor { get; set; }
167 * Specifies the static class constructor.
169 public Constructor static_constructor { get; set; }
172 * Specifies the instance destructor.
174 public Destructor? destructor {
175 get { return _destructor; }
176 set {
177 _destructor = value;
178 if (_destructor != null) {
179 if (_destructor.this_parameter != null) {
180 _destructor.scope.remove (_destructor.this_parameter.name);
182 _destructor.this_parameter = new Parameter ("this", get_this_type ());
183 _destructor.scope.add (_destructor.this_parameter.name, _destructor.this_parameter);
189 * Specifies the class destructor.
191 public Destructor? static_destructor { get; set; }
194 * Specifies the class destructor.
196 public Destructor? class_destructor { get; set; }
199 * Specifies whether this class denotes an error base.
201 public bool is_error_base {
202 get {
203 return get_attribute ("ErrorBase") != null;
207 Destructor? _destructor;
210 * Creates a new class.
212 * @param name type name
213 * @param source_reference reference to source code
214 * @param comment class documentation
215 * @return newly created class
217 public Class (string name, SourceReference? source_reference = null, Comment? comment = null) {
218 base (name, source_reference, comment);
222 * Adds the specified class or interface to the list of base types of
223 * this class.
225 * @param type a class or interface reference
227 public void add_base_type (DataType type) {
228 base_types.add (type);
229 type.parent_node = this;
233 * Returns a copy of the base type list.
235 * @return list of base types
237 public List<DataType> get_base_types () {
238 return base_types;
242 * Adds the specified constant as a member to this class.
244 * @param c a constant
246 public override void add_constant (Constant c) {
247 constants.add (c);
248 scope.add (c.name, c);
252 * Adds the specified field as a member to this class.
254 * @param f a field
256 public override void add_field (Field f) {
257 fields.add (f);
258 if (f.access == SymbolAccessibility.PRIVATE && f.binding == MemberBinding.INSTANCE) {
259 has_private_fields = true;
260 } else if (f.access == SymbolAccessibility.PRIVATE && f.binding == MemberBinding.CLASS) {
261 has_class_private_fields = true;
263 scope.add (f.name, f);
267 * Returns a copy of the list of fields.
269 * @return list of fields
271 public List<Field> get_fields () {
272 return fields;
276 * Returns a copy of the list of constants.
278 * @return list of constants
280 public List<Constant> get_constants () {
281 return constants;
285 * Adds the specified method as a member to this class.
287 * @param m a method
289 public override void add_method (Method m) {
290 if (m.binding == MemberBinding.INSTANCE || m is CreationMethod) {
291 if (m.this_parameter != null) {
292 m.scope.remove (m.this_parameter.name);
294 m.this_parameter = new Parameter ("this", get_this_type ());
295 m.scope.add (m.this_parameter.name, m.this_parameter);
297 if (!(m.return_type is VoidType) && m.get_postconditions ().size > 0) {
298 if (m.result_var != null) {
299 m.scope.remove (m.result_var.name);
301 m.result_var = new LocalVariable (m.return_type.copy (), "result", null, source_reference);
302 m.result_var.is_result = true;
304 if (m is CreationMethod) {
305 if (m.name == null) {
306 default_construction_method = (CreationMethod) m;
307 m.name = ".new";
310 var cm = (CreationMethod) m;
311 if (cm.class_name != null && cm.class_name != name) {
312 // class_name is null for constructors generated by GIdlParser
313 Report.error (m.source_reference, "missing return type in method `%s.%s´".printf (get_full_name (), cm.class_name));
314 m.error = true;
315 return;
319 methods.add (m);
320 if (m.base_interface_type == null) {
321 scope.add (m.name, m);
322 } else {
323 // explicit interface method implementation
324 scope.add (null, m);
329 * Returns a copy of the list of methods.
331 * @return list of methods
333 public override List<Method> get_methods () {
334 return methods;
338 * Adds the specified property as a member to this class.
340 * @param prop a property
342 public override void add_property (Property prop) {
343 properties.add (prop);
344 scope.add (prop.name, prop);
346 prop.this_parameter = new Parameter ("this", get_this_type ());
347 prop.scope.add (prop.this_parameter.name, prop.this_parameter);
349 if (prop.field != null) {
350 add_field (prop.field);
355 * Returns a copy of the list of properties.
357 * @return list of properties
359 public override List<Property> get_properties () {
360 return properties;
364 * Adds the specified signal as a member to this class.
366 * @param sig a signal
368 public override void add_signal (Signal sig) {
369 signals.add (sig);
370 scope.add (sig.name, sig);
374 * Returns a copy of the list of signals.
376 * @return list of signals
378 public override List<Signal> get_signals () {
379 return signals;
383 * Adds the specified class as an inner class.
385 * @param cl a class
387 public override void add_class (Class cl) {
388 classes.add (cl);
389 scope.add (cl.name, cl);
393 * Adds the specified struct as an inner struct.
395 * @param st a struct
397 public override void add_struct (Struct st) {
398 structs.add (st);
399 scope.add (st.name, st);
403 * Adds the specified enum as an inner enum.
405 * @param en an enum
407 public override void add_enum (Enum en) {
408 enums.add (en);
409 scope.add (en.name, en);
413 * Adds the specified delegate as an inner delegate.
415 * @param d a delegate
417 public override void add_delegate (Delegate d) {
418 delegates.add (d);
419 scope.add (d.name, d);
422 public override void add_constructor (Constructor c) {
423 if (c.binding == MemberBinding.INSTANCE) {
424 if (constructor != null) {
425 Report.error (c.source_reference, "class already contains a constructor");
427 constructor = c;
428 } else if (c.binding == MemberBinding.CLASS) {
429 if (class_constructor != null) {
430 Report.error (c.source_reference, "class already contains a class constructor");
432 class_constructor = c;
433 } else {
434 if (static_constructor != null) {
435 Report.error (c.source_reference, "class already contains a static constructor");
437 static_constructor = c;
441 public override void add_destructor (Destructor d) {
442 if (d.binding == MemberBinding.INSTANCE) {
443 if (destructor != null) {
444 Report.error (d.source_reference, "class already contains a destructor");
446 destructor = d;
447 } else if (d.binding == MemberBinding.CLASS) {
448 if (class_destructor != null) {
449 Report.error (d.source_reference, "class already contains a class destructor");
451 class_destructor = d;
452 } else {
453 if (static_destructor != null) {
454 Report.error (d.source_reference, "class already contains a static destructor");
456 static_destructor = d;
460 public override void accept (CodeVisitor visitor) {
461 visitor.visit_class (this);
464 public override void accept_children (CodeVisitor visitor) {
465 foreach (DataType type in base_types) {
466 type.accept (visitor);
469 foreach (TypeParameter p in get_type_parameters ()) {
470 p.accept (visitor);
473 /* process enums first to avoid order problems in C code */
474 foreach (Enum en in enums) {
475 en.accept (visitor);
478 foreach (Field f in fields) {
479 f.accept (visitor);
482 foreach (Constant c in constants) {
483 c.accept (visitor);
486 foreach (Method m in methods) {
487 m.accept (visitor);
490 foreach (Property prop in properties) {
491 prop.accept (visitor);
494 foreach (Signal sig in signals) {
495 sig.accept (visitor);
498 if (constructor != null) {
499 constructor.accept (visitor);
502 if (class_constructor != null) {
503 class_constructor.accept (visitor);
506 if (static_constructor != null) {
507 static_constructor.accept (visitor);
510 if (destructor != null) {
511 destructor.accept (visitor);
514 if (static_destructor != null) {
515 static_destructor.accept (visitor);
518 if (class_destructor != null) {
519 class_destructor.accept (visitor);
522 foreach (Class cl in classes) {
523 cl.accept (visitor);
526 foreach (Struct st in structs) {
527 st.accept (visitor);
530 foreach (Delegate d in delegates) {
531 d.accept (visitor);
535 public override bool is_reference_type () {
536 return true;
539 public bool is_fundamental () {
540 if (!is_compact && base_class == null) {
541 return true;
543 return false;
546 public override bool is_subtype_of (TypeSymbol t) {
547 if (this == t) {
548 return true;
551 foreach (DataType base_type in base_types) {
552 if (base_type.data_type != null && base_type.data_type.is_subtype_of (t)) {
553 return true;
557 return false;
560 public override void replace_type (DataType old_type, DataType new_type) {
561 for (int i = 0; i < base_types.size; i++) {
562 if (base_types[i] == old_type) {
563 base_types[i] = new_type;
564 new_type.parent_node = this;
565 return;
570 private void get_all_prerequisites (Interface iface, List<TypeSymbol> list) {
571 foreach (DataType prereq in iface.get_prerequisites ()) {
572 TypeSymbol type = prereq.data_type;
573 /* skip on previous errors */
574 if (type == null) {
575 continue;
578 list.add (type);
579 if (type is Interface) {
580 get_all_prerequisites ((Interface) type, list);
586 private bool class_is_a (Class cl, TypeSymbol t) {
587 if (cl == t) {
588 return true;
591 foreach (DataType base_type in cl.get_base_types ()) {
592 if (base_type.data_type is Class) {
593 if (class_is_a ((Class) base_type.data_type, t)) {
594 return true;
596 } else if (base_type.data_type == t) {
597 return true;
601 return false;
604 public override bool check (CodeContext context) {
605 if (checked) {
606 return !error;
609 checked = true;
611 var old_source_file = context.analyzer.current_source_file;
612 var old_symbol = context.analyzer.current_symbol;
614 if (source_reference != null) {
615 context.analyzer.current_source_file = source_reference.file;
617 context.analyzer.current_symbol = this;
619 foreach (DataType base_type_reference in get_base_types ()) {
620 if (!base_type_reference.check (context)) {
621 error = true;
622 return false;
625 if (!(base_type_reference is ObjectType)) {
626 error = true;
627 Report.error (source_reference, "base type `%s` of class `%s` is not an object type".printf (base_type_reference.to_string (), get_full_name ()));
628 return false;
631 // check whether base type is at least as accessible as the class
632 if (!context.analyzer.is_type_accessible (this, base_type_reference)) {
633 error = true;
634 Report.error (source_reference, "base type `%s` is less accessible than class `%s`".printf (base_type_reference.to_string (), get_full_name ()));
635 return false;
638 int n_type_args = base_type_reference.get_type_arguments ().size;
639 int n_type_params = ((ObjectTypeSymbol) base_type_reference.data_type).get_type_parameters ().size;
640 if (n_type_args < n_type_params) {
641 error = true;
642 Report.error (base_type_reference.source_reference, "too few type arguments");
643 return false;
644 } else if (n_type_args > n_type_params) {
645 error = true;
646 Report.error (base_type_reference.source_reference, "too many type arguments");
647 return false;
651 foreach (DataType type in base_types) {
652 type.check (context);
655 foreach (TypeParameter p in get_type_parameters ()) {
656 p.check (context);
659 /* process enums first to avoid order problems in C code */
660 foreach (Enum en in enums) {
661 en.check (context);
664 foreach (Field f in fields) {
665 f.check (context);
668 foreach (Constant c in constants) {
669 c.check (context);
672 foreach (Method m in methods) {
673 m.check (context);
676 foreach (Property prop in properties) {
677 if (prop.get_attribute ("NoAccessorMethod") != null && !is_subtype_of (context.analyzer.object_type)) {
678 error = true;
679 Report.error (prop.source_reference, "NoAccessorMethod is only allowed for properties in classes derived from GLib.Object");
680 return false;
682 prop.check (context);
685 foreach (Signal sig in signals) {
686 sig.check (context);
689 if (constructor != null) {
690 constructor.check (context);
693 if (class_constructor != null) {
694 class_constructor.check (context);
697 if (static_constructor != null) {
698 static_constructor.check (context);
701 if (destructor != null) {
702 destructor.check (context);
705 if (static_destructor != null) {
706 static_destructor.check (context);
709 if (class_destructor != null) {
710 class_destructor.check (context);
713 foreach (Class cl in classes) {
714 cl.check (context);
717 foreach (Struct st in structs) {
718 st.check (context);
721 foreach (Delegate d in delegates) {
722 d.check (context);
725 /* compact classes cannot implement interfaces */
726 if (is_compact) {
727 foreach (DataType base_type in get_base_types ()) {
728 if (base_type.data_type is Interface) {
729 error = true;
730 Report.error (source_reference, "compact classes `%s` may not implement interfaces".printf (get_full_name ()));
734 if (!external && !external_package && base_class != null && base_class != context.analyzer.gsource_type) {
735 foreach (Field f in fields) {
736 if (f.binding == MemberBinding.INSTANCE) {
737 error = true;
738 Report.error (source_reference, "derived compact classes may not have instance fields");
739 break;
745 /* gather all prerequisites */
746 List<TypeSymbol> prerequisites = new ArrayList<TypeSymbol> ();
747 foreach (DataType base_type in get_base_types ()) {
748 if (base_type.data_type is Interface) {
749 get_all_prerequisites ((Interface) base_type.data_type, prerequisites);
752 /* check whether all prerequisites are met */
753 List<string> missing_prereqs = new ArrayList<string> ();
754 foreach (TypeSymbol prereq in prerequisites) {
755 if (!class_is_a (this, prereq)) {
756 missing_prereqs.insert (0, prereq.get_full_name ());
759 /* report any missing prerequisites */
760 if (missing_prereqs.size > 0) {
761 error = true;
763 string error_string = "%s: some prerequisites (".printf (get_full_name ());
764 bool first = true;
765 foreach (string s in missing_prereqs) {
766 if (first) {
767 error_string = "%s`%s'".printf (error_string, s);
768 first = false;
769 } else {
770 error_string = "%s, `%s'".printf (error_string, s);
773 error_string += ") are not met";
774 Report.error (source_reference, error_string);
777 /* VAPI classes don't have to specify overridden methods */
778 if (source_type == SourceFileType.SOURCE) {
779 /* all abstract symbols defined in base types have to be at least defined (or implemented) also in this type */
780 foreach (DataType base_type in get_base_types ()) {
781 if (base_type.data_type is Interface) {
782 Interface iface = (Interface) base_type.data_type;
784 if (base_class != null && base_class.is_subtype_of (iface)) {
785 // reimplementation of interface, class is not required to reimplement all methods
786 break;
789 /* We do not need to do expensive equality checking here since this is done
790 * already. We only need to guarantee the symbols are present.
793 /* check methods */
794 foreach (Method m in iface.get_methods ()) {
795 if (m.is_abstract) {
796 var implemented = false;
797 var base_class = this;
798 while (base_class != null) {
799 foreach (var impl in base_class.get_methods ()) {
800 if (impl.name == m.name && (impl.base_interface_type == null || impl.base_interface_type.data_type == iface)) {
801 // method is used as interface implementation, so it is not unused
802 impl.version.check (source_reference);
803 impl.used = true;
804 implemented = true;
805 break;
808 base_class = base_class.base_class;
810 if (!implemented) {
811 error = true;
812 Report.error (source_reference, "`%s' does not implement interface method `%s'".printf (get_full_name (), m.get_full_name ()));
817 /* check properties */
818 foreach (Property prop in iface.get_properties ()) {
819 if (prop.is_abstract) {
820 Symbol sym = null;
821 var base_class = this;
822 while (base_class != null && !(sym is Property)) {
823 sym = base_class.scope.lookup (prop.name);
824 base_class = base_class.base_class;
826 if (sym is Property) {
827 var base_prop = (Property) sym;
828 string? invalid_match = null;
829 // No check at all for "new" classified properties, really?
830 if (!base_prop.hides && !base_prop.compatible (prop, out invalid_match)) {
831 error = true;
832 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));
834 // property is used as interface implementation, so it is not unused
835 sym.version.check (source_reference);
836 sym.used = true;
837 } else {
838 error = true;
839 Report.error (source_reference, "`%s' does not implement interface property `%s'".printf (get_full_name (), prop.get_full_name ()));
846 /* all abstract symbols defined in base classes have to be implemented in non-abstract classes */
847 if (!is_abstract) {
848 var base_class = base_class;
849 while (base_class != null && base_class.is_abstract) {
850 foreach (Method base_method in base_class.get_methods ()) {
851 if (base_method.is_abstract) {
852 var override_method = SemanticAnalyzer.symbol_lookup_inherited (this, base_method.name) as Method;
853 if (override_method == null || !override_method.overrides) {
854 error = true;
855 Report.error (source_reference, "`%s' does not implement abstract method `%s'".printf (get_full_name (), base_method.get_full_name ()));
859 foreach (Property base_property in base_class.get_properties ()) {
860 if (base_property.is_abstract) {
861 var override_property = SemanticAnalyzer.symbol_lookup_inherited (this, base_property.name) as Property;
862 if (override_property == null || !override_property.overrides) {
863 error = true;
864 Report.error (source_reference, "`%s' does not implement abstract property `%s'".printf (get_full_name (), base_property.get_full_name ()));
868 base_class = base_class.base_class;
873 context.analyzer.current_source_file = old_source_file;
874 context.analyzer.current_symbol = old_symbol;
876 return !error;
880 // vim:sw=8 noet