# The module of the analysis
# Used to correctly query the model
- var mmodule: MModule
+ var mmodule: MModule is noinit
# The static type of the receiver
# Mainly used for type tests and type resolutions
- var anchor: nullable MClassType = null
+ var anchor: MClassType is noinit
# The analyzed mclassdef
- var mclassdef: nullable MClassDef = null
+ var mclassdef: MClassDef is noinit
# The analyzed property
- var mpropdef: nullable MPropDef
+ var mpropdef: MPropDef
var selfvariable = new Variable("self")
init
do
var mpropdef = self.mpropdef
+ var mclassdef = mpropdef.mclassdef
+ mmodule = mclassdef.mmodule
+ self.mclassdef = mclassdef
+ self.anchor = mclassdef.bound_mtype
- if mpropdef != null then
- self.mpropdef = mpropdef
- var mclassdef = mpropdef.mclassdef
- self.mclassdef = mclassdef
- self.anchor = mclassdef.bound_mtype
-
- var mclass = mclassdef.mclass
+ var mclass = mclassdef.mclass
- var selfvariable = new Variable("self")
- self.selfvariable = selfvariable
- selfvariable.declared_type = mclass.mclass_type
+ var selfvariable = new Variable("self")
+ self.selfvariable = selfvariable
+ selfvariable.declared_type = mclass.mclass_type
- var mprop = mpropdef.mproperty
- if mprop isa MMethod and mprop.is_new then
- is_toplevel_context = true
- end
+ var mprop = mpropdef.mproperty
+ if mprop isa MMethod and mprop.is_new then
+ is_toplevel_context = true
end
end
fun resolve_mtype(node: AType): nullable MType
do
- return self.modelbuilder.resolve_mtype(mmodule, mclassdef, node)
+ return self.modelbuilder.resolve_mtype(mclassdef, node)
end
fun try_get_mclass(node: ANode, name: String): nullable MClass
class CallSite
super MEntity
- redef var location: Location
+ redef var location
# The static type of the receiver (possibly unresolved)
var recv: MType
redef class Variable
# The declared type of the variable
- var declared_type: nullable MType is writable
+ var declared_type: nullable MType = null is writable
# Was the variable type-adapted?
# This is used to speedup type retrieval while it remains `false`
var mpropdef = self.mpropdef
if mpropdef == null then return # skip error
- var v = new TypeVisitor(modelbuilder, mpropdef.mclassdef.mmodule, mpropdef)
+ var v = new TypeVisitor(modelbuilder, mpropdef)
self.selfvariable = v.selfvariable
var mmethoddef = self.mpropdef.as(not null)
var mpropdef = self.mreadpropdef
if mpropdef == null or mpropdef.msignature == null then return # skip error
- var v = new TypeVisitor(modelbuilder, mpropdef.mclassdef.mmodule, mpropdef)
+ var v = new TypeVisitor(modelbuilder, mpropdef)
self.selfvariable = v.selfvariable
var nexpr = self.n_expr
redef class ACharExpr
redef fun accept_typing(v) do
var mclass: nullable MClass = null
- if is_ascii then
- mclass = v.get_mclass(self, "Byte")
- else if is_code_point then
+ if is_code_point then
mclass = v.get_mclass(self, "Int")
else
mclass = v.get_mclass(self, "Char")
# The property invoked by the send.
var callsite: nullable CallSite
+ # Is self a safe call (with `x?.foo`)?
+ # If so and the receiver is null, then the arguments won't be evaluated
+ # and the call skipped (replaced with null).
+ var is_safe: Bool = false
+
redef fun bad_expr_message(child)
do
if child == self.n_expr then
do
var nrecv = self.n_expr
var recvtype = v.visit_expr(nrecv)
+
+ if nrecv isa ASafeExpr then
+ # Has the receiver the form `x?.foo`?
+ # For parsing "reasons" the `?` is in the receiver node, not the call node.
+ is_safe = true
+ end
+
var name = self.property_name
var node = self.property_node
var ret = msignature.return_mtype
if ret != null then
+ if is_safe then
+ # A safe receiver makes that the call is not executed and returns null
+ ret = ret.as_nullable
+ end
self.mtype = ret
else
self.is_typed = true
if mtype == null or mtype2 == null then return
+ if mtype == v.type_bool(self) and (n_expr2 isa AFalseExpr or n_expr2 isa ATrueExpr) then
+ v.modelbuilder.warning(self, "useless-truism", "Warning: useless comparison to a Bool literal.")
+ end
+
if not mtype2 isa MNullType then return
v.check_can_be_null(n_expr, mtype)
redef fun accept_typing(v)
do
var anchor = v.anchor
- assert anchor != null
var recvtype = v.get_variable(self, v.selfvariable)
assert recvtype != null
var mproperty = v.mpropdef.mproperty
private fun process_superinit(v: TypeVisitor)
do
var anchor = v.anchor
- assert anchor != null
var recvtype = v.get_variable(self, v.selfvariable)
assert recvtype != null
var mpropdef = v.mpropdef
end
end
+redef class ASafeExpr
+ redef fun accept_typing(v)
+ do
+ var mtype = v.visit_expr(n_expr)
+ if mtype == null then return # Skip error
+
+ if mtype isa MNullType then
+ # While `null?.foo` is semantically well defined and should not execute `foo` and just return `null`,
+ # currently `null.foo` is forbidden so it seems coherent to also forbid `null?.foo`
+ v.modelbuilder.error(self, "Error: safe operator `?` on `null`.")
+ return
+ end
+
+ self.mtype = mtype.as_notnull
+
+ if not v.can_be_null(mtype) then
+ v.modelbuilder.warning(self, "useless-safe", "Warning: useless safe operator `?` on non-nullable value.")
+ return
+ end
+ end
+end
+
redef class AVarargExpr
redef fun accept_typing(v)
do