X-Git-Url: http://nitlanguage.org diff --git a/src/modelize/modelize_property.nit b/src/modelize/modelize_property.nit index 42dee1e..35a50c3 100644 --- a/src/modelize/modelize_property.nit +++ b/src/modelize/modelize_property.nit @@ -181,6 +181,7 @@ redef class ModelBuilder mparameters.add(mparameter) end initializers.add(npropdef.mpropdef.mproperty) + npropdef.mpropdef.mproperty.is_autoinit = true end if npropdef isa AAttrPropdef then if npropdef.mpropdef == null then return # Skip broken attribute @@ -190,6 +191,7 @@ redef class ModelBuilder # For autoinit attributes, call the reader to force # the lazy initialization of the attribute. initializers.add(npropdef.mreadpropdef.mproperty) + npropdef.mreadpropdef.mproperty.is_autoinit = true continue end if npropdef.has_value then continue @@ -202,9 +204,11 @@ redef class ModelBuilder if msetter == null then # No setter, it is a old-style attribute, so just add it initializers.add(npropdef.mpropdef.mproperty) + npropdef.mpropdef.mproperty.is_autoinit = true else # Add the setter to the list initializers.add(msetter.mproperty) + msetter.mproperty.is_autoinit = true end end end @@ -218,39 +222,99 @@ redef class ModelBuilder return end - # Search the longest-one and checks for conflict - var longest = spropdefs.first - if spropdefs.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 spropdefs do - if spd.initializers.length > longest.initializers.length then longest = spd + # Look at the autoinit class-annotation + var autoinit = nclassdef.get_single_annotation("autoinit", self) + var noautoinit = nclassdef.get_single_annotation("noautoinit", self) + if autoinit != null then + # Just throws the collected initializers + mparameters.clear + initializers.clear + + if noautoinit != null then + error(autoinit, "Error: `autoinit` and `noautoinit` are incompatible.") + end + + if autoinit.n_args.is_empty then + error(autoinit, "Syntax error: `autoinit` expects method identifiers, use `noautoinit` to clear all autoinits.") + end + + # Get and check each argument + for narg in autoinit.n_args do + var id = narg.as_id + if id == null then + error(narg, "Syntax error: `autoinit` expects method identifiers.") + return + end + + # Search the property. + # To avoid bad surprises, try to get the setter first. + var p = try_get_mproperty_by_name(narg, mclassdef, id + "=") + if p == null then + p = try_get_mproperty_by_name(narg, mclassdef, id) + end + if p == null then + error(narg, "Error: unknown method `{id}`") + return + end + if not p.is_autoinit then + error(narg, "Error: `{p}` is not an autoinit method") + return + end + + # Register the initializer and the parameters + initializers.add(p) + var pd = p.intro + if pd isa MMethodDef then + # Get the signature resolved for the current receiver + var sig = pd.msignature.resolve_for(mclassdef.mclass.mclass_type, mclassdef.bound_mtype, mclassdef.mmodule, false) + mparameters.add_all sig.mparameters + else + # TODO attributes? + abort + end + end + else if noautoinit != null then + if initializers.is_empty then + warning(noautoinit, "useless-noautoinit", "Warning: the list of autoinit is already empty.") end - # part 2. compare - for spd in spropdefs do - var i = 0 - for p in spd.initializers do - if p != longest.initializers[i] then - self.error(nclassdef, "Error: conflict for inherited inits {spd}({spd.initializers.join(", ")}) and {longest}({longest.initializers.join(", ")})") - return + # Just clear initializers + mparameters.clear + initializers.clear + else + # Search the longest-one and checks for conflict + var longest = spropdefs.first + if spropdefs.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 spropdefs do + if spd.initializers.length > longest.initializers.length then longest = spd + end + # part 2. compare + for spd in spropdefs do + var i = 0 + for p in spd.initializers do + if p != longest.initializers[i] then + self.error(nclassdef, "Error: conflict for inherited inits {spd}({spd.initializers.join(", ")}) and {longest}({longest.initializers.join(", ")})") + return + end + i += 1 end - i += 1 end end - end - # Can we just inherit? - if spropdefs.length == 1 and mparameters.is_empty and defined_init == null then - self.toolcontext.info("{mclassdef} inherits the basic constructor {longest}", 3) - mclassdef.mclass.root_init = longest - return - end + # Can we just inherit? + if spropdefs.length == 1 and mparameters.is_empty and defined_init == null then + self.toolcontext.info("{mclassdef} inherits the basic constructor {longest}", 3) + mclassdef.mclass.root_init = longest + return + end - # Combine the inherited list to what is collected - if longest.initializers.length > 0 then - mparameters.prepend longest.new_msignature.mparameters - initializers.prepend longest.initializers + # Combine the inherited list to what is collected + if longest.initializers.length > 0 then + mparameters.prepend longest.new_msignature.mparameters + initializers.prepend longest.initializers + end end # If we already have a basic init definition, then setup its initializers @@ -405,9 +469,9 @@ redef class APropdef # The associated propdef once build by a `ModelBuilder` var mpropdef: nullable MPROPDEF is writable - private fun build_property(modelbuilder: ModelBuilder, mclassdef: MClassDef) is abstract - private fun build_signature(modelbuilder: ModelBuilder) is abstract - private fun check_signature(modelbuilder: ModelBuilder) is abstract + private fun build_property(modelbuilder: ModelBuilder, mclassdef: MClassDef) do end + private fun build_signature(modelbuilder: ModelBuilder) do end + private fun check_signature(modelbuilder: ModelBuilder) do end private fun new_property_visibility(modelbuilder: ModelBuilder, mclassdef: MClassDef, nvisibility: nullable AVisibility): MVisibility do var mvisibility = public_visibility @@ -799,6 +863,10 @@ redef class AMethPropdef mpropdef.is_abstract = self.get_single_annotation("abstract", modelbuilder) != null mpropdef.is_intern = self.get_single_annotation("intern", modelbuilder) != null mpropdef.is_extern = self.n_extern_code_block != null or self.get_single_annotation("extern", modelbuilder) != null + + # Check annotations + var at = self.get_single_annotation("lazy", modelbuilder) + if at != null then modelbuilder.error(at, "Syntax error: `lazy` must be used on attributes.") end redef fun check_signature(modelbuilder) @@ -928,10 +996,11 @@ redef class AAttrPropdef has_value = n_expr != null or n_block != null var atnoinit = self.get_single_annotation("noinit", modelbuilder) + if atnoinit == null then atnoinit = self.get_single_annotation("noautoinit", modelbuilder) if atnoinit != null then noinit = true if has_value then - modelbuilder.error(atnoinit, "Error: `noinit` attributes cannot have an initial value") + modelbuilder.error(atnoinit, "Error: `noautoinit` attributes cannot have an initial value") return end end