Prevent statements to be used as expressions.
authorJean Privat <jean@pryen.org>
Tue, 20 Jan 2009 18:10:46 +0000 (13:10 -0500)
committerJean Privat <jean@pryen.org>
Tue, 20 Jan 2009 18:10:46 +0000 (13:10 -0500)
Add two method check_expr and check_conform_expr that check if an
PExpr is a real expression (not a statement) and its conformity.

Update existing tests to match the possibly updated error message.
Add a new test, base_meth_call, to detect some 'stmt as expr' errors.

31 files changed:
src/syntax/syntax_base.nit
src/syntax/typing.nit
tests/base_meth_call.nit [new file with mode: 0644]
tests/sav/base_array_alt1.sav
tests/sav/base_gen_alt1.sav
tests/sav/base_gen_int_alt1.sav
tests/sav/base_meth_call.sav [new file with mode: 0644]
tests/sav/base_meth_call_alt1.sav [new file with mode: 0644]
tests/sav/base_meth_call_alt2.sav [new file with mode: 0644]
tests/sav/base_meth_call_alt3.sav [new file with mode: 0644]
tests/sav/base_meth_call_alt4.sav [new file with mode: 0644]
tests/sav/base_virtual_type2_alt1.sav
tests/sav/base_virtual_type3_alt1.sav
tests/sav/base_virtual_type4_alt1.sav
tests/sav/base_virtual_type5_alt1.sav
tests/sav/base_virtual_type5_alt2.sav
tests/sav/base_virtual_type_alt1.sav
tests/sav/base_virtual_type_alt2.sav
tests/sav/base_virtual_type_alt3.sav
tests/sav/error_array_ambig.sav
tests/sav/error_attr_assign.sav
tests/sav/error_decl_type_var.sav
tests/sav/error_for_coll.sav
tests/sav/error_if_bool.sav
tests/sav/error_left_bool.sav
tests/sav/error_loop_bool_while.sav
tests/sav/error_ret_type.sav
tests/sav/error_right_bool.sav
tests/sav/error_star_type.sav
tests/sav/error_var_assign.sav
tests/sav/test_combined_assignment_alt1.sav

index 4e4bd01..ecbeffa 100644 (file)
@@ -345,6 +345,26 @@ special Visitor
                error(n, "Type error: expected {stype}, got {subtype}")
                return false
        end
+       
+       # Check that an expression has a static type and that 
+       # Display an error and return false if n is a statement
+       # Require that the static type of n is known
+       meth check_expr(n: PExpr): Bool
+       do
+               # FIXME: The tc.error_count is a workaround since currently there is no way
+               # to distingate statements from buggy expressions: both have a null stype
+               if tc.error_count == 0 and n.stype == null then
+                       error(n, "Type error: expected expression.")
+                       return false
+               end
+               return true
+       end
+
+       # Combine check_conform and check_expr
+       meth check_conform_expr(n: PExpr, stype: MMType): Bool
+       do
+               if check_expr(n) then return check_conform(n, n.stype, stype) else return false
+       end
 
 
        protected init(tc: ToolContext, module: MMSrcModule)
index 09d88a3..7da9d4e 100644 (file)
@@ -206,7 +206,7 @@ redef class AAttrPropdef
        do
                super
                if n_expr != null then
-                       v.check_conform(n_expr, n_expr.stype, prop.signature.return_type)
+                       v.check_conform_expr(n_expr, prop.signature.return_type)
                end
        end
 end
@@ -310,9 +310,10 @@ redef class AVardeclExpr
                if n_type != null then
                        va.stype = n_type.stype
                        if n_expr != null then
-                               v.check_conform(self, n_expr.stype, va.stype)
+                               v.check_conform_expr(n_expr, va.stype)
                        end
                else
+                       v.check_expr(n_expr)
                        va.stype = n_expr.stype
                end
        end
@@ -339,7 +340,7 @@ redef class AReturnExpr
                else if n_expr != null and t == null then
                        v.error(self, "Error: Return with value in a procedure.")
                else if n_expr != null and t != null then
-                       v.check_conform(self, n_expr.stype, t)
+                       v.check_conform_expr(n_expr, t)
                end
        end
 end
@@ -349,7 +350,7 @@ redef class AIfExpr
        do
                var old_var_ctx = v.variable_ctx
                v.visit(n_expr)
-               v.check_conform(self, n_expr.stype, v.type_bool)
+               v.check_conform_expr(n_expr, v.type_bool)
 
                if n_expr.if_true_variable_ctx != null then
                        v.variable_ctx = n_expr.if_true_variable_ctx
@@ -369,7 +370,7 @@ end
 redef class AWhileExpr
        redef meth after_typing(v)
        do
-               v.check_conform(self, n_expr.stype, v.type_bool)
+               v.check_conform_expr(n_expr, v.type_bool)
        end
 end
 
@@ -392,7 +393,7 @@ redef class AForVardeclExpr
                v.variable_ctx.add(va)
 
                var expr_type = n_expr.stype
-               if not v.check_conform(self, expr_type, v.type_collection) then
+               if not v.check_conform_expr(n_expr, v.type_collection) then
                        return
                end
                var prop = expr_type.local_class.select_method(once ("iterator".to_symbol))
@@ -415,7 +416,7 @@ end
 redef class AAssertExpr
        redef meth after_typing(v)
        do
-               v.check_conform(self, n_expr.stype, v.type_bool)
+               v.check_conform_expr(n_expr, v.type_bool)
                if n_expr.if_true_variable_ctx != null then v.variable_ctx = n_expr.if_true_variable_ctx
        end
 end
@@ -433,7 +434,7 @@ redef class AVarAssignExpr
        redef meth after_typing(v)
        do
                var t = v.variable_ctx.stype(variable)
-               v.check_conform(self, n_value.stype, t)
+               v.check_conform_expr(n_value, t)
        end
 end
 
@@ -453,7 +454,7 @@ redef class AReassignFormExpr
                prop.global.check_visibility(v, self, v.module, false)
                var psig = prop.signature_for(type_lvalue)
                _assign_method = prop
-               v.check_conform(n_value, n_value.stype, psig[0].not_for_self)
+               v.check_conform_expr(n_value, psig[0].not_for_self)
                v.check_conform(self, psig.return_type.not_for_self, n_value.stype)
        end
 
@@ -504,7 +505,9 @@ redef class AIfexprExpr
                v.variable_ctx = old_var_ctx
                v.visit(n_else)
 
-               v.check_conform(self, n_expr.stype, v.type_bool)
+               v.check_conform_expr(n_expr, v.type_bool)
+
+               if not v.check_expr(n_then) or not v.check_expr(n_else) then return
 
                var t = n_then.stype
                var te = n_else.stype
@@ -529,8 +532,8 @@ end
 redef class AOrExpr
        redef meth after_typing(v)
        do
-               v.check_conform(self, n_expr.stype, v.type_bool)
-               v.check_conform(self, n_expr2.stype, v.type_bool)
+               v.check_conform_expr(n_expr, v.type_bool)
+               v.check_conform_expr(n_expr2, v.type_bool)
                _stype = v.type_bool
        end
 end
@@ -552,8 +555,8 @@ redef class AAndExpr
 
                v.variable_ctx = old_var_ctx
 
-               v.check_conform(self, n_expr.stype, v.type_bool)
-               v.check_conform(self, n_expr2.stype, v.type_bool)
+               v.check_conform_expr(n_expr, v.type_bool)
+               v.check_conform_expr(n_expr2, v.type_bool)
                _stype = v.type_bool
        end
 end
@@ -561,7 +564,7 @@ end
 redef class ANotExpr
        redef meth after_typing(v)
        do
-               v.check_conform(self, n_expr.stype, v.type_bool)
+               v.check_conform_expr(n_expr, v.type_bool)
                _stype = v.type_bool
        end
 end
@@ -622,7 +625,7 @@ redef class AArrayExpr
                        end
                end
                for n in n_exprs do
-                       v.check_conform(self, n.stype, stype)
+                       v.check_conform_expr(n, stype)
                end
                _stype = v.type_array(stype)
        end
@@ -643,7 +646,8 @@ redef class ARangeExpr
                        return
                end
                var dtype = v.type_discrete
-               v.check_conform(self, ntype, dtype)
+               v.check_conform_expr(n_expr, dtype)
+               v.check_conform_expr(n_expr2, dtype)
                _stype = v.type_range(ntype)
        end
 end
@@ -718,10 +722,8 @@ redef class AAttrFormExpr
        # Compute the attribute accessed
        private meth do_typing(v: TypingVisitor)
        do
+               if not v.check_expr(n_expr) then return
                var type_recv = n_expr.stype
-               if type_recv == null then
-                       return
-               end
                var name = n_id.to_symbol
                var prop = type_recv.local_class.select_attribute(name)
                if prop == null then
@@ -755,7 +757,7 @@ redef class AAttrAssignExpr
                if prop == null then
                        return
                end
-               v.check_conform(self, n_value.stype, attr_type)
+               v.check_conform_expr(n_value, attr_type)
        end
 end
 
@@ -840,7 +842,7 @@ special PExpr
                                var star = new Array[PExpr]
                                for i in [0..(raw_arity-par_arity)] do
                                        a = raw_args[arg_idx]
-                                       v.check_conform(self, a.stype, par_type)
+                                       v.check_conform_expr(a, par_type)
                                        star.add(a)
                                        arg_idx = arg_idx + 1
                                end
@@ -849,7 +851,7 @@ special PExpr
                                a = aa
                        else
                                a = raw_args[arg_idx]
-                               v.check_conform(self, a.stype, par_type)
+                               v.check_conform_expr(a, par_type)
                                arg_idx = arg_idx + 1
                        end
                        args.add(a)
@@ -946,6 +948,7 @@ special ASuperInitCall
 
        private meth do_all_typing(v: TypingVisitor)
        do
+               if not v.check_expr(n_expr) then return
                do_typing(v, n_expr.stype, n_expr.is_implicit_self, n_expr.is_self, name, raw_arguments)
                if prop == null then return
                if prop.global.is_init then
@@ -969,6 +972,7 @@ special AReassignFormExpr
        readable attr _read_prop: MMMethod
        redef meth do_all_typing(v)
        do
+               if not v.check_expr(n_expr) then return
                var raw_args = raw_arguments
                do_typing(v, n_expr.stype, n_expr.is_implicit_self, n_expr.is_self, name, raw_args)
                if prop == null then return
@@ -1146,8 +1150,8 @@ end
 redef class AAsCastExpr
        redef meth after_typing(v)
        do
+               v.check_expr(n_expr)
                _stype = n_type.stype
-               var et = n_expr.stype
        end
 end
 
diff --git a/tests/base_meth_call.nit b/tests/base_meth_call.nit
new file mode 100644 (file)
index 0000000..aa35116
--- /dev/null
@@ -0,0 +1,45 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2004-2008 Jean Privat <jean@pryen.org>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import kernel
+
+class A
+       meth foo(a: Int) do a.output
+       meth bar(a: Int): Int do
+               ' '.output
+               a.output
+               return a
+       end
+
+       meth baz do
+               foo(1)
+               bar(2).output
+               var i = bar(3)
+               i.output
+               i = bar(4)
+               i.output
+               foo(bar(5))
+               bar(6)
+#alt1#         foo(7).output
+#alt2#         var j = foo(8)
+#alt2#         j.output
+#alt3#         i = foo(9)
+#alt3#         i.output
+#alt4#         foo(foo(10))
+       end
+end
+
+(new A).baz
index d768bb4..225cbcb 100644 (file)
@@ -1 +1 @@
-alt/base_array_alt1.nit:27,11--18: Type error: expected Int, got Bool
+alt/base_array_alt1.nit:27,15--18: Type error: expected Int, got Bool
index 8be7508..06f16e0 100644 (file)
@@ -1 +1 @@
-alt/base_gen_alt1.nit:62,1--14: Type error: expected B, got A
+alt/base_gen_alt1.nit:62,11--11: Type error: expected B, got A
index 9c39e7b..e465b69 100644 (file)
@@ -1 +1 @@
-alt/base_gen_int_alt1.nit:56,1--14: Type error: expected Int, got A
+alt/base_gen_int_alt1.nit:56,11--11: Type error: expected Int, got A
diff --git a/tests/sav/base_meth_call.sav b/tests/sav/base_meth_call.sav
new file mode 100644 (file)
index 0000000..ec5b356
--- /dev/null
@@ -0,0 +1,10 @@
+1
+ 2
+2
+ 3
+3
+ 4
+4
+ 5
+5
+ 6
diff --git a/tests/sav/base_meth_call_alt1.sav b/tests/sav/base_meth_call_alt1.sav
new file mode 100644 (file)
index 0000000..bf34c44
--- /dev/null
@@ -0,0 +1 @@
+alt/base_meth_call_alt1.nit:36,3--7: Type error: expected expression.
diff --git a/tests/sav/base_meth_call_alt2.sav b/tests/sav/base_meth_call_alt2.sav
new file mode 100644 (file)
index 0000000..a526edf
--- /dev/null
@@ -0,0 +1 @@
+alt/base_meth_call_alt2.nit:37,11--15: Type error: expected expression.
diff --git a/tests/sav/base_meth_call_alt3.sav b/tests/sav/base_meth_call_alt3.sav
new file mode 100644 (file)
index 0000000..8b0a295
--- /dev/null
@@ -0,0 +1 @@
+alt/base_meth_call_alt3.nit:39,7--11: Type error: expected expression.
diff --git a/tests/sav/base_meth_call_alt4.sav b/tests/sav/base_meth_call_alt4.sav
new file mode 100644 (file)
index 0000000..958c0e3
--- /dev/null
@@ -0,0 +1 @@
+alt/base_meth_call_alt4.nit:41,7--12: Type error: expected expression.
index c6a327d..128244e 100644 (file)
@@ -1 +1 @@
-alt/base_virtual_type2_alt1.nit:46,1--7: Type error: expected U, got T
+alt/base_virtual_type2_alt1.nit:46,5--7: Type error: expected U, got T
index 447a3f1..57642b4 100644 (file)
@@ -1 +1 @@
-alt/base_virtual_type3_alt1.nit:32,1--15: Type error: expected Int, got T
+alt/base_virtual_type3_alt1.nit:32,5--15: Type error: expected Int, got T
index f9796d8..59b07a9 100644 (file)
@@ -1 +1 @@
-alt/base_virtual_type4_alt1.nit:24,2--12: Type error: expected U, got T
+alt/base_virtual_type4_alt1.nit:24,8--12: Type error: expected U, got T
index 14eee4e..6d3aa69 100644 (file)
@@ -1 +1 @@
-alt/base_virtual_type5_alt1.nit:26,1--7: Type error: expected U, got Int
+alt/base_virtual_type5_alt1.nit:26,7--7: Type error: expected U, got Int
index 3bc0367..e6c4bee 100644 (file)
@@ -1 +1 @@
-alt/base_virtual_type5_alt2.nit:27,1--11: Type error: expected U, got T
+alt/base_virtual_type5_alt2.nit:27,7--11: Type error: expected U, got T
index bc14863..e5f24e1 100644 (file)
@@ -1 +1 @@
-alt/base_virtual_type_alt1.nit:39,1--7: Type error: expected Int, got T
+alt/base_virtual_type_alt1.nit:39,5--7: Type error: expected Int, got T
index 9bf3325..57c3b33 100644 (file)
@@ -1 +1 @@
-alt/base_virtual_type_alt2.nit:40,1--7: Type error: expected T, got Int
+alt/base_virtual_type_alt2.nit:40,7--7: Type error: expected T, got Int
index 8d06fe8..fd06cba 100644 (file)
@@ -1 +1 @@
-alt/base_virtual_type_alt3.nit:43,1--7: Type error: expected T, got B
+alt/base_virtual_type_alt3.nit:43,7--7: Type error: expected T, got B
index 759d9f3..950150d 100644 (file)
@@ -1 +1 @@
-./error_array_ambig.nit:17,10--15: Type error: expected Int, got Char
+./error_array_ambig.nit:17,13--15: Type error: expected Int, got Char
index 169cb2a..daf6916 100644 (file)
@@ -1 +1 @@
-./error_attr_assign.nit:21,3--13: Type error: expected Int, got Char
+./error_attr_assign.nit:21,11--13: Type error: expected Int, got Char
index 1819b4d..245a386 100644 (file)
@@ -1 +1 @@
-./error_decl_type_var.nit:17,1--16: Type error: expected Int, got Char
+./error_decl_type_var.nit:17,14--16: Type error: expected Int, got Char
index 4bba358..f0cc832 100644 (file)
@@ -1 +1 @@
-./error_for_coll.nit:17,1--10: Type error: expected Collection[E], got Int
+./error_for_coll.nit:17,10--10: Type error: expected Collection[E], got Int
index fdf1a5b..1abea62 100644 (file)
@@ -1 +1 @@
-./error_if_bool.nit:17,1--4: Type error: expected Bool, got Int
+./error_if_bool.nit:17,4--4: Type error: expected Bool, got Int
index eca5a55..a01938b 100644 (file)
@@ -1 +1 @@
-./error_left_bool.nit:17,4--13: Type error: expected Bool, got Int
+./error_left_bool.nit:17,4--4: Type error: expected Bool, got Int
index c3c8020..29c7837 100644 (file)
@@ -1 +1 @@
-./error_loop_bool_while.nit:17,1--10: Type error: expected Bool, got Int
+./error_loop_bool_while.nit:17,7--7: Type error: expected Bool, got Int
index 9e68a80..caf13dc 100644 (file)
@@ -1 +1 @@
-./error_ret_type.nit:19,2--11: Type error: expected Int, got Char
+./error_ret_type.nit:19,9--11: Type error: expected Int, got Char
index 99122d5..3414c35 100644 (file)
@@ -1 +1 @@
-./error_right_bool.nit:17,4--12: Type error: expected Bool, got Int
+./error_right_bool.nit:17,12--12: Type error: expected Bool, got Int
index d517812..52f8b11 100644 (file)
@@ -1 +1 @@
-./error_star_type.nit:18,1--10: Type error: expected Int, got Char
+./error_star_type.nit:18,8--10: Type error: expected Int, got Char
index 0918510..4e3fac6 100644 (file)
@@ -1 +1 @@
-./error_var_assign.nit:18,1--7: Type error: expected Int, got Char
+./error_var_assign.nit:18,5--7: Type error: expected Int, got Char
index be52fbf..c7e9c6a 100644 (file)
@@ -1,4 +1,4 @@
 alt/test_combined_assignment_alt1.nit:22,3--9: Error: Method '+' doesn't exists in Object.
 alt/test_combined_assignment_alt1.nit:27,3--9: Error: Method '+' doesn't exists in Object.
-alt/test_combined_assignment_alt1.nit:28,3--10: Type error: expected Int, got Object
+alt/test_combined_assignment_alt1.nit:28,10--10: Type error: expected Int, got Object
 alt/test_combined_assignment_alt1.nit:48,1--8: Error: Method '+' doesn't exists in Object.