+2008-05-25 Jürg Billeter <j@bitron.ch>
+
+ * vala/valacodenode.vala:
+ * vala/valacreationmethod.vala:
+ * vala/valaexpression.vala:
+ * vala/valagenieparser.vala:
+ * vala/valainterfacewriter.vala:
+ * vala/valamethod.vala:
+ * vala/valanamespace.vala:
+ * vala/valaparser.vala:
+ * vala/valasemanticanalyzer.vala:
+ * vala/valasourcefile.vala:
+ * gobject/valaccodeclassbinding.vala:
+ * gobject/valaccodegenerator.vala:
+ * gobject/valaccodeinvocationexpressionbinding.vala:
+ * gobject/valaccodemethodbinding.vala:
+ * vapigen/valagidlparser.vala:
+ * vapigen/valavapigen.vala:
+
+ Track error types that expressions can throw,
+ based on patch by Jared Moore, fixes bug 482999
+
2008-05-24 Jürg Billeter <j@bitron.ch>
* gobject/valaccodeassignmentbinding.vala:
ccall.add_argument (new CCodeIdentifier (param.name));
}
- if (m.get_error_domains ().size > 0) {
+ if (m.get_error_types ().size > 0) {
ccall.add_argument (new CCodeIdentifier ("error"));
}
cdecl.add_declarator (cvar);
cfrag.append (cdecl);
- if (local.initializer != null && local.initializer.can_fail) {
+ if (local.initializer != null && local.initializer.tree_can_fail) {
add_simple_check (local.initializer, cfrag);
}
var ccond = new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, new CCodeIdentifier ("inner_error"), new CCodeConstant ("NULL"));
cfrag.append (new CCodeIfStatement (ccond, cerror_block));
- } else if (current_method != null && current_method.get_error_domains ().size > 0) {
+ } else if (current_method != null && current_method.get_error_types ().size > 0) {
// current method can fail, propagate error
// TODO ensure one of the error domains matches
cfrag.append (new CCodeIfStatement (ccond, cerror_block));
} else {
- // TODO improve check and move to semantic analyzer
- Report.warning (node.source_reference, "unhandled error");
+ // unhandled error
var cerror_block = new CCodeBlock ();
// print critical message
public override void visit_expression_statement (ExpressionStatement stmt) {
stmt.ccodenode = new CCodeExpressionStatement ((CCodeExpression) stmt.expression.ccodenode);
- if (stmt.tree_can_fail && stmt.expression.can_fail) {
+ if (stmt.tree_can_fail && stmt.expression.tree_can_fail) {
// simple case, no node breakdown necessary
var cfrag = new CCodeFragment ();
ccoldecl.add_declarator (ccolvardecl);
cblock.add_statement (ccoldecl);
- if (stmt.tree_can_fail && stmt.collection.can_fail) {
+ if (stmt.tree_can_fail && stmt.collection.tree_can_fail) {
// exception handling
var cfrag = new CCodeFragment ();
add_simple_check (stmt.collection, cfrag);
creation_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, instance));
}
- if (expr.can_fail) {
+ if (expr.tree_can_fail) {
// method can fail
current_method_inner_error = true;
creation_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier ("inner_error")));
}
- if (m.get_error_domains ().size > 0) {
+ if (m.get_error_types ().size > 0) {
var cparam = new CCodeFormalParameter ("error", "GError**");
cparam_map.set (get_param_pos (-1), cparam);
}
i++;
}
- if (m.get_error_domains ().size > 0) {
+ if (m.get_error_types ().size > 0) {
carg_map.set (get_param_pos (-1), new CCodeIdentifier ("error"));
}
}
}
- if (expr.can_fail) {
+ if (expr.tree_can_fail) {
// method can fail
codegen.current_method_inner_error = true;
// add &inner_error before the ellipsis arguments
}
}
- if (m.get_error_domains ().size > 0) {
+ if (m.get_error_types ().size > 0) {
var cparam = new CCodeFormalParameter ("error", "GError**");
cparam_map.set (codegen.get_param_pos (-1), cparam);
carg_map.set (codegen.get_param_pos (-1), new CCodeIdentifier (cparam.name));
}
}
- if (m.get_error_domains ().size > 0) {
+ if (m.get_error_types ().size > 0) {
var cparam = new CCodeFormalParameter ("error", "GError**");
cparam_map.set (codegen.get_param_pos (-1), cparam);
}
*/
using GLib;
+using Gee;
/**
* Represents a part of the parsed source code.
/**
* Contains all attributes that have been specified for this code node.
*/
- public List<Attribute> attributes;
+ public GLib.List<Attribute> attributes;
/**
* Generated CCodeNode that corresponds to this code node.
/**
* Specifies that this node or a child node may throw an exception.
*/
- public bool tree_can_fail { get; set; }
+ public bool tree_can_fail {
+ get { return _error_types.size > 0; }
+ }
+
+ /**
+ * Specifies the exceptions that can be thrown by this node or a child node
+ */
+ public Gee.List<DataType> get_error_types () {
+ return _error_types;
+ }
+
+ private Gee.List<DataType> _error_types = new ArrayList<DataType> ();
+
+ /**
+ * Adds an error type to the exceptions that can be thrown by this node
+ * or a child node
+ */
+ public void add_error_type (DataType error_type) {
+ _error_types.add (error_type);
+ error_type.parent_node = this;
+ }
+
+ /**
+ * Adds a collection of error types to the exceptions that can be thrown by this node
+ * or a child node
+ */
+ public void add_error_types (Gee.Collection<DataType> error_types) {
+ foreach (DataType error_type in error_types) {
+ _error_types.add (error_type);
+ error_type.parent_node = this;
+ }
+ }
/**
* Visits this code node with the specified CodeVisitor.
param.accept (visitor);
}
- foreach (DataType error_domain in get_error_domains ()) {
- error_domain.accept (visitor);
+ foreach (DataType error_type in get_error_types ()) {
+ error_type.accept (visitor);
}
if (body != null) {
public bool ref_sink { get; set; }
/**
- * Specifies that this expression may throw an exception.
- */
- public bool can_fail { get; set; }
-
- /**
* Specifies that this expression is used as lvalue, i.e. the
* left hand side of an assignment.
*/
if (accept (TokenType.RAISES)) {
do {
- method.add_error_domain (parse_type ());
+ method.add_error_type (parse_type ());
} while (accept (TokenType.COMMA));
}
expect (TokenType.CLOSE_PARENS);
if (accept (TokenType.RAISES)) {
do {
- method.add_error_domain (parse_type ());
+ method.add_error_type (parse_type ());
} while (accept (TokenType.COMMA));
}
method.access = SymbolAccessibility.PUBLIC;
}
write_params (m.get_parameters ());
- write_error_domains (m.get_error_domains ());
+ write_error_domains (m.get_error_types ());
write_string (";");
private string _vfunc_name;
private string _sentinel;
private bool _no_array_length;
- private Gee.List<DataType> error_domains = new ArrayList<DataType> ();
private Gee.List<Expression> preconditions = new ArrayList<Expression> ();
private Gee.List<Expression> postconditions = new ArrayList<Expression> ();
private DataType _return_type;
param.accept (visitor);
}
- foreach (DataType error_domain in error_domains) {
- error_domain.accept (visitor);
+ foreach (DataType error_type in get_error_types ()) {
+ error_type.accept (visitor);
}
if (result_var != null) {
}
/* this method may throw more but not less errors than the base method */
- foreach (DataType method_error_domain in error_domains) {
+ foreach (DataType method_error_type in get_error_types ()) {
bool match = false;
- foreach (DataType base_method_error_domain in base_method.error_domains) {
- if (method_error_domain.compatible (base_method_error_domain)) {
+ foreach (DataType base_method_error_type in base_method.get_error_types ()) {
+ if (method_error_type.compatible (base_method_error_type)) {
match = true;
break;
}
}
if (!match) {
- invalid_match = "incompatible error domain `%s'".printf (method_error_domain.to_string ());
+ invalid_match = "incompatible error type `%s'".printf (method_error_type.to_string ());
return false;
}
}
}
/**
- * Adds an error domain to this method.
- *
- * @param error_domain an error domain
- */
- public void add_error_domain (DataType error_domain) {
- error_domains.add (error_domain);
- error_domain.parent_node = this;
- }
-
- /**
- * Returns a copy of the list of error domains of this method.
- *
- * @return list of error domains
- */
- public Collection<DataType> get_error_domains () {
- return new ReadOnlyCollection<DataType> (error_domains);
- }
-
- /**
* Adds a precondition to this method.
*
* @param precondition a boolean precondition expression
return_type = new_type;
return;
}
- for (int i = 0; i < error_domains.size; i++) {
- if (error_domains[i] == old_type) {
- error_domains[i] = new_type;
+ var error_types = get_error_types ();
+ for (int i = 0; i < error_types.size; i++) {
+ if (error_types[i] == old_type) {
+ error_types[i] = new_type;
return;
}
}
foreach (Enum en in ns.get_enums ()) {
old_ns.add_enum (en);
}
- foreach (ErrorDomain ed in ns.get_error_domains ()) {
+ foreach (ErrorDomain ed in ns.get_error_types ()) {
old_ns.add_error_domain (ed);
}
foreach (Constant c in ns.get_constants ()) {
*
* @return error domain list
*/
- public Collection<ErrorDomain> get_error_domains () {
+ public Collection<ErrorDomain> get_error_types () {
return new ReadOnlyCollection<ErrorDomain> (error_domains);
}
expect (TokenType.CLOSE_PARENS);
if (accept (TokenType.THROWS)) {
do {
- method.add_error_domain (parse_type ());
+ method.add_error_type (parse_type ());
} while (accept (TokenType.COMMA));
}
while (accept (TokenType.REQUIRES)) {
expect (TokenType.CLOSE_PARENS);
if (accept (TokenType.THROWS)) {
do {
- method.add_error_domain (parse_type ());
+ method.add_error_type (parse_type ());
} while (accept (TokenType.COMMA));
}
method.access = access;
return;
}
}
+
+ // check that all errors that can be thrown in the method body are declared
+ if (m.body != null) {
+ foreach (DataType body_error_type in m.body.get_error_types ()) {
+ bool can_propagate_error = false;
+ foreach (DataType method_error_type in m.get_error_types ()) {
+ if (body_error_type.compatible (method_error_type)) {
+ can_propagate_error = true;
+ }
+ }
+ if (!can_propagate_error) {
+ Report.warning (body_error_type.source_reference, "unhandled error `%s'".printf (body_error_type.to_string()));
+ }
+ }
+ }
}
private void find_base_class_method (Method m, Class cl) {
local.active = false;
}
+ foreach (Statement stmt in b.get_statements()) {
+ b.add_error_types (stmt.get_error_types ());
+ }
+
current_symbol = current_symbol.parent_symbol;
}
+ public override void visit_declaration_statement (DeclarationStatement stmt) {
+ var local = stmt.declaration as LocalVariable;
+ if (local != null && local.initializer != null) {
+ foreach (DataType error_type in local.initializer.get_error_types ()) {
+ // ensure we can trace back which expression may throw errors of this type
+ var initializer_error_type = error_type.copy ();
+ initializer_error_type.source_reference = local.initializer.source_reference;
+
+ stmt.add_error_type (initializer_error_type);
+ }
+ }
+ }
+
public override void visit_local_variable (LocalVariable local) {
if (local.initializer != null) {
local.initializer.expected_type = local.variable_type;
return;
}
- stmt.tree_can_fail = stmt.expression.tree_can_fail;
+ stmt.add_error_types (stmt.expression.get_error_types ());
}
public override void visit_if_statement (IfStatement stmt) {
Report.error (stmt.condition.source_reference, "Condition must be boolean");
return;
}
+
+ stmt.add_error_types (stmt.condition.get_error_types ());
+ stmt.add_error_types (stmt.true_statement.get_error_types ());
+
+ if (stmt.false_statement != null) {
+ stmt.add_error_types (stmt.false_statement.get_error_types ());
+ }
}
public override void visit_switch_section (SwitchSection section) {
Report.error (stmt.condition.source_reference, "Condition must be boolean");
return;
}
+
+ stmt.add_error_types (stmt.condition.get_error_types ());
+ stmt.add_error_types (stmt.body.get_error_types ());
}
public override void visit_do_statement (DoStatement stmt) {
Report.error (stmt.condition.source_reference, "Condition must be boolean");
return;
}
+
+ stmt.add_error_types (stmt.condition.get_error_types ());
+ stmt.add_error_types (stmt.body.get_error_types ());
}
public override void visit_for_statement (ForStatement stmt) {
Report.error (stmt.condition.source_reference, "Condition must be boolean");
return;
}
+
+ if (stmt.condition != null) {
+ stmt.add_error_types (stmt.condition.get_error_types ());
+ }
+
+ stmt.add_error_types (stmt.body.get_error_types ());
+ foreach (Expression exp in stmt.get_initializer ()) {
+ stmt.add_error_types (exp.get_error_types ());
+ }
+ foreach (Expression exp in stmt.get_iterator ()) {
+ stmt.add_error_types (exp.get_error_types ());
+ }
}
public override void visit_foreach_statement (ForeachStatement stmt) {
}
}
- stmt.tree_can_fail = stmt.collection.tree_can_fail || stmt.body.tree_can_fail;
+ stmt.add_error_types (stmt.collection.get_error_types ());
+ stmt.add_error_types (stmt.body.get_error_types ());
}
public override void visit_return_statement (ReturnStatement stmt) {
&& !current_return_type.nullable) {
Report.warning (stmt.source_reference, "`null' incompatible with return type `%s`".printf (current_return_type.to_string ()));
}
+
+ if (stmt.return_expression != null) {
+ stmt.add_error_types (stmt.return_expression.get_error_types ());
+ }
}
public override void visit_throw_statement (ThrowStatement stmt) {
}
var m = new DynamicMethod (expr.inner.value_type, expr.member_name, ret_type, expr.source_reference);
m.invocation = invoc;
- m.add_error_domain (new ErrorType (null));
+ m.add_error_type (new ErrorType (null));
m.access = SymbolAccessibility.PUBLIC;
m.add_parameter (new FormalParameter.with_ellipsis ());
context.add_dynamic_member (m);
if (mtype is MethodType) {
var m = ((MethodType) mtype).method_symbol;
- expr.tree_can_fail = expr.can_fail = (m.get_error_domains ().size > 0);
+ foreach (DataType error_type in m.get_error_types ()) {
+ // ensure we can trace back which expression may throw errors of this type
+ var call_error_type = error_type.copy ();
+ call_error_type.source_reference = expr.source_reference;
+
+ expr.add_error_type (call_error_type);
+ }
}
expr.value_type = ret_type;
check_arguments (expr, new MethodType (m), m.get_parameters (), args);
- expr.tree_can_fail = expr.can_fail = (m.get_error_domains ().size > 0);
+ foreach (DataType error_type in m.get_error_types ()) {
+ // ensure we can trace back which expression may throw errors of this type
+ var call_error_type = error_type.copy ();
+ call_error_type.source_reference = expr.source_reference;
+
+ expr.add_error_type (call_error_type);
+ }
} else if (expr.type_reference is ErrorType) {
expr.accept_children (this);
a.value_type = null;
}
- a.tree_can_fail = a.left.tree_can_fail || a.right.tree_can_fail;
+ a.add_error_types (a.left.get_error_types ());
+ a.add_error_types (a.right.get_error_types ());
}
}
public char* get_mapped_contents () {
if (mapped_file == null) {
- mapped_file = new MappedFile (filename, false);
+ try {
+ mapped_file = new MappedFile (filename, false);
+ } catch (FileError e) {
+ Report.error (null, "Unable to map file `%s': %s".printf (filename, e.message));
+ return null;
+ }
}
return mapped_file.get_contents ();
}
if (suppress_throws == false && param_is_exception (param)) {
- m.add_error_domain (parse_type (param.type));
+ m.add_error_type (parse_type (param.type));
continue;
}
}
private static string[]? get_packages_from_depsfile (string depsfile) {
- string contents;
- if (FileUtils.get_contents (depsfile, out contents)) {
+ try {
+ string contents;
+ FileUtils.get_contents (depsfile, out contents);
return contents.strip ().split ("\n");
+ } catch (FileError e) {
+ // deps files are optional
+ return null;
}
- return null;
}
private int run () {