Merge: Loader refactor
authorJean Privat <jean@pryen.org>
Mon, 19 Oct 2015 12:50:24 +0000 (08:50 -0400)
committerJean Privat <jean@pryen.org>
Mon, 19 Oct 2015 12:50:24 +0000 (08:50 -0400)
A big refactorization of the loader

This include cleanup, renaming and documentation of the existing code.

The major change is the removal of `ModulePath` used to designate an identified module that is not yet loaded. This caused major issues since once the module was loaded, the MModule object has to be used instead.
Now, genuine `MModule` are used to also represent modules identified in the file system but not yet loaded.
This simplify the code of the loader, nitls and nitcatalog.

By the way, the option `-d` (and `-M`) of nitls that was broken by the previous changes in the loader is now fixed.

Another change is the introduction of `scan_full` that is a high-level method to be used by clients like nitls or nitcatalog that just need to identify packages, groups and modules in the filesystem without loading them.

By the way, nitcalatog works on directories of projects now. eg. `nitcatalog ../lib)

Note, because this PR introduces a lot of interrelated changes, It was hard to come up with nice commits. The alternative was to have a single big commit.

Pull-Request: #1750
Reviewed-by: Alexis Laferrière <alexis.laf@xymus.net>
Reviewed-by: Lucas Bajolet <r4pass@hotmail.com>

13 files changed:
contrib/online_ide/sources/nit/pnacl_nit.nit
src/loader.nit
src/model/mmodule.nit
src/model/model_viz.nit
src/modelbuilder.nit
src/nitcatalog.nit
src/nitls.nit
src/nitunit.nit
tests/nitls.args
tests/sav/base_import2_alt1.res
tests/sav/nitls_args6.res [new file with mode: 0644]
tests/sav/nitmetrics_args1.res
tests/sav/nitx_args3.res

index 567b0a2..122b507 100644 (file)
@@ -97,7 +97,7 @@ redef class ModelBuilder
        redef fun module_absolute_path(path: String): String do return path
 
        # We don't use paths as the interpreter, so we don't use location or lookpaths args (see the default implementation).
-       redef fun search_module_in_paths(location: nullable Location, name: String, lookpaths: Collection[String]): nullable ModulePath
+       redef fun search_module_in_paths(location: nullable Location, name: String, lookpaths: Collection[String]): nullable MModule
        do
                var candidate: nullable String = null
                var try_file = "{name}.nit"
@@ -114,7 +114,7 @@ redef class ModelBuilder
                        end
                end
                if candidate == null then return null
-               return identify_file(candidate)
+               return identify_module(candidate)
        end
 end
 
index fb1801b..aa88cdc 100644 (file)
 # limitations under the License.
 
 # Loading of Nit source files
+#
+# The loader takes care of looking for module and projects in the file system, and the associated case of errors.
+# The loading requires several steps:
+#
+# Identify: create an empty model entity associated to a name or a file path.
+# Identification is used for instance when names are given in the command line.
+# See `identify_module` and `identify_group`.
+#
+# Scan: visit directories and identify their contents.
+# Scanning is done to enable the searching of modules in projects.
+# See `scan_group` and `scan_full`.
+#
+# Parse: load the AST and associate it with the model entity.
+# See `MModule::parse`.
+#
+# Import: means recursively load modules imported by a module.
+# See `build_module_importation`.
+#
+# Load: means doing the full sequence: identify, parse and import.
+# See `ModelBuilder::parse`, `ModelBuilder::parse_full`, `MModule::load` `ModelBuilder::load_module.
 module loader
 
 import modelbuilder_base
@@ -73,8 +93,6 @@ redef class ModelBuilder
                for a in modules do
                        var nmodule = self.load_module(a)
                        if nmodule == null then continue # Skip error
-                       # Load imported module
-                       build_module_importation(nmodule)
                        var mmodule = nmodule.mmodule
                        if mmodule == null then continue # skip error
                        mmodules.add mmodule
@@ -92,52 +110,18 @@ redef class ModelBuilder
                return mmodules.to_a
        end
 
-       # Load recursively all modules of the group `mgroup`.
-       # See `parse` for details.
-       fun parse_group(mgroup: MGroup): Array[MModule]
-       do
-               var res = new Array[MModule]
-               scan_group(mgroup)
-               for mg in mgroup.in_nesting.smallers do
-                       for mp in mg.module_paths do
-                               var nmodule = self.load_module(mp.filepath)
-                               if nmodule == null then continue # Skip error
-                               # Load imported module
-                               build_module_importation(nmodule)
-                               var mmodule = nmodule.mmodule
-                               if mmodule == null then continue # Skip error
-                               res.add mmodule
-                       end
-               end
-               return res
-       end
-
-       # Load a bunch of modules and groups.
-       #
-       # Each name can be:
-       #
-       # * a path to a module, a group or a directory of packages.
-       # * a short name of a module or a group that are looked in the `paths` (-I)
-       #
-       # Then, for each entry, if it is:
+       # Identify a bunch of modules and groups.
        #
-       # * a module, then is it parser and returned.
-       # * a group then recursively all its modules are parsed.
-       # * a directory of packages then all the modules of all packages are parsed.
-       # * else an error is displayed.
-       #
-       # See `parse` for details.
-       fun parse_full(names: Sequence[String]): Array[MModule]
+       # This does the same as `parse_full` but does only the identification (cf. `identify_module`)
+       fun scan_full(names: Sequence[String]): Array[MModule]
        do
-               var time0 = get_time
-               # Parse and recursively load
-               self.toolcontext.info("*** PARSE ***", 1)
-               var mmodules = new ArraySet[MModule]
+               var mmodules = new Array[MModule]
                for a in names do
-                       # Case of a group
-                       var mgroup = self.get_mgroup(a)
+                       # Case of a group (root or sub-directory)
+                       var mgroup = self.identify_group(a)
                        if mgroup != null then
-                               mmodules.add_all parse_group(mgroup)
+                               scan_group(mgroup)
+                               for mg in mgroup.in_nesting.smallers do mmodules.add_all mg.mmodules
                                continue
                        end
 
@@ -146,21 +130,18 @@ redef class ModelBuilder
                        if stat != null and stat.is_dir then
                                self.toolcontext.info("look in directory {a}", 2)
                                var fs = a.files
+                               alpha_comparator.sort(fs)
                                # Try each entry as a group or a module
                                for f in fs do
                                        var af = a/f
-                                       mgroup = get_mgroup(af)
+                                       mgroup = identify_group(af)
                                        if mgroup != null then
-                                               mmodules.add_all parse_group(mgroup)
+                                               scan_group(mgroup)
+                                               for mg in mgroup.in_nesting.smallers do mmodules.add_all mg.mmodules
                                                continue
                                        end
-                                       var mp = identify_file(af)
-                                       if mp != null then
-                                               var nmodule = self.load_module(af)
-                                               if nmodule == null then continue # Skip error
-                                               build_module_importation(nmodule)
-                                               var mmodule = nmodule.mmodule
-                                               if mmodule == null then continue # Skip error
+                                       var mmodule = identify_module(af)
+                                       if mmodule != null then
                                                mmodules.add mmodule
                                        else
                                                self.toolcontext.info("ignore file {af}", 2)
@@ -169,12 +150,41 @@ redef class ModelBuilder
                                continue
                        end
 
-                       var nmodule = self.load_module(a)
-                       if nmodule == null then continue # Skip error
-                       # Load imported module
-                       build_module_importation(nmodule)
-                       var mmodule = nmodule.mmodule
-                       if mmodule == null then continue # Skip error
+                       var mmodule = identify_module(a)
+                       if mmodule == null then
+                               continue
+                       end
+
+                       mmodules.add mmodule
+               end
+               return mmodules
+       end
+
+       # Load a bunch of modules and groups.
+       #
+       # Each name can be:
+       #
+       # * a path to a module, a group or a directory of packages.
+       # * a short name of a module or a group that are looked in the `paths` (-I)
+       #
+       # Then, for each entry, if it is:
+       #
+       # * a module, then is it parsed and returned.
+       # * a group then recursively all its modules are parsed.
+       # * a directory of packages then all the modules of all packages are parsed.
+       # * else an error is displayed.
+       #
+       # See `parse` for details.
+       fun parse_full(names: Sequence[String]): Array[MModule]
+       do
+               var time0 = get_time
+               # Parse and recursively load
+               self.toolcontext.info("*** PARSE ***", 1)
+               var mmodules = new ArraySet[MModule]
+               var scans = scan_full(names)
+               for mmodule in scans do
+                       var ast = mmodule.load(self)
+                       if ast == null then continue # Skip error
                        mmodules.add mmodule
                end
                var time1 = get_time
@@ -199,8 +209,8 @@ redef class ModelBuilder
        # Path can be added (or removed) by the client
        var paths = new Array[String]
 
-       # Like (and used by) `get_mmodule_by_name` but just return the ModulePath
-       fun search_mmodule_by_name(anode: nullable ANode, mgroup: nullable MGroup, name: String): nullable ModulePath
+       # Like (and used by) `get_mmodule_by_name` but does not force the parsing of the MModule (cf. `identify_module`)
+       fun search_mmodule_by_name(anode: nullable ANode, mgroup: nullable MGroup, name: String): nullable MModule
        do
                # First, look in groups
                var c = mgroup
@@ -208,7 +218,7 @@ redef class ModelBuilder
                        var r = c.mpackage.root
                        assert r != null
                        scan_group(r)
-                       var res = r.mmodule_paths_by_name(name)
+                       var res = r.mmodules_by_name(name)
                        if res.not_empty then return res.first
                end
 
@@ -247,42 +257,32 @@ redef class ModelBuilder
        # If no module exists or there is a name conflict, then an error on `anode` is displayed and null is returned.
        fun get_mmodule_by_name(anode: nullable ANode, mgroup: nullable MGroup, name: String): nullable MModule
        do
-               var path = search_mmodule_by_name(anode, mgroup, name)
-               if path == null then return null # Forward error
-               return load_module_path(path)
-       end
-
-       # Load and process importation of a given ModulePath.
-       #
-       # Basically chains `load_module` and `build_module_importation`.
-       fun load_module_path(path: ModulePath): nullable MModule
-       do
-               var res = self.load_module(path.filepath)
-               if res == null then return null # Forward error
-               # Load imported module
-               build_module_importation(res)
-               return res.mmodule
+               var mmodule = search_mmodule_by_name(anode, mgroup, name)
+               if mmodule == null then return null # Forward error
+               var ast = mmodule.load(self)
+               if ast == null then return null # Forward error
+               return mmodule
        end
 
        # Search a module `name` from path `lookpaths`.
-       # If found, the path of the file is returned
-       private fun search_module_in_paths(location: nullable Location, name: String, lookpaths: Collection[String]): nullable ModulePath
+       # If found, the module is returned.
+       private fun search_module_in_paths(location: nullable Location, name: String, lookpaths: Collection[String]): nullable MModule
        do
-               var res = new ArraySet[ModulePath]
+               var res = new ArraySet[MModule]
                for dirname in lookpaths do
                        # Try a single module file
-                       var mp = identify_file((dirname/"{name}.nit").simplify_path)
+                       var mp = identify_module((dirname/"{name}.nit").simplify_path)
                        if mp != null then res.add mp
                        # Try the default module of a group
-                       var g = get_mgroup((dirname/name).simplify_path)
+                       var g = identify_group((dirname/name).simplify_path)
                        if g != null then
                                scan_group(g)
-                               res.add_all g.mmodule_paths_by_name(name)
+                               res.add_all g.mmodules_by_name(name)
                        end
                end
                if res.is_empty then return null
                if res.length > 1 then
-                       toolcontext.error(location, "Error: conflicting module files for `{name}`: `{res.join(",")}`")
+                       toolcontext.error(location, "Error: conflicting module files for `{name}`: `{[for x in res do x.filepath or else x.full_name].join("`, `")}`")
                end
                return res.first
        end
@@ -293,7 +293,7 @@ redef class ModelBuilder
                var res = new ArraySet[MGroup]
                for dirname in lookpaths do
                        # try a single group directory
-                       var mg = get_mgroup(dirname/name)
+                       var mg = identify_group(dirname/name)
                        if mg != null then
                                res.add mg
                        end
@@ -301,26 +301,36 @@ redef class ModelBuilder
                return res
        end
 
-       # Cache for `identify_file` by realpath
-       private var identified_files_by_path = new HashMap[String, nullable ModulePath]
+       # Cache for `identify_module` by relative and real paths
+       private var identified_modules_by_path = new HashMap[String, nullable MModule]
 
        # All the currently identified modules.
-       # See `identify_file`.
-       var identified_files = new Array[ModulePath]
+       # See `identify_module`.
+       #
+       # An identified module exists in the model but might be not yet parsed (no AST), or not yet analysed (no importation).
+       var identified_modules = new Array[MModule]
+
+       # All the currently parsed modules.
+       #
+       # A parsed module exists in the model but might be not yet analysed (no importation).
+       var parsed_modules = new Array[MModule]
 
        # Identify a source file and load the associated package and groups if required.
        #
        # This method does what the user expects when giving an argument to a Nit tool.
        #
        # * If `path` is an existing Nit source file (with the `.nit` extension),
-       #   then the associated ModulePath is returned
+       #   then the associated MModule is returned
        # * If `path` is a directory (with a `/`),
-       #   then the ModulePath of its default module is returned (if any)
+       #   then the MModule of its default module is returned (if any)
        # * If `path` is a simple identifier (eg. `digraph`),
        #   then the main module of the package `digraph` is searched in `paths` and returned.
        #
        # Silently return `null` if `path` does not exists or cannot be identified.
-       fun identify_file(path: String): nullable ModulePath
+       #
+       # On success, it returns a module that is possibly not yet parsed (no AST), or not yet analysed (no importation).
+       # If the module was already identified, or loaded, it is returned.
+       fun identify_module(path: String): nullable MModule
        do
                # special case for not a nit file
                if not path.has_suffix(".nit") then
@@ -333,7 +343,7 @@ redef class ModelBuilder
                        # Found nothing? maybe it is a group...
                        var candidate = null
                        if path.file_exists then
-                               var mgroup = get_mgroup(path)
+                               var mgroup = identify_group(path)
                                if mgroup != null then
                                        var owner_path = mgroup.filepath.join_path(mgroup.name + ".nit")
                                        if owner_path.file_exists then candidate = owner_path
@@ -352,15 +362,15 @@ redef class ModelBuilder
                end
 
                # Fast track, the path is already known
-               if identified_files_by_path.has_key(path) then return identified_files_by_path[path]
+               if identified_modules_by_path.has_key(path) then return identified_modules_by_path[path]
                var rp = module_absolute_path(path)
-               if identified_files_by_path.has_key(rp) then return identified_files_by_path[rp]
+               if identified_modules_by_path.has_key(rp) then return identified_modules_by_path[rp]
 
                var pn = path.basename(".nit")
 
                # Search for a group
                var mgrouppath = path.join_path("..").simplify_path
-               var mgroup = get_mgroup(mgrouppath)
+               var mgroup = identify_group(mgrouppath)
 
                if mgroup == null then
                        # singleton package
@@ -378,12 +388,14 @@ redef class ModelBuilder
                        end
                end
 
-               var res = new ModulePath(pn, path, mgroup)
-               mgroup.module_paths.add(res)
+               var src = new SourceFile.from_string(path, "")
+               var loc = new Location(src, 0, 0, 0, 0)
+               var res = new MModule(model, mgroup, pn, loc)
+               res.filepath = path
 
-               identified_files_by_path[rp] = res
-               identified_files_by_path[path] = res
-               identified_files.add(res)
+               identified_modules_by_path[rp] = res
+               identified_modules_by_path[path] = res
+               identified_modules.add(res)
                return res
        end
 
@@ -394,7 +406,7 @@ redef class ModelBuilder
        # If the directory is not a group null is returned.
        #
        # Note: `paths` is also used to look for mgroups
-       fun get_mgroup(dirpath: String): nullable MGroup
+       fun identify_group(dirpath: String): nullable MGroup
        do
                var stat = dirpath.file_stat
 
@@ -455,7 +467,7 @@ redef class ModelBuilder
                        var stopper = parentpath / "packages.ini"
                        if not stopper.file_exists then
                                # Recursively get the parent group
-                               parent = get_mgroup(parentpath)
+                               parent = identify_group(parentpath)
                                if parent == null then
                                        # Parent is not a group, thus we are not a group either
                                        mgroups[rdp] = null
@@ -514,12 +526,12 @@ redef class ModelBuilder
                return mdoc
        end
 
-       # Force the identification of all ModulePath of the group and sub-groups in the file system.
+       # Force the identification of all MModule of the group and sub-groups in the file system.
        #
        # When a group is scanned, its sub-groups hierarchy is filled (see `MGroup::in_nesting`)
-       # and the potential modules (and nested modules) are identified (see `MGroup::module_paths`).
+       # and the potential modules (and nested modules) are identified (see `MGroup::modules`).
        #
-       # Basically, this recursively call `get_mgroup` and `identify_file` on each directory entry.
+       # Basically, this recursively call `identify_group` and `identify_module` on each directory entry.
        #
        # No-op if the group was already scanned (see `MGroup::scanned`).
        fun scan_group(mgroup: MGroup) do
@@ -528,12 +540,14 @@ redef class ModelBuilder
                var p = mgroup.filepath
                # a virtual group has nothing to scan
                if p == null then return
-               for f in p.files do
+               var files = p.files
+               alpha_comparator.sort(files)
+               for f in files do
                        var fp = p/f
-                       var g = get_mgroup(fp)
+                       var g = identify_group(fp)
                        # Recursively scan for groups of the same package
                        if g == null then
-                               identify_file(fp)
+                               identify_module(fp)
                        else if g.mpackage == mgroup.mpackage then
                                scan_group(g)
                        end
@@ -547,6 +561,10 @@ redef class ModelBuilder
 
        # Try to load a module AST using a path.
        # Display an error if there is a problem (IO / lexer / parser) and return null
+       #
+       # The AST is loaded as is total independence of the model and its entities.
+       #
+       # AST are not cached or reused thus a new AST is returned on success.
        fun load_module_ast(filename: String): nullable AModule
        do
                if not filename.has_suffix(".nit") then
@@ -587,7 +605,7 @@ redef class ModelBuilder
                var keep = new Array[String]
                var res = new Array[String]
                for a in args do
-                       var l = identify_file(a)
+                       var l = identify_module(a)
                        if l == null then
                                keep.add a
                        else
@@ -603,13 +621,12 @@ redef class ModelBuilder
        # Display an error if there is a problem (IO / lexer / parser) and return null.
        # Note: usually, you do not need this method, use `get_mmodule_by_name` instead.
        #
-       # The MModule is created however, the importation is not performed,
-       # therefore you should call `build_module_importation`.
+       # The MModule is located, created, parsed and the importation is performed.
        fun load_module(filename: String): nullable AModule
        do
                # Look for the module
-               var file = identify_file(filename)
-               if file == null then
+               var mmodule = identify_module(filename)
+               if mmodule == null then
                        if filename.file_exists then
                                toolcontext.error(null, "Error: `{filename}` is not a Nit source file.")
                        else
@@ -618,25 +635,8 @@ redef class ModelBuilder
                        return null
                end
 
-               # Already known and loaded? then return it
-               var mmodule = file.mmodule
-               if mmodule != null then
-                       return mmodule2nmodule[mmodule]
-               end
-
-               # Load it manually
-               var nmodule = load_module_ast(file.filepath)
-               if nmodule == null then return null # forward error
-
-               # build the mmodule and load imported modules
-               mmodule = build_a_mmodule(file.mgroup, file.name, nmodule)
-
-               if mmodule == null then return null # forward error
-
-               # Update the file information
-               file.mmodule = mmodule
-
-               return nmodule
+               # Load it
+               return mmodule.load(self)
        end
 
        # Injection of a new module without source.
@@ -665,22 +665,25 @@ redef class ModelBuilder
        end
 
        # Visit the AST and create the `MModule` object
-       private fun build_a_mmodule(mgroup: nullable MGroup, mod_name: String, nmodule: AModule): nullable MModule
+       private fun build_a_mmodule(mgroup: nullable MGroup, nmodule: AModule)
        do
+               var mmodule = nmodule.mmodule
+               assert mmodule != null
+
                # Check the module name
                var decl = nmodule.n_moduledecl
                if decl != null then
                        var decl_name = decl.n_name.n_id.text
-                       if decl_name != mod_name then
-                               error(decl.n_name, "Error: module name mismatch; declared {decl_name} file named {mod_name}.")
+                       if decl_name != mmodule.name then
+                               error(decl.n_name, "Error: module name mismatch; declared {decl_name} file named {mmodule.name}.")
                        end
                end
 
                # Check for conflicting module names in the package
                if mgroup != null then
-                       var others = model.get_mmodules_by_name(mod_name)
+                       var others = model.get_mmodules_by_name(mmodule.name)
                        if others != null then for other in others do
-                               if other.mgroup!= null and other.mgroup.mpackage == mgroup.mpackage then
+                               if other != mmodule and mmodule2nmodule.has_key(mmodule) and other.mgroup!= null and other.mgroup.mpackage == mgroup.mpackage then
                                        var node: ANode
                                        if decl == null then node = nmodule else node = decl.n_name
                                        error(node, "Error: a module named `{other.full_name}` already exists at {other.location}.")
@@ -689,9 +692,6 @@ redef class ModelBuilder
                        end
                end
 
-               # Create the module
-               var mmodule = new MModule(model, mgroup, mod_name, nmodule.location)
-               nmodule.mmodule = mmodule
                nmodules.add(nmodule)
                self.mmodule2nmodule[mmodule] = nmodule
 
@@ -714,14 +714,12 @@ redef class ModelBuilder
                        # Is the module a test suite?
                        mmodule.is_test_suite = not decl.get_annotations("test_suite").is_empty
                end
-
-               return mmodule
        end
 
        # Resolve the module identification for a given `AModuleName`.
        #
        # This method handles qualified names as used in `AModuleName`.
-       fun seach_module_by_amodule_name(n_name: AModuleName, mgroup: nullable MGroup): nullable ModulePath
+       fun seach_module_by_amodule_name(n_name: AModuleName, mgroup: nullable MGroup): nullable MModule
        do
                var mod_name = n_name.n_id.text
 
@@ -741,12 +739,12 @@ redef class ModelBuilder
                        assert r != null
                        scan_group(r)
                        # Get all modules with the final name
-                       var res = r.mmodule_paths_by_name(mod_name)
+                       var res = r.mmodules_by_name(mod_name)
                        # Filter out the name that does not match the qualifiers
                        res = [for x in res do if match_amodulename(n_name, x) then x]
                        if res.not_empty then
                                if res.length > 1 then
-                                       error(n_name, "Error: conflicting module files for `{mod_name}`: `{res.join(",")}`")
+                                       error(n_name, "Error: conflicting module files for `{mod_name}`: `{[for x in res do x.filepath or else x.full_name].join("`, `")}`")
                                end
                                return res.first
                        end
@@ -761,16 +759,16 @@ redef class ModelBuilder
                        return null
                end
 
-               var res = new ArraySet[ModulePath]
+               var res = new ArraySet[MModule]
                for r in roots do
                        # Then, for each root, collect modules that matches the qualifiers
                        scan_group(r)
-                       var root_res = r.mmodule_paths_by_name(mod_name)
+                       var root_res = r.mmodules_by_name(mod_name)
                        for x in root_res do if match_amodulename(n_name, x) then res.add x
                end
                if res.not_empty then
                        if res.length > 1 then
-                               error(n_name, "Error: conflicting module files for `{mod_name}`: `{res.join(",")}`")
+                               error(n_name, "Error: conflicting module files for `{mod_name}`: `{[for x in res do x.filepath or else x.full_name].join("`, `")}`")
                        end
                        return res.first
                end
@@ -785,7 +783,7 @@ redef class ModelBuilder
        # but not `baz/foo.nit` nor `foo/bar.nit`
        #
        # Is used by `seach_module_by_amodule_name` to validate qualified names.
-       private fun match_amodulename(n_name: AModuleName, m: ModulePath): Bool
+       private fun match_amodulename(n_name: AModuleName, m: MModule): Bool
        do
                var g: nullable MGroup = m.mgroup
                for grp in n_name.n_path.reverse_iterator do
@@ -798,7 +796,10 @@ redef class ModelBuilder
 
        # Analyze the module importation and fill the module_importation_hierarchy
        #
-       # Unless you used `load_module`, the importation is already done and this method does a no-op.
+       # If the importation was already done (`nmodule.is_importation_done`), this method does a no-op.
+       #
+       # REQUIRE `nmodule.mmodule != null`
+       # ENSURE `nmodule.is_importation_done`
        fun build_module_importation(nmodule: AModule)
        do
                if nmodule.is_importation_done then return
@@ -817,14 +818,14 @@ redef class ModelBuilder
                        end
 
                        # Load the imported module
-                       var suppath = seach_module_by_amodule_name(aimport.n_name, mmodule.mgroup)
-                       if suppath == null then
+                       var sup = seach_module_by_amodule_name(aimport.n_name, mmodule.mgroup)
+                       if sup == null then
                                mmodule.is_broken = true
                                nmodule.mmodule = null # invalidate the module
                                continue # Skip error
                        end
-                       var sup = load_module_path(suppath)
-                       if sup == null then
+                       var ast = sup.load(self)
+                       if ast == null then
                                mmodule.is_broken = true
                                nmodule.mmodule = null # invalidate the module
                                continue # Skip error
@@ -882,7 +883,7 @@ redef class ModelBuilder
                                end
 
                                # The rule
-                               var rule = new Array[Object]
+                               var rule = new Array[MModule]
 
                                # First element is the goal, thus
                                rule.add suppath
@@ -940,14 +941,11 @@ redef class ModelBuilder
        # It means that the first module is the module to automatically import.
        # The remaining modules are the conditions of the rule.
        #
-       # Each module is either represented by a MModule (if the module is already loaded)
-       # or by a ModulePath (if the module is not yet loaded).
-       #
        # Rules are declared by `build_module_importation` and are applied by `apply_conditional_importations`
        # (and `build_module_importation` that calls it).
        #
        # TODO (when the loader will be rewritten): use a better representation and move up rules in the model.
-       private var conditional_importations = new Array[SequenceRead[Object]]
+       private var conditional_importations = new Array[SequenceRead[MModule]]
 
        # Extends the current importations according to imported rules about conditional importation
        fun apply_conditional_importations(mmodule: MModule)
@@ -961,28 +959,16 @@ redef class ModelBuilder
                        for ci in conditional_importations do
                                # Check conditions
                                for i in [1..ci.length[ do
-                                       var rule_element = ci[i]
-                                       # An element of a rule is either a MModule or a ModulePath
-                                       # We need the mmodule to resonate on the importation
-                                       var m
-                                       if rule_element isa MModule then
-                                               m = rule_element
-                                       else if rule_element isa ModulePath then
-                                               m = rule_element.mmodule
-                                               # Is loaded?
-                                               if m == null then continue label
-                                       else
-                                               abort
-                                       end
+                                       var m = ci[i]
                                        # Is imported?
                                        if not mmodule.in_importation.greaters.has(m) then continue label
                                end
                                # Still here? It means that all conditions modules are loaded and imported
 
                                # Identify the module to automatically import
-                               var suppath = ci.first.as(ModulePath)
-                               var sup = load_module_path(suppath)
-                               if sup == null then continue
+                               var sup = ci.first
+                               var ast = sup.load(self)
+                               if ast == null then continue
 
                                # Do nothing if already imported
                                if mmodule.in_importation.greaters.has(sup) then continue label
@@ -1018,22 +1004,50 @@ redef class ModelBuilder
        end
 end
 
-# File-system location of a module (file) that is identified but not always loaded.
-class ModulePath
-       # The name of the module
-       # (it's the basename of the filepath)
-       var name: String
+redef class MModule
+       # The path of the module source
+       var filepath: nullable String = null
 
-       # The human path of the module
-       var filepath: String
+       # Force the parsing of the module using `modelbuilder`.
+       #
+       # If the module was already parsed, the existing ASI is returned.
+       # Else the source file is loaded, and parsed and some
+       #
+       # The importation is not done by this
+       #
+       # REQUIRE: `filepath != null`
+       # ENSURE: `modelbuilder.parsed_modules.has(self)`
+       fun parse(modelbuilder: ModelBuilder): nullable AModule
+       do
+               # Already known and loaded? then return it
+               var nmodule = modelbuilder.mmodule2nmodule.get_or_null(self)
+               if nmodule != null then return nmodule
 
-       # The group (and the package) of the possible module
-       var mgroup: MGroup
+               var filepath = self.filepath
+               assert filepath != null
+               # Load it manually
+               nmodule = modelbuilder.load_module_ast(filepath)
+               if nmodule == null then return null # forward error
 
-       # The loaded module (if any)
-       var mmodule: nullable MModule = null
+               # build the mmodule
+               nmodule.mmodule = self
+               modelbuilder.build_a_mmodule(mgroup, nmodule)
+
+               modelbuilder.parsed_modules.add self
+               return nmodule
+       end
+
+       # Parse and process importation of a given MModule.
+       #
+       # Basically chains `parse` and `build_module_importation`.
+       fun load(modelbuilder: ModelBuilder): nullable AModule
+       do
+               var nmodule = parse(modelbuilder)
+               if nmodule == null then return null
 
-       redef fun to_s do return filepath
+               modelbuilder.build_module_importation(nmodule)
+               return nmodule
+       end
 end
 
 redef class MPackage
@@ -1046,9 +1060,6 @@ redef class MPackage
 end
 
 redef class MGroup
-       # Modules paths associated with the group
-       var module_paths = new Array[ModulePath]
-
        # Is the group interesting for a final user?
        #
        # Groups are mandatory in the model but for simple packages they are not
@@ -1061,8 +1072,7 @@ redef class MGroup
        # * it has a documentation
        fun is_interesting: Bool
        do
-               return module_paths.length > 1 or
-                       mmodules.length > 1 or
+               return mmodules.length > 1 or
                        not in_nesting.direct_smallers.is_empty or
                        mdoc != null or
                        (mmodules.length == 1 and default_mmodule == null)
@@ -1077,11 +1087,11 @@ redef class MGroup
        #
        # If `self` is not scanned (see `ModelBuilder::scan_group`) the
        # results might be partial.
-       fun mmodule_paths_by_name(name: String): Array[ModulePath]
+       fun mmodules_by_name(name: String): Array[MModule]
        do
-               var res = new Array[ModulePath]
+               var res = new Array[MModule]
                for g in in_nesting.smallers do
-                       for mp in g.module_paths do
+                       for mp in g.mmodules do
                                if mp.name == name then
                                        res.add mp
                                end
@@ -1103,7 +1113,8 @@ end
 
 redef class AModule
        # The associated MModule once build by a `ModelBuilder`
-       var mmodule: nullable MModule
+       var mmodule: nullable MModule = null
+
        # Flag that indicate if the importation is already completed
        var is_importation_done: Bool = false
 end
index 7e2e9b3..db4b8c6 100644 (file)
@@ -22,7 +22,11 @@ import mpackage
 private import more_collections
 
 # The container class of a Nit object-oriented model.
+#
 # A model knows modules, classes and properties and can retrieve them.
+#
+# However, a model is not a program or a library as it can contains modules
+# found by the system (including broken ones) but not used.
 redef class Model
        # All known modules
        var mmodules = new Array[MModule]
index 66ab460..d516da7 100644 (file)
@@ -19,9 +19,8 @@ import model
 import ordered_tree
 
 # A simple specialisation of OrderedTree to display packages, groups and modules
-# FIXME do not use Object, but a better common interface of MModule and MGroup
 class MPackageTree
-       super OrderedTree[Object]
+       super OrderedTree[MConcern]
 
        # The model where to look for information
        var model: Model
@@ -58,11 +57,10 @@ class MPackageTree
 end
 
 # Compare modules and groups using the
-# FIXME do not use Object, but a better common interface of MModule and MGroup
 private class LinexComparator
        super Comparator
 
-       redef type COMPARED: Object
+       redef type COMPARED: MConcern
 
        var mins = new HashMap [MGroup, nullable MModule]
        var maxs = new HashMap [MGroup, nullable MModule]
@@ -111,7 +109,7 @@ private class LinexComparator
                var order = ma.model.mmodule_importation_hierarchy
                return order.compare(ma, mb)
        end
-       var tree: OrderedTree[Object]
+       var tree: OrderedTree[MConcern]
 end
 
 redef class Model
index eaccbb0..2a28de4 100644 (file)
@@ -89,7 +89,7 @@ redef class ModelBuilder
        # Run phases on all loaded modules
        fun run_phases
        do
-               var mmodules = model.mmodules.to_a
+               var mmodules = parsed_modules.to_a
                model.mmodule_importation_hierarchy.sort(mmodules)
                var nmodules = new Array[AModule]
                for mm in mmodules do
index 59d76b0..21847ee 100644 (file)
@@ -245,7 +245,7 @@ class Catalog
        end
 
        # Recursively generate a level in the file tree of the *content* section
-       private fun gen_content_level(ot: OrderedTree[Object], os: Array[Object], res: Template)
+       private fun gen_content_level(ot: OrderedTree[MConcern], os: Array[Object], res: Template)
        do
                res.add "<ul>\n"
                for o in os do
@@ -255,13 +255,10 @@ class Catalog
                                var mdoc = o.mdoc
                                if mdoc != null then d = ": {mdoc.html_synopsis.write_to_string}"
                                res.add "<strong>{o.name}</strong>{d} ({o.filepath.to_s})"
-                       else if o isa ModulePath then
+                       else if o isa MModule then
                                var d = ""
-                               var m = o.mmodule
-                               if m != null then
-                                       var mdoc = m.mdoc
-                                       if mdoc != null then d = ": {mdoc.html_synopsis.write_to_string}"
-                               end
+                               var mdoc = o.mdoc
+                               if mdoc != null then d = ": {mdoc.html_synopsis.write_to_string}"
                                res.add "<strong>{o.name}</strong>{d} ({o.filepath.to_s})"
                        else
                                abort
@@ -293,14 +290,14 @@ class Catalog
                end
 
                res.add "<h2>Content</h2>"
-               var ot = new OrderedTree[Object]
+               var ot = new OrderedTree[MConcern]
                for g in mpackage.mgroups do
                        var pa = g.parent
                        if g.is_interesting then
                                ot.add(pa, g)
                                pa = g
                        end
-                       for mp in g.module_paths do
+                       for mp in g.mmodules do
                                ot.add(pa, mp)
                        end
                end
@@ -464,7 +461,7 @@ class Catalog
                var mmethods = 0
                var loc = 0
                for g in mpackage.mgroups do
-                       mmodules += g.module_paths.length
+                       mmodules += g.mmodules.length
                        for m in g.mmodules do
                                var am = modelbuilder.mmodule2node(m)
                                if am != null then
@@ -685,9 +682,11 @@ var modelbuilder = new ModelBuilder(model, tc)
 var catalog = new Catalog(modelbuilder)
 
 # Get files or groups
-for a in tc.option_context.rest do
-       modelbuilder.get_mgroup(a)
-       modelbuilder.identify_file(a)
+var args = tc.option_context.rest
+if opt_no_parse.value then
+       modelbuilder.scan_full(args)
+else
+       modelbuilder.parse_full(args)
 end
 
 # Scan packages and compute information
@@ -698,7 +697,6 @@ for p in model.mpackages do
 
        # Load the module to process importation information
        if opt_no_parse.value then continue
-       modelbuilder.parse_group(g)
 
        catalog.deps.add_node(p)
        for gg in p.mgroups do for m in gg.mmodules do
index 1b492c4..7ed5e40 100644 (file)
@@ -22,7 +22,7 @@ import ordered_tree
 import console
 
 class ProjTree
-       super OrderedTree[Object]
+       super OrderedTree[MConcern]
 
        var opt_paths = false
        var tc: ToolContext
@@ -47,23 +47,23 @@ class ProjTree
                                        return "{o.name}{d} ({o.filepath.yellow})"
                                end
                        end
-               else if o isa ModulePath then
+               else if o isa MModule then
                        if opt_paths then
-                               return o.filepath
+                               return o.filepath.as(not null)
                        else
                                var d = ""
                                var dd = ""
-                               if o.mmodule != null and o.mmodule.mdoc != null then
+                               if o.mdoc != null then
                                        if tc.opt_no_color.value then
-                                               d = ": {o.mmodule.mdoc.content.first}"
+                                               d = ": {o.mdoc.content.first}"
                                        else
-                                               d = ": {o.mmodule.mdoc.content.first.green}"
+                                               d = ": {o.mdoc.content.first.green}"
                                        end
                                end
-                               if o.mmodule != null and not o.mmodule.in_importation.direct_greaters.is_empty then
+                               if not o.in_importation.direct_greaters.is_empty then
                                        var ms = new Array[String]
-                                       for m in o.mmodule.in_importation.direct_greaters do
-                                               if m.mgroup.mpackage == o.mmodule.mgroup.mpackage then
+                                       for m in o.in_importation.direct_greaters do
+                                               if m.mgroup.mpackage == o.mgroup.mpackage then
                                                        ms.add m.name
                                                else
                                                        ms.add m.full_name
@@ -87,24 +87,6 @@ class ProjTree
        end
 end
 
-class AlphaEntityComparator
-       super Comparator
-       fun nameof(a: COMPARED): String
-       do
-               if a isa MGroup then
-                       return a.name
-               else if a isa ModulePath then
-                       return a.name
-               else
-                       abort
-               end
-       end
-       redef fun compare(a,b)
-       do
-               return nameof(a) <=> nameof(b)
-       end
-end
-
 var tc = new ToolContext
 
 var opt_keep = new OptionBool("Ignore errors and files that are not a Nit source file", "-k", "--keep")
@@ -161,7 +143,7 @@ end
 if sum == 0 then
        # If one of the file is a group, default is `opt_tree` instead of `opt_package`
        for a in files do
-               var g = mb.get_mgroup(a)
+               var g = mb.identify_group(a)
                if g != null then
                        opt_tree.value = true
                        opt_package.value = false
@@ -170,72 +152,49 @@ if sum == 0 then
        end
 end
 
-# Identify all relevant files
-for a in files do
-       var g = mb.get_mgroup(a)
-       var mp = mb.identify_file(a)
-       if g != null and not opt_package.value then
-               mb.scan_group(g)
-       end
-       if g == null and mp == null then
-               # not a group not a module, then look at files in the directory
-               var fs = a.files
-               for f in fs do
-                       g = mb.get_mgroup(a/f)
-                       if g != null and not opt_package.value then
-                               mb.scan_group(g)
-                       end
-                       mp = mb.identify_file(a/f)
-                       #print "{a/f}: {mp or else "?"}"
-               end
-       end
-end
+var mmodules = mb.scan_full(files)
 
 # Load modules to get more informations
-for mp in mb.identified_files do
+for mmodule in mmodules do
        if not opt_paths.value or opt_depends.value then
-               var mm = mb.load_module(mp.filepath)
-               if mm != null and opt_depends.value then
-                       mb.build_module_importation(mm)
+               var ast = mmodule.parse(mb)
+               if ast != null and opt_depends.value then
+                       mb.build_module_importation(ast)
                end
        end
 end
 #tc.check_errors
 
+if opt_depends.value then
+       # Extends the list of module with the loaded ones
+       mmodules = mb.parsed_modules.to_a
+end
 
 var ot = new ProjTree(tc)
-var sorter = new AlphaEntityComparator
 if opt_tree.value then
        ot.opt_paths = opt_paths.value
-       for p in model.mpackages do
-               for g in p.mgroups do
-                       var pa = g.parent
-                       if g.is_interesting then
-                               ot.add(pa, g)
-                               pa = g
-                       end
-                       for mp in g.module_paths do
-                               ot.add(pa, mp)
-                       end
+       var mgroups = new HashSet[MGroup]
+       for mp in mmodules do
+               var pa = mp.mgroup
+               while pa != null and not pa.is_interesting do pa = pa.parent
+               ot.add(pa, mp)
+               if pa != null then mgroups.add pa
+       end
+       for g in mgroups do
+               var pa = g.parent
+               if g.is_interesting then
+                       ot.add(pa, g)
                end
        end
-       ot.sort_with(sorter)
+       ot.sort_with(alpha_comparator)
        ot.write_to(stdout)
 end
 
 if opt_source.value then
-       var list = new Array[ModulePath]
-       for p in model.mpackages do
-               for g in p.mgroups do
-                       for mp in g.module_paths do
-                               list.add mp
-                       end
-               end
-       end
-       sorter.sort(list)
-       for mp in list do
+       alpha_comparator.sort(mmodules)
+       for mp in mmodules do
                if opt_paths.value then
-                       print mp.filepath
+                       print mp.filepath.as(not null)
                else
                        print "{mp.mgroup.full_name}/{ot.display(mp)}"
                end
@@ -243,12 +202,16 @@ if opt_source.value then
 end
 
 if opt_package.value then
-       var list = new Array[MGroup]
-       for p in model.mpackages do
-               list.add p.root.as(not null)
+       var mpackages = new Array[MPackage]
+       for m in mmodules do
+               var p = m.mgroup.mpackage
+               if mpackages.has(p) then continue
+               mpackages.add p
        end
-       sorter.sort(list)
-       for g in list do
+
+       alpha_comparator.sort(mpackages)
+       for p in mpackages do
+               var g = p.root.as(not null)
                var path = g.filepath.as(not null)
                if opt_paths.value then
                        print path
index e86f92f..522591d 100644 (file)
@@ -77,7 +77,7 @@ for a in args do
 end
 
 for a in module_files do
-       var g = modelbuilder.get_mgroup(a)
+       var g = modelbuilder.identify_group(a)
        if g == null then continue
        page.add modelbuilder.test_group(g)
 end
index d9e9ce0..568f76a 100644 (file)
@@ -3,3 +3,4 @@ base_simple3.nit project1
 -t -r base_simple3.nit project1
 -s base_simple3.nit project1
 -M base_simple3.nit base_simple_import.nit
+-td project1/module3.nit
index 2bac1c7..93ab33e 100644 (file)
@@ -1 +1 @@
-alt/base_import2_alt1.nit:16,8--25: Error: conflicting module files for `module_0`: `./project1/module_0.nit,./project1/subdir/module_0.nit`
+alt/base_import2_alt1.nit:16,8--25: Error: conflicting module files for `module_0`: `./project1/module_0.nit`, `./project1/subdir/module_0.nit`
diff --git a/tests/sav/nitls_args6.res b/tests/sav/nitls_args6.res
new file mode 100644 (file)
index 0000000..8ebf96a
--- /dev/null
@@ -0,0 +1,5 @@
+project1 (\e[33mproject1\e[m)
+|--\e[1mmodule1\e[m (\e[33mproject1/module1.nit\e[m)
+|--\e[1mmodule3\e[m (\e[33mproject1/module3.nit\e[m)\e[37m (module4)\e[m
+`--subdir (\e[33mproject1/subdir\e[m)
+   `--\e[1mmodule4\e[m (\e[33mproject1/subdir/module4.nit\e[m)\e[37m (module1)\e[m
index cb5affc..7684a23 100644 (file)
@@ -1,4 +1,92 @@
 *** METRICS ***
+--- AST Metrics ---
+## All nodes of the AST
+ population: 51
+ minimum value: 1
+ maximum value: 40
+ total value: 289
+ average value: 5.66
+ distribution:
+  <=1: sub-population=16 (31.37%); cumulated value=16 (5.53%)
+  <=2: sub-population=5 (9.80%); cumulated value=10 (3.46%)
+  <=4: sub-population=9 (17.64%); cumulated value=28 (9.68%)
+  <=8: sub-population=12 (23.52%); cumulated value=76 (26.29%)
+  <=16: sub-population=4 (7.84%); cumulated value=44 (15.22%)
+  <=32: sub-population=4 (7.84%); cumulated value=75 (25.95%)
+  <=64: sub-population=1 (1.96%); cumulated value=40 (13.84%)
+ list:
+  TId: 40 (13.84%)
+  APublicVisibility: 19 (6.57%)
+  AQid: 19 (6.57%)
+  AListExprs: 19 (6.57%)
+  ACallExpr: 18 (6.22%)
+  TClassid: 15 (5.19%)
+  TInteger: 10 (3.46%)
+  AIntegerExpr: 10 (3.46%)
+  AType: 9 (3.11%)
+  TKwend: 8 (2.76%)
+  ...
+  ACallAssignExpr: 1 (0.34%)
+  AAnnotations: 1 (0.34%)
+  TKwreturn: 1 (0.34%)
+  AReturnExpr: 1 (0.34%)
+  AInterfaceClasskind: 1 (0.34%)
+  TKwinterface: 1 (0.34%)
+  ANoImport: 1 (0.34%)
+  AMainMethPropdef: 1 (0.34%)
+  AMainClassdef: 1 (0.34%)
+  TKwimport: 1 (0.34%)
+## All identifiers of the AST
+ population: 20
+ minimum value: 1
+ maximum value: 11
+ total value: 55
+ average value: 2.75
+ distribution:
+  <=1: sub-population=3 (15.00%); cumulated value=3 (5.45%)
+  <=2: sub-population=12 (60.00%); cumulated value=24 (43.63%)
+  <=4: sub-population=3 (15.00%); cumulated value=10 (18.18%)
+  <=8: sub-population=1 (5.00%); cumulated value=7 (12.72%)
+  <=16: sub-population=1 (5.00%); cumulated value=11 (20.00%)
+ list:
+  output: 11 (20.00%)
+  Int: 7 (12.72%)
+  run: 4 (7.27%)
+  c: 3 (5.45%)
+  val: 3 (5.45%)
+  i: 2 (3.63%)
+  bar: 2 (3.63%)
+  foo: 2 (3.63%)
+  val2: 2 (3.63%)
+  val1: 2 (3.63%)
+  C: 2 (3.63%)
+  v: 2 (3.63%)
+  baz: 2 (3.63%)
+  a: 2 (3.63%)
+  A: 2 (3.63%)
+  B: 2 (3.63%)
+  b: 2 (3.63%)
+  intern: 1 (1.81%)
+  Object: 1 (1.81%)
+  Bool: 1 (1.81%)
+--- Detection of variance constraints on formal parameter types ---
+-- Generic classes --
+ list:
+  non generic: 7 (100.00%)
+  total classes: 7
+  total formal parameters: 0
+-- Including `private` properties --
+  covariants: 0 (na%)
+  contravariants: 0 (na%)
+  bivariants: 0 (na%)
+  invariants: 0 (na%)
+  total: 0
+-- Excluding `private` properties --
+  covariants: 0 (na%)
+  contravariants: 0 (na%)
+  bivariants: 0 (na%)
+  invariants: 0 (na%)
+  total: 0
 
 # MModules metrics
 
          min: base_simple3 (1)
          std: 0.0
          sum: 1
---- Metrics of refinement usage ---
-Number of modules: 1
-
-Number of classes: 7
-  Number of interface kind: 1 (14.28%)
-  Number of enum kind: 2 (28.57%)
-  Number of class kind: 4 (57.14%)
-
-Number of class definitions: 7
-Number of refined classes: 0 (0.00%)
-Average number of class refinments by classes: 0.00
-Average number of class refinments by refined classes: na
-
-Number of properties: 18
-  Number of MAttribute: 3 (16.66%)
-  Number of MMethod: 15 (83.33%)
-
-Number of property definitions: 20
-Number of redefined properties: 1 (5.55%)
-Average number of property redefinitions by property: 0.11
-Average number of property redefinitions by redefined property: 2.00
---- Explicit vs. Implicit Self ---
-Total number of self: 5
-Total number of implicit self: 4 (80.00%)
---- Construction of tables ---
-Number of runtime classes: 6 (excluding interfaces and abstract classes)
-Average number of composing class definition by runtime class: 2.00
-Total size of tables (classes and instances): 23 (not including stuff like info for subtyping or call-next-method)
-Average size of table by runtime class: 3.83
-Values never redefined: 17 (73.91%)
 --- Poset metrics ---
 ## Module importation hierarchy
 Number of nodes: 1
@@ -273,94 +331,38 @@ Distribution of direct smallers
  distribution:
   <=0: sub-population=6 (85.71%); cumulated value=0 (0.00%)
   <=8: sub-population=1 (14.28%); cumulated value=6 (100.00%)
---- AST Metrics ---
-## All nodes of the AST
- population: 51
- minimum value: 1
- maximum value: 40
- total value: 289
- average value: 5.66
- distribution:
-  <=1: sub-population=16 (31.37%); cumulated value=16 (5.53%)
-  <=2: sub-population=5 (9.80%); cumulated value=10 (3.46%)
-  <=4: sub-population=9 (17.64%); cumulated value=28 (9.68%)
-  <=8: sub-population=12 (23.52%); cumulated value=76 (26.29%)
-  <=16: sub-population=4 (7.84%); cumulated value=44 (15.22%)
-  <=32: sub-population=4 (7.84%); cumulated value=75 (25.95%)
-  <=64: sub-population=1 (1.96%); cumulated value=40 (13.84%)
- list:
-  TId: 40 (13.84%)
-  APublicVisibility: 19 (6.57%)
-  AQid: 19 (6.57%)
-  AListExprs: 19 (6.57%)
-  ACallExpr: 18 (6.22%)
-  TClassid: 15 (5.19%)
-  TInteger: 10 (3.46%)
-  AIntegerExpr: 10 (3.46%)
-  AType: 9 (3.11%)
-  TKwend: 8 (2.76%)
-  ...
-  ACallAssignExpr: 1 (0.34%)
-  AAnnotations: 1 (0.34%)
-  TKwreturn: 1 (0.34%)
-  AReturnExpr: 1 (0.34%)
-  AInterfaceClasskind: 1 (0.34%)
-  TKwinterface: 1 (0.34%)
-  ANoImport: 1 (0.34%)
-  AMainMethPropdef: 1 (0.34%)
-  AMainClassdef: 1 (0.34%)
-  TKwimport: 1 (0.34%)
-## All identifiers of the AST
- population: 20
- minimum value: 1
- maximum value: 11
- total value: 55
- average value: 2.75
- distribution:
-  <=1: sub-population=3 (15.00%); cumulated value=3 (5.45%)
-  <=2: sub-population=12 (60.00%); cumulated value=24 (43.63%)
-  <=4: sub-population=3 (15.00%); cumulated value=10 (18.18%)
-  <=8: sub-population=1 (5.00%); cumulated value=7 (12.72%)
-  <=16: sub-population=1 (5.00%); cumulated value=11 (20.00%)
- list:
-  output: 11 (20.00%)
-  Int: 7 (12.72%)
-  run: 4 (7.27%)
-  c: 3 (5.45%)
-  val: 3 (5.45%)
-  i: 2 (3.63%)
-  bar: 2 (3.63%)
-  foo: 2 (3.63%)
-  val2: 2 (3.63%)
-  val1: 2 (3.63%)
-  C: 2 (3.63%)
-  v: 2 (3.63%)
-  baz: 2 (3.63%)
-  a: 2 (3.63%)
-  A: 2 (3.63%)
-  B: 2 (3.63%)
-  b: 2 (3.63%)
-  intern: 1 (1.81%)
-  Object: 1 (1.81%)
-  Bool: 1 (1.81%)
---- Detection of variance constraints on formal parameter types ---
--- Generic classes --
- list:
-  non generic: 7 (100.00%)
-  total classes: 7
-  total formal parameters: 0
--- Including `private` properties --
-  covariants: 0 (na%)
-  contravariants: 0 (na%)
-  bivariants: 0 (na%)
-  invariants: 0 (na%)
-  total: 0
--- Excluding `private` properties --
-  covariants: 0 (na%)
-  contravariants: 0 (na%)
-  bivariants: 0 (na%)
-  invariants: 0 (na%)
-  total: 0
+--- Metrics of refinement usage ---
+Number of modules: 1
+
+Number of classes: 7
+  Number of interface kind: 1 (14.28%)
+  Number of enum kind: 2 (28.57%)
+  Number of class kind: 4 (57.14%)
+
+Number of class definitions: 7
+Number of refined classes: 0 (0.00%)
+Average number of class refinments by classes: 0.00
+Average number of class refinments by refined classes: na
+
+Number of properties: 18
+  Number of MAttribute: 3 (16.66%)
+  Number of MMethod: 15 (83.33%)
+
+Number of property definitions: 20
+Number of redefined properties: 1 (5.55%)
+Average number of property redefinitions by property: 0.11
+Average number of property redefinitions by redefined property: 2.00
+--- Explicit vs. Implicit Self ---
+Total number of self: 5
+Total number of implicit self: 4 (80.00%)
+--- Construction of tables ---
+Number of runtime classes: 6 (excluding interfaces and abstract classes)
+Average number of composing class definition by runtime class: 2.00
+Total size of tables (classes and instances): 23 (not including stuff like info for subtyping or call-next-method)
+Average size of table by runtime class: 3.83
+Values never redefined: 17 (73.91%)
+generating package_hierarchy.dot
+generating module_hierarchy.dot
 
 # MClasses metrics
 
@@ -488,8 +490,6 @@ Distribution of direct smallers
          min: Object (0)
          std: 0.926
          sum: 6
-generating package_hierarchy.dot
-generating module_hierarchy.dot
 
 # Inheritance metrics
 
index 6bf30a6..b8ecc63 100644 (file)
@@ -4,15 +4,15 @@
  \e[1m\e[32mP\e[m\e[m \e[1m\e[34mbase_simple3\e[m\e[m
    \e[1m\e[30mbase_simple3\e[m\e[m
    package base_simple3
-   \e[30mbase_simple3.nit:17,1--66,13\e[m
+   \e[30mbase_simple3.nit\e[m
 
  \e[1m\e[32mG\e[m\e[m \e[1m\e[34mbase_simple3\e[m\e[m
    \e[1m\e[30mbase_simple3\e[m\e[m
    group base_simple3
-   \e[30mbase_simple3.nit:17,1--66,13\e[m
+   \e[30mbase_simple3.nit\e[m
 
  \e[1m\e[32mM\e[m\e[m \e[1m\e[34mbase_simple3\e[m\e[m
    \e[1m\e[30mbase_simple3::base_simple3\e[m\e[m
    module base_simple3
-   \e[30mbase_simple3.nit:17,1--66,13\e[m
+   \e[30mbase_simple3.nit\e[m