Commit c6fa0202 authored by Jürg Billeter's avatar Jürg Billeter Committed by Jürg Billeter

check whether specified collection is iterable in foreach statements,

2007-04-30  Jürg Billeter  <j@bitron.ch>

	* vala/valasemanticanalyzer.vala: check whether specified collection is
	  iterable in foreach statements, fixes bug 434514

svn path=/trunk/; revision=294
parent 06ef9db4
2007-04-30 Jürg Billeter <j@bitron.ch>
* vala/valasemanticanalyzer.vala: check whether specified collection is
iterable in foreach statements, fixes bug 434514
2007-04-30 Jürg Billeter <j@bitron.ch>
* vala/valasemanticanalyzer.vala: allow any integer type as index in an
......
......@@ -31,16 +31,16 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
* Specifies whether automatic memory management is active.
*/
public bool memory_management { get; set; }
Symbol root_symbol;
Symbol current_symbol;
SourceFile current_source_file;
TypeReference current_return_type;
Class current_class;
Struct current_struct;
List<weak NamespaceReference> current_using_directives;
TypeReference bool_type;
TypeReference string_type;
TypeReference int_type;
......@@ -48,13 +48,15 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
TypeReference type_type;
DataType pointer_type;
DataType initially_unowned_type;
DataType glist_type;
DataType gslist_type;
private int next_lambda_id = 0;
public SemanticAnalyzer (bool manage_memory = true) {
memory_management = manage_memory;
}
/**
* Analyze and check code in the specified context.
*
......@@ -70,30 +72,33 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
string_type.data_type = (DataType) root_symbol.lookup ("string").node;
pointer_type = (DataType) root_symbol.lookup ("pointer").node;
int_type = new TypeReference ();
int_type.data_type = (DataType) root_symbol.lookup ("int").node;
uint_type = new TypeReference ();
uint_type.data_type = (DataType) root_symbol.lookup ("uint").node;
// TODO: don't require GLib namespace in semantic analyzer
var glib_ns = root_symbol.lookup ("GLib");
if (glib_ns != null) {
initially_unowned_type = (DataType) glib_ns.lookup ("InitiallyUnowned").node;
type_type = new TypeReference ();
type_type.data_type = (DataType) glib_ns.lookup ("Type").node;
glist_type = (DataType) glib_ns.lookup ("List").node;
gslist_type = (DataType) glib_ns.lookup ("SList").node;
}
current_symbol = root_symbol;
context.accept (this);
}
public override void visit_begin_source_file (SourceFile! file) {
current_source_file = file;
current_using_directives = file.get_using_directives ();
next_lambda_id = 0;
}
......@@ -112,16 +117,16 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
public override void visit_begin_class (Class! cl) {
current_symbol = cl.symbol;
current_class = cl;
if (cl.base_class != null) {
current_source_file.add_symbol_dependency (cl.base_class.symbol, SourceFileDependencyType.HEADER_FULL);
}
foreach (TypeReference base_type_reference in cl.get_base_types ()) {
current_source_file.add_symbol_dependency (base_type_reference.data_type.symbol, SourceFileDependencyType.HEADER_FULL);
}
}
private ref List<DataType> get_all_prerequisites (Interface! iface) {
List<DataType> ret = null;
......@@ -135,19 +140,19 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
ret.prepend (type);
if (type is Interface) {
ret.concat (get_all_prerequisites ((Interface) type));
}
}
ret.reverse ();
return #ret;
}
private bool class_is_a (Class! cl, DataType! t) {
if (cl == t) {
return true;
}
foreach (TypeReference base_type in cl.get_base_types ()) {
if (base_type.data_type is Class) {
if (class_is_a ((Class) base_type.data_type, t)) {
......@@ -160,7 +165,7 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
return false;
}
public override void visit_end_class (Class! cl) {
/* gather all prerequisites */
List<DataType> prerequisites = null;
......@@ -179,7 +184,7 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
/* report any missing prerequisites */
if (missing_prereqs != null) {
cl.error = true;
string error_string = "%s: some prerequisites (".printf (cl.symbol.get_full_name ());
bool first = true;
foreach (string s in missing_prereqs) {
......@@ -193,7 +198,7 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
error_string += ") are not met";
Report.error (cl.source_reference, error_string);
}
/* all abstract symbols defined in base types have to be at least defined (or implemented) also in this type */
foreach (TypeReference base_type in cl.get_base_types ()) {
if (base_type.data_type is Interface) {
......@@ -234,7 +239,7 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
base_class = base_class.base_class;
}
}
current_symbol = current_symbol.parent_symbol;
current_class = null;
}
......@@ -251,7 +256,7 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
public override void visit_begin_interface (Interface! iface) {
current_symbol = iface.symbol;
foreach (TypeReference prerequisite_reference in iface.get_prerequisites ()) {
current_source_file.add_symbol_dependency (prerequisite_reference.data_type.symbol, SourceFileDependencyType.HEADER_FULL);
}
......@@ -274,14 +279,14 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
Report.error (iface.source_reference, "%s: Interfaces cannot have multiple instantiable prerequisites (`%s' and `%s')".printf (iface.symbol.get_full_name (), class_or_interface.symbol.get_full_name (), prereq_class.symbol.get_full_name ()));
return;
}
prereq_class = (Class)class_or_interface;
}
}
current_symbol = current_symbol.parent_symbol;
}
public override void visit_constant (Constant! c) {
if (!current_source_file.pkg) {
if (c.initializer == null) {
......@@ -305,7 +310,7 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
}
}
public override void visit_begin_method (Method! m) {
public override void visit_begin_method (Method! m) {
current_symbol = m.symbol;
current_return_type = m.return_type;
......@@ -330,7 +335,7 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
Report.error (m.source_reference, "Return type and/or parameters of overriding method `%s' do not match overridden method `%s'.".printf (m.symbol.get_full_name (), base_method.symbol.get_full_name ()));
return;
}
m.base_method = base_method;
return;
}
......@@ -354,7 +359,7 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
Report.error (m.source_reference, "Return type and/or parameters of overriding method `%s' do not match overridden method `%s'.".printf (m.symbol.get_full_name (), base_method.symbol.get_full_name ()));
return;
}
m.base_interface_method = base_method;
return;
}
......@@ -373,7 +378,7 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
var up_method = (Method) current_symbol.parent_symbol.node;
current_return_type = up_method.return_type;
}
if (current_symbol.node is Class) {
if (!(m is CreationMethod)) {
find_base_interface_method (m, (Class) current_symbol.node);
......@@ -391,12 +396,12 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
}
}
}
public override void visit_begin_creation_method (CreationMethod! m) {
m.return_type = new TypeReference ();
m.return_type.data_type = (DataType) current_symbol.node;
m.return_type.transfers_ownership = true;
if (current_symbol.node is Class) {
// check for floating reference
var cl = (Class) current_symbol.node;
......@@ -405,18 +410,18 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
m.return_type.floating_reference = true;
break;
}
cl = cl.base_class;
}
}
current_symbol = m.symbol;
current_return_type = m.return_type;
}
public override void visit_end_creation_method (CreationMethod! m) {
visit_end_method (m);
if (m.body != null && current_class != null) {
int n_params = 0;
foreach (Statement stmt in m.body.get_statements ()) {
......@@ -440,7 +445,7 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
current_source_file.add_symbol_dependency (p.type_reference.data_type.symbol, SourceFileDependencyType.SOURCE);
}
}
/* special treatment for construct formal parameters used in creation methods */
if (p.construct_parameter) {
if (!(p.symbol.parent_symbol.node is CreationMethod)) {
......@@ -448,11 +453,11 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
Report.error (p.source_reference, "construct parameters are only allowed in type creation methods");
return;
}
var method_body = ((CreationMethod)p.symbol.parent_symbol.node).body;
var left = new MemberAccess.simple (p.name);
var right = new MemberAccess.simple (p.name);
/* try to lookup the requested property */
var prop_sym = symbol_lookup_inherited (current_class.symbol, p.name);
if (!(prop_sym.node is Property)) {
......@@ -461,9 +466,9 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
return;
}
left.symbol_reference = prop_sym;
right.symbol_reference = p.symbol;
method_body.add_statement (new ExpressionStatement (new Assignment (left, right)));
}
}
......@@ -533,7 +538,7 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
public override void visit_begin_property_accessor (PropertyAccessor! acc) {
var prop = (Property) acc.symbol.parent_symbol.node;
if (acc.readable) {
current_return_type = prop.type_reference;
} else {
......@@ -573,14 +578,14 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
foreach (VariableDeclarator decl in b.get_local_variables ()) {
decl.symbol.active = false;
}
current_symbol = current_symbol.parent_symbol;
}
public override void visit_variable_declarator (VariableDeclarator! decl) {
if (decl.type_reference == null) {
/* var type */
if (decl.initializer == null) {
decl.error = true;
Report.error (decl.source_reference, "var declaration not allowed without initializer");
......@@ -591,12 +596,12 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
Report.error (decl.source_reference, "var declaration not allowed with non-typed initializer");
return;
}
decl.type_reference = decl.initializer.static_type.copy ();
decl.type_reference.takes_ownership = (decl.type_reference.data_type == null || decl.type_reference.data_type.is_reference_type ());
decl.type_reference.transfers_ownership = false;
}
if (decl.initializer != null) {
if (decl.initializer.static_type == null) {
if (!(decl.initializer is MemberAccess)) {
......@@ -604,19 +609,19 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
Report.error (decl.source_reference, "expression type not allowed as initializer");
return;
}
if (decl.initializer.symbol_reference.node is Method &&
decl.type_reference.data_type is Callback) {
var m = (Method) decl.initializer.symbol_reference.node;
var cb = (Callback) decl.type_reference.data_type;
/* check whether method matches callback type */
if (!cb.matches_method (m)) {
decl.error = true;
Report.error (decl.source_reference, "declaration of method `%s' doesn't match declaration of callback `%s'".printf (m.symbol.get_full_name (), cb.symbol.get_full_name ()));
return;
}
decl.initializer.static_type = decl.type_reference;
} else {
decl.error = true;
......@@ -624,7 +629,7 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
return;
}
}
if (memory_management) {
if (decl.initializer.static_type.transfers_ownership) {
/* rhs transfers ownership of the expression */
......@@ -637,20 +642,20 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
}
}
}
if (decl.type_reference.data_type != null) {
current_source_file.add_symbol_dependency (decl.type_reference.data_type.symbol, SourceFileDependencyType.SOURCE);
}
decl.symbol = new Symbol (decl);
current_symbol.add (decl.name, decl.symbol);
var block = (Block) current_symbol.node;
block.add_local_variable (decl);
decl.symbol.active = true;
}
/**
* Visit operation called for initializer lists
*
......@@ -663,19 +668,19 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
var inits = list.get_initializers ();
int rank = ((Array)list.expected_type.data_type).rank;
var child_type = list.expected_type.copy ();
if (rank > 1) {
child_type.data_type = edt.element_type.get_array (rank - 1);
} else {
child_type.data_type = edt.element_type;
}
foreach (Expression e in inits) {
e.expected_type = child_type.copy ();
}
}
}
/**
* Visit operation called for initializer lists
*
......@@ -688,7 +693,7 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
int rank = edt.rank;
var child_type = list.expected_type.copy ();
bool error = false;
if (rank > 1) {
child_type.data_type = edt.element_type.get_array (rank - 1);
foreach (Expression e in inits) {
......@@ -722,7 +727,7 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
}
}
}
if (!error) {
/* everything seems to be correct */
list.static_type = list.expected_type;
......@@ -744,7 +749,7 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
stmt.error = true;
return;
}
if (stmt.condition.static_type.data_type != bool_type.data_type) {
stmt.error = true;
Report.error (stmt.condition.source_reference, "Condition must be boolean");
......@@ -772,38 +777,49 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
if (stmt.type_reference.data_type != null) {
current_source_file.add_symbol_dependency (stmt.type_reference.data_type.symbol, SourceFileDependencyType.SOURCE);
}
stmt.variable_declarator = new VariableDeclarator (stmt.variable_name);
stmt.variable_declarator.type_reference = stmt.type_reference;
stmt.variable_declarator.symbol = new Symbol (stmt.variable_declarator);
stmt.body.symbol.add (stmt.variable_name, stmt.variable_declarator.symbol);
}
public override void visit_end_foreach_statement (ForeachStatement! stmt) {
var collection_type = stmt.collection.static_type.data_type;
if (!(collection_type is Array || collection_type == glist_type || collection_type == gslist_type)) {
stmt.error = true;
Report.error (stmt.source_reference, "Collection not iterable");
return;
}
}
public override void visit_end_return_statement (ReturnStatement! stmt) {
if (current_return_type == null) {
stmt.error = true;
Report.error (stmt.source_reference, "Return not allowed in this context");
return;
}
if (stmt.return_expression == null && current_return_type.data_type != null) {
stmt.error = true;
Report.error (stmt.source_reference, "Return without value in non-void function");
return;
}
if (stmt.return_expression != null &&
current_return_type.data_type == null &&
current_return_type.type_parameter == null) {
Report.error (stmt.source_reference, "Return with value in void function");
return;
}
if (stmt.return_expression != null &&
!is_type_compatible (stmt.return_expression.static_type, current_return_type)) {
Report.error (stmt.source_reference, "Return: Cannot convert from `%s' to `%s'".printf (stmt.return_expression.static_type.to_string (), current_return_type.to_string ()));
return;
}
if (stmt.return_expression != null &&
stmt.return_expression.static_type.transfers_ownership &&
!current_return_type.transfers_ownership) {
......@@ -811,7 +827,7 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
Report.error (stmt.source_reference, "Return value transfers ownership but method return type hasn't been declared to transfer ownership");
return;
}
if (stmt.return_expression != null &&
stmt.return_expression.symbol_reference != null &&
stmt.return_expression.symbol_reference.node is VariableDeclarator &&
......@@ -820,7 +836,7 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
Report.warning (stmt.source_reference, "Local variable with strong reference used as return value and method return type hasn't been declared to transfer ownership");
}
}
/**
* Visit operation called for lock statements.
*
......@@ -834,17 +850,17 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
Report.error (stmt.resource.source_reference, "Expression is either not a member access or does not denote a lockable member");
return;
}
/* parent symbol must be the current class */
if (stmt.resource.symbol_reference.parent_symbol.node != current_class) {
stmt.error = true;
stmt.resource.error = true;
Report.error (stmt.resource.source_reference, "Only members of the current class are lockable");
}
((Lockable)stmt.resource.symbol_reference.node).set_lock_used (true);
}
public override void visit_begin_array_creation_expression (ArrayCreationExpression! expr) {
if (expr.initializer_list != null) {
expr.initializer_list.expected_type = expr.element_type.copy ();
......@@ -852,7 +868,7 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
// FIXME: add element type to type_argument
}
}
/**
* Visit operations called for array creation expresions.
*
......@@ -861,7 +877,7 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
public override void visit_end_array_creation_expression (ArrayCreationExpression! expr) {
int i;
List<weak Expression> size = expr.get_sizes ();
/* check for errors in the size list */
if (size != null) {
foreach (Expression e in size) {
......@@ -873,17 +889,17 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
Report.error (e.source_reference, "Expression of integer type expected");
}
}
if (expr.error) {
return;
}
}
/* check for wrong elements inside the initializer */
if (expr.initializer_list != null && expr.initializer_list.static_type == null) {
return;
}
/* try to construct the type of the array */
if (expr.element_type == null) {
expr.error = true;
......@@ -899,9 +915,9 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
}
expr.static_type.transfers_ownership = true;
expr.static_type.takes_ownership = true;
expr.static_type.add_type_argument (expr.element_type);
}
}
public override void visit_boolean_literal (BooleanLiteral! expr) {
expr.static_type = bool_type;
......@@ -929,14 +945,14 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
public override void visit_null_literal (NullLiteral! expr) {
/* empty TypeReference represents null */
expr.static_type = new TypeReference ();
}
public override void visit_literal_expression (LiteralExpression! expr) {
expr.static_type = expr.literal.static_type;
}
ref TypeReference get_static_type_for_node (CodeNode! node) {
if (node is Field) {
var f = (Field) node;
......@@ -964,7 +980,7 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
}
return null;
}
public static Symbol symbol_lookup_inherited (Symbol! sym, string! name) {
var result = sym.lookup (name);
if (result != null) {
......@@ -1021,7 +1037,7 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
if (expr.inner == null) {
base_symbol = current_symbol;
var sym = current_symbol;
while (sym != null && expr.symbol_reference == null) {
expr.symbol_reference = symbol_lookup_inherited (sym, expr.member_name);
......@@ -1047,7 +1063,7 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
expr.error = true;
return;
}
if (expr.inner is MemberAccess || expr.inner is BaseAccess) {
base_symbol = expr.inner.symbol_reference;
if (base_symbol.node is Namespace ||
......@@ -1055,19 +1071,19 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
expr.symbol_reference = base_symbol.lookup (expr.member_name);
}
}
if (expr.symbol_reference == null && expr.inner.static_type != null) {
base_symbol = expr.inner.static_type.data_type.symbol;
expr.symbol_reference = symbol_lookup_inherited (base_symbol, expr.member_name);
}
}
if (expr.symbol_reference == null) {
expr.error = true;
Report.error (expr.source_reference, "The name `%s' does not exist in the context of `%s'".printf (expr.member_name, base_symbol.get_full_name ()));
return;
}
var member = expr.symbol_reference.node;
MemberAccessibility access = MemberAccessibility.PUBLIC;
if (member is Field) {
......@@ -1075,23 +1091,23 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
} else if (member is Method) {
access = ((Method) member).access;
}
if (access == MemberAccessibility.PRIVATE) {
var target_type = (DataType) member.symbol.parent_symbol.node;
var this_type = find_parent_type (current_symbol);
if (target_type != this_type) {
expr.error = true;
Report.error (expr.source_reference, "Access to private member `%s' denied".printf (member.symbol.get_full_name ()));
return;
}
}
current_source_file.add_symbol_dependency (expr.symbol_reference, SourceFileDependencyType.SOURCE);
expr.static_type = get_static_type_for_node (expr.symbol_reference.node);
}
private bool is_type_compatible (TypeReference! expression_type, TypeReference! expected_type) {
/* only null is compatible to null */
if (expected_type.data_type == null && expected_type.type_parameter == null) {
......@@ -1109,28 +1125,28 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
expected_type.data_type == pointer_type) {
return true;
}
/* null is not compatible with any other type (i.e. value types) */
return false;
}
/* temporarily ignore type parameters */
if (expected_type.type_parameter != null) {
return true;
}
if (expression_type.data_type is Array != expected_type.data_type is Array) {
return false;
}
if (expression_type.data_type is Enum && expected_type.data_type == int_type.data_type) {
return true;
}
if (expression_type.data_type == expected_type.data_type) {
return true;
}
if (expression_type.data_type is Struct && expected_type.data_type is Struct) {
var expr_struct = (Struct) expression_type.data_type;
var expect_struct = (Struct) expected_type.data_type;
......@@ -1147,7 +1163,7 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
}
}
}
return expression_type.data_type.is_subtype_of (expected_type.data_type);
}
......@@ -1157,17 +1173,17 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
expr.error = true;
return;
}
var msym = expr.call.symbol_reference;
if (msym == null) {
/* if no symbol found, skip this check */
expr.error = true;
return;
}