X-Git-Url: http://nitlanguage.org diff --git a/src/modelize/modelize_property.nit b/src/modelize/modelize_property.nit index ba7a1f3..bd14308 100644 --- a/src/modelize/modelize_property.nit +++ b/src/modelize/modelize_property.nit @@ -86,7 +86,8 @@ redef class ModelBuilder # Force building recursively if nclassdef.build_properties_is_done then return nclassdef.build_properties_is_done = true - var mclassdef = nclassdef.mclassdef.as(not null) + var mclassdef = nclassdef.mclassdef + if mclassdef == null then return # skip error if mclassdef.in_hierarchy == null then return # Skip error for superclassdef in mclassdef.in_hierarchy.direct_greaters do if not mclassdef2nclassdef.has_key(superclassdef) then continue @@ -181,7 +182,7 @@ redef class ModelBuilder var initializers = new Array[MProperty] for npropdef in nclassdef.n_propdefs do if npropdef isa AMethPropdef then - if npropdef.mpropdef == null then return # Skip broken attribute + if npropdef.mpropdef == null then return # Skip broken method var at = npropdef.get_single_annotation("autoinit", self) if at == null then continue # Skip non tagged init @@ -189,38 +190,39 @@ redef class ModelBuilder if sig == null then continue # Skip broken method if not npropdef.mpropdef.is_intro then - self.error(at, "Error: `autoinit` cannot be set on redefinitions") + self.error(at, "Error: `autoinit` cannot be set on redefinitions.") continue end for param in sig.mparameters do var ret_type = param.mtype - var mparameter = new MParameter(param.name, ret_type, false) + var mparameter = new MParameter(param.name, ret_type, false, ret_type isa MNullableType) 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 + var mreadpropdef = npropdef.mreadpropdef + if mreadpropdef == null or mreadpropdef.msignature == null then return # Skip broken attribute if npropdef.noinit then continue # Skip noinit attribute - var atautoinit = npropdef.get_single_annotation("autoinit", self) - if atautoinit != null then - # For autoinit attributes, call the reader to force + var atlateinit = npropdef.get_single_annotation("lateinit", self) + if atlateinit != null then + # For lateinit attributes, call the reader to force # the lazy initialization of the attribute. - initializers.add(npropdef.mreadpropdef.mproperty) - npropdef.mreadpropdef.mproperty.is_autoinit = true + initializers.add(mreadpropdef.mproperty) + mreadpropdef.mproperty.is_autoinit = true continue end if npropdef.has_value then continue - var paramname = npropdef.mpropdef.mproperty.name.substring_from(1) - var ret_type = npropdef.mpropdef.static_mtype + var paramname = mreadpropdef.mproperty.name + var ret_type = mreadpropdef.msignature.return_mtype if ret_type == null then return - var mparameter = new MParameter(paramname, ret_type, false) + var mparameter = new MParameter(paramname, ret_type, false, ret_type isa MNullableType) mparameters.add(mparameter) var msetter = npropdef.mwritepropdef if msetter == null then - # No setter, it is a old-style attribute, so just add it + # No setter, it is a readonly attribute, so just add it initializers.add(npropdef.mpropdef.mproperty) npropdef.mpropdef.mproperty.is_autoinit = true else @@ -236,7 +238,7 @@ redef class ModelBuilder # Look for most-specific new-stype init definitions var spropdefs = the_root_init_mmethod.lookup_super_definitions(mclassdef.mmodule, mclassdef.bound_mtype) if spropdefs.is_empty then - toolcontext.error(nclassdef.location, "Error: {mclassdef} does not specialize {the_root_init_mmethod.intro_mclassdef}. Possible duplication of the root class `Object`?") + toolcontext.error(nclassdef.location, "Error: `{mclassdef}` does not specialize `{the_root_init_mmethod.intro_mclassdef}`. Possible duplication of the root class `Object`?") return end @@ -253,14 +255,14 @@ redef class ModelBuilder end if autoinit.n_args.is_empty then - error(autoinit, "Syntax error: `autoinit` expects method identifiers, use `noautoinit` to clear all autoinits.") + 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.") + error(narg, "Syntax Error: `autoinit` expects method identifiers.") return end @@ -285,35 +287,36 @@ redef class ModelBuilder 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 + # Because the last parameter of setters is never default, try to default them for the autoinit. + for param in sig.mparameters do + if not param.is_default and param.mtype isa MNullableType then + param = new MParameter(param.name, param.mtype, param.is_vararg, true) + end + mparameters.add(param) + end 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 - # 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 + # Check for conflict in the order of initializers + # Each initializer list must me a prefix of the longest list + # If `noautoinit` is set, just ignore conflicts + if noautoinit == null then 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(", ")})") + # TODO: invalidate the initializer to avoid more errors return end i += 1 @@ -321,17 +324,27 @@ redef class ModelBuilder 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 + if noautoinit != null then + # If there is local or inherited initializers, then complain. + if initializers.is_empty and longest.initializers.is_empty then + warning(noautoinit, "useless-noautoinit", "Warning: the list of autoinit is already empty.") + end + # Just clear initializers + mparameters.clear + initializers.clear + else + # 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 end @@ -368,7 +381,7 @@ redef class ModelBuilder # It is a case-by case var vis_type: nullable MVisibility = null # The own visibility of the type var mmodule_type: nullable MModule = null # The original module of the type - mtype = mtype.as_notnullable + mtype = mtype.undecorate if mtype isa MClassType then vis_type = mtype.mclass.visibility mmodule_type = mtype.mclass.intro.mmodule @@ -388,10 +401,10 @@ redef class ModelBuilder assert mmodule_type != null var vis_module_type = mmodule.visibility_for(mmodule_type) # the visibility of the original module if mproperty.visibility > vis_type then - error(node, "Error: The {mproperty.visibility} property `{mproperty}` cannot contain the {vis_type} type `{mtype}`") + error(node, "Error: the {mproperty.visibility} property `{mproperty}` cannot contain the {vis_type} type `{mtype}`.") return else if mproperty.visibility > vis_module_type then - error(node, "Error: The {mproperty.visibility} property `{mproperty}` cannot contain the type `{mtype}` from the {vis_module_type} module `{mmodule_type}`") + error(node, "Error: the {mproperty.visibility} property `{mproperty}` cannot contain the type `{mtype}` from the {vis_module_type} module `{mmodule_type}`.") return end end @@ -418,7 +431,7 @@ redef class ModelBuilder var mtype = mproperty.mvirtualtype var poset = new POSet[MType] - # The work-list of type to resolve + # The work-list of types to resolve var todo = new List[MType] todo.add mtype @@ -460,9 +473,9 @@ redef class ModelBuilder for next in nexts do if poset.has_edge(next, t) then if mtype == next then - error(node, "Error: circularity of virtual type definition: {next} <-> {t}") + error(node, "Error: circularity of virtual type definition: {next} <-> {t}.") else - error(node, "Error: circularity of virtual type definition: {mtype} -> {next} <-> {t}") + error(node, "Error: circularity of virtual type definition: {mtype} -> {next} <-> {t}.") end return false else @@ -525,17 +538,17 @@ redef class MClassDef # SELF must be declared in Object, otherwise this will create conflicts if intro_mclassdef.mclass.name != "Object" then - modelbuilder.error(nintro, "Error: the virtual type SELF must be declared in Object.") + modelbuilder.error(nintro, "Error: the virtual type `SELF` must be declared in `Object`.") end # SELF must be public if mprop.visibility != public_visibility then - modelbuilder.error(nintro, "Error: the virtual type SELF must be public.") + modelbuilder.error(nintro, "Error: the virtual type `SELF` must be public.") end # SELF must not be fixed if intro.is_fixed then - modelbuilder.error(nintro, "Error: the virtual type SELF cannot be fixed.") + modelbuilder.error(nintro, "Error: the virtual type `SELF` cannot be fixed.") end return @@ -564,17 +577,17 @@ redef class APropdef if nvisibility != null then mvisibility = nvisibility.mvisibility if mvisibility == intrude_visibility then - modelbuilder.error(nvisibility, "Error: intrude is not a legal visibility for properties.") + modelbuilder.error(nvisibility, "Error: `intrude` is not a legal visibility for properties.") mvisibility = public_visibility end end if mclassdef.mclass.visibility == private_visibility then if mvisibility == protected_visibility then assert nvisibility != null - modelbuilder.error(nvisibility, "Error: The only legal visibility for properties in a private class is private.") + modelbuilder.error(nvisibility, "Error: `private` is the only legal visibility for properties in a private class.") else if mvisibility == private_visibility then assert nvisibility != null - modelbuilder.advice(nvisibility, "useless-visibility", "Warning: private is superfluous since the only legal visibility for properties in a private class is private.") + modelbuilder.advice(nvisibility, "useless-visibility", "Warning: `private` is superfluous since the only legal visibility for properties in a private class is private.") end mvisibility = private_visibility end @@ -610,29 +623,20 @@ redef class APropdef if nvisibility == null then return var mvisibility = nvisibility.mvisibility if mvisibility != mprop.visibility and mvisibility != public_visibility then - modelbuilder.error(nvisibility, "Error: redefinition changed the visibility from a {mprop.visibility} to a {mvisibility}") + modelbuilder.error(nvisibility, "Error: redefinition changed the visibility from `{mprop.visibility}` to `{mvisibility}`.") end end private fun check_redef_keyword(modelbuilder: ModelBuilder, mclassdef: MClassDef, kwredef: nullable Token, need_redef: Bool, mprop: MProperty): Bool do if mclassdef.mprop2npropdef.has_key(mprop) then - modelbuilder.error(self, "Error: A property {mprop} is already defined in class {mclassdef.mclass} at line {mclassdef.mprop2npropdef[mprop].location.line_start}.") - return false - end - if mprop isa MMethod and mprop.is_toplevel != (parent isa ATopClassdef) then - if mprop.is_toplevel then - modelbuilder.error(self, "Error: {mprop} is a top level method.") - else - modelbuilder.error(self, "Error: {mprop} is not a top level method.") - end + modelbuilder.error(self, "Error: a property `{mprop}` is already defined in class `{mclassdef.mclass}` at line {mclassdef.mprop2npropdef[mprop].location.line_start}.") return false - end if mprop isa MMethod and mprop.is_root_init then return true if kwredef == null then if need_redef then - modelbuilder.error(self, "Redef error: {mclassdef.mclass}::{mprop.name} is an inherited property. To redefine it, add the redef keyword.") + modelbuilder.error(self, "Redef Error: `{mclassdef.mclass}::{mprop.name}` is an inherited property. To redefine it, add the `redef` keyword.") return false end @@ -649,13 +653,15 @@ redef class APropdef end else if not need_redef then - modelbuilder.error(self, "Error: No property {mclassdef.mclass}::{mprop.name} is inherited. Remove the redef keyword to define a new property.") + modelbuilder.error(self, "Error: no property `{mclassdef.mclass}::{mprop.name}` is inherited. Remove the `redef` keyword to define a new property.") return false end end return true end + # Checks for useless type in redef signatures. + private fun check_repeated_types(modelbuilder: ModelBuilder) do end end redef class ASignature @@ -690,7 +696,7 @@ redef class ASignature end if np.n_dotdotdot != null then if self.vararg_rank != -1 then - modelbuilder.error(np, "Error: {param_names[self.vararg_rank]} is already a vararg") + modelbuilder.error(np, "Error: `{param_names[self.vararg_rank]}` is already a vararg") return false else self.vararg_rank = param_names.length - 1 @@ -797,8 +803,19 @@ redef class AMethPropdef name = amethodid.collect_text name_node = amethodid - if name == "-" and self.n_signature.n_params.length == 0 then + var arity = self.n_signature.n_params.length + if name == "+" and arity == 0 then + name = "unary +" + else if name == "-" and arity == 0 then name = "unary -" + else if name == "~" and arity == 0 then + name = "unary ~" + else + if amethodid.is_binary and arity != 1 then + modelbuilder.error(self.n_signature, "Syntax Error: binary operator `{name}` requires exactly one parameter; got {arity}.") + else if amethodid.min_arity > arity then + modelbuilder.error(self.n_signature, "Syntax Error: `{name}` requires at least {amethodid.min_arity} parameter(s); got {arity}.") + end end end @@ -822,7 +839,7 @@ redef class AMethPropdef mprop.is_init = is_init mprop.is_new = n_kwnew != null if mprop.is_new then mclassdef.mclass.has_new_factory = true - if parent isa ATopClassdef then mprop.is_toplevel = true + if name == "sys" then mprop.is_toplevel = true # special case for sys allowed in `new` factories self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, false, mprop) else if not self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, not self isa AMainMethPropdef, mprop) then return @@ -872,6 +889,9 @@ redef class AMethPropdef end end + var accept_special_last_parameter = self.n_methid == null or self.n_methid.accept_special_last_parameter + var return_is_mandatory = self.n_methid != null and self.n_methid.return_is_mandatory + # Retrieve info from the signature AST var param_names = new Array[String] # Names of parameters from the AST var param_types = new Array[MType] # Types of parameters from the AST @@ -899,7 +919,7 @@ redef class AMethPropdef if param_names.length != msignature.arity then var node: ANode if nsig != null then node = nsig else node = self - modelbuilder.error(node, "Redef error: {mpropdef} redefines {mpropdef.mproperty.intro} with {param_names.length} parameter(s), {msignature.arity} expected. Signature is {mpropdef}{msignature}") + modelbuilder.error(node, "Redef Error: expected {msignature.arity} parameter(s) for `{mpropdef.mproperty.name}{msignature}`; got {param_names.length}. See introduction at `{mpropdef.mproperty.full_name}`.") return end else if mpropdef.mproperty.is_init and not mpropdef.mproperty.is_new then @@ -931,13 +951,19 @@ redef class AMethPropdef if param_names.length != param_types.length then # Some parameters are typed, other parameters are not typed. - modelbuilder.error(nsig.n_params[param_types.length], "Error: Untyped parameter `{param_names[param_types.length]}'.") + modelbuilder.error(nsig.n_params[param_types.length], "Error: untyped parameter `{param_names[param_types.length]}'.") return end 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) + var is_default = false + if vararg_rank == -1 and param_types[i] isa MNullableType then + if i < param_names.length-1 or accept_special_last_parameter then + is_default = true + end + end + var mparameter = new MParameter(param_names[i], param_types[i], i == vararg_rank, is_default) if nsig != null then nsig.n_params[i].mparameter = mparameter mparameters.add(mparameter) end @@ -945,6 +971,14 @@ redef class AMethPropdef # In `new`-factories, the return type is by default the classtype. if ret_type == null and mpropdef.mproperty.is_new then ret_type = mclassdef.mclass.mclass_type + # Special checks for operator methods + if not accept_special_last_parameter and mparameters.not_empty and mparameters.last.is_vararg then + modelbuilder.error(self.n_signature.n_params.last, "Error: illegal variadic parameter `{mparameters.last}` for `{mpropdef.mproperty.name}`.") + end + if ret_type == null and return_is_mandatory then + modelbuilder.error(self.n_methid, "Error: mandatory return type for `{mpropdef.mproperty.name}`.") + end + msignature = new MSignature(mparameters, ret_type) mpropdef.msignature = msignature mpropdef.is_abstract = self.get_single_annotation("abstract", modelbuilder) != null @@ -953,7 +987,7 @@ redef class AMethPropdef # 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.") + if at != null then modelbuilder.error(at, "Syntax Error: `lazy` must be used on attributes.") end redef fun check_signature(modelbuilder) @@ -983,7 +1017,8 @@ redef class AMethPropdef var precursor_ret_type = msignature.return_mtype var ret_type = mysignature.return_mtype if ret_type != null and precursor_ret_type == null then - modelbuilder.error(nsig.n_type.as(not null), "Redef Error: {mpropdef.mproperty} is a procedure, not a function.") + modelbuilder.error(nsig.n_type.as(not null), "Redef Error: `{mpropdef.mproperty}` is a procedure, not a function.") + self.mpropdef.msignature = null return end @@ -994,7 +1029,8 @@ redef class AMethPropdef var prt = msignature.mparameters[i].mtype var node = nsig.n_params[i] if not modelbuilder.check_sametype(node, mmodule, mclassdef.bound_mtype, myt, prt) then - modelbuilder.error(node, "Redef Error: Wrong type for parameter `{mysignature.mparameters[i].name}'. found {myt}, expected {prt} as in {mpropdef.mproperty.intro}.") + modelbuilder.error(node, "Redef Error: expected `{prt}` for parameter `{mysignature.mparameters[i].name}'; got `{myt}`.") + self.mpropdef.msignature = null end end end @@ -1006,7 +1042,8 @@ redef class AMethPropdef # Inherit the return type ret_type = precursor_ret_type else if not modelbuilder.check_subtype(node, mmodule, mclassdef.bound_mtype, ret_type, precursor_ret_type) then - modelbuilder.error(node, "Redef Error: Wrong return type. found {ret_type}, expected {precursor_ret_type} as in {mpropdef.mproperty.intro}.") + modelbuilder.error(node, "Redef Error: expected `{precursor_ret_type}` for return type; got `{ret_type}`.") + self.mpropdef.msignature = null end end end @@ -1020,12 +1057,88 @@ redef class AMethPropdef var nt = nsig.n_type if nt != null then modelbuilder.check_visibility(nt, nt.mtype.as(not null), mpropdef) end + check_repeated_types(modelbuilder) end + + # For parameters, type is always useless in a redef. + # For return type, type is useless if not covariant with introduction. + redef fun check_repeated_types(modelbuilder) do + if mpropdef.is_intro or n_signature == null then return + # check params + for param in n_signature.n_params do + if param.n_type != null then + modelbuilder.advice(param.n_type, "useless-signature", "Warning: useless type repetition on parameter `{param.n_id.text}` for redefined method `{mpropdef.name}`") + end + end + # get intro + var intro = mpropdef.mproperty.intro + var n_intro = modelbuilder.mpropdef2npropdef.get_or_null(intro) + if n_intro == null or not n_intro isa AMethPropdef then return + # check return type + var ret_type = n_signature.ret_type + if ret_type != null and ret_type == n_intro.n_signature.ret_type then + modelbuilder.advice(n_signature.n_type, "useless-signature", "Warning: useless return type repetition for redefined method `{mpropdef.name}`") + end + end +end + +redef class AMethid + # Is a return required? + # + # * True for operators and brackets. + # * False for id and assignment. + fun return_is_mandatory: Bool do return true + + # Can the last parameter be special like a vararg? + # + # * False for operators: the last one is in fact the only one. + # * False for assignments: it is the right part of the assignment. + # * True for ids and brackets. + fun accept_special_last_parameter: Bool do return false + + # The minimum required number of parameters. + # + # * 1 for binary operators + # * 1 for brackets + # * 1 for assignments + # * 2 for bracket assignments + # * 0 for ids + fun min_arity: Int do return 1 + + # Is the `self` a binary operator? + fun is_binary: Bool do return true +end + +redef class AIdMethid + redef fun return_is_mandatory do return false + redef fun accept_special_last_parameter do return true + redef fun min_arity do return 0 + redef fun is_binary do return false +end + +redef class ABraMethid + redef fun accept_special_last_parameter do return true + redef fun is_binary do return false +end + +redef class ABraassignMethid + redef fun return_is_mandatory do return false + redef fun min_arity do return 2 + redef fun is_binary do return false +end + +redef class AAssignMethid + redef fun return_is_mandatory do return false + redef fun is_binary do return false end redef class AAttrPropdef redef type MPROPDEF: MAttributeDef + # The static type of the property (declared, inferred or inherited) + # This attribute is also used to check if the property was analyzed and is valid. + var mtype: nullable MType + # Is the node tagged `noinit`? var noinit = false @@ -1055,12 +1168,8 @@ redef class AAttrPropdef var atabstract = self.get_single_annotation("abstract", modelbuilder) if atabstract == null then - if mclass.kind == interface_kind then - modelbuilder.error(self, "Error: Attempt to define attribute {name} in the interface {mclass}.") - else if mclass.kind == enum_kind then - modelbuilder.error(self, "Error: Attempt to define attribute {name} in the enum class {mclass}.") - else if mclass.kind == extern_kind then - modelbuilder.error(self, "Error: Attempt to define attribute {name} in the extern class {mclass}.") + 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, private_visibility) @@ -1091,7 +1200,7 @@ redef class AAttrPropdef has_value = n_expr != null or n_block != null if atabstract != null and has_value then - modelbuilder.error(atabstract, "Error: `abstract` attributes cannot have an initial value") + modelbuilder.error(atabstract, "Error: `abstract` attributes cannot have an initial value.") return end @@ -1100,28 +1209,29 @@ redef class AAttrPropdef if atnoinit != null then noinit = true if has_value then - modelbuilder.error(atnoinit, "Error: `noautoinit` attributes cannot have an initial value") + modelbuilder.error(atnoinit, "Error: `noautoinit` attributes cannot have an initial value.") return end if atabstract != null then - modelbuilder.error(atnoinit, "Error: `noautoinit` attributes cannot be abstract") + modelbuilder.error(atnoinit, "Error: `noautoinit` attributes cannot be abstract.") return end end var atlazy = self.get_single_annotation("lazy", modelbuilder) - var atautoinit = self.get_single_annotation("autoinit", modelbuilder) - if atlazy != null or atautoinit != null then - if atlazy != null and atautoinit != null then - modelbuilder.error(atlazy, "Error: lazy incompatible with autoinit") + 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: a lazy attribute needs a value") - else if atautoinit != null then - modelbuilder.error(atautoinit, "Error: a autoinit attribute needs a value") + 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 @@ -1133,12 +1243,16 @@ redef class AAttrPropdef var atreadonly = self.get_single_annotation("readonly", modelbuilder) if atreadonly != null then if not has_value then - modelbuilder.error(atreadonly, "Error: a readonly attribute needs a value") + modelbuilder.error(atreadonly, "Error: `readonly` attributes need a value.") end # No setter, so just leave return end + if not mclassdef.is_intro and not has_value and not noinit then + modelbuilder.advice(self, "attr-in-refinement", "Warning: attributes in refinement need a value or `noautoinit`.") + end + var writename = name + "=" var atwritable = self.get_single_annotation("writable", modelbuilder) if atwritable != null then @@ -1211,6 +1325,9 @@ redef class AAttrPropdef else if nexpr isa AIntExpr then var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Int") if cla != null then mtype = cla.mclass_type + else if nexpr isa AByteExpr then + var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Byte") + if cla != null then mtype = cla.mclass_type else if nexpr isa AFloatExpr then var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Float") if cla != null then mtype = cla.mclass_type @@ -1227,7 +1344,7 @@ redef class AAttrPropdef var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "String") if cla != null then mtype = cla.mclass_type else - modelbuilder.error(self, "Error: Untyped attribute {mreadpropdef}. Implicit typing allowed only for literals and new.") + modelbuilder.error(self, "Error: untyped attribute `{mreadpropdef}`. Implicit typing allowed only for literals and new.") end if mtype == null then return @@ -1242,10 +1359,12 @@ redef class AAttrPropdef end if mtype == null then - modelbuilder.error(self, "Error: Untyped attribute {mreadpropdef}") + modelbuilder.error(self, "Error: untyped attribute `{mreadpropdef}`.") return end + self.mtype = mtype + if mpropdef != null then mpropdef.static_mtype = mtype end @@ -1259,7 +1378,7 @@ redef class AAttrPropdef if mwritepropdef != null then var name: String name = n_id2.text - var mparameter = new MParameter(name, mtype, false) + var mparameter = new MParameter(name, mtype, false, false) var msignature = new MSignature([mparameter], null) mwritepropdef.msignature = msignature end @@ -1268,6 +1387,7 @@ redef class AAttrPropdef if mlazypropdef != null then mlazypropdef.static_mtype = modelbuilder.model.get_mclasses_by_name("Bool").first.mclass_type end + check_repeated_types(modelbuilder) end redef fun check_signature(modelbuilder) @@ -1275,7 +1395,7 @@ redef class AAttrPropdef var mpropdef = self.mpropdef if mpropdef == null then return # Error thus skipped var ntype = self.n_type - var mtype = self.mpropdef.static_mtype + var mtype = self.mtype if mtype == null then return # Error thus skipped var mclassdef = mpropdef.mclassdef @@ -1297,7 +1417,7 @@ redef class AAttrPropdef if precursor_type == null then return if mtype != precursor_type then - modelbuilder.error(ntype.as(not null), "Redef Error: Wrong static type. found {mtype}, expected {precursor_type}.") + modelbuilder.error(ntype.as(not null), "Redef Error: expected `{precursor_type}` type as a bound; got `{mtype}`.") return end end @@ -1336,7 +1456,7 @@ redef class AAttrPropdef if mysignature.arity != msignature.arity then var node: ANode if nsig != null then node = nsig else node = self - modelbuilder.error(node, "Redef Error: {mysignature.arity} parameters found, {msignature.arity} expected. Signature is {mpropdef}{msignature}") + modelbuilder.error(node, "Redef Error: expected {msignature.arity} parameter(s) for `{mpropdef.mproperty.name}{msignature}`; got {mysignature.arity}. See introduction at `{mpropdef.mproperty.full_name}`.") return end var precursor_ret_type = msignature.return_mtype @@ -1344,7 +1464,7 @@ redef class AAttrPropdef if ret_type != null and precursor_ret_type == null then var node: ANode if nsig != null then node = nsig else node = self - modelbuilder.error(node, "Redef Error: {mpropdef.mproperty} is a procedure, not a function.") + modelbuilder.error(node, "Redef Error: `{mpropdef.mproperty}` is a procedure, not a function.") return end @@ -1356,7 +1476,7 @@ redef class AAttrPropdef var node: ANode if nsig != null then node = nsig else node = self if not modelbuilder.check_sametype(node, mmodule, mclassdef.bound_mtype, myt, prt) then - modelbuilder.error(node, "Redef Error: Wrong type for parameter `{mysignature.mparameters[i].name}'. found {myt}, expected {prt}.") + modelbuilder.error(node, "Redef Error: expected `{prt}` type for parameter `{mysignature.mparameters[i].name}'; got `{myt}`.") end end end @@ -1367,11 +1487,30 @@ redef class AAttrPropdef # Inherit the return type ret_type = precursor_ret_type else if not modelbuilder.check_subtype(node, mmodule, mclassdef.bound_mtype, ret_type, precursor_ret_type) then - modelbuilder.error(node, "Redef Error: Wrong return type. found {ret_type}, expected {precursor_ret_type}.") + modelbuilder.error(node, "Redef Error: expected `{precursor_ret_type}` return type; got `{ret_type}`.") end end end end + + # Type is useless if the attribute type is the same thant the intro. + redef fun check_repeated_types(modelbuilder) do + if mreadpropdef.is_intro or n_type == null then return + # get intro + var intro = mreadpropdef.mproperty.intro + var n_intro = modelbuilder.mpropdef2npropdef.get_or_null(intro) + if n_intro == null then return + # get intro type + var ntype = null + if n_intro isa AMethPropdef then + ntype = n_intro.n_signature.ret_type + else if n_intro isa AAttrPropdef and n_intro.n_type != null then + ntype = n_intro.n_type.mtype + end + # check + if ntype ==null or ntype != n_type.mtype then return + modelbuilder.advice(n_type, "useless-signature", "Warning: useless type repetition on redefined attribute `{mpropdef.name}`") + end end redef class ATypePropdef @@ -1385,7 +1524,7 @@ redef class ATypePropdef var mvisibility = new_property_visibility(modelbuilder, mclassdef, self.n_visibility) mprop = new MVirtualTypeProp(mclassdef, name, mvisibility) for c in name.chars do if c >= 'a' and c<= 'z' then - modelbuilder.warning(n_id, "bad-type-name", "Warning: lowercase in the virtual type {name}") + modelbuilder.warning(n_id, "bad-type-name", "Warning: lowercase in the virtual type `{name}`.") break end if not self.check_redef_keyword(modelbuilder, mclassdef, self.n_kwredef, false, mprop) then return @@ -1453,7 +1592,7 @@ redef class ATypePropdef var supbound = p.bound if supbound == null then break # broken super bound, skip error if p.is_fixed then - modelbuilder.error(self, "Redef Error: Virtual type {mpropdef.mproperty} is fixed in super-class {p.mclassdef.mclass}") + modelbuilder.error(self, "Redef Error: virtual type `{mpropdef.mproperty}` is fixed in super-class `{p.mclassdef.mclass}`.") break end if p.mclassdef.mclass == mclassdef.mclass then @@ -1462,7 +1601,7 @@ redef class ATypePropdef break end if not modelbuilder.check_subtype(n_type, mmodule, anchor, bound, supbound) then - modelbuilder.error(n_type, "Redef Error: Wrong bound type. Found {bound}, expected a subtype of {supbound}, as in {p}.") + modelbuilder.error(n_type, "Redef Error: expected `{supbound}` bound type; got `{bound}`.") break end end