+ # Can `mtype` be null (up to the current knowledge)?
+ fun can_be_null(mtype: MType): Bool
+ do
+ if mtype isa MNullableType or mtype isa MNullType then return true
+ if mtype isa MFormalType then
+ var x = anchor_to(mtype)
+ if x isa MNullableType or x isa MNullType then return true
+ end
+ return false
+ end
+
+ # Check that `mtype` can be null (up to the current knowledge).
+ #
+ # If not then display a `useless-null-test` warning on node and return false.
+ # Else return true.
+ fun check_can_be_null(anode: ANode, mtype: MType): Bool
+ do
+ if can_be_null(mtype) then return true
+
+ if mtype isa MFormalType then
+ var res = anchor_to(mtype)
+ modelbuilder.warning(anode, "useless-null-test", "Warning: expression is not null, since it is a `{mtype}: {res}`.")
+ else
+ modelbuilder.warning(anode, "useless-null-test", "Warning: expression is not null, since it is a `{mtype}`.")
+ end
+ return false
+ end
+
+ # Special verification on != and == for null
+ # Return true
+ fun null_test(anode: ABinopExpr)
+ do
+ var mtype = anode.n_expr.mtype
+ var mtype2 = anode.n_expr2.mtype
+
+ if mtype == null or mtype2 == null then return
+
+ if not mtype2 isa MNullType then return
+
+ if mtype isa MNullType then return
+
+ # Check of useless null
+ if not check_can_be_null(anode.n_expr, mtype) then return
+
+ mtype = mtype.as_notnull
+
+ # Check for type adaptation
+ var variable = anode.n_expr.its_variable
+ if variable == null then return
+
+ # One is null (mtype2 see above) the other is not null
+ if anode isa AEqExpr then
+ anode.after_flow_context.when_true.set_var(self, variable, mtype2)
+ anode.after_flow_context.when_false.set_var(self, variable, mtype)
+ else if anode isa ANeExpr then
+ anode.after_flow_context.when_false.set_var(self, variable, mtype2)
+ anode.after_flow_context.when_true.set_var(self, variable, mtype)
+ else
+ abort
+ end
+ end
+