Merge: model: is_accessor
[nit.git] / src / model / model.nit
index 21b182e..dc9b978 100644 (file)
@@ -50,6 +50,13 @@ redef class Model
        #
        # Each classdef is associated with its super-classdefs in regard to
        # its module of definition.
+       #
+       # ~~~
+       # var m = new ModelDiamond
+       # assert     m.mclassdef_hierarchy.has_edge(m.mclassdef_b, m.mclassdef_a)
+       # assert not m.mclassdef_hierarchy.has_edge(m.mclassdef_a, m.mclassdef_b)
+       # assert not m.mclassdef_hierarchy.has_edge(m.mclassdef_b, m.mclassdef_c)
+       # ~~~
        var mclassdef_hierarchy = new POSet[MClassDef]
 
        # Class-type hierarchy restricted to the introduction.
@@ -81,6 +88,12 @@ redef class Model
        # (instead of an empty array)
        #
        # Visibility or modules are not considered
+       #
+       # ~~~
+       # var m = new ModelStandalone
+       # assert m.get_mclasses_by_name("Object") == [m.mclass_o]
+       # assert m.get_mclasses_by_name("Fail") == null
+       # ~~~
        fun get_mclasses_by_name(name: String): nullable Array[MClass]
        do
                return mclasses_by_name.get_or_null(name)
@@ -575,6 +588,8 @@ class MClass
        # Is `self` and abstract class?
        var is_abstract: Bool is lazy do return kind == abstract_kind
 
+       redef var is_test is lazy do return intro.is_test
+
        redef fun mdoc_or_fallback
        do
                # Don’t use `intro.mdoc_or_fallback` because it would create an infinite
@@ -872,7 +887,7 @@ abstract class MType
                if anchor == null then anchor = sub # UGLY: any anchor will work
                var resolved_sub = sub.anchor_to(mmodule, anchor)
                var res = resolved_sub.collect_mclasses(mmodule).has(sup.mclass)
-               if res == false then return false
+               if not res then return false
                if not sup isa MGenericType then return true
                var sub2 = sub.supertype_to(mmodule, anchor, sup.mclass)
                assert sub2.mclass == sup.mclass
@@ -880,7 +895,7 @@ abstract class MType
                        var sub_arg = sub2.arguments[i]
                        var sup_arg = sup.arguments[i]
                        res = sub_arg.is_subtype(mmodule, anchor, sup_arg)
-                       if res == false then return false
+                       if not res then return false
                end
                return true
        end
@@ -1962,16 +1977,34 @@ class MSignature
                var b = new FlatBuffer
                if not mparameters.is_empty then
                        b.append("(")
+                       var last_mtype = null
                        for i in [0..mparameters.length[ do
                                var mparameter = mparameters[i]
+
+                               # Group types that are common to contiguous parameters
+                               if mparameter.mtype != last_mtype and last_mtype != null then
+                                       b.append(": ")
+                                       b.append(last_mtype.to_s)
+                               end
+
                                if i > 0 then b.append(", ")
                                b.append(mparameter.name)
-                               b.append(": ")
-                               b.append(mparameter.mtype.to_s)
+
                                if mparameter.is_vararg then
+                                       b.append(": ")
+                                       b.append(mparameter.mtype.to_s)
                                        b.append("...")
+                                       last_mtype = null
+                               else
+                                       last_mtype = mparameter.mtype
                                end
                        end
+
+                       if last_mtype != null then
+                               b.append(": ")
+                               b.append(last_mtype.to_s)
+                       end
+
                        b.append(")")
                end
                var ret = self.return_mtype
@@ -2296,6 +2329,20 @@ abstract class MProperty
        end
 
        private var lookup_all_definitions_cache = new HashMap2[MModule, MType, Array[MPROPDEF]]
+
+       redef var is_test is lazy do return intro.is_test
+
+       # Does self have the `before` annotation?
+       var is_before: Bool is lazy do return intro.is_before
+
+       # Does self have the `before_all` annotation?
+       var is_before_all: Bool is lazy do return intro.is_before_all
+
+       # Does self have the `after` annotation?
+       var is_after: Bool is lazy do return intro.is_after
+
+       # Does self have the `after_all` annotation?
+       var is_after_all: Bool is lazy do return intro.is_after_all
 end
 
 # A global method
@@ -2330,6 +2377,29 @@ class MMethod
        # A specific method that is safe to call on null.
        # Currently, only `==`, `!=` and `is_same_instance` are safe
        fun is_null_safe: Bool do return name == "==" or name == "!=" or name == "is_same_instance"
+
+       # Is this method a getter (auto or not)?
+       #
+       # See `getter_for`.
+       fun is_getter: Bool do return getter_for != null
+
+       # The attribute this getter is for
+       #
+       # Return `null` is this method is not a getter.
+       var getter_for: nullable MAttribute = null is writable
+
+       # Is this method a setter (auto or not)?
+       #
+       # See `setter_for`.
+       fun is_setter: Bool do return setter_for != null
+
+       # The attribute this setter is for
+       #
+       # Return `null` is this method is not a setter.
+       var setter_for: nullable MAttribute = null is writable
+
+       # Is this method a getter or a setter?
+       fun is_accessor: Bool do return is_getter or is_setter
 end
 
 # A global attribute
@@ -2338,6 +2408,21 @@ class MAttribute
 
        redef type MPROPDEF: MAttributeDef
 
+       # Does this attribute have a getter (auto or not)?
+       #
+       # See `getter`.
+       fun has_getter: Bool do return getter != null
+
+       # The getter of this attribute (if any)
+       var getter: nullable MProperty = null is writable
+
+       # Does this attribute have a setter (auto or not)?
+       #
+       # See `setter`.
+       fun has_setter: Bool do return setter != null
+
+       # The setter of this attribute (if any)
+       var setter: nullable MProperty = null is writable
 end
 
 # A global virtual type
@@ -2373,7 +2458,7 @@ abstract class MPropDef
        # The associated global property
        var mproperty: MPROPERTY
 
-       redef var location: Location
+       redef var location
 
        redef fun visibility do return mproperty.visibility
 
@@ -2430,11 +2515,9 @@ abstract class MPropDef
                                        res.append "::"
                                end
                        end
-                       if mclassdef.mclass != mproperty.intro_mclassdef.mclass then
-                               # precise "B" only if not the same class than "A"
-                               res.append mproperty.intro_mclassdef.name
-                               res.append "::"
-                       end
+                       # precise "B" because it is not the same class than "A"
+                       res.append mproperty.intro_mclassdef.name
+                       res.append "::"
                        # Always use the property name "x"
                        res.append mproperty.name
                end
@@ -2452,10 +2535,8 @@ abstract class MPropDef
                                res.append mproperty.intro_mclassdef.mmodule.c_name
                                res.append "__"
                        end
-                       if mclassdef.mclass != mproperty.intro_mclassdef.mclass then
-                               res.append mproperty.intro_mclassdef.name.to_cmangle
-                               res.append "__"
-                       end
+                       res.append mproperty.intro_mclassdef.name.to_cmangle
+                       res.append "__"
                        res.append mproperty.name.to_cmangle
                end
                return res.to_s
@@ -2489,6 +2570,18 @@ abstract class MPropDef
        end
 
        redef fun mdoc_or_fallback do return mdoc or else mproperty.mdoc_or_fallback
+
+       # Does self have the `before` annotation?
+       var is_before = false is writable
+
+       # Does self have the `before_all` annotation?
+       var is_before_all = false is writable
+
+       # Does self have the `after` annotation?
+       var is_after = false is writable
+
+       # Does self have the `after_all` annotation?
+       var is_after_all = false is writable
 end
 
 # A local definition of a method
@@ -2572,6 +2665,12 @@ end
 class MClassKind
        redef var to_s
 
+       # Can a class of kind `self` define a membership predicate?
+       var can_customize_isa: Bool
+
+       # Can a class of kind `self` define a constructor?
+       var can_init: Bool
+
        # Is a constructor required?
        var need_init: Bool
 
@@ -2580,29 +2679,102 @@ class MClassKind
        # Can a class of kind `self` specializes a class of kind `other`?
        fun can_specialize(other: MClassKind): Bool
        do
-               if other == interface_kind then return true # everybody can specialize interfaces
-               if self == interface_kind or self == enum_kind then
-                       # no other case for interfaces
+               if other == interface_kind then
+                       # everybody can specialize interfaces
+                       return true
+               else if self == interface_kind or self == enum_kind then
+                       # no other case for interfaces and enums
                        return false
+               else if self == subset_kind then
+                       # A subset may specialize anything, except another subset.
+                       # TODO: Allow sub-subsets once we can handle them.
+                       return other != subset_kind
                else if self == extern_kind then
                        # only compatible with themselves
                        return self == other
-               else if other == enum_kind or other == extern_kind then
-                       # abstract_kind and concrete_kind are incompatible
-                       return false
+               else
+                       # assert self == abstract_kind or self == concrete_kind
+                       return other == abstract_kind or other == concrete_kind
                end
-               # remain only abstract_kind and concrete_kind
-               return true
        end
 end
 
 # The class kind `abstract`
-fun abstract_kind: MClassKind do return once new MClassKind("abstract class", true)
+fun abstract_kind: MClassKind do return once new MClassKind("abstract class", false, true, true)
 # The class kind `concrete`
-fun concrete_kind: MClassKind do return once new MClassKind("class", true)
+fun concrete_kind: MClassKind do return once new MClassKind("class", false, true, true)
 # The class kind `interface`
-fun interface_kind: MClassKind do return once new MClassKind("interface", false)
+fun interface_kind: MClassKind do return once new MClassKind("interface", false, true, false)
 # The class kind `enum`
-fun enum_kind: MClassKind do return once new MClassKind("enum", false)
+fun enum_kind: MClassKind do return once new MClassKind("enum", false, true, false)
 # The class kind `extern`
-fun extern_kind: MClassKind do return once new MClassKind("extern class", false)
+fun extern_kind: MClassKind do return once new MClassKind("extern class", false, true, false)
+# The class kind `subset`
+fun subset_kind: MClassKind do return once new MClassKind("subset", true, false, false)
+
+# A standalone pre-constructed model used to test various model-related methods.
+#
+# When instantiated, a standalone model is already filled with entities that are exposed as attributes.
+class ModelStandalone
+       super Model
+
+       redef var location = new Location.opaque_file("ModelStandalone")
+
+       # The first module
+       var mmodule0 = new MModule(self, null, "module0", location)
+
+       # The root Object class
+       var mclass_o = new MClass(mmodule0, "Object", location, null, interface_kind, public_visibility)
+
+       # The introduction of `mclass_o`
+       var mclassdef_o = new MClassDef(mmodule0, mclass_o.mclass_type, location)
+end
+
+# A standalone model with the common class diamond-hierarchy ABCD
+class ModelDiamond
+       super ModelStandalone
+
+       # A, a simple subclass of Object
+       var mclass_a = new MClass(mmodule0, "A", location, null, concrete_kind, public_visibility)
+
+       # The introduction of `mclass_a`
+       var mclassdef_a: MClassDef do
+               var res = new MClassDef(mmodule0, mclass_a.mclass_type, location)
+               res.set_supertypes([mclass_o.mclass_type])
+               res.add_in_hierarchy
+               return res
+       end
+
+       # B, a subclass of A (`mclass_a`)
+       var mclass_b = new MClass(mmodule0, "B", location, null, concrete_kind, public_visibility)
+
+       # The introduction of `mclass_b`
+       var mclassdef_b: MClassDef do
+               var res = new MClassDef(mmodule0, mclass_b.mclass_type, location)
+               res.set_supertypes([mclass_a.mclass_type])
+               res.add_in_hierarchy
+               return res
+       end
+
+       # C, another subclass of A (`mclass_a`)
+       var mclass_c = new MClass(mmodule0, "C", location, null, concrete_kind, public_visibility)
+
+       # The introduction of `mclass_c`
+       var mclassdef_c: MClassDef do
+               var res = new MClassDef(mmodule0, mclass_c.mclass_type, location)
+               res.set_supertypes([mclass_a.mclass_type])
+               res.add_in_hierarchy
+               return res
+       end
+
+       # D, a multiple subclass of B (`mclass_b`) and C (`mclass_c`)
+       var mclass_d = new MClass(mmodule0, "D", location, null, concrete_kind, public_visibility)
+
+       # The introduction of `mclass_d`
+       var mclassdef_d: MClassDef do
+               var res = new MClassDef(mmodule0, mclass_d.mclass_type, location)
+               res.set_supertypes([mclass_b.mclass_type, mclass_c.mclass_type])
+               res.add_in_hierarchy
+               return res
+       end
+end