# 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: MPropDef
do
var mpropdef = self.mpropdef
var mclassdef = mpropdef.mclassdef
+ mmodule = mclassdef.mmodule
self.mclassdef = mclassdef
self.anchor = mclassdef.bound_mtype
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
return mclass.mclass_type
end
- fun get_method(node: ANode, recvtype: MType, name: String, recv_is_self: Bool): nullable CallSite
+ # Construction of a specific callsite according to the current context.
+ # Three entry points exist to create a callsite based on knowledge.
+ # The `build_callsite_by_name` is a top entry point, the method find the mpropdefs to call by the name of this.
+ # see `build_callsite_by_property` and `build_callsite_by_propdef` for more detail.
+ # If you already know the mpropdef to call use directly the `get_method_by_propdef` method
+ # If you just know the mproperty use the `build_callsite_by_property` method to display error if no `mpropdef` is found in the context
+ fun build_callsite_by_name(node: ANode, recvtype: MType, name: String, recv_is_self: Bool): nullable CallSite
do
var unsafe_type = self.anchor_to(recvtype)
assert mproperty isa MMethod
+ return build_callsite_by_property(node, recvtype, mproperty, recv_is_self)
+ end
+
+ # The `build_callsite_by_property` finds the mpropdefs to call by the `MMethod`.
+ # If the mpropdef is found in the context it builds a new `Callsite`.
+ fun build_callsite_by_property(node: ANode, recvtype: MType, mproperty: MMethod, recv_is_self: Bool): nullable CallSite
+ do
+ var unsafe_type = self.anchor_to(recvtype)
+
+ if recvtype isa MNullType then
+ var objclass = get_mclass(node, "Object")
+ if objclass == null then return null # Forward error
+ unsafe_type = objclass.mclass_type
+ end
# `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`.")
+ self.error(node, "Error: method `{mproperty.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.")
+ error(node, "Error: `{mproperty.name}` is not a top-level method, thus need a receiver.")
end
if not recv_is_self and mproperty.is_toplevel then
- error(node, "Error: cannot call `{name}`, a top-level method, with a receiver.")
+ error(node, "Error: cannot call `{mproperty.name}`, a top-level method, with a receiver.")
end
if mproperty.visibility == protected_visibility and not recv_is_self and self.mmodule.visibility_for(mproperty.intro_mclassdef.mmodule) < intrude_visibility and not modelbuilder.toolcontext.opt_ignore_visibility.value then
- self.modelbuilder.error(node, "Error: method `{name}` is protected and can only accessed by `self`.")
+ self.modelbuilder.error(node, "Error: method `{mproperty.name}` is protected and can only accessed by `self`.")
return null
end
if info != null and self.mpropdef.mproperty.deprecation == null then
var mdoc = info.mdoc
if mdoc != null then
- self.modelbuilder.warning(node, "deprecated-method", "Deprecation Warning: method `{name}` is deprecated: {mdoc.content.first}")
+ self.modelbuilder.warning(node, "deprecated-method", "Deprecation Warning: method `{mproperty.name}` is deprecated: {mdoc.content.first}")
else
- self.modelbuilder.warning(node, "deprecated-method", "Deprecation Warning: method `{name}` is deprecated.")
+ self.modelbuilder.warning(node, "deprecated-method", "Deprecation Warning: method `{mproperty.name}` is deprecated.")
end
end
var propdefs = mproperty.lookup_definitions(self.mmodule, unsafe_type)
var mpropdef
if propdefs.length == 0 then
- self.modelbuilder.error(node, "Type Error: no definition found for property `{name}` in `{unsafe_type}`.")
- return null
+ self.modelbuilder.error(node, "Type Error: no definition found for property `{mproperty.name}` in `{unsafe_type}`.")
+ abort
+ #return null
else if propdefs.length == 1 then
mpropdef = propdefs.first
else
- self.modelbuilder.warning(node, "property-conflict", "Warning: conflicting property definitions for property `{name}` in `{unsafe_type}`: {propdefs.join(" ")}")
+ self.modelbuilder.warning(node, "property-conflict", "Warning: conflicting property definitions for property `{mproperty.name}` in `{unsafe_type}`: {propdefs.join(" ")}")
mpropdef = mproperty.intro
end
+ return build_callsite_by_propdef(node, recvtype, mpropdef, recv_is_self)
+ end
+ # The `build_callsite_by_propdef` builds the callsite directly with the `mprodef` passed in argument.
+ fun build_callsite_by_propdef(node: ANode, recvtype: MType, mpropdef: MMethodDef, recv_is_self: Bool): nullable CallSite
+ do
var msignature = mpropdef.new_msignature or else mpropdef.msignature
if msignature == null then return null # skip error
msignature = resolve_for(msignature, recvtype, recv_is_self).as(MSignature)
end
end
- var callsite = new CallSite(node.hot_location, recvtype, mmodule, anchor, recv_is_self, mproperty, mpropdef, msignature, erasure_cast)
+ var callsite = new CallSite(node.hot_location, recvtype, mmodule, anchor, recv_is_self, mpropdef.mproperty, mpropdef, msignature, erasure_cast)
return callsite
end
- fun try_get_method(node: ANode, recvtype: MType, name: String, recv_is_self: Bool): nullable CallSite
+ fun try_build_callsite_by_name(node: ANode, recvtype: MType, name: String, recv_is_self: Bool): nullable CallSite
do
var unsafe_type = self.anchor_to(recvtype)
var mproperty = self.try_get_mproperty_by_name2(node, unsafe_type, name)
if mproperty == null then return null
- return get_method(node, recvtype, name, recv_is_self)
+ return build_callsite_by_name(node, recvtype, name, recv_is_self)
end
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
self.read_type = readtype
- var callsite = v.get_method(self.n_assign_op, readtype, reassign_name, false)
+ var callsite = v.build_callsite_by_name(self.n_assign_op, readtype, reassign_name, false)
if callsite == null then return null # Skip error
self.reassign_callsite = callsite
if objcla == null then return
# check iterator method
- var itdef = v.get_method(self, mtype, "iterator", n_expr isa ASelfExpr)
+ var itdef = v.build_callsite_by_name(self, mtype, "iterator", n_expr isa ASelfExpr)
if itdef == null then
v.error(self, "Type Error: `for` expects a type providing an `iterator` method, got `{mtype}`.")
return
self.coltype = mtype.as(MClassType)
# get methods is_ok, next, item
- var ikdef = v.get_method(self, ittype, "is_ok", false)
+ var ikdef = v.build_callsite_by_name(self, ittype, "is_ok", false)
if ikdef == null then
v.error(self, "Type Error: `for` expects a method `is_ok` in type `{ittype}`.")
return
end
self.method_is_ok = ikdef
- var itemdef = v.get_method(self, ittype, "item", false)
+ var itemdef = v.build_callsite_by_name(self, ittype, "item", false)
if itemdef == null then
v.error(self, "Type Error: `for` expects a method `item` in type `{ittype}`.")
return
end
self.method_item = itemdef
- var nextdef = v.get_method(self, ittype, "next", false)
+ var nextdef = v.build_callsite_by_name(self, ittype, "next", false)
if nextdef == null then
v.error(self, "Type Error: `for` expects a method `next` in type {ittype}.")
return
end
self.method_next = nextdef
- self.method_finish = v.try_get_method(self, ittype, "finish", false)
+ self.method_finish = v.try_build_callsite_by_name(self, ittype, "finish", false)
if is_map then
- var keydef = v.get_method(self, ittype, "key", false)
+ var keydef = v.build_callsite_by_name(self, ittype, "key", false)
if keydef == null then
v.error(self, "Type Error: `for` expects a method `key` in type `{ittype}`.")
return
var vtype = variable.declared_type.as(not null)
if n_expr isa AOrangeExpr then
- self.method_lt = v.get_method(self, vtype, "<", false)
+ self.method_lt = v.build_callsite_by_name(self, vtype, "<", false)
else
- self.method_lt = v.get_method(self, vtype, "<=", false)
+ self.method_lt = v.build_callsite_by_name(self, vtype, "<=", false)
end
- self.method_successor = v.get_method(self, vtype, "successor", false)
+ self.method_successor = v.build_callsite_by_name(self, vtype, "successor", false)
end
end
end
var mtype = v.visit_expr(n_expr)
if mtype == null then return
- method_start = v.get_method(self, mtype, "start", n_expr isa ASelfExpr)
- method_finish = v.get_method(self, mtype, "finish", n_expr isa ASelfExpr)
+ method_start = v.build_callsite_by_name(self, mtype, "start", n_expr isa ASelfExpr)
+ method_finish = v.build_callsite_by_name(self, mtype, "finish", n_expr isa ASelfExpr)
v.visit_stmt(n_block)
self.mtype = n_block.mtype
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")
var mclass = v.get_mclass(self, "String")
if mclass == null then return # Forward error
if is_bytestring then
- to_bytes_with_copy = v.get_method(self, v.mmodule.c_string_type, "to_bytes_with_copy", false)
+ to_bytes_with_copy = v.build_callsite_by_name(self, v.mmodule.c_string_type, "to_bytes_with_copy", false)
mclass = v.get_mclass(self, "Bytes")
else if is_re then
- to_re = v.get_method(self, mclass.mclass_type, "to_re", false)
+ to_re = v.build_callsite_by_name(self, mclass.mclass_type, "to_re", false)
for i in suffix.chars do
mclass = v.get_mclass(self, "Regex")
if mclass == null then
var service = ""
if i == 'i' then
service = "ignore_case="
- ignore_case = v.get_method(self, mclass.mclass_type, service, false)
+ ignore_case = v.build_callsite_by_name(self, mclass.mclass_type, service, false)
else if i == 'm' then
service = "newline="
- newline = v.get_method(self, mclass.mclass_type, service, false)
+ newline = v.build_callsite_by_name(self, mclass.mclass_type, service, false)
else if i == 'b' then
service = "extended="
- extended = v.get_method(self, mclass.mclass_type, service, false)
+ extended = v.build_callsite_by_name(self, mclass.mclass_type, service, false)
else
v.error(self, "Type Error: Unrecognized suffix {i} in prefixed Regex")
abort
if mclass == null then return # Forward error
var array_mtype = mclass.get_mtype([mtype])
- with_capacity_callsite = v.get_method(self, array_mtype, "with_capacity", false)
- push_callsite = v.get_method(self, array_mtype, "push", false)
+ with_capacity_callsite = v.build_callsite_by_name(self, array_mtype, "with_capacity", false)
+ push_callsite = v.build_callsite_by_name(self, array_mtype, "push", false)
self.mtype = array_mtype
end
# get the constructor
var callsite
if self isa ACrangeExpr then
- callsite = v.get_method(self, mtype, "init", false)
+ callsite = v.build_callsite_by_name(self, mtype, "init", false)
else if self isa AOrangeExpr then
- callsite = v.get_method(self, mtype, "without_last", false)
+ callsite = v.build_callsite_by_name(self, mtype, "without_last", false)
else
abort
end
# 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 systype = sysclass.mclass_type
mproperty = v.try_get_mproperty_by_name2(node, systype, name)
if mproperty != null then
- callsite = v.get_method(node, systype, name, false)
+ callsite = v.build_callsite_by_name(node, systype, name, false)
if callsite == null then return # Forward error
# Update information, we are looking at `sys` now, not `self`
nrecv.is_sys = true
end
if callsite == null then
# If still nothing, just exit
- callsite = v.get_method(node, recvtype, name, nrecv isa ASelfExpr)
+ callsite = v.build_callsite_by_name(node, recvtype, name, nrecv isa ASelfExpr)
if callsite == null then return
end
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)
if recvtype == null then return # Forward error
var for_self = self.n_expr isa ASelfExpr
- var callsite = v.get_method(node, recvtype, name, for_self)
+ var callsite = v.build_callsite_by_name(node, recvtype, name, for_self)
if callsite == null then return
self.callsite = callsite
return
end
- var wcallsite = v.get_method(node, recvtype, name + "=", self.n_expr isa ASelfExpr)
+ var wcallsite = v.build_callsite_by_name(node, recvtype, name + "=", self.n_expr isa ASelfExpr)
if wcallsite == null then return
self.write_callsite = wcallsite
redef fun compute_raw_arguments do return n_args.to_a
end
+redef class ACallrefExpr
+ redef fun property_name do return n_qid.n_id.text
+ redef fun property_node do return n_qid
+ redef fun compute_raw_arguments do return n_args.to_a
+
+ redef fun accept_typing(v)
+ do
+ super # do the job as if it was a real call
+
+ # TODO: inspect self.callsite to get information about the method
+ var res = callsite.mproperty
+
+ # TODO: return a functionnal type
+ self.mtype = null
+ v.error(self, "Error: NOT YET IMPLEMENTED callref expressions.")
+ end
+end
+
redef class AExprs
fun to_a: Array[AExpr] do return self.n_exprs.to_a
end
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
return
end
- var callsite = v.get_method(node, recvtype, name, false)
+ var callsite = v.build_callsite_by_name(node, recvtype, name, false)
if callsite == null then return
if not callsite.mproperty.is_new then
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