typing: factorize AEqExpr and ANeExpr into AEqFormExpr
[nit.git] / src / semantize / typing.nit
index 3c7c9b1..4868fc9 100644 (file)
@@ -301,15 +301,9 @@ private class TypeVisitor
 
                #debug("recv: {recvtype} (aka {unsafe_type})")
                if recvtype isa MNullType then
-                       # `null` only accepts some methods of object.
-                       if name == "==" or name == "!=" or name == "is_same_instance" then
-                               var objclass = get_mclass(node, "Object")
-                               if objclass == null then return null # Forward error
-                               unsafe_type = objclass.mclass_type
-                       else
-                               self.error(node, "Error: method `{name}` called on `null`.")
-                               return null
-                       end
+                       var objclass = get_mclass(node, "Object")
+                       if objclass == null then return null # Forward error
+                       unsafe_type = objclass.mclass_type
                end
 
                var mproperty = self.try_get_mproperty_by_name2(node, unsafe_type, name)
@@ -331,6 +325,14 @@ private class TypeVisitor
 
                assert mproperty isa MMethod
 
+               # `null` only accepts some methods of object.
+               if recvtype isa MNullType and not mproperty.is_null_safe then
+                       self.error(node, "Error: method `{name}` called on `null`.")
+                       return null
+               else if unsafe_type isa MNullableType and not mproperty.is_null_safe then
+                       modelbuilder.advice(node, "call-on-nullable", "Warning: method call on a nullable receiver `{recvtype}`.")
+               end
+
                if is_toplevel_context and recv_is_self and not mproperty.is_toplevel then
                        error(node, "Error: `{name}` is not a top-level method, thus need a receiver.")
                end
@@ -646,7 +648,7 @@ end
 
 redef class Variable
        # The declared type of the variable
-       var declared_type: nullable MType
+       var declared_type: nullable MType is writable
 
        # Was the variable type-adapted?
        # This is used to speedup type retrieval while it remains `false`
@@ -758,15 +760,15 @@ redef class AAttrPropdef
        do
                if not has_value then return
 
-               var mpropdef = self.mpropdef
-               if mpropdef == null then return # skip error
+               var mpropdef = self.mreadpropdef
+               if mpropdef == null or mpropdef.msignature == null then return # skip error
 
                var v = new TypeVisitor(modelbuilder, mpropdef.mclassdef.mmodule, mpropdef)
                self.selfvariable = v.selfvariable
 
                var nexpr = self.n_expr
                if nexpr != null then
-                       var mtype = self.mpropdef.static_mtype
+                       var mtype = self.mtype
                        v.visit_expr_subtype(nexpr, mtype)
                end
                var nblock = self.n_block
@@ -958,7 +960,7 @@ redef class AVarReassignExpr
 
                v.set_variable(self, variable, rettype)
 
-               self.is_typed = true
+               self.is_typed = rettype != null
        end
 end
 
@@ -1004,9 +1006,11 @@ redef class AReturnExpr
                        else
                                v.visit_expr(nexpr)
                                v.error(nexpr, "Error: `return` with value in a procedure.")
+                               return
                        end
                else if ret_type != null then
                        v.error(self, "Error: `return` without value in a function.")
+                       return
                end
                self.is_typed = true
        end
@@ -1300,7 +1304,8 @@ redef class AOrElseExpr
                end
 
                if t1 isa MNullType then
-                       v.error(n_expr, "Type Error: `or else` on `null`.")
+                       self.mtype = t2
+                       return
                else if v.check_can_be_null(n_expr, t1) then
                        t1 = t1.as_notnull
                end
@@ -1684,14 +1689,8 @@ redef class ABinopExpr
        redef fun property_name do return operator
        redef fun property_node do return n_op
 end
-redef class AEqExpr
-       redef fun accept_typing(v)
-       do
-               super
-               v.null_test(self)
-       end
-end
-redef class ANeExpr
+
+redef class AEqFormExpr
        redef fun accept_typing(v)
        do
                super
@@ -2059,7 +2058,7 @@ redef class AAttrAssignExpr
                var mtype = self.attr_type
 
                v.visit_expr_subtype(self.n_value, mtype)
-               self.is_typed = true
+               self.is_typed = mtype != null
        end
 end
 
@@ -2070,9 +2069,9 @@ redef class AAttrReassignExpr
                var mtype = self.attr_type
                if mtype == null then return # Skip error
 
-               self.resolve_reassignment(v, mtype, mtype)
+               var rettype = self.resolve_reassignment(v, mtype, mtype)
 
-               self.is_typed = true
+               self.is_typed = rettype != null
        end
 end