Merge: modelize_property: Define a new API to modify AAttrPropdef
[nit.git] / src / modelize / modelize_property.nit
index c236170..6f7f209 100644 (file)
@@ -53,6 +53,17 @@ redef class ModelBuilder
                mpropdef2npropdef[mpropdef] = npropdef
        end
 
+       # Associate a `nclassdef` with its `mclassdef`
+       #
+       # Be careful, this method is unsafe, no checking is done when it's used.
+       # The safe way to add mclass it's to use the `build_property`
+       #
+       # See `mclassdef2nclassdef`
+       fun unsafe_add_mclassdef2nclassdef(mclassdef: MClassDef, nclassdef: AClassdef)
+       do
+               mclassdef2nclassdef[mclassdef] = nclassdef
+       end
+
        # Retrieve the associated AST node of a mpropertydef.
        # This method is used to associate model entity with syntactic entities.
        #
@@ -149,7 +160,11 @@ redef class ModelBuilder
                var mclassdef = nclassdef.mclassdef.as(not null)
 
                # Are we a refinement
-               if not mclassdef.is_intro then return
+               if not mclassdef.is_intro then
+                       # Set the default_init of the mclassdef with the intro default_init
+                       mclassdef.default_init = mclassdef.mclass.intro.default_init
+                       return
+               end
 
                # Look for the init in Object, or create it
                if mclassdef.mclass.name == "Object" and the_root_init_mmethod == null then
@@ -173,17 +188,15 @@ redef class ModelBuilder
                        if mpropdef.mproperty.is_root_init then
                                assert defined_init == null
                                defined_init = mpropdef
-                       else if mpropdef.mproperty.name == "autoinit" then
-                               # An explicit old-style init named "init", so return
+                       else if mpropdef.name == "defaultinit" then
                                return
                        end
                end
 
-               if mclassdef.auto_init != null then
-                       return
-               end
+               if mclassdef.default_init != null then return
 
-               if not nclassdef isa AStdClassdef then return
+               # If the class is not AStdClassdef or it's an enum just return. No defaultinit is need.
+               if not nclassdef isa AStdClassdef or nclassdef.n_classkind isa AEnumClasskind then return
 
                # Collect undefined attributes
                var mparameters = new Array[MParameter]
@@ -195,7 +208,6 @@ redef class ModelBuilder
                                if mpropdef == null then return # Skip broken method
                                var sig = mpropdef.msignature
                                if sig == null then continue # Skip broken method
-
                                mparameters.add_all sig.mparameters
                                initializers.add(mpropdef.mproperty)
                                mpropdef.mproperty.is_autoinit = true
@@ -240,8 +252,9 @@ redef class ModelBuilder
 
                # Look for most-specific new-stype init definitions
                var spropdefs = new ArraySet[MMethodDef]
-               for x in mclassdef.in_hierarchy.direct_greaters do
-                       var y = x.mclass.intro.auto_init
+
+               for x in mclassdef.get_direct_supermtype do
+                       var y = x.mclass.intro.default_init
                        if y == null then continue
                        if y.is_broken or y.msignature == null then return
                        spropdefs.add y
@@ -299,30 +312,28 @@ redef class ModelBuilder
                                end
                        end
                else if spropdefs.not_empty then
-                       # Search for inherited manual autoinit
+                       # Search for inherited manual defaultinit
                        var manual = null
                        for s in spropdefs do
                                if mpropdef2npropdef.has_key(s) then
-                                       self.toolcontext.info("{mclassdef} inherits a manual autoinit {s}", 3)
-                                       #mclassdef.autoinit = s
-                                       #return
+                                       self.toolcontext.info("{mclassdef} inherits a manual defaultinit {s}", 3)
                                        manual = s
                                end
                        end
-
                        # Search the longest-one and checks for conflict
                        var longest = spropdefs.first
                        if spropdefs.length > 1 then
                                # part 1. find the longest list
                                for spd in spropdefs do
                                        if spd.initializers.length > longest.initializers.length then longest = spd
+
                                        if spd != manual and manual != null then
-                                               self.toolcontext.info("{mclassdef} conflict between manual autoinit {manual} and automatic autoinit {spd}.", 3)
+                                               self.toolcontext.info("{mclassdef} conflict between manual defaultinit {manual} and automatic defaultinit {spd}.", 3)
                                        end
                                end
                                # conflict with manual autoinit?
                                if longest != manual and manual != null then
-                                       self.error(nclassdef, "Error: conflict between manual autoinit {manual} and automatic autoinit {longest}.")
+                                       self.error(nclassdef, "Error: conflict between manual defaultinit {manual} and automatic defaultinit {longest}.")
                                end
                                # part 2. compare
                                # Check for conflict in the order of initializers
@@ -365,13 +376,13 @@ redef class ModelBuilder
 
                # Create a specific new autoinit constructor
                do
-                       var mprop = new MMethod(mclassdef, "autoinit", nclassdef.location, public_visibility)
+                       var mprop = new MMethod(mclassdef, "defaultinit", nclassdef.location, public_visibility)
                        mprop.is_init = true
                        var mpropdef = new MMethodDef(mclassdef, mprop, nclassdef.location)
                        mpropdef.initializers.add_all(initializers)
                        var msignature = new MSignature(mparameters, null)
                        mpropdef.msignature = msignature
-                       mclassdef.auto_init = mpropdef
+                       mclassdef.default_init = mpropdef
                        self.toolcontext.info("{mclassdef} gets a free auto constructor `{mpropdef}{msignature}`. {spropdefs}", 3)
                        mclassdef.mclass.the_root_init_mmethod = the_root_init_mmethod
                end
@@ -789,12 +800,15 @@ redef class AMethPropdef
                var name: String
                var amethodid = self.n_methid
                var name_node: ANode
+               var is_old_style_init = false
                if amethodid == null then
                        if n_kwinit != null then
                                name = "init"
                                name_node = n_kwinit
-                               if self.n_signature.n_params.not_empty or get_single_annotation("old_style_init", modelbuilder) != null then
-                                       name = "autoinit"
+                               var old_style_annot = get_single_annotation("old_style_init", modelbuilder)
+                               if  old_style_annot != null or self.n_signature.n_params.not_empty then
+                                       name = "defaultinit"
+                                       if old_style_annot != null then is_old_style_init = true
                                end
                        else if n_kwnew != null then
                                name = "new"
@@ -874,13 +888,14 @@ redef class AMethPropdef
                mclassdef.mprop2npropdef[mprop] = self
 
                var mpropdef = new MMethodDef(mclassdef, mprop, self.location)
-               if mprop.name == "autoinit" and mclassdef.is_intro then
-                       assert mclassdef.auto_init == null
-                       mclassdef.auto_init = mpropdef
-                       if mpropdef.is_intro then
-                               mpropdef.initializers.add mprop
-                               mpropdef.is_calling_init = true
-                       end
+               if mpropdef.name == "defaultinit" and mclassdef.is_intro then
+                       assert mclassdef.default_init == null
+                       mpropdef.is_old_style_init = is_old_style_init
+                       mclassdef.default_init = mpropdef
+                       # Set the initializers with the mproperty.
+                       # This point is need when a super class define this own default_init and inherited class use the default_init generated automaticlely.
+                       mpropdef.initializers.add mprop
+                       mpropdef.is_calling_init = true
                end
 
                set_doc(mpropdef, modelbuilder)
@@ -1177,6 +1192,10 @@ redef class AAttrPropdef
        # Could be through `n_expr`, `n_block` or `is_lazy`
        var has_value = false
 
+       # The name of the attribute
+       # Note: The name of the attribute is in reality the name of the mreadpropdef
+       var name: String = n_id2.text is lazy
+
        # The guard associated to a lazy attribute.
        # Because some engines does not have a working `isset`,
        # this additional attribute is used to guard the lazy initialization.
@@ -1191,48 +1210,14 @@ redef class AAttrPropdef
        redef fun build_property(modelbuilder, mclassdef)
        do
                var mclass = mclassdef.mclass
-               var nid2 = n_id2
-               var name = nid2.text
 
                var atabstract = self.get_single_annotation("abstract", modelbuilder)
-               if atabstract == null then
-                       if not mclass.kind.need_init then
-                               modelbuilder.error(self, "Error: attempt to define attribute `{name}` in the {mclass.kind} `{mclass}`.")
-                       end
 
-                       var mprop = new MAttribute(mclassdef, "_" + name, self.location, private_visibility)
-                       var mpropdef = new MAttributeDef(mclassdef, mprop, self.location)
-                       self.mpropdef = mpropdef
-                       modelbuilder.mpropdef2npropdef[mpropdef] = self
-               end
+               if atabstract == null then build_attribute_property(modelbuilder, mclassdef)
 
-               var readname = name
-               var mreadprop = modelbuilder.try_get_mproperty_by_name(nid2, mclassdef, readname).as(nullable MMethod)
-               if mreadprop == null then
-                       var mvisibility = new_property_visibility(modelbuilder, mclassdef, self.n_visibility)
-                       mreadprop = new MMethod(mclassdef, readname, self.location, mvisibility)
-                       if not self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, false, mreadprop) then
-                               mreadprop.is_broken = true
-                               return
-                       end
-               else
-                       if mreadprop.is_broken then return
-                       if not self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, true, mreadprop) then return
-                       check_redef_property_visibility(modelbuilder, self.n_visibility, mreadprop)
-               end
-               mclassdef.mprop2npropdef[mreadprop] = self
-
-               var attr_mpropdef = mpropdef
-               if attr_mpropdef != null then
-                       mreadprop.getter_for = attr_mpropdef.mproperty
-                       attr_mpropdef.mproperty.getter = mreadprop
-               end
+               # Construction of the read property. If it's not correctly built just return.
+               if not build_read_property(modelbuilder, mclassdef) then return
 
-               var mreadpropdef = new MMethodDef(mclassdef, mreadprop, self.location)
-               self.mreadpropdef = mreadpropdef
-               modelbuilder.mpropdef2npropdef[mreadpropdef] = self
-               set_doc(mreadpropdef, modelbuilder)
-               if mpropdef != null then mpropdef.mdoc = mreadpropdef.mdoc
                if atabstract != null then mreadpropdef.is_abstract = true
 
                has_value = n_expr != null or n_block != null
@@ -1255,29 +1240,8 @@ redef class AAttrPropdef
                        end
                end
 
-               var atlazy = self.get_single_annotation("lazy", modelbuilder)
-               var atlateinit = self.get_single_annotation("lateinit", modelbuilder)
-               if atlazy != null or atlateinit != null then
-                       if atlazy != null and atlateinit != null then
-                               modelbuilder.error(atlazy, "Error: `lazy` incompatible with `lateinit`.")
-                               return
-                       end
-                       if not has_value then
-                               if atlazy != null then
-                                       modelbuilder.error(atlazy, "Error: `lazy` attributes need a value.")
-                               else if atlateinit != null then
-                                       modelbuilder.error(atlateinit, "Error: `lateinit` attributes need a value.")
-                               end
-                               has_value = true
-                               return
-                       end
-                       is_lazy = true
-                       var mlazyprop = new MAttribute(mclassdef, "lazy _" + name, self.location, none_visibility)
-                       mlazyprop.is_fictive = true
-                       var mlazypropdef = new MAttributeDef(mclassdef, mlazyprop, self.location)
-                       mlazypropdef.is_fictive = true
-                       self.mlazypropdef = mlazypropdef
-               end
+               # Construction of the read property. If it's not correctly built just return.
+               if not build_lazy_property(modelbuilder, mclassdef) then return
 
                var atoptional = self.get_single_annotation("optional", modelbuilder)
                if atoptional != null then
@@ -1300,6 +1264,88 @@ redef class AAttrPropdef
                        modelbuilder.advice(self, "attr-in-refinement", "Warning: attributes in refinement need a value or `noautoinit`.")
                end
 
+               # Construction of the read property. If it's not correctly built just return.
+               if not build_write_property(modelbuilder, mclassdef, false) then return
+
+               if atabstract != null then mwritepropdef.is_abstract = true
+
+               var atautoinit = self.get_single_annotation("autoinit", modelbuilder)
+               if atautoinit != null then
+                       if has_value then
+                               modelbuilder.error(atautoinit, "Error: `autoinit` attributes cannot have an initial value.")
+                       else if not mwritepropdef.is_intro then
+                               modelbuilder.error(atautoinit, "Error: `autoinit` attributes cannot be set on redefinitions.")
+                       else if not mclassdef.is_intro then
+                               modelbuilder.error(atautoinit, "Error: `autoinit` attributes cannot be used in class refinements.")
+                       else if atabstract == null then
+                               modelbuilder.warning(atautoinit, "useless-autoinit", "Warning: superfluous `autoinit` on attribute.")
+                       end
+               else if atabstract != null then
+                       # By default, abstract attribute are not autoinit
+                       noinit = true
+               end
+       end
+
+       # Build the attribute property
+       fun build_attribute_property(modelbuilder: ModelBuilder, mclassdef: MClassDef)
+       do
+               var mclass = mclassdef.mclass
+               var attribute_name = "_" + name
+
+               if not mclass.kind.need_init then
+                       modelbuilder.error(self, "Error: attempt to define attribute `{name}` in the {mclass.kind} `{mclass}`.")
+               end
+               var mprop = new MAttribute(mclassdef, "_" + name, self.location, private_visibility)
+               var mpropdef = new MAttributeDef(mclassdef, mprop, self.location)
+               self.mpropdef = mpropdef
+               modelbuilder.mpropdef2npropdef[mpropdef] = self
+       end
+
+       # Build the read method property to get the value of the attribute
+       # Return `true` if the property was correctly created else return `false`.
+       # Warning the signature of the property is not set. This step is done by `build_signature`.
+       fun build_read_property(modelbuilder: ModelBuilder, mclassdef: MClassDef): Bool
+       do
+               var mclass = mclassdef.mclass
+
+               var readname = name
+               var mreadprop = modelbuilder.try_get_mproperty_by_name(self, mclassdef, readname).as(nullable MMethod)
+               if mreadprop == null then
+                       var mvisibility = new_property_visibility(modelbuilder, mclassdef, self.n_visibility)
+                       mreadprop = new MMethod(mclassdef, readname, self.location, mvisibility)
+                       if not self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, false, mreadprop) then
+                               mreadprop.is_broken = true
+                               return false
+                       end
+               else
+                       if mreadprop.is_broken then return false
+                       if not self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, true, mreadprop) then return false
+                       check_redef_property_visibility(modelbuilder, self.n_visibility, mreadprop)
+               end
+               mclassdef.mprop2npropdef[mreadprop] = self
+
+               var attr_mpropdef = mpropdef
+               if attr_mpropdef != null then
+                       mreadprop.getter_for = attr_mpropdef.mproperty
+                       attr_mpropdef.mproperty.getter = mreadprop
+               end
+
+               var mreadpropdef = new MMethodDef(mclassdef, mreadprop, self.location)
+               self.mreadpropdef = mreadpropdef
+               modelbuilder.mpropdef2npropdef[mreadpropdef] = self
+               set_doc(mreadpropdef, modelbuilder)
+               if mpropdef != null then mpropdef.mdoc = mreadpropdef.mdoc
+
+               return true
+       end
+
+       # Build the write method property to set the attribute value
+       # Return `true` if the property was correctly created else return `false`.
+       # Warning the signature of the property is not set.
+       fun build_write_property(modelbuilder: ModelBuilder, mclassdef: MClassDef, is_same_visibility: Bool): Bool
+       do
+               var mclass = mclassdef.mclass
+
                var writename = name + "="
                var atwritable = self.get_single_annotation("writable", modelbuilder)
                if atwritable != null then
@@ -1307,7 +1353,7 @@ redef class AAttrPropdef
                                writename = atwritable.arg_as_id(modelbuilder) or else writename
                        end
                end
-               var mwriteprop = modelbuilder.try_get_mproperty_by_name(nid2, mclassdef, writename).as(nullable MMethod)
+               var mwriteprop = modelbuilder.try_get_mproperty_by_name(self, mclassdef, writename).as(nullable MMethod)
                var nwkwredef: nullable Token = null
                if atwritable != null then nwkwredef = atwritable.n_kwredef
                if mwriteprop == null then
@@ -1315,25 +1361,26 @@ redef class AAttrPropdef
                        if atwritable != null then
                                mvisibility = new_property_visibility(modelbuilder, mclassdef, atwritable.n_visibility)
                        else
-                               mvisibility = mreadprop.visibility
+                               mvisibility = mreadpropdef.mproperty.visibility
                                # By default, use protected visibility at most
-                               if mvisibility > protected_visibility then mvisibility = protected_visibility
+                               if mvisibility > protected_visibility and not is_same_visibility then mvisibility = protected_visibility
                        end
                        mwriteprop = new MMethod(mclassdef, writename, self.location, mvisibility)
                        if not self.check_redef_keyword(modelbuilder, mclassdef, nwkwredef, false, mwriteprop) then
                                mwriteprop.is_broken = true
-                               return
+                               return false
                        end
-                       mwriteprop.deprecation = mreadprop.deprecation
+                       mwriteprop.deprecation = mreadpropdef.mproperty.deprecation
                else
-                       if mwriteprop.is_broken then return
-                       if not self.check_redef_keyword(modelbuilder, mclassdef, nwkwredef or else n_kwredef, true, mwriteprop) then return
+                       if mwriteprop.is_broken then return false
+                       if not self.check_redef_keyword(modelbuilder, mclassdef, nwkwredef or else n_kwredef, true, mwriteprop) then return false
                        if atwritable != null then
                                check_redef_property_visibility(modelbuilder, atwritable.n_visibility, mwriteprop)
                        end
                end
                mclassdef.mprop2npropdef[mwriteprop] = self
 
+               var attr_mpropdef = mpropdef
                if attr_mpropdef != null then
                        mwriteprop.setter_for = attr_mpropdef.mproperty
                        attr_mpropdef.mproperty.setter = mwriteprop
@@ -1343,23 +1390,35 @@ redef class AAttrPropdef
                self.mwritepropdef = mwritepropdef
                modelbuilder.mpropdef2npropdef[mwritepropdef] = self
                mwritepropdef.mdoc = mreadpropdef.mdoc
-               if atabstract != null then mwritepropdef.is_abstract = true
 
-               var atautoinit = self.get_single_annotation("autoinit", modelbuilder)
-               if atautoinit != null then
-                       if has_value then
-                               modelbuilder.error(atautoinit, "Error: `autoinit` attributes cannot have an initial value.")
-                       else if not mwritepropdef.is_intro then
-                               modelbuilder.error(atautoinit, "Error: `autoinit` attributes cannot be set on redefinitions.")
-                       else if not mclassdef.is_intro then
-                               modelbuilder.error(atautoinit, "Error: `autoinit` attributes cannot be used in class refinements.")
-                       else if atabstract == null then
-                               modelbuilder.warning(atautoinit, "useless-autoinit", "Warning: superfluous `autoinit` on attribute.")
+               return true
+       end
+
+       # Build the lazy attribute property
+       # Return `true` if the property was correctly created else return `false`.
+       fun build_lazy_property(modelbuilder: ModelBuilder, mclassdef: MClassDef): Bool
+       do
+               var mclass = mclassdef.mclass
+
+               var atlazy = self.get_single_annotation("lazy", modelbuilder)
+               var atlateinit = self.get_single_annotation("lateinit", modelbuilder)
+               if atlazy != null or atlateinit != null then
+                       if atlazy != null and atlateinit != null then
+                               modelbuilder.error(atlazy, "Error: `lazy` incompatible with `lateinit`.")
+                               return false
                        end
-               else if atabstract != null then
-                       # By default, abstract attribute are not autoinit
-                       noinit = true
+                       if not has_value then
+                               if atlazy != null then
+                                       modelbuilder.error(atlazy, "Error: `lazy` attributes need a value.")
+                               else if atlateinit != null then
+                                       modelbuilder.error(atlateinit, "Error: `lateinit` attributes need a value.")
+                               end
+                               has_value = true
+                               return false
+                       end
+                       create_lazy
                end
+               return true
        end
 
        redef fun build_signature(modelbuilder)
@@ -1417,23 +1476,9 @@ redef class AAttrPropdef
                        mpropdef.static_mtype = mtype
                end
 
-               do
-                       var msignature = new MSignature(new Array[MParameter], mtype)
-                       mreadpropdef.msignature = msignature
-               end
+               build_read_signature
 
-               var mwritepropdef = self.mwritepropdef
-               if mwritepropdef != null then
-                       var mwritetype = mtype
-                       if is_optional then
-                               mwritetype = mwritetype.as_nullable
-                       end
-                       var name: String
-                       name = n_id2.text
-                       var mparameter = new MParameter(name, mwritetype, false)
-                       var msignature = new MSignature([mparameter], null)
-                       mwritepropdef.msignature = msignature
-               end
+               if self.mwritepropdef != null then build_write_signature
 
                var mlazypropdef = self.mlazypropdef
                if mlazypropdef != null then
@@ -1442,6 +1487,91 @@ redef class AAttrPropdef
                check_repeated_types(modelbuilder)
        end
 
+       # Build the read method signature
+       # `except`: mreadpropdef != null
+       # `expect`: mtype != null
+       fun build_read_signature
+       is
+               expect(mreadpropdef != null and mtype != null)
+       do
+               var msignature = new MSignature(new Array[MParameter], mtype)
+               mreadpropdef.msignature = msignature
+       end
+
+       # Build the write method signature
+       # `except`: mwritepropdef != null
+       # `expect`: mtype != null
+       fun build_write_signature
+       is
+               expect(mwritepropdef != null and mtype != null)
+       do
+               var mwritetype = mtype.as(not null)
+               if is_optional then
+                       mwritetype = mwritetype.as_nullable
+               end
+               var mparameter = new MParameter(name, mwritetype, false)
+               var msignature = new MSignature([mparameter], null)
+               mwritepropdef.msignature = msignature
+       end
+
+       # Create a new setter for the attribute.
+       #
+       # `modelbuilder`: It's used to link the new `mwritepropdef` and `self`
+       # `visibility`: Is the setter has the same visibilty of the `mreadpropdef`.
+       #       If `not is_same_visibility and mreadpropdef.mproperty.visibility > protected_visibility` the `mwritepropdef` visibility will be set to protected.
+       fun create_setter(modelbuilder: ModelBuilder, is_same_visibility: nullable Bool): AAttrPropdef
+       is
+               expect(mreadpropdef != null) # Use to define the visibility, the mclassdef and the doc of the `mwritepropdef`
+       do
+               if mwritepropdef != null then return self # Self already has a `mwritepropdef`
+               var same_visibility = false
+               if is_same_visibility != null then same_visibility = is_same_visibility
+
+               self.build_write_property(modelbuilder, mreadpropdef.mclassdef, same_visibility)
+               self.build_write_signature
+               return self
+       end
+
+       # Set the default `self` value
+       #
+       # `expr`: Represents the default value of the attribute. If `expr isa ABlockExpr` `self.n_block` will be set.
+       fun define_default(expr: AExpr): AAttrPropdef
+       do
+               self.has_value = true
+               if expr isa ABlockExpr then
+                       self.n_block = expr
+               else
+                       self.n_expr = expr
+               end
+               return self
+       end
+
+       # Set `self` as optional
+       fun define_as_optional: AAttrPropdef
+       is
+               expect(has_value)
+       do
+               is_optional = true
+               return self
+       end
+
+       # Create the lazy attribute.
+       #
+       # see `mlazypropdef` for more information about this property.
+       fun create_lazy: AAttrPropdef
+       is
+               expect(has_value and mpropdef != null) # The only way to get a null `mpropdef` is when the attribute is defined as `abstract`. But if the attribute has a value, it cannot be abstract.
+       do
+               if self.mlazypropdef != null then return self # Self already has a `mlazypropdef`
+               is_lazy = true
+               var mlazyprop = new MAttribute(mpropdef.mclassdef, "lazy _" + name, self.location, none_visibility)
+               mlazyprop.is_fictive = true
+               var mlazypropdef = new MAttributeDef(mpropdef.mclassdef, mlazyprop, self.location)
+               mlazypropdef.is_fictive = true
+               self.mlazypropdef = mlazypropdef
+               return self
+       end
+
        # Detect the static type from the value assigned to the attribute `self`
        #
        # Return the static type if it can be safely inferred.