src: cleanup importations
[nit.git] / src / auto_super_init.nit
index 1fecf9f..19d60f5 100644 (file)
@@ -19,8 +19,7 @@
 module auto_super_init
 
 import typing
-import modelbuilder
-import phase
+private import annotation
 
 redef class ToolContext
        var auto_super_init_phase: Phase = new AutoSuperInitPhase(self, [typing_phase])
@@ -28,7 +27,7 @@ end
 
 private class AutoSuperInitPhase
        super Phase
-       redef fun process_npropdef(npropdef) do if npropdef isa AConcreteMethPropdef then npropdef.do_auto_super_init(toolcontext.modelbuilder)
+       redef fun process_npropdef(npropdef) do if npropdef isa AMethPropdef then npropdef.do_auto_super_init(toolcontext.modelbuilder)
 end
 
 private class AutoSuperInitVisitor
@@ -43,13 +42,16 @@ private class AutoSuperInitVisitor
                n.visit_all(self)
        end
 
-       var has_explicit_super_init: Bool = false
+       var has_explicit_super_init: nullable ANode = null
 end
 
 
-redef class AConcreteMethPropdef
-       # In case of constructor, the list of implicit auto super init constructors invoked (if needed)
-       var auto_super_inits: nullable Array[MMethod] = null
+redef class AMethPropdef
+       # In case of introduced constructor, the list of implicit auto super init constructors invoked (if needed)
+       var auto_super_inits: nullable Array[CallSite] = null
+
+       # In case of redefined constructors, is an implicit call-to-super required?
+       var auto_super_call = false
 
        fun do_auto_super_init(modelbuilder: ModelBuilder)
        do
@@ -57,9 +59,16 @@ redef class AConcreteMethPropdef
                var mpropdef = self.mpropdef.as(not null)
                var mmodule = mpropdef.mclassdef.mmodule
                var anchor = mclassdef.bound_mtype
+               var recvtype = mclassdef.mclass.mclass_type
+
+               # Get the annotation, but check its pertinence before returning
+               var nosuper = get_single_annotation("nosuper", modelbuilder)
 
                # Collect only for constructors
-               if not mpropdef.mproperty.is_init then return
+               if not mpropdef.mproperty.is_init then
+                       if nosuper != null then modelbuilder.error(nosuper, "Error: nosuper only in `init`")
+                       return
+               end
 
                # FIXME: THIS IS STUPID (be here to keep the old code working)
                if not mpropdef.mclassdef.is_intro then return
@@ -78,12 +87,27 @@ redef class AConcreteMethPropdef
                if nblock != null then
                        var v = new AutoSuperInitVisitor
                        v.enter_visit(nblock)
-                       if v.has_explicit_super_init then return
+                       var anode = v.has_explicit_super_init
+                       if anode != null then
+                               if nosuper != null then modelbuilder.error(anode, "Error: method is annotated nosuper but a constructor call is present")
+                               return
+                       end
+               end
+
+               if nosuper != null then return
+
+               # Still here? So it means that we must add an implicit super-call on redefinitions.
+               if not mpropdef.is_intro then
+                       auto_super_call = true
+                       mpropdef.has_supercall = true
+                       return
                end
 
                # Still here? So it means that we must determine what super inits need to be automatically invoked
+               # The code that follow is required to deal with complex cases with old-style and new-style inits
 
-               var auto_super_inits = new Array[MMethod]
+               # Look for old-style super constructors
+               var auto_super_inits = new Array[CallSite]
                for msupertype in mclassdef.supertypes do
                        # FIXME: the order is quite arbitrary
                        if not msupertype.mclass.kind.need_init then continue
@@ -97,14 +121,62 @@ redef class AConcreteMethPropdef
                                return
                        end
                        assert candidate isa MMethod
-                       auto_super_inits.add(candidate)
+
+                       # Skip new-style init
+                       if candidate.is_root_init then continue
+
+                       var candidatedefs = candidate.lookup_definitions(mmodule, anchor)
+                       var candidatedef = candidatedefs.first
+                       # TODO, we drop the others propdefs in the callsite, that is not great :(
+
+                       var msignature = candidatedef.new_msignature or else candidatedef.msignature
+                       msignature = msignature.resolve_for(recvtype, anchor, mmodule, true)
+
+                       var callsite = new CallSite(self, recvtype, mmodule, anchor, true, candidate, candidatedef, msignature, false)
+                       auto_super_inits.add(callsite)
+               end
+
+               # No old style? The look for new-style super constructors (called from a old style constructor)
+               var the_root_init_mmethod = modelbuilder.the_root_init_mmethod
+               if the_root_init_mmethod != null and auto_super_inits.is_empty then
+                       var candidatedefs = the_root_init_mmethod.lookup_definitions(mmodule, anchor)
+
+                       # Search the longest-one and checks for conflict
+                       var candidatedef = candidatedefs.first
+                       if candidatedefs.length > 1 then
+                               # Check for conflict in the order of initializers
+                               # Each initializer list must me a prefix of the longest list
+                               # part 1. find the longest list
+                               for spd in candidatedefs do
+                                       if spd.initializers.length > candidatedef.initializers.length then candidatedef = spd
+                               end
+                               # compare
+                               for spd in candidatedefs do
+                                       var i = 0
+                                       for p in spd.initializers do
+                                               if p != candidatedef.initializers[i] then
+                                                       modelbuilder.error(self, "Error: Cannot do an implicit constructor call to comflicting for inherited inits {spd}({spd.initializers.join(", ")}) and {candidatedef}({candidatedef.initializers.join(", ")}). NOTE: Do not mix old-style and new-style init!")
+                                                       return
+                                               end
+                                               i += 1
+                                       end
+                               end
+                       end
+
+                       var msignature = candidatedef.new_msignature or else candidatedef.msignature
+                       msignature = msignature.resolve_for(recvtype, anchor, mmodule, true)
+
+                       var callsite = new CallSite(self, recvtype, mmodule, anchor, true, the_root_init_mmethod, candidatedef, msignature, false)
+                       auto_super_inits.add(callsite)
                end
                if auto_super_inits.is_empty then
                        modelbuilder.error(self, "Error: No constructors to call implicitely in {mpropdef}. Call one explicitely.")
                        return
                end
+
+               # Can the super-constructors be called?
                for auto_super_init in auto_super_inits do
-                       var auto_super_init_def = auto_super_init.intro
+                       var auto_super_init_def = auto_super_init.mpropdef
                        var msig = mpropdef.msignature.as(not null)
                        var supermsig = auto_super_init.msignature
                        if supermsig.arity > msig.arity then
@@ -139,7 +211,7 @@ redef class ASendExpr
                var mproperty = self.callsite.mproperty
                if mproperty == null then return
                if mproperty.is_init then
-                       v.has_explicit_super_init = true
+                       v.has_explicit_super_init = self
                end
        end
 end
@@ -149,6 +221,6 @@ redef class ASuperExpr
        do
                # If the super is a standard call-next-method then there it is considered am explicit super init call
                # The the super is a "super int" then it is also an explicit super init call
-               v.has_explicit_super_init = true
+               v.has_explicit_super_init = self
        end
 end