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>
26 * Represents a class declaration in the source code.
28 public class Vala
.Class
: ObjectTypeSymbol
{
30 * Specifies the base class.
32 public Class base_class
{ get; set; }
35 * Specifies whether this class is abstract. Abstract classes may not be
38 public bool is_abstract
{ get; set; }
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
{
47 if (_is_compact
== null) {
48 if (base_class
!= null) {
49 _is_compact
= base_class
.is_compact
;
51 _is_compact
= get_attribute ("Compact") != null;
58 set_attribute ("Compact", value
);
63 * Instances of immutable classes are immutable after construction.
65 public bool is_immutable
{
67 if (_is_immutable
== null) {
68 if (base_class
!= null) {
69 _is_immutable
= base_class
.is_immutable
;
71 _is_immutable
= get_attribute ("Immutable") != null;
77 _is_immutable
= value
;
78 set_attribute ("Immutable", value
);
83 * Specifies whether this class has private fields.
85 public bool has_private_fields
{ get; set; }
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
> ();
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
; }
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
{
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
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 () {
188 * Adds the specified field as a member to this class.
192 public override void add_field (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.
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
;
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
));
238 // explicit interface method implementation
239 if (m
.base_interface_type
!= null) {
240 scope
.remove (m
.name
);
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");
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
;
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");
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
;
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 ()) {
312 /* process enums first to avoid order problems in C code */
313 foreach (Enum en
in get_enums ()) {
317 foreach (Field f
in get_fields ()) {
321 foreach (Constant c
in get_constants ()) {
325 foreach (Method m
in get_methods ()) {
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 ()) {
365 foreach (Struct st
in get_structs ()) {
369 foreach (Delegate d
in get_delegates ()) {
374 public override bool is_reference_type () {
378 public bool is_fundamental () {
379 if (!is_compact
&& base_class
== null) {
385 public override bool is_subtype_of (TypeSymbol t
) {
390 foreach (DataType base_type
in base_types
) {
391 if (base_type
.data_type
!= null && base_type
.data_type
.is_subtype_of (t
)) {
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
;
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 */
418 if (type is Interface
) {
419 get_all_prerequisites ((Interface
) type
, list
);
425 private bool class_is_a (Class cl
, TypeSymbol t
) {
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
)) {
435 } else if (base_type
.data_type
== t
) {
443 public override bool check (CodeContext context
) {
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
)) {
464 if (!(base_type_reference is ObjectType
)) {
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 ()));
470 // check whether base type is at least as accessible as the class
471 if (!context
.analyzer
.is_type_accessible (this
, base_type_reference
)) {
473 Report
.error (source_reference
, "base type `%s` is less accessible than class `%s`".printf (base_type_reference
.to_string (), get_full_name ()));
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
) {
481 Report
.error (base_type_reference
.source_reference
, "too few type arguments");
483 } else if (n_type_args
> n_type_params
) {
485 Report
.error (base_type_reference
.source_reference
, "too many type arguments");
490 foreach (DataType type
in base_types
) {
491 type
.check (context
);
494 foreach (TypeParameter p
in get_type_parameters ()) {
498 /* process enums first to avoid order problems in C code */
499 foreach (Enum en
in get_enums ()) {
503 foreach (Field f
in get_fields ()) {
507 foreach (Constant c
in get_constants ()) {
511 foreach (Method m
in get_methods ()) {
515 foreach (Property prop
in get_properties ()) {
516 if (prop
.get_attribute ("NoAccessorMethod") != null && !is_subtype_of (context
.analyzer
.object_type
)) {
518 Report
.error (prop
.source_reference
, "NoAccessorMethod is only allowed for properties in classes derived from GLib.Object");
521 prop
.check (context
);
524 foreach (Signal sig
in get_signals ()) {
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 ()) {
556 foreach (Struct st
in get_structs ()) {
560 foreach (Delegate d
in get_delegates ()) {
564 /* compact classes cannot implement interfaces */
566 foreach (DataType base_type
in get_base_types ()) {
567 if (base_type
.data_type is Interface
) {
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
) {
577 Report
.error (source_reference
, "derived compact classes may not have instance fields");
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) {
602 string error_string
= "%s: some prerequisites (".printf (get_full_name ());
604 foreach (string s
in missing_prereqs
) {
606 error_string
= "%s`%s'".printf (error_string
, s
);
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
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.
633 foreach (Method m
in iface
.get_methods ()) {
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
);
647 base_class
= base_class
.base_class
;
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
) {
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
)) {
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
);
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 */
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
) {
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
) {
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
;