From d72e5f9f46247621b77234ca0f36493d51cbe14e Mon Sep 17 00:00:00 2001 From: Florian Deljarry Date: Tue, 9 Jul 2019 16:21:41 -0400 Subject: [PATCH] typing: get_method refactoring Split the get_method to three different entry points. These new entry points make it possible to adapt the treatment to the actual context. Signed-off-by: Florian Deljarry --- src/semantize/typing.nit | 104 +++++++++++++++++++++++++++++----------------- 1 file changed, 65 insertions(+), 39 deletions(-) diff --git a/src/semantize/typing.nit b/src/semantize/typing.nit index 3463b07..b173445 100644 --- a/src/semantize/typing.nit +++ b/src/semantize/typing.nit @@ -297,7 +297,13 @@ private class TypeVisitor 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) @@ -327,23 +333,37 @@ private class TypeVisitor 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 @@ -351,25 +371,31 @@ private class TypeVisitor 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) @@ -386,16 +412,16 @@ private class TypeVisitor 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 @@ -1113,7 +1139,7 @@ redef class AReassignFormExpr 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 @@ -1318,7 +1344,7 @@ redef class AForGroup 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 @@ -1375,31 +1401,31 @@ redef class AForGroup 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 @@ -1412,12 +1438,12 @@ redef class AForGroup 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 @@ -1431,8 +1457,8 @@ redef class AWithExpr 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 @@ -1605,10 +1631,10 @@ redef class AugmentedStringFormExpr 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 @@ -1618,13 +1644,13 @@ redef class AugmentedStringFormExpr 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 @@ -1718,8 +1744,8 @@ redef class AArrayExpr 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 @@ -1753,9 +1779,9 @@ redef class ARangeExpr # 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 @@ -1930,7 +1956,7 @@ redef class ASendExpr 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 @@ -1942,7 +1968,7 @@ redef class ASendExpr 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 @@ -2076,7 +2102,7 @@ redef class ASendReassignFormExpr 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 @@ -2091,7 +2117,7 @@ redef class ASendReassignFormExpr 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 @@ -2314,7 +2340,7 @@ redef class ANewExpr 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 -- 1.7.9.5