nitc :: modelize_class $ AAbstractClasskind
An abstract class modifier (abstract class
)
nitc :: modelize_class $ AClasskind
The modifier for the kind of class (abstract, interface, etc.)nitc :: modelize_class $ AConcreteClasskind
A default, or concrete class modifier (justclass
)
nitc :: modelize_class $ AEnumClasskind
An enum/universal class modifier (enum class
)
nitc :: modelize_class $ AExternClasskind
An extern class modifier (extern class
)
nitc :: modelize_class $ AFormaldef
The definition of a formal generic parameter type. egX: Y
nitc :: modelize_class $ AInterfaceClasskind
An interface class modifier (interface
)
nitc :: modelize_class $ ModelBuilder
A model builder knows how to load nit source files and build the associated modelnitc :: modelize_class $ AAbstractClasskind
An abstract class modifier (abstract class
)
nitc :: modelize_class $ AClasskind
The modifier for the kind of class (abstract, interface, etc.)nitc :: modelize_class $ AConcreteClasskind
A default, or concrete class modifier (justclass
)
nitc :: modelize_class $ AEnumClasskind
An enum/universal class modifier (enum class
)
nitc :: modelize_class $ AExternClasskind
An extern class modifier (extern class
)
nitc :: modelize_class $ AFormaldef
The definition of a formal generic parameter type. egX: Y
nitc :: modelize_class $ AInterfaceClasskind
An interface class modifier (interface
)
nitc :: modelize_class $ ModelBuilder
A model builder knows how to load nit source files and build the associated modelSerializable::inspect
to show more useful information
more_collections :: more_collections
Highly specific, but useful, collections-related classes.serialization :: serialization_core
Abstract services to serialize Nit objects to different formatsnitc :: toolcontext
Common command-line tool infrastructure than handle options and error messagescore :: union_find
union–find algorithm using an efficient disjoint-set data structurenitc :: modelbuilder
nitc :: actors_generation_phase
Generate a support module for each module that contain a class annotated withis actor
nitc :: actors_injection_phase
Injects model for the classes annotated with "is actor" sonitc :: api_metrics
nitc :: astbuilder
Instantiation and transformation of semantic nodes in the AST of expressions and statementscflags
and ldflags
to specify
nitc :: commands_ini
extra_java_files
to compile extra java files
nitc :: i18n_phase
Basic support of internationalization through the generation of id-to-string tablesnitc :: light_only
Compiler support for the light FFI only, detects unsupported usage of callbacksnitc
.
nitc :: nitmetrics
A program that collects various metrics on nit programs and librariesnitc :: nitrestful
Tool generating boilerplate code linking RESTful actions to Nit methodsthreaded
annotation
nitc :: separate_erasure_compiler
Separate compilation of a Nit program with generic type erasurenitc :: serialization_code_gen_phase
Phase generating methods (code) to serialize Nit objectsnitc :: serialization_model_phase
Phase generating methods (model-only) to serialize Nit objectsclone
method of the astbuilder tool
nitc :: uml_module
Services for generation of a UML package diagram based on aModel
# Analysis and verification of class definitions to instantiate model element
module modelize_class
import modelbuilder
redef class ToolContext
# Run `AModule::build_classes` on each module
var modelize_class_phase: Phase = new ModelizeClassPhase(self, null)
end
private class ModelizeClassPhase
super Phase
redef fun process_nmodule(nmodule)
do
toolcontext.modelbuilder.build_classes(nmodule)
end
end
redef class ModelBuilder
# Visit the AST and create the `MClass` objects
private fun build_a_mclass(nmodule: AModule, nclassdef: AClassdef)
do
var mmodule = nmodule.mmodule.as(not null)
var name: String
var nkind: nullable AClasskind
var mkind: MClassKind
var nvisibility: nullable AVisibility
var mvisibility: nullable MVisibility
var arity = 0
var names = new Array[String]
var mclass
if nclassdef isa AStdClassdef then
var qid = nclassdef.n_qid
assert qid != null
name = qid.n_id.text
nkind = nclassdef.n_classkind
mkind = nkind.mkind
nvisibility = nclassdef.n_visibility
mvisibility = nvisibility.mvisibility
arity = nclassdef.n_formaldefs.length
if mvisibility == protected_visibility then
error(nvisibility, "Error: only properties can be protected.")
return
else if mvisibility == intrude_visibility then
error(nvisibility, "Error: intrude is not a legal visibility for classes.")
return
end
# Collect formal parameter names
for i in [0..arity[ do
var nfd = nclassdef.n_formaldefs[i]
var ptname = nfd.n_id.text
if names.has(ptname) then
error(nfd, "Error: a formal parameter type `{ptname}` already exists.")
return
end
for c in ptname.chars do if c >= 'a' and c<= 'z' then
warning(nfd, "formal-type-name", "Warning: lowercase in the formal parameter type `{ptname}`.")
break
end
names.add(ptname)
end
mclass = try_get_mclass_by_qid(qid, mmodule)
if mclass == null and (qid.n_qualified != null or nclassdef.n_kwredef != null) then
class_not_found(qid, mmodule)
nclassdef.is_broken = true
return
end
else if nclassdef isa ATopClassdef and nclassdef.n_propdefs.first.as(AMethPropdef).n_methid.collect_text == "sys" then
# Special case to keep `sys` in object.
# Needed to keep working bootstrap and a working java FFI together.
# TODO: remove once safe to remove
name = "Object"
nkind = null
mkind = interface_kind
nvisibility = null
mvisibility = public_visibility
mclass = try_get_mclass_by_name(nclassdef, mmodule, name)
else
name = "Sys"
nkind = null
mkind = concrete_kind
nvisibility = null
mvisibility = public_visibility
mclass = try_get_mclass_by_name(nclassdef, mmodule, name)
end
if mclass == null then
# Check for conflicting class full-names in the package
if mmodule.mgroup != null and mvisibility >= protected_visibility then
var mclasses = model.get_mclasses_by_name(name)
if mclasses != null then for other in mclasses do
if other.intro_mmodule.mgroup != null and other.intro_mmodule.mgroup.mpackage == mmodule.mgroup.mpackage then
# Skip classes that are buggy
if other.try_intro == null then continue
warning(nclassdef, "full-name-conflict", "Error: a class named `{other.full_name}` is already defined in module `{other.intro_mmodule}` at {other.intro.location}.")
break
end
end
end
mclass = new MClass(mmodule, name, nclassdef.location, names, mkind, mvisibility)
#print "new class {mclass}"
else if nclassdef isa AStdClassdef and nmodule.mclass2nclassdef.has_key(mclass) then
error(nclassdef, "Error: a class `{name}` is already defined at line {nmodule.mclass2nclassdef[mclass].location.line_start}.")
mclass.is_broken = true
return
else if nclassdef isa AStdClassdef and nclassdef.n_kwredef == null then
error(nclassdef, "Redef Error: `{name}` is an imported class. Add the `redef` keyword to refine it.")
mclass.is_broken = true
return
else if arity != 0 and mclass.arity != arity then
error(nclassdef, "Redef Error: expected {mclass.arity} formal parameter(s) for {mclass.signature_to_s}; got {arity}.")
mclass.is_broken = true
return
else if nkind != null and mkind != concrete_kind and mclass.kind != mkind then
error(nkind, "Redef Error: refinement changed the kind from `{mclass.kind}` to `{mkind}`.")
else if nvisibility != null and mvisibility != public_visibility and mclass.visibility != mvisibility then
error(nvisibility, "Redef Error: refinement changed the visibility from `{mclass.visibility}` to `{mvisibility}`")
end
nclassdef.mclass = mclass
if not nmodule.mclass2nclassdef.has_key(mclass) then
nmodule.mclass2nclassdef[mclass] = nclassdef
nclassdef.all_defs = [nclassdef]
else
nmodule.mclass2nclassdef[mclass].all_defs.add(nclassdef)
end
end
# Visit the AST and create the `MClassDef` objects
private fun build_a_mclassdef(nmodule: AModule, nclassdef: AClassdef)
do
var mmodule = nmodule.mmodule.as(not null)
var mclass = nclassdef.mclass
if mclass == null then return # Skip error
# In case of non-standard AClassdef, try to attach to an already existing mclassdef
var other_nclassdef = nmodule.mclass2nclassdef[mclass]
if other_nclassdef != nclassdef then
assert not nclassdef isa AStdClassdef
nclassdef.mclassdef = other_nclassdef.mclassdef
return
end
var bound_mtype = build_a_bound_mtype(nmodule, nclassdef)
if bound_mtype == null then return
var mclassdef = new MClassDef(mmodule, bound_mtype, nclassdef.location)
nclassdef.mclassdef = mclassdef
self.mclassdef2nclassdef[mclassdef] = nclassdef
if nclassdef isa AStdClassdef then
var ndoc = nclassdef.n_doc
if ndoc != null then
var mdoc = ndoc.to_mdoc
mclassdef.mdoc = mdoc
mdoc.original_mentity = mclassdef
else if mclassdef.is_intro and mclass.visibility >= public_visibility then
advice(nclassdef, "missing-doc", "Documentation warning: Undocumented public class `{mclass}`")
end
end
if mclassdef.is_intro then
self.toolcontext.info("{mclassdef} introduces new {mclass.kind} {mclass.full_name}", 3)
else
self.toolcontext.info("{mclassdef} refines {mclass.kind} {mclass.full_name}", 3)
end
end
# Determine the type parameter bounds for `nclassdef`.
#
# In case of error, return `null`.
#
# REQUIRE: `nmodule.mmodule != null`
# REQUIRE: `nclassdef.mclass != null`
private fun build_a_bound_mtype(nmodule: AModule, nclassdef: AClassdef): nullable MClassType
do
var mmodule = nmodule.mmodule.as(not null)
var mclass = nclassdef.mclass.as(not null)
var bounds = new Array[MType]
if nclassdef isa AStdClassdef and mclass.arity > 0 then
var objectclass = try_get_mclass_by_name(nmodule, mmodule, "Object")
# Revolve bound for formal parameters
for i in [0..mclass.arity[ do
if nclassdef.n_formaldefs.is_empty then
# Inherit the bound
var bound = mclass.intro.bound_mtype.arguments[i]
bounds.add(bound)
continue
end
var nfd = nclassdef.n_formaldefs[i]
var pname = mclass.mparameters[i].name
if nfd.n_id.text != pname then
error(nfd.n_id, "Error: formal parameter type #{i} `{nfd.n_id.text}` must be named `{pname}` as in the original definition in module `{mclass.intro.mmodule}`.")
end
var nfdt = nfd.n_type
if nfdt != null then
var bound = resolve_mtype3_unchecked(mmodule, null, null, nfdt, false)
if bound == null then return null # Forward error
if bound.need_anchor then
# No F-bounds!
error(nfd, "Error: formal parameter type `{pname}` bounded with a formal parameter type.")
else
bounds.add(bound)
nfd.bound = bound
end
else if mclass.mclassdefs.is_empty then
if objectclass == null then
error(nfd, "Error: formal parameter type `{pname}` unbounded but no `Object` class exists.")
return null
end
# No bound, then implicitely bound by nullable Object
var bound = objectclass.mclass_type.as_nullable
bounds.add(bound)
nfd.bound = bound
else
# Inherit the bound
var bound = mclass.intro.bound_mtype.arguments[i]
bounds.add(bound)
nfd.bound = bound
end
end
end
return mclass.get_mtype(bounds)
end
# Visit the AST and set the super-types of the `MClassDef` objects
private fun build_a_mclassdef_inheritance(nmodule: AModule, nclassdef: AClassdef)
do
var mmodule = nmodule.mmodule
if mmodule == null then return
var mclass = nclassdef.mclass
if mclass == null then return
var mclassdef = nclassdef.mclassdef
if mclassdef == null then return
var supertypes = collect_supertypes(nmodule, nclassdef, mclassdef.is_intro)
mclassdef.set_supertypes(supertypes)
if not supertypes.is_empty then self.toolcontext.info("{mclassdef} new super-types: {supertypes.join(", ")}", 3)
end
# List the supertypes specified or implied by `nclassdef`.
#
# REQUIRE: `nmodule.mmodule != null`
# REQUIRE: `nclassdef.mclass != null`
private fun collect_supertypes(nmodule: AModule, nclassdef: AClassdef,
is_intro: Bool): Array[MClassType]
do
var mmodule = nmodule.mmodule.as(not null)
var mclass = nclassdef.mclass.as(not null)
var name = mclass.name
var kind = mclass.kind
var objectclass = try_get_mclass_by_name(nmodule, mmodule, "Object")
var pointerclass = try_get_mclass_by_name(nmodule, mmodule, "Pointer")
# Do we need to specify Object as a super class?
var specobject = true
# Do we need to specify Pointer as a super class? (is only valid
# if `nclassdef` is an extern class)
var specpointer = true
var supertypes = new Array[MClassType]
if nclassdef isa AStdClassdef then
for nsc in nclassdef.n_superclasses do
specobject = false
var ntype = nsc.n_type
var mtype = resolve_mtype3_unchecked(mmodule, mclass, null,
ntype, false)
if mtype == null then continue # Skip because of error
if not mtype isa MClassType then
error(ntype, "Error: a supertype cannot be a formal type.")
continue
end
var superclass = mtype.mclass
var super_kind = superclass.kind
if not kind.can_specialize(super_kind) then
error(ntype, "Error: {kind} `{mclass}` cannot specialize {super_kind} `{superclass}`.")
end
supertypes.add mtype
#print "new super : {mclass} < {mtype}"
if super_kind == extern_kind then specpointer = false
end
end
if is_intro and objectclass != null then
if kind == extern_kind and name != "Pointer" then
# it is an extern class, but not a Pointer
if pointerclass == null then
error(nclassdef, "Error: `Pointer` must be defined first.")
return supertypes
end
if specpointer then supertypes.add pointerclass.mclass_type
else if specobject then
if name != "Object" then
# it is a standard class without super class (but is not Object)
supertypes.add objectclass.mclass_type
else if kind != interface_kind then
error(nclassdef, "Error: `Object` must be an {interface_kind}.")
end
end
end
return supertypes
end
# Check the validity of the specialization heirarchy
private fun check_supertypes(nmodule: AModule, nclassdef: AClassdef)
do
var mmodule = nmodule.mmodule
if mmodule == null then return
var mclass = nclassdef.mclass
if mclass == null then return
var mclassdef = nclassdef.mclassdef
if mclassdef == null then return
for s in mclassdef.supertypes do
if s.is_subtype(mmodule, mclassdef.bound_mtype, mclassdef.bound_mtype) then
error(nclassdef, "Error: inheritance loop for class `{mclass}` with type `{s}`.")
end
end
end
# Build the classes of the module `nmodule`.
private fun build_classes(nmodule: AModule)
do
# Force building recursively
if nmodule.build_classes_is_done then return
nmodule.build_classes_is_done = true
var mmodule = nmodule.mmodule
if mmodule == null then return
for imp in mmodule.in_importation.direct_greaters do
var nimp = mmodule2node(imp)
if nimp != null then build_classes(nimp)
end
# Create all classes
# process AStdClassdef before so that non-AStdClassdef classes can be attached to existing ones, if any
for nclassdef in nmodule.n_classdefs do
if not nclassdef isa AStdClassdef then continue
self.build_a_mclass(nmodule, nclassdef)
end
for nclassdef in nmodule.n_classdefs do
if nclassdef isa AStdClassdef then continue
self.build_a_mclass(nmodule, nclassdef)
end
# Create all classdefs
for nclassdef in nmodule.n_classdefs do
if not nclassdef isa AStdClassdef then continue
self.build_a_mclassdef(nmodule, nclassdef)
end
for nclassdef in nmodule.n_classdefs do
if nclassdef isa AStdClassdef then continue
self.build_a_mclassdef(nmodule, nclassdef)
end
# Create inheritance on all classdefs
for nclassdef in nmodule.n_classdefs do
self.build_a_mclassdef_inheritance(nmodule, nclassdef)
end
# Create the mclassdef hierarchy
for mclassdef in mmodule.mclassdefs do
mclassdef.add_in_hierarchy
end
# Check inheritance
for nclassdef in nmodule.n_classdefs do
self.check_supertypes(nmodule, nclassdef)
end
# Check unchecked ntypes
for nclassdef in nmodule.n_classdefs do
if nclassdef isa AStdClassdef then
var mclassdef = nclassdef.mclassdef
var mclass
var anchor
if mclassdef == null then
mclass = null
anchor = null
else
mclass = mclassdef.mclass
anchor = mclassdef.bound_mtype
end
# check bound of formal parameter
for nfd in nclassdef.n_formaldefs do
var nfdt = nfd.n_type
if nfdt != null and nfdt.mtype != null then
var bound = resolve_mtype3(mmodule, mclass, anchor, nfdt)
if bound == null then return # Forward error
end
end
# check declared super types
for nsc in nclassdef.n_superclasses do
var ntype = nsc.n_type
if ntype.mtype != null then
var mtype = resolve_mtype3(mmodule, mclass, anchor, ntype)
if mtype == null then return # Forward error
end
end
end
end
# Check clash of ancestors
for nclassdef in nmodule.n_classdefs do
var mclassdef = nclassdef.mclassdef
if mclassdef == null then continue
var superclasses = new HashMap[MClass, MClassType]
for scd in mclassdef.in_hierarchy.greaters do
for st in scd.supertypes do
if not superclasses.has_key(st.mclass) then
superclasses[st.mclass] = st
else if superclasses[st.mclass] != st then
var st1 = superclasses[st.mclass].resolve_for(mclassdef.mclass.mclass_type, mclassdef.bound_mtype, mmodule, false)
var st2 = st.resolve_for(mclassdef.mclass.mclass_type, mclassdef.bound_mtype, mmodule, false)
if st1 != st2 then
error(nclassdef, "Error: incompatible ancestors for `{mclassdef.mclass}`; conflict: `{st1}` and `{st2}`")
end
end
end
end
end
# TODO: Check that the super-class is not intrusive
# Check that the superclasses are not already known (by transitivity)
for nclassdef in nmodule.n_classdefs do
if not nclassdef isa AStdClassdef or nclassdef.is_broken then continue
var mclassdef = nclassdef.mclassdef
if mclassdef == null then continue
# Get the direct superclasses
# Since we are a mclassdef, just look at the mclassdef hierarchy
var parents = new Array[MClass]
for sup in mclassdef.in_hierarchy.direct_greaters do
parents.add(sup.mclass)
end
# Used to track duplicates of superclasses
var seen_parents = new ArrayMap[MClass, AType]
# The Object class
var objectclass = try_get_mclass_by_name(nmodule, mmodule, "Object")
# Check each declared superclass to see if it belong to the direct superclass
for nsc in nclassdef.n_superclasses do
var ntype = nsc.n_type
var mtype = ntype.mtype
# If the supertype is `null` or don’t refer to a class, we
# already raised an error.
if not mtype isa MClassType then continue
var sc = mtype.mclass
if not parents.has(sc) or sc == objectclass then
# Skip the warning on generated code
if ntype.location.file != null and not ntype.location.file.filename.is_empty then
warning(ntype, "useless-superclass", "Warning: superfluous super-class `{mtype}` in class `{mclassdef.mclass}`.")
end
else if not seen_parents.has_key(sc) then
seen_parents[sc] = ntype
else
warning(ntype, "useless-superclass", "Warning: duplicated super-class `{mtype}` in class `{mclassdef.mclass}`.")
end
end
end
end
# Registration of the nclassdef associated to each mclassdef
private var mclassdef2nclassdef = new HashMap[MClassDef, AClassdef]
# Retrieve the associated AST node of a mclassdef.
#
# This method is used to associate model entity with syntactic entities.
# If the class definition is not associated with a node, returns `null`.
fun mclassdef2node(mclassdef: MClassDef): nullable AClassdef do
return mclassdef2nclassdef.get_or_null(mclassdef)
end
end
redef class AModule
# Flag that indicate if the class building is already completed
var build_classes_is_done: Bool = false
# What is the AClassdef associated to a `MClass`?
# Used to check multiple definition of a class.
var mclass2nclassdef: Map[MClass, AClassdef] = new HashMap[MClass, AClassdef]
end
redef class AClassdef
# The associated MClass once build by a `ModelBuilder`
var mclass: nullable MClass
# The associated MClassDef once build by a `ModelBuilder`
var mclassdef: nullable MClassDef
# All (self and other) definitions for the same mclassdef
var all_defs: nullable Array[AClassdef]
end
redef class AClasskind
# The class kind associated with the AST node class
private fun mkind: MClassKind is abstract
end
redef class AConcreteClasskind
redef fun mkind do return concrete_kind
end
redef class AAbstractClasskind
redef fun mkind do return abstract_kind
end
redef class AInterfaceClasskind
redef fun mkind do return interface_kind
end
redef class AEnumClasskind
redef fun mkind do return enum_kind
end
redef class AExternClasskind
redef fun mkind do return extern_kind
end
redef class ASubsetClasskind
redef fun mkind do return subset_kind
end
redef class AFormaldef
# The associated parameter type
var mtype: nullable MParameterType = null
# The associated bound
var bound: nullable MType = null
end
src/modelize/modelize_class.nit:17,1--551,3