Line data Source code
1 : /* valacreationmethod.vala
2 : *
3 : * Copyright (C) 2007-2010 Jürg Billeter
4 : * Copyright (C) 2007-2008 Raffaele Sandrini
5 : *
6 : * This library is free software; you can redistribute it and/or
7 : * modify it under the terms of the GNU Lesser General Public
8 : * License as published by the Free Software Foundation; either
9 : * version 2.1 of the License, or (at your option) any later version.
10 :
11 : * This library is distributed in the hope that it will be useful,
12 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 : * Lesser General Public License for more details.
15 :
16 : * You should have received a copy of the GNU Lesser General Public
17 : * License along with this library; if not, write to the Free Software
18 : * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 : *
20 : * Author:
21 : * Raffaele Sandrini <raffaele@sandrini.ch>
22 : * Jürg Billeter <j@bitron.ch>
23 : */
24 :
25 : using GLib;
26 :
27 : /**
28 : * Represents a type creation method.
29 : */
30 1538 : public class Vala.CreationMethod : Method {
31 : /**
32 : * Specifies the name of the type this creation method belongs to.
33 : */
34 2832087 : public string class_name { get; set; }
35 :
36 : /**
37 : * Specifies whether this constructor chains up to a base
38 : * constructor or a different constructor of the same class.
39 : */
40 4568 : public bool chain_up { get; set; }
41 :
42 : /**
43 : * Creates a new method.
44 : *
45 : * @param name method name
46 : * @param source_reference reference to source code
47 : * @return newly created method
48 : */
49 566546 : public CreationMethod (string? class_name, string? name, SourceReference? source_reference = null, Comment? comment = null) {
50 566546 : base (name, new VoidType (), source_reference, comment);
51 566546 : this.class_name = class_name;
52 : }
53 :
54 1244520 : public override void accept (CodeVisitor visitor) {
55 1244520 : visitor.visit_creation_method (this);
56 : }
57 :
58 884400 : public override void accept_children (CodeVisitor visitor) {
59 3938296 : foreach (Parameter param in get_parameters()) {
60 1526948 : param.accept (visitor);
61 : }
62 :
63 884400 : if (error_types != null) {
64 287019 : foreach (DataType error_type in error_types) {
65 95673 : error_type.accept (visitor);
66 : }
67 : }
68 :
69 884412 : foreach (Expression precondition in get_preconditions ()) {
70 6 : precondition.accept (visitor);
71 : }
72 :
73 884412 : foreach (Expression postcondition in get_postconditions ()) {
74 6 : postcondition.accept (visitor);
75 : }
76 :
77 884400 : if (body != null) {
78 6435 : body.accept (visitor);
79 : }
80 : }
81 :
82 532664 : public override bool check (CodeContext context) {
83 532664 : if (checked) {
84 7398 : return !error;
85 : }
86 :
87 525266 : checked = true;
88 :
89 525266 : if (class_name != null && class_name != parent_symbol.name) {
90 : // class_name is null for constructors generated by GIdlParser
91 0 : Report.error (source_reference, "missing return type in method `%s.%s´", context.analyzer.current_symbol.get_full_name (), class_name);
92 0 : error = true;
93 0 : return false;
94 : }
95 :
96 525266 : if (this_parameter != null) {
97 525266 : this_parameter.check (context);
98 : }
99 :
100 525266 : if (coroutine && !external_package && !context.has_package ("gio-2.0")) {
101 0 : error = true;
102 0 : Report.error (source_reference, "gio-2.0 package required for async constructors");
103 0 : return false;
104 : }
105 :
106 525266 : var old_source_file = context.analyzer.current_source_file;
107 526985 : var old_symbol = context.analyzer.current_symbol;
108 :
109 525266 : if (source_reference != null) {
110 525266 : context.analyzer.current_source_file = source_reference.file;
111 : }
112 525266 : context.analyzer.current_symbol = this;
113 :
114 525266 : int i = 0;
115 2342275 : foreach (Parameter param in get_parameters()) {
116 908505 : if (!param.check (context)) {
117 1 : error = true;
118 : }
119 908505 : if (i == 0 && param.ellipsis && body != null) {
120 1 : error = true;
121 1 : Report.error (param.source_reference, "Named parameter required before `...'");
122 : }
123 908505 : i++;
124 :
125 : // Add local variable to provide access to params arrays which will be constructed out of the given va-args
126 908513 : if (param.params_array && body != null) {
127 9 : if (params_array_var != null) {
128 1 : error = true;
129 1 : Report.error (param.source_reference, "Only one params-array parameter is allowed");
130 1 : continue;
131 : }
132 8 : if (!context.experimental) {
133 8 : Report.warning (param.source_reference, "Support of params-arrays is experimental");
134 : }
135 8 : var type = (ArrayType) param.variable_type.copy ();
136 8 : type.element_type.value_owned = type.value_owned;
137 8 : type.value_owned = true;
138 8 : if (type.element_type.is_real_struct_type () && !type.element_type.nullable) {
139 1 : error = true;
140 1 : Report.error (param.source_reference, "Only nullable struct elements are supported in params-array");
141 : }
142 8 : if (type.length != null) {
143 1 : error = true;
144 1 : Report.error (param.source_reference, "Passing length to params-array is not supported yet");
145 : }
146 8 : params_array_var = new LocalVariable (type, param.name, null, param.source_reference);
147 8 : body.insert_statement (0, new DeclarationStatement (params_array_var, param.source_reference));
148 : }
149 : }
150 :
151 525266 : if (error_types != null) {
152 169720 : foreach (DataType error_type in error_types) {
153 56574 : error_type.check (context);
154 :
155 : // check whether error type is at least as accessible as the creation method
156 56574 : if (!error_type.is_accessible (this)) {
157 1 : error = true;
158 1 : Report.error (source_reference, "error type `%s' is less accessible than creation method `%s'", error_type.to_string (), get_full_name ());
159 1 : return false;
160 : }
161 : }
162 : }
163 :
164 525269 : foreach (Expression precondition in get_preconditions ()) {
165 2 : precondition.check (context);
166 : }
167 :
168 525269 : foreach (Expression postcondition in get_postconditions ()) {
169 2 : postcondition.check (context);
170 : }
171 :
172 525265 : if (body != null) {
173 2604 : body.check (context);
174 :
175 2604 : unowned Class? cl = parent_symbol as Class;
176 :
177 : // ensure we chain up to base constructor
178 2604 : if (!chain_up && cl != null && cl.base_class != null) {
179 592 : if (context.profile == Profile.GOBJECT
180 575 : && cl.base_class.default_construction_method != null
181 575 : && !cl.base_class.default_construction_method.has_construct_function) {
182 : // directly chain up to Object
183 17 : var old_insert_block = context.analyzer.insert_block;
184 17 : context.analyzer.current_symbol = body;
185 34 : context.analyzer.insert_block = body;
186 :
187 17 : var stmt = new ExpressionStatement (new MethodCall (new MemberAccess (new MemberAccess.simple ("GLib", source_reference), "Object", source_reference), source_reference), source_reference);
188 17 : body.insert_statement (0, stmt);
189 17 : stmt.check (context);
190 :
191 17 : context.analyzer.current_symbol = this;
192 17 : context.analyzer.insert_block = old_insert_block;
193 558 : } else if (cl.base_class.default_construction_method == null
194 558 : || cl.base_class.default_construction_method.access == SymbolAccessibility.PRIVATE) {
195 1 : Report.error (source_reference, "unable to chain up to private base constructor");
196 1113 : } else if (cl.base_class.default_construction_method.get_required_arguments () > 0) {
197 1 : Report.error (source_reference, "unable to chain up to base constructor requiring arguments");
198 : } else {
199 556 : var old_insert_block = context.analyzer.insert_block;
200 556 : context.analyzer.current_symbol = body;
201 1112 : context.analyzer.insert_block = body;
202 :
203 556 : var base_call = new MethodCall (new BaseAccess (source_reference), source_reference);
204 556 : if (coroutine && cl.base_class.default_construction_method.coroutine) {
205 0 : base_call.is_yield_expression = true;
206 : }
207 556 : var stmt = new ExpressionStatement (base_call, source_reference);
208 556 : body.insert_statement (0, stmt);
209 556 : stmt.check (context);
210 :
211 556 : context.analyzer.current_symbol = this;
212 653 : context.analyzer.insert_block = old_insert_block;
213 : }
214 : }
215 : }
216 :
217 525265 : context.analyzer.current_source_file = old_source_file;
218 525265 : context.analyzer.current_symbol = old_symbol;
219 :
220 : // check that all errors that can be thrown in the method body are declared
221 527863 : if (body != null && !body.error) {
222 2598 : var body_errors = new ArrayList<DataType> ();
223 2598 : body.get_error_types (body_errors);
224 5442 : foreach (DataType body_error_type in body_errors) {
225 1422 : bool can_propagate_error = false;
226 1422 : if (error_types != null) {
227 4263 : foreach (DataType method_error_type in error_types) {
228 1421 : if (body_error_type.compatible (method_error_type)) {
229 1421 : can_propagate_error = true;
230 : }
231 : }
232 : }
233 1422 : if (!can_propagate_error && !((ErrorType) body_error_type).dynamic_error) {
234 1 : Report.warning (body_error_type.source_reference, "unhandled error `%s'", body_error_type.to_string());
235 : }
236 : }
237 : }
238 :
239 525265 : return !error;
240 : }
241 : }
|