Warn when using result variable with incompatible type to prepare possible
[vala-lang.git] / vala / valamemberaccess.vala
blob1622d170b0a7016ed4ec55e4c3c3118b21cad9d9
1 /* valamemberaccess.vala
3 * Copyright (C) 2006-2008 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;
24 using Gee;
26 /**
27 * Represents an access to a type member in the source code.
29 public class Vala.MemberAccess : Expression {
30 /**
31 * The parent of the member.
33 public Expression? inner {
34 get {
35 return _inner;
37 set {
38 _inner = value;
39 if (_inner != null) {
40 _inner.parent_node = this;
45 /**
46 * The name of the member.
48 public string member_name { get; set; }
50 /**
51 * Pointer member access.
53 public bool pointer_member_access { get; set; }
55 /**
56 * Represents access to an instance member without an actual instance,
57 * e.g. `MyClass.an_instance_method`.
59 public bool prototype_access { get; set; }
61 /**
62 * Specifies whether the member is used for object creation.
64 public bool creation_member { get; set; }
66 /**
67 * Qualified access to global symbol.
69 public bool qualified { get; set; }
71 private Expression? _inner;
72 private Gee.List<DataType> type_argument_list = new ArrayList<DataType> ();
74 /**
75 * Creates a new member access expression.
77 * @param inner parent of the member
78 * @param member_name member name
79 * @param source_reference reference to source code
80 * @return newly created member access expression
82 public MemberAccess (Expression? inner, string member_name, SourceReference? source_reference = null) {
83 this.inner = inner;
84 this.member_name = member_name;
85 this.source_reference = source_reference;
88 public MemberAccess.simple (string member_name, SourceReference? source_reference = null) {
89 this.member_name = member_name;
90 this.source_reference = source_reference;
93 public MemberAccess.pointer (Expression inner, string member_name, SourceReference? source_reference = null) {
94 this.inner = inner;
95 this.member_name = member_name;
96 this.source_reference = source_reference;
97 pointer_member_access = true;
101 * Appends the specified type as generic type argument.
103 * @param arg a type reference
105 public void add_type_argument (DataType arg) {
106 type_argument_list.add (arg);
107 arg.parent_node = this;
111 * Returns a copy of the list of generic type arguments.
113 * @return type argument list
115 public Gee.List<DataType> get_type_arguments () {
116 return new ReadOnlyList<DataType> (type_argument_list);
119 public override void accept (CodeVisitor visitor) {
120 visitor.visit_member_access (this);
122 visitor.visit_expression (this);
125 public override void accept_children (CodeVisitor visitor) {
126 if (inner != null) {
127 inner.accept (visitor);
130 foreach (DataType type_arg in type_argument_list) {
131 type_arg.accept (visitor);
135 public override string to_string () {
136 if (inner == null) {
137 return member_name;
138 } else {
139 return "%s.%s".printf (inner.to_string (), member_name);
143 public override void replace_expression (Expression old_node, Expression new_node) {
144 if (inner == old_node) {
145 inner = new_node;
149 public override bool is_pure () {
150 // accessing property could have side-effects
151 return (inner == null || inner.is_pure ()) && !(symbol_reference is Property);
154 public override void replace_type (DataType old_type, DataType new_type) {
155 for (int i = 0; i < type_argument_list.size; i++) {
156 if (type_argument_list[i] == old_type) {
157 type_argument_list[i] = new_type;
158 return;
163 public override bool is_constant () {
164 if (symbol_reference is Constant) {
165 return true;
166 } else {
167 return false;
171 public override bool is_non_null () {
172 var c = symbol_reference as Constant;
173 if (c != null) {
174 return !c.type_reference.nullable;
175 } else {
176 return false;
180 public override bool check (SemanticAnalyzer analyzer) {
181 if (checked) {
182 return !error;
185 checked = true;
187 if (inner != null) {
188 inner.check (analyzer);
191 foreach (DataType type_arg in type_argument_list) {
192 type_arg.check (analyzer);
195 Symbol base_symbol = null;
196 FormalParameter this_parameter = null;
197 bool may_access_instance_members = false;
199 symbol_reference = null;
201 if (qualified) {
202 base_symbol = analyzer.root_symbol;
203 symbol_reference = analyzer.root_symbol.scope.lookup (member_name);
204 } else if (inner == null) {
205 if (member_name == "this") {
206 if (!analyzer.is_in_instance_method ()) {
207 error = true;
208 Report.error (source_reference, "This access invalid outside of instance methods");
209 return false;
213 base_symbol = analyzer.current_symbol;
215 var sym = analyzer.current_symbol;
216 while (sym != null && symbol_reference == null) {
217 if (this_parameter == null) {
218 if (sym is CreationMethod) {
219 var cm = (CreationMethod) sym;
220 this_parameter = cm.this_parameter;
221 may_access_instance_members = true;
222 } else if (sym is Property) {
223 var prop = (Property) sym;
224 this_parameter = prop.this_parameter;
225 may_access_instance_members = true;
226 } else if (sym is Constructor) {
227 var c = (Constructor) sym;
228 this_parameter = c.this_parameter;
229 may_access_instance_members = true;
230 } else if (sym is Destructor) {
231 var d = (Destructor) sym;
232 this_parameter = d.this_parameter;
233 may_access_instance_members = true;
234 } else if (sym is Method) {
235 var m = (Method) sym;
236 this_parameter = m.this_parameter;
237 may_access_instance_members = (m.binding == MemberBinding.INSTANCE);
241 symbol_reference = analyzer.symbol_lookup_inherited (sym, member_name);
242 sym = sym.parent_symbol;
245 if (symbol_reference == null) {
246 foreach (UsingDirective ns in analyzer.current_source_file.get_using_directives ()) {
247 var local_sym = ns.namespace_symbol.scope.lookup (member_name);
248 if (local_sym != null) {
249 if (symbol_reference != null) {
250 error = true;
251 Report.error (source_reference, "`%s' is an ambiguous reference between `%s' and `%s'".printf (member_name, symbol_reference.get_full_name (), local_sym.get_full_name ()));
252 return false;
254 symbol_reference = local_sym;
258 } else {
259 if (inner.error) {
260 /* if there was an error in the inner expression, skip this check */
261 error = true;
262 return false;
265 if (pointer_member_access) {
266 var pointer_type = inner.value_type as PointerType;
267 if (pointer_type != null && pointer_type.base_type is ValueType) {
268 // transform foo->bar to (*foo).bar
269 inner = new PointerIndirection (inner, source_reference);
270 inner.check (analyzer);
271 pointer_member_access = false;
275 if (inner is MemberAccess) {
276 var ma = (MemberAccess) inner;
277 if (ma.prototype_access) {
278 error = true;
279 Report.error (source_reference, "Access to instance member `%s' denied".printf (inner.symbol_reference.get_full_name ()));
280 return false;
284 if (inner is MemberAccess || inner is BaseAccess) {
285 base_symbol = inner.symbol_reference;
287 if (symbol_reference == null && (base_symbol is Namespace || base_symbol is TypeSymbol)) {
288 symbol_reference = base_symbol.scope.lookup (member_name);
289 if (inner is BaseAccess) {
290 // inner expression is base access
291 // access to instance members of the base type possible
292 may_access_instance_members = true;
297 if (symbol_reference == null && inner.value_type != null) {
298 if (pointer_member_access) {
299 symbol_reference = inner.value_type.get_pointer_member (member_name);
300 } else {
301 if (inner.value_type.data_type != null) {
302 base_symbol = inner.value_type.data_type;
304 symbol_reference = inner.value_type.get_member (member_name);
306 if (symbol_reference != null) {
307 // inner expression is variable, field, or parameter
308 // access to instance members of the corresponding type possible
309 may_access_instance_members = true;
313 if (symbol_reference == null && inner.value_type != null && inner.value_type.is_dynamic) {
314 // allow late bound members for dynamic types
315 var dynamic_object_type = (ObjectType) inner.value_type;
316 if (parent_node is MethodCall) {
317 var invoc = (MethodCall) parent_node;
318 if (invoc.call == this) {
319 // dynamic method
320 DataType ret_type;
321 if (invoc.target_type != null) {
322 ret_type = invoc.target_type.copy ();
323 ret_type.value_owned = true;
324 } else if (invoc.parent_node is ExpressionStatement) {
325 ret_type = new VoidType ();
326 } else {
327 // expect dynamic object of the same type
328 ret_type = inner.value_type.copy ();
330 var m = new DynamicMethod (inner.value_type, member_name, ret_type, source_reference);
331 m.invocation = invoc;
332 m.add_error_type (new ErrorType (null, null));
333 m.access = SymbolAccessibility.PUBLIC;
334 m.add_parameter (new FormalParameter.with_ellipsis ());
335 dynamic_object_type.type_symbol.scope.add (null, m);
336 symbol_reference = m;
338 } else if (parent_node is Assignment) {
339 var a = (Assignment) parent_node;
340 if (a.left == this
341 && (a.operator == AssignmentOperator.ADD
342 || a.operator == AssignmentOperator.SUB)) {
343 // dynamic signal
344 var s = new DynamicSignal (inner.value_type, member_name, new VoidType (), source_reference);
345 s.handler = a.right;
346 s.access = SymbolAccessibility.PUBLIC;
347 dynamic_object_type.type_symbol.scope.add (null, s);
348 symbol_reference = s;
349 } else if (a.left == this) {
350 // dynamic property assignment
351 var prop = new DynamicProperty (inner.value_type, member_name, source_reference);
352 prop.access = SymbolAccessibility.PUBLIC;
353 prop.set_accessor = new PropertyAccessor (false, true, false, null, prop.source_reference);
354 prop.set_accessor.access = SymbolAccessibility.PUBLIC;
355 prop.owner = inner.value_type.data_type.scope;
356 dynamic_object_type.type_symbol.scope.add (null, prop);
357 symbol_reference = prop;
360 if (symbol_reference == null) {
361 // dynamic property read access
362 var prop = new DynamicProperty (inner.value_type, member_name, source_reference);
363 if (target_type != null) {
364 prop.property_type = target_type;
365 } else {
366 // expect dynamic object of the same type
367 prop.property_type = inner.value_type.copy ();
369 prop.access = SymbolAccessibility.PUBLIC;
370 prop.get_accessor = new PropertyAccessor (true, false, false, null, prop.source_reference);
371 prop.get_accessor.access = SymbolAccessibility.PUBLIC;
372 prop.owner = inner.value_type.data_type.scope;
373 dynamic_object_type.type_symbol.scope.add (null, prop);
374 symbol_reference = prop;
376 if (symbol_reference != null) {
377 may_access_instance_members = true;
382 if (symbol_reference == null) {
383 error = true;
385 string base_type_name = "(null)";
386 if (inner != null && inner.value_type != null) {
387 base_type_name = inner.value_type.to_string ();
388 } else if (base_symbol != null) {
389 base_type_name = base_symbol.get_full_name ();
392 Report.error (source_reference, "The name `%s' does not exist in the context of `%s'".printf (member_name, base_type_name));
393 return false;
396 var member = symbol_reference;
397 var access = SymbolAccessibility.PUBLIC;
398 bool instance = false;
399 bool klass = false;
401 if (!member.check (analyzer)) {
402 return false;
405 if (member is Field) {
406 var f = (Field) member;
407 access = f.access;
408 instance = (f.binding == MemberBinding.INSTANCE);
409 klass = (f.binding == MemberBinding.CLASS);
410 } else if (member is Method) {
411 var m = (Method) member;
412 access = m.access;
413 if (!(m is CreationMethod)) {
414 instance = (m.binding == MemberBinding.INSTANCE);
416 klass = (m.binding == MemberBinding.CLASS);
417 } else if (member is Property) {
418 var prop = (Property) member;
419 if (!prop.check (analyzer)) {
420 error = true;
421 return false;
423 access = prop.access;
424 if (lvalue) {
425 if (prop.set_accessor == null) {
426 error = true;
427 Report.error (source_reference, "Property `%s' is read-only".printf (prop.get_full_name ()));
428 return false;
430 if (prop.access == SymbolAccessibility.PUBLIC) {
431 access = prop.set_accessor.access;
432 } else if (prop.access == SymbolAccessibility.PROTECTED
433 && prop.set_accessor.access != SymbolAccessibility.PUBLIC) {
434 access = prop.set_accessor.access;
436 } else {
437 if (prop.get_accessor == null) {
438 error = true;
439 Report.error (source_reference, "Property `%s' is write-only".printf (prop.get_full_name ()));
440 return false;
442 if (prop.access == SymbolAccessibility.PUBLIC) {
443 access = prop.get_accessor.access;
444 } else if (prop.access == SymbolAccessibility.PROTECTED
445 && prop.get_accessor.access != SymbolAccessibility.PUBLIC) {
446 access = prop.get_accessor.access;
449 instance = (prop.binding == MemberBinding.INSTANCE);
450 } else if (member is Signal) {
451 instance = true;
454 member.used = true;
456 if (access == SymbolAccessibility.PRIVATE) {
457 var target_type = member.parent_symbol;
459 bool in_target_type = false;
460 for (Symbol this_symbol = analyzer.current_symbol; this_symbol != null; this_symbol = this_symbol.parent_symbol) {
461 if (target_type == this_symbol) {
462 in_target_type = true;
463 break;
467 if (!in_target_type) {
468 error = true;
469 Report.error (source_reference, "Access to private member `%s' denied".printf (member.get_full_name ()));
470 return false;
473 if ((instance || klass) && !may_access_instance_members) {
474 prototype_access = true;
476 if (symbol_reference is Method) {
477 // also set static type for prototype access
478 // required when using instance methods as delegates in constants
479 // TODO replace by MethodPrototype
480 value_type = analyzer.get_value_type_for_symbol (symbol_reference, lvalue);
481 } else if (symbol_reference is Field) {
482 value_type = new FieldPrototype ((Field) symbol_reference);
483 } else {
484 value_type = new InvalidType ();
486 } else {
487 // implicit this access
488 if (instance && inner == null) {
489 inner = new MemberAccess (null, "this", source_reference);
490 inner.value_type = this_parameter.parameter_type.copy ();
491 inner.symbol_reference = this_parameter;
494 formal_value_type = analyzer.get_value_type_for_symbol (symbol_reference, lvalue);
495 if (inner != null && formal_value_type != null) {
496 value_type = formal_value_type.get_actual_type (inner.value_type, this);
497 } else {
498 value_type = formal_value_type;
501 if (symbol_reference is Method) {
502 var m = (Method) symbol_reference;
504 Method base_method;
505 if (m.base_method != null) {
506 base_method = m.base_method;
507 } else if (m.base_interface_method != null) {
508 base_method = m.base_interface_method;
509 } else {
510 base_method = m;
513 if (instance && base_method.parent_symbol != null) {
514 inner.target_type = analyzer.get_data_type_for_symbol ((TypeSymbol) base_method.parent_symbol);
516 } else if (symbol_reference is Property) {
517 var prop = (Property) symbol_reference;
519 Property base_property;
520 if (prop.base_property != null) {
521 base_property = prop.base_property;
522 } else if (prop.base_interface_property != null) {
523 base_property = prop.base_interface_property;
524 } else {
525 base_property = prop;
528 if (instance && base_property.parent_symbol != null) {
529 inner.target_type = analyzer.get_data_type_for_symbol ((TypeSymbol) base_property.parent_symbol);
531 } else if ((symbol_reference is Field
532 || symbol_reference is Signal)
533 && instance && symbol_reference.parent_symbol != null) {
534 inner.target_type = analyzer.get_data_type_for_symbol ((TypeSymbol) symbol_reference.parent_symbol);
538 analyzer.current_source_file.add_symbol_dependency (symbol_reference, SourceFileDependencyType.SOURCE);
540 return !error;
543 public override void get_defined_variables (Collection<LocalVariable> collection) {
544 if (inner != null) {
545 inner.get_defined_variables (collection);
549 public override void get_used_variables (Collection<LocalVariable> collection) {
550 if (inner != null) {
551 inner.get_used_variables (collection);
553 var local = symbol_reference as LocalVariable;
554 if (local != null) {
555 collection.add (local);