rta: add `live_types_to_csv` to provide human-readable info on types
[nit.git] / src / modelize_property.nit
index 115f348..89eb0f3 100644 (file)
 # Analysis and verification of property definitions to instantiate model element
 module modelize_property
 
-import parser
-import model
-import poset
-import opts
-import toolcontext
-import phase
-import modelbuilder
+import modelize_class
 
 redef class ToolContext
        var modelize_property_phase: Phase = new ModelizePropertyPhase(self, [modelize_class_phase])
@@ -41,10 +35,10 @@ end
 
 redef class ModelBuilder
        # Register the npropdef associated to each mpropdef
-       # FIXME: why not refine the MPropDef class with a nullable attribute?
+       # FIXME: why not refine the `MPropDef` class with a nullable attribute?
        var mpropdef2npropdef: HashMap[MPropDef, APropdef] = new HashMap[MPropDef, APropdef]
 
-       # Build the properties of `nclassdef'.
+       # Build the properties of `nclassdef`.
        # REQUIRE: all superclasses are built.
        private fun build_properties(nclassdef: AClassdef)
        do
@@ -70,7 +64,7 @@ redef class ModelBuilder
        end
 
        # Introduce or inherit default constructor
-       # This is the last part of `build_properties'.
+       # This is the last part of `build_properties`.
        private fun process_default_constructors(nclassdef: AClassdef)
        do
                var mclassdef = nclassdef.mclassdef.as(not null)
@@ -164,6 +158,10 @@ redef class MClass
        var inherit_init_from: nullable MClass = null
 end
 
+redef class MClassDef
+       private var propdef_names = new HashSet[String]
+end
+
 redef class AClassdef
        var build_properties_is_done: Bool = false
        # The list of super-constructor to call at the start of the free constructor
@@ -173,7 +171,7 @@ redef class AClassdef
        # The free init (implicitely constructed by the class if required)
        var mfree_init: nullable MMethodDef = null
 
-       # What is the APropdef associated to a MProperty?
+       # What is the `APropdef` associated to a `MProperty`?
        # Used to check multiple definition of a property.
        var mprop2npropdef: Map[MProperty, APropdef] = new HashMap[MProperty, APropdef]
 end
@@ -201,19 +199,25 @@ private class TextCollectorVisitor
 end
 
 redef class APropdef
-       private fun build_property(modelbuilder: ModelBuilder, nclassdef: AClassdef)
-       do
-       end
-       private fun build_signature(modelbuilder: ModelBuilder, nclassdef: AClassdef)
-       do
-       end
-       private fun check_signature(modelbuilder: ModelBuilder, nclassdef: AClassdef)
-       do
-       end
+       # The associated main model entity
+       type MPROPDEF: MPropDef
+
+       # The associated propdef once build by a `ModelBuilder`
+       var mpropdef: nullable MPROPDEF writable
+
+       private fun build_property(modelbuilder: ModelBuilder, nclassdef: AClassdef) is abstract
+       private fun build_signature(modelbuilder: ModelBuilder, nclassdef: AClassdef) is abstract
+       private fun check_signature(modelbuilder: ModelBuilder, nclassdef: AClassdef) is abstract
        private fun new_property_visibility(modelbuilder: ModelBuilder, nclassdef: AClassdef, nvisibility: nullable AVisibility): MVisibility
        do
                var mvisibility = public_visibility
-               if nvisibility != null then mvisibility = nvisibility.mvisibility
+               if nvisibility != null then
+                       mvisibility = nvisibility.mvisibility
+                       if mvisibility == intrude_visibility then
+                               modelbuilder.error(nvisibility, "Error: intrude is not a legal visibility for properties.")
+                               mvisibility = public_visibility
+                       end
+               end
                if nclassdef.mclassdef.mclass.visibility == private_visibility then
                        if mvisibility == protected_visibility then
                                assert nvisibility != null
@@ -304,10 +308,6 @@ redef class ASignature
                        if self.ret_type == null then return false # Skip errir
                end
 
-               for nclosure in self.n_closure_decls do
-                       if not nclosure.n_signature.visit_signature(modelbuilder, nclassdef) then return false
-               end
-
                self.is_visited = true
                return true
        end
@@ -324,6 +324,7 @@ redef class ASignature
                var mparameters = new Array[MParameter]
                for i in [0..param_names.length[ do
                        var mparameter = new MParameter(param_names[i], param_types[i], i == vararg_rank)
+                       self.n_params[i].mparameter = mparameter
                        mparameters.add(mparameter)
                end
 
@@ -332,12 +333,14 @@ redef class ASignature
        end
 end
 
+redef class AParam
+       # The associated mparameter if any
+       var mparameter: nullable MParameter = null
+end
+
 redef class AMethPropdef
-       # The associated MMethodDef once build by a `ModelBuilder'
-       var mpropdef: nullable MMethodDef
+       redef type MPROPDEF: MMethodDef
 
-       # The associated super init if any
-       var super_init: nullable MMethod
        redef fun build_property(modelbuilder, nclassdef)
        do
                var is_init = self isa AInitPropdef
@@ -393,6 +396,20 @@ redef class AMethPropdef
 
                var mpropdef = new MMethodDef(mclassdef, mprop, self.location)
 
+               if mclassdef.propdef_names.has(mprop.name) then
+                       var loc: nullable Location = null
+                       for i in mclassdef.mpropdefs do
+                               if i.mproperty.name == mprop.name then
+                                       loc = i.location
+                                       break
+                               end
+                       end
+                       if loc == null then abort
+                       modelbuilder.error(self, "Error: a property {mprop} is already defined in class {mclassdef.mclass} at {loc}")
+               end
+
+               mclassdef.propdef_names.add(mpropdef.mproperty.name)
+
                self.mpropdef = mpropdef
                modelbuilder.mpropdef2npropdef[mpropdef] = self
                if mpropdef.is_intro then
@@ -472,21 +489,13 @@ redef class AMethPropdef
                var mparameters = new Array[MParameter]
                for i in [0..param_names.length[ do
                        var mparameter = new MParameter(param_names[i], param_types[i], i == vararg_rank)
+                       if nsig != null then nsig.n_params[i].mparameter = mparameter
                        mparameters.add(mparameter)
                end
 
                msignature = new MSignature(mparameters, ret_type)
                mpropdef.msignature = msignature
-
-               if nsig != null then
-                       for nclosure in nsig.n_closure_decls do
-                               var clos_signature = nclosure.n_signature.build_signature(modelbuilder, nclassdef)
-                               if clos_signature == null then return
-                               var mparameter = new MParameter(nclosure.n_id.text, clos_signature, false)
-                               msignature.mclosures.add(mparameter)
-                       end
-               end
-
+               mpropdef.is_abstract = self isa ADeferredMethPropdef
        end
 
        redef fun check_signature(modelbuilder, nclassdef)
@@ -516,9 +525,9 @@ redef class AMethPropdef
                                for i in [0..mysignature.arity[ do
                                        var myt = mysignature.mparameters[i].mtype
                                        var prt = msignature.mparameters[i].mtype
-                                       if not myt.is_subtype(mmodule, nclassdef.mclassdef.bound_mtype, prt) and
+                                       if not myt.is_subtype(mmodule, nclassdef.mclassdef.bound_mtype, prt) or
                                                        not prt.is_subtype(mmodule, nclassdef.mclassdef.bound_mtype, myt) then
-                                               modelbuilder.error(nsig.n_params[i], "Redef Error: Wrong type for parameter `{mysignature.mparameters[i].name}'. found {myt}, expected {prt}.")
+                                               modelbuilder.error(nsig.n_params[i], "Redef Error: Wrong type for parameter `{mysignature.mparameters[i].name}'. found {myt}, expected {prt} as in {mpropdef.mproperty.intro}.")
                                        end
                                end
                        end
@@ -527,7 +536,7 @@ redef class AMethPropdef
                                        # Inherit the return type
                                        ret_type = precursor_ret_type
                                else if not ret_type.is_subtype(mmodule, nclassdef.mclassdef.bound_mtype, precursor_ret_type) then
-                                       modelbuilder.error(nsig.n_type.as(not null), "Redef Error: Wrong return type. found {ret_type}, expected {precursor_ret_type}.")
+                                       modelbuilder.error(nsig.n_type.as(not null), "Redef Error: Wrong return type. found {ret_type}, expected {precursor_ret_type} as in {mpropdef.mproperty.intro}.")
                                end
                        end
                end
@@ -535,12 +544,12 @@ redef class AMethPropdef
 end
 
 redef class AAttrPropdef
-       # The associated MAttributeDef once build by a `ModelBuilder'
-       var mpropdef: nullable MAttributeDef
+       redef type MPROPDEF: MAttributeDef
+
        # The associated getter (read accessor) if any
-       var mreadpropdef: nullable MMethodDef
+       var mreadpropdef: nullable MMethodDef writable
        # The associated setter (write accessor) if any
-       var mwritepropdef: nullable MMethodDef
+       var mwritepropdef: nullable MMethodDef writable
        redef fun build_property(modelbuilder, nclassdef)
        do
                var mclassdef = nclassdef.mclassdef.as(not null)
@@ -682,8 +691,8 @@ redef class AAttrPropdef
                        if mtype == null then return
                end
 
+               var nexpr = self.n_expr
                if mtype == null then
-                       var nexpr = self.n_expr
                        if nexpr != null then
                                if nexpr isa ANewExpr then
                                        mtype = modelbuilder.resolve_mtype(nclassdef, nexpr.n_type)
@@ -712,6 +721,14 @@ redef class AAttrPropdef
                        else
                                modelbuilder.error(self, "Error: Untyped attribute {mpropdef}")
                        end
+               else
+                       assert ntype != null
+                       if nexpr isa ANewExpr then
+                               var xmtype = modelbuilder.resolve_mtype(nclassdef, nexpr.n_type)
+                               if xmtype == mtype and modelbuilder.toolcontext.opt_warn.value >= 2 then
+                                       modelbuilder.warning(ntype, "Warning: useless type definition")
+                               end
+                       end
                end
 
                if mtype == null then return
@@ -822,8 +839,8 @@ redef class AAttrPropdef
 end
 
 redef class ATypePropdef
-       # The associated MVirtualTypeDef once build by a `ModelBuilder'
-       var mpropdef: nullable MVirtualTypeDef
+       redef type MPROPDEF: MVirtualTypeDef
+
        redef fun build_property(modelbuilder, nclassdef)
        do
                var mclassdef = nclassdef.mclassdef.as(not null)
@@ -832,6 +849,10 @@ redef class ATypePropdef
                if mprop == null then
                        var mvisibility = new_property_visibility(modelbuilder, nclassdef, self.n_visibility)
                        mprop = new MVirtualTypeProp(mclassdef, name, mvisibility)
+                       for c in name do if c >= 'a' and c<= 'z' then
+                               modelbuilder.warning(n_id, "Warning: lowercase in the virtual type {name}")
+                               break
+                       end
                        if not self.check_redef_keyword(modelbuilder, nclassdef, self.n_kwredef, false, mprop) then return
                else
                        if not self.check_redef_keyword(modelbuilder, nclassdef, self.n_kwredef, true, mprop) then return
@@ -861,6 +882,9 @@ redef class ATypePropdef
 
        redef fun check_signature(modelbuilder, nclassdef)
        do
+               var mpropdef = self.mpropdef
+               if mpropdef == null then return # Error thus skiped
+
                var bound = self.mpropdef.bound
 
                # Fast case: the bound is not a formal type