#
# 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.
# (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)
super OrderedTree[MConcern]
end
+redef class MGroup
+ redef var is_test is lazy do
+ var parent = self.parent
+ if parent != null and parent.is_test then return true
+ return name == "tests"
+ end
+end
+
redef class MModule
# All the classes introduced in the module
var intro_mclasses = new Array[MClass]
# Visibility is not considered.
#
# Note: this function is expensive and is usually used for the main
- # module of a program only. Do not use it to do you own subtype
+ # module of a program only. Do not use it to do your own subtype
# functions.
fun flatten_mclass_hierarchy: POSet[MClass]
do
var res = self.flatten_mclass_hierarchy_cache
if res != null then return res
- res = new POSet[MClass]
+ self.flatten_mclass_hierarchy_cache = new POSet[MClass]
for m in self.in_importation.greaters do
for cd in m.mclassdefs do
- var c = cd.mclass
- res.add_node(c)
- for s in cd.supertypes do
- res.add_edge(c, s.mclass)
- end
+ unsafe_update_hierarchy_cache(cd)
end
end
- self.flatten_mclass_hierarchy_cache = res
- return res
- end
+ return self.flatten_mclass_hierarchy_cache.as(not null)
+ end
+
+ # Adds another class definition in the modue.
+ # Updates the class hierarchy cache.
+ fun add_mclassdef(mclassdef: MClassDef)
+ do
+ self.mclassdefs.add(mclassdef)
+ if self.flatten_mclass_hierarchy_cache != null then
+ unsafe_update_hierarchy_cache(mclassdef)
+ end
+ end
+
+ # Adds a class definition inside `flatten_mclass_hierarchy_cache` without
+ # null check. The caller must have initialized the cache.
+ protected fun unsafe_update_hierarchy_cache(mclassdef: MClassDef)
+ do
+ var hierarchy = self.flatten_mclass_hierarchy_cache.as(not null)
+ # Update the cache
+ var c = mclassdef.mclass
+ hierarchy.add_node(c)
+ for s in mclassdef.supertypes do
+ hierarchy.add_edge(c, s.mclass)
+ end
+ end
# Sort a given array of classes using the linearization order of the module
# The most general is first, the most specific is last
# A named class
#
-# `MClass` are global to the model; it means that a `MClass` is not bound to a
-# specific `MModule`.
+# `MClass`es are global to the model; it means that a `MClass` is not bound
+# to a specific `MModule`.
#
# This characteristic helps the reasoning about classes in a program since a
# single `MClass` object always denote the same class.
end
# The kind of the class (interface, abstract class, etc.)
- # In Nit, the kind of a class cannot evolve in refinements
+ #
+ # In Nit, the kind of a class cannot evolve in refinements.
var kind: MClassKind
# The visibility of the class
- # In Nit, the visibility of a class cannot evolve in refinements
+ #
+ # In Nit, the visibility of a class cannot evolve in refinements.
redef var visibility
init
# Warning: such a definition may not exist in the early life of the object.
# In this case, the method will abort.
#
- # Use `try_intro` instead
+ # Use `try_intro` instead.
var intro: MClassDef is noinit
- # The definition that introduces the class or null if not yet known.
+ # The definition that introduces the class or `null` if not yet known.
#
- # See `intro`
+ # SEE: `intro`
fun try_intro: nullable MClassDef do
if isset _intro then return _intro else return null
end
# 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
init
do
self.mclass = bound_mtype.mclass
- mmodule.mclassdefs.add(self)
+ mmodule.add_mclassdef(self)
mclass.mclassdefs.add(self)
if mclass.intro_mmodule == mmodule then
assert not isset mclass._intro
# All property introductions and redefinitions in `self` (not inheritance).
var mpropdefs = new Array[MPropDef]
+ # The special default_init constructor
+ var default_init: nullable MMethodDef = null is writable
+
# All property introductions and redefinitions (not inheritance) in `self` by its associated property.
var mpropdefs_by_property = new HashMap[MProperty, MPropDef]
+ # Return the direct parent mtype of `self`
+ # Exemple
+ # ~~~nitish
+ # module 1
+ #
+ # class A
+ # class B
+ # super A
+ #
+ # module 2
+ #
+ # redef class A
+ # class C
+ # super B
+ #
+ # mclassdef_C.get_direct_supermtype == [B]
+ # ~~~~
+ fun get_direct_supermtype: Collection[MClassType]
+ do
+ # Get the potentiel direct parents
+ var parents = in_hierarchy.direct_greaters
+ # Stock the potentiel direct parents
+ var res = supertypes
+ for parent in parents do
+ # remove all super parents of the potentiel direct parents
+ res.remove_all(parent.supertypes)
+ # if the length of the potentiel direct parent equal 1 break
+ if res.length == 1 then break
+ end
+ return res
+ end
+
redef fun mdoc_or_fallback do return mdoc or else mclass.mdoc_or_fallback
end
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
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
if t == mtype then return self
return t.as_nullable
end
+
+ redef fun mdoc_or_fallback do return mtype.mdoc_or_fallback
end
# A non-null version of a formal type.
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
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
# 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
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
# The associated global property
var mproperty: MPROPERTY
- redef var location: Location
+ redef var location
redef fun visibility do return mproperty.visibility
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
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
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
# The signature attached to the property definition
var msignature: nullable MSignature = null is writable
- # The signature attached to the `new` call on a root-init
- # This is a concatenation of the signatures of the initializers
- #
- # REQUIRE `mproperty.is_root_init == (new_msignature != null)`
- var new_msignature: nullable MSignature = null is writable
-
# List of initialisers to call in root-inits
#
# They could be setters or attributes
- #
- # REQUIRE `mproperty.is_root_init == (new_msignature != null)`
var initializers = new Array[MProperty]
+ # Does the method take the responsibility to call `init`?
+ #
+ # If the method is used as an initializer, then
+ # using this information prevents to call `init` twice.
+ var is_calling_init = false is writable
+
+ # Does the method is a old_style_init?
+ #
+ var is_old_style_init = false is writable
+
# Is the method definition abstract?
var is_abstract: Bool = false is writable
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
# TODO: private init because enumeration.
- # Can a class of kind `self` specializes a class of kine `other`?
+ # 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