Insert "%s" argument in printf calls with non-literal format string
[vala-lang.git] / vala / valabinaryexpression.vala
blob54f58906af6ef6dd1714ecc6a3fb3b992a30c109
1 /* valabinaryexpression.vala
3 * Copyright (C) 2006-2009 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 Gee;
25 /**
26 * Represents an expression with two operands in the source code.
28 * Supports +, -, *, /, %, <<, >>, <, >, <=, >=, ==, !=, &, |, ^, &&, ||.
30 public class Vala.BinaryExpression : Expression {
31 /**
32 * The binary operator.
34 public BinaryOperator operator { get; set; }
36 /**
37 * The left operand.
39 public Expression left {
40 get {
41 return _left;
43 set {
44 _left = value;
45 _left.parent_node = this;
49 /**
50 * The right operand.
52 public Expression right {
53 get {
54 return _right;
56 set {
57 _right = value;
58 _right.parent_node = this;
62 private Expression _left;
63 private Expression _right;
65 /**
66 * Creates a new binary expression.
68 * @param op binary operator
69 * @param left left operand
70 * @param right right operand
71 * @param source reference to source code
72 * @return newly created binary expression
74 public BinaryExpression (BinaryOperator op, Expression _left, Expression _right, SourceReference? source = null) {
75 operator = op;
76 left = _left;
77 right = _right;
78 source_reference = source;
81 public override void accept (CodeVisitor visitor) {
82 visitor.visit_binary_expression (this);
84 visitor.visit_expression (this);
87 public override void accept_children (CodeVisitor visitor) {
88 left.accept (visitor);
89 right.accept (visitor);
92 public override void replace_expression (Expression old_node, Expression new_node) {
93 if (left == old_node) {
94 left = new_node;
96 if (right == old_node) {
97 right = new_node;
101 private string get_operator_string () {
102 switch (_operator) {
103 case BinaryOperator.PLUS: return "+";
104 case BinaryOperator.MINUS: return "-";
105 case BinaryOperator.MUL: return "*";
106 case BinaryOperator.DIV: return "/";
107 case BinaryOperator.MOD: return "%";
108 case BinaryOperator.SHIFT_LEFT: return "<<";
109 case BinaryOperator.SHIFT_RIGHT: return ">>";
110 case BinaryOperator.LESS_THAN: return "<";
111 case BinaryOperator.GREATER_THAN: return ">";
112 case BinaryOperator.LESS_THAN_OR_EQUAL: return "<=";
113 case BinaryOperator.GREATER_THAN_OR_EQUAL: return ">=";
114 case BinaryOperator.EQUALITY: return "==";
115 case BinaryOperator.INEQUALITY: return "!+";
116 case BinaryOperator.BITWISE_AND: return "&";
117 case BinaryOperator.BITWISE_OR: return "|";
118 case BinaryOperator.BITWISE_XOR: return "^";
119 case BinaryOperator.AND: return "&&";
120 case BinaryOperator.OR: return "||";
121 case BinaryOperator.IN: return "in";
124 assert_not_reached ();
127 public override string to_string () {
128 return _left.to_string () + get_operator_string () + _right.to_string ();
131 public override bool is_constant () {
132 return left.is_constant () && right.is_constant ();
135 public override bool is_pure () {
136 return left.is_pure () && right.is_pure ();
139 public override bool is_non_null () {
140 return left.is_non_null () && right.is_non_null ();
143 public override bool check (SemanticAnalyzer analyzer) {
144 if (checked) {
145 return !error;
148 checked = true;
150 // some expressions are not in a block,
151 // for example, expressions in method contracts
152 if (analyzer.current_symbol is Block
153 && (operator == BinaryOperator.AND || operator == BinaryOperator.OR)) {
154 // convert conditional expression into if statement
155 // required for flow analysis and exception handling
157 var local = new LocalVariable (analyzer.bool_type.copy (), get_temp_name (), null, source_reference);
158 var decl = new DeclarationStatement (local, source_reference);
159 decl.check (analyzer);
161 var right_stmt = new ExpressionStatement (new Assignment (new MemberAccess.simple (local.name, right.source_reference), right, AssignmentOperator.SIMPLE, right.source_reference), right.source_reference);
163 var stmt = new ExpressionStatement (new Assignment (new MemberAccess.simple (local.name, left.source_reference), new BooleanLiteral ((operator == BinaryOperator.OR), left.source_reference), AssignmentOperator.SIMPLE, left.source_reference), left.source_reference);
165 var true_block = new Block (source_reference);
166 var false_block = new Block (source_reference);
168 if (operator == BinaryOperator.AND) {
169 true_block.add_statement (right_stmt);
170 false_block.add_statement (stmt);
171 } else {
172 true_block.add_statement (stmt);
173 false_block.add_statement (right_stmt);
176 var if_stmt = new IfStatement (left, true_block, false_block, source_reference);
178 insert_statement (analyzer.insert_block, decl);
179 insert_statement (analyzer.insert_block, if_stmt);
181 if (!if_stmt.check (analyzer)) {
182 error = true;
183 return false;
186 var ma = new MemberAccess.simple (local.name, source_reference);
187 ma.target_type = target_type;
188 ma.check (analyzer);
190 parent_node.replace_expression (this, ma);
192 return true;
195 if (!left.check (analyzer) || !right.check (analyzer)) {
196 /* if there were any errors in inner expressions, skip type check */
197 error = true;
198 return false;
201 if (left.value_type == null) {
202 Report.error (left.source_reference, "invalid left operand");
203 error = true;
204 return false;
207 if (operator != BinaryOperator.IN && right.value_type == null) {
208 Report.error (right.source_reference, "invalid right operand");
209 error = true;
210 return false;
213 if (left.value_type.data_type == analyzer.string_type.data_type
214 && operator == BinaryOperator.PLUS) {
215 // string concatenation
217 if (right.value_type == null || right.value_type.data_type != analyzer.string_type.data_type) {
218 error = true;
219 Report.error (source_reference, "Operands must be strings");
220 return false;
223 value_type = analyzer.string_type.copy ();
224 if (left.is_constant () && right.is_constant ()) {
225 value_type.value_owned = false;
226 } else {
227 value_type.value_owned = true;
229 } else if (left.value_type is ArrayType && operator == BinaryOperator.PLUS) {
230 // array concatenation
232 var array_type = (ArrayType) left.value_type;
234 if (right.value_type == null || !right.value_type.compatible (array_type.element_type)) {
235 error = true;
236 Report.error (source_reference, "Incompatible operand");
237 return false;
240 value_type = array_type.copy ();
241 value_type.value_owned = true;
242 } else if (operator == BinaryOperator.PLUS
243 || operator == BinaryOperator.MINUS
244 || operator == BinaryOperator.MUL
245 || operator == BinaryOperator.DIV) {
246 // check for pointer arithmetic
247 if (left.value_type is PointerType) {
248 var offset_type = right.value_type.data_type as Struct;
249 if (offset_type != null && offset_type.is_integer_type ()) {
250 if (operator == BinaryOperator.PLUS
251 || operator == BinaryOperator.MINUS) {
252 // pointer arithmetic: pointer +/- offset
253 value_type = left.value_type.copy ();
255 } else if (right.value_type is PointerType) {
256 // pointer arithmetic: pointer - pointer
257 value_type = analyzer.size_t_type;
261 if (value_type == null) {
262 value_type = analyzer.get_arithmetic_result_type (left.value_type, right.value_type);
265 if (value_type == null) {
266 error = true;
267 Report.error (source_reference, "Arithmetic operation not supported for types `%s' and `%s'".printf (left.value_type.to_string (), right.value_type.to_string ()));
268 return false;
270 } else if (operator == BinaryOperator.MOD
271 || operator == BinaryOperator.SHIFT_LEFT
272 || operator == BinaryOperator.SHIFT_RIGHT
273 || operator == BinaryOperator.BITWISE_XOR) {
274 value_type = analyzer.get_arithmetic_result_type (left.value_type, right.value_type);
276 if (value_type == null) {
277 error = true;
278 Report.error (source_reference, "Arithmetic operation not supported for types `%s' and `%s'".printf (left.value_type.to_string (), right.value_type.to_string ()));
279 return false;
281 } else if (operator == BinaryOperator.LESS_THAN
282 || operator == BinaryOperator.GREATER_THAN
283 || operator == BinaryOperator.LESS_THAN_OR_EQUAL
284 || operator == BinaryOperator.GREATER_THAN_OR_EQUAL) {
285 if (left.value_type.compatible (analyzer.string_type)
286 && right.value_type.compatible (analyzer.string_type)) {
287 // string comparison
288 } else if (left.value_type is PointerType && right.value_type is PointerType) {
289 // pointer arithmetic
290 } else {
291 var resulting_type = analyzer.get_arithmetic_result_type (left.value_type, right.value_type);
293 if (resulting_type == null) {
294 error = true;
295 Report.error (source_reference, "Relational operation not supported for types `%s' and `%s'".printf (left.value_type.to_string (), right.value_type.to_string ()));
296 return false;
300 value_type = analyzer.bool_type;
301 } else if (operator == BinaryOperator.EQUALITY
302 || operator == BinaryOperator.INEQUALITY) {
303 /* relational operation */
305 if (!right.value_type.compatible (left.value_type)
306 && !left.value_type.compatible (right.value_type)) {
307 Report.error (source_reference, "Equality operation: `%s' and `%s' are incompatible".printf (right.value_type.to_string (), left.value_type.to_string ()));
308 error = true;
309 return false;
312 if (left.value_type.compatible (analyzer.string_type)
313 && right.value_type.compatible (analyzer.string_type)) {
314 // string comparison
317 value_type = analyzer.bool_type;
318 } else if (operator == BinaryOperator.BITWISE_AND
319 || operator == BinaryOperator.BITWISE_OR) {
320 // integer type or flags type
322 value_type = left.value_type;
323 } else if (operator == BinaryOperator.AND
324 || operator == BinaryOperator.OR) {
325 if (!left.value_type.compatible (analyzer.bool_type) || !right.value_type.compatible (analyzer.bool_type)) {
326 error = true;
327 Report.error (source_reference, "Operands must be boolean");
330 value_type = analyzer.bool_type;
331 } else if (operator == BinaryOperator.IN) {
332 if (left.value_type.compatible (analyzer.int_type)
333 && right.value_type.compatible (analyzer.int_type)) {
334 // integers or enums
335 } else {
336 // otherwise require a bool contains () method
337 var contains_method = right.value_type.get_member ("contains") as Method;
338 if (contains_method == null) {
339 Report.error (source_reference, "`%s' does not have a `contains' method".printf (right.value_type.to_string ()));
340 error = true;
341 return false;
343 if (contains_method.get_parameters ().size != 1) {
344 Report.error (source_reference, "`%s' must have one parameter".printf (contains_method.get_full_name ()));
345 error = true;
346 return false;
348 if (!contains_method.return_type.compatible (analyzer.bool_type)) {
349 Report.error (source_reference, "`%s' must return a boolean value".printf (contains_method.get_full_name ()));
350 error = true;
351 return false;
354 var contains_call = new MethodCall (new MemberAccess (right, "contains"));
355 contains_call.add_argument (left);
356 parent_node.replace_expression (this, contains_call);
357 return contains_call.check (analyzer);
360 value_type = analyzer.bool_type;
362 } else {
363 assert_not_reached ();
366 return !error;
369 public override void get_defined_variables (Collection<LocalVariable> collection) {
370 left.get_defined_variables (collection);
371 right.get_defined_variables (collection);
374 public override void get_used_variables (Collection<LocalVariable> collection) {
375 left.get_used_variables (collection);
376 right.get_used_variables (collection);
380 public enum Vala.BinaryOperator {
381 NONE,
382 PLUS,
383 MINUS,
384 MUL,
385 DIV,
386 MOD,
387 SHIFT_LEFT,
388 SHIFT_RIGHT,
389 LESS_THAN,
390 GREATER_THAN,
391 LESS_THAN_OR_EQUAL,
392 GREATER_THAN_OR_EQUAL,
393 EQUALITY,
394 INEQUALITY,
395 BITWISE_AND,
396 BITWISE_OR,
397 BITWISE_XOR,
398 AND,