Track error types that expressions can throw, based on patch by Jared
authorJuerg Billeter <j@bitron.ch>
Sun, 25 May 2008 09:09:39 +0000 (09:09 +0000)
committerJürg Billeter <juergbi@src.gnome.org>
Sun, 25 May 2008 09:09:39 +0000 (09:09 +0000)
2008-05-25  Juerg 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

svn path=/trunk/; revision=1420

17 files changed:
ChangeLog
gobject/valaccodeclassbinding.vala
gobject/valaccodegenerator.vala
gobject/valaccodeinvocationexpressionbinding.vala
gobject/valaccodemethodbinding.vala
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
vapigen/valagidlparser.vala
vapigen/valavapigen.vala

index 022da3d..6ee0596 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,25 @@
+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:
index 2c0d8fa..e37695e 100644 (file)
@@ -949,7 +949,7 @@ public class Vala.CCodeClassBinding : CCodeTypesymbolBinding {
                        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"));
                }
 
index 378071e..366cd34 100644 (file)
@@ -1206,7 +1206,7 @@ public class Vala.CCodeGenerator : CodeGenerator {
                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);
                }
 
@@ -1559,7 +1559,7 @@ public class Vala.CCodeGenerator : CodeGenerator {
                        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
 
@@ -1580,8 +1580,7 @@ public class Vala.CCodeGenerator : CodeGenerator {
 
                        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
@@ -1597,7 +1596,7 @@ public class Vala.CCodeGenerator : CodeGenerator {
        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 ();
@@ -1874,7 +1873,7 @@ public class Vala.CCodeGenerator : CodeGenerator {
                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);
@@ -3091,7 +3090,7 @@ public class Vala.CCodeGenerator : CodeGenerator {
                                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")));
@@ -3512,7 +3511,7 @@ public class Vala.CCodeGenerator : CodeGenerator {
 
                }
 
-               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);
                }
@@ -3574,7 +3573,7 @@ public class Vala.CCodeGenerator : CodeGenerator {
                        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"));
                }
 
index ce571a8..e0164cb 100644 (file)
@@ -345,7 +345,7 @@ public class Vala.CCodeInvocationExpressionBinding : CCodeExpressionBinding {
                        }
                }
 
-               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
index eff5f94..4dca498 100644 (file)
@@ -452,7 +452,7 @@ public class Vala.CCodeMethodBinding : CCodeBinding {
                                }
                        }
 
-                       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));
@@ -635,7 +635,7 @@ public class Vala.CCodeMethodBinding : CCodeBinding {
                        }
                }
 
-               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);
                }
index 480c1db..d92232e 100644 (file)
@@ -21,6 +21,7 @@
  */
 
 using GLib;
+using Gee;
 
 /**
  * Represents a part of the parsed source code.
@@ -43,7 +44,7 @@ public abstract class Vala.CodeNode : Object {
        /**
         * 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.
@@ -71,7 +72,38 @@ public abstract class Vala.CodeNode : Object {
        /**
         * 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.
index 390e7f7..88d6097 100644 (file)
@@ -60,8 +60,8 @@ public class Vala.CreationMethod : Method {
                        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) {
index acc99b2..207fc87 100644 (file)
@@ -68,11 +68,6 @@ public abstract class Vala.Expression : CodeNode {
        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.
         */
index f2f2e90..0daa103 100644 (file)
@@ -2385,7 +2385,7 @@ public class Vala.Genie.Parser : CodeVisitor {
 
                if (accept (TokenType.RAISES)) {
                        do {
-                               method.add_error_domain (parse_type ());
+                               method.add_error_type (parse_type ());
                        } while (accept (TokenType.COMMA));
                }
 
@@ -3031,7 +3031,7 @@ public class Vala.Genie.Parser : CodeVisitor {
                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;
index 4223d35..940b2ed 100644 (file)
@@ -709,7 +709,7 @@ public class Vala.InterfaceWriter : CodeVisitor {
                }
                
                write_params (m.get_parameters ());
-               write_error_domains (m.get_error_domains ());
+               write_error_domains (m.get_error_types ());
 
                write_string (";");
 
index 929fd25..6dca30b 100644 (file)
@@ -189,7 +189,6 @@ public class Vala.Method : Member {
        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;
@@ -263,8 +262,8 @@ public class Vala.Method : Member {
                        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) {
@@ -419,17 +418,17 @@ public class Vala.Method : Member {
                }
 
                /* 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;
                        }
                }
@@ -438,25 +437,6 @@ public class Vala.Method : Member {
        }
 
        /**
-        * 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
@@ -499,9 +479,10 @@ public class Vala.Method : Member {
                        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;
                        }
                }
index e6cce38..6679f7b 100644 (file)
@@ -87,7 +87,7 @@ public class Vala.Namespace : Symbol {
                        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 ()) {
@@ -237,7 +237,7 @@ public class Vala.Namespace : Symbol {
         *
         * @return error domain list
         */
-       public Collection<ErrorDomain> get_error_domains () {
+       public Collection<ErrorDomain> get_error_types () {
                return new ReadOnlyCollection<ErrorDomain> (error_domains);
        }
        
index 4438b74..186536a 100644 (file)
@@ -2080,7 +2080,7 @@ public class Vala.Parser : CodeVisitor {
                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)) {
@@ -2616,7 +2616,7 @@ public class Vala.Parser : CodeVisitor {
                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;
index d954c83..39e3ed3 100644 (file)
@@ -469,6 +469,21 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
                                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) {
@@ -788,9 +803,26 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
                        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;
@@ -949,7 +981,7 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
                        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) {
@@ -966,6 +998,13 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
                        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) {
@@ -1001,6 +1040,9 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
                        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) {
@@ -1017,6 +1059,9 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
                        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) {
@@ -1033,6 +1078,18 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
                        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) {
@@ -1118,7 +1175,8 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
                        }
                }
 
-               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) {
@@ -1172,6 +1230,10 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
                    && !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) {
@@ -1603,7 +1665,7 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
                                                }
                                                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);
@@ -1863,7 +1925,13 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
 
                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;
@@ -2445,7 +2513,13 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
 
                        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);
 
@@ -3236,6 +3310,7 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
                        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 ());
        }
 }
index 7287a7f..5c15b7f 100644 (file)
@@ -406,7 +406,12 @@ public class Vala.SourceFile : Object {
 
        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 ();
index 2e482f8..dac27e8 100644 (file)
@@ -1334,7 +1334,7 @@ public class Vala.GIdlParser : CodeVisitor {
                        }
 
                        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;
                        }
 
index 8d38669..ae982d2 100644 (file)
@@ -80,11 +80,14 @@ class Vala.VAPIGen : Object {
        }
        
        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 () {