Merge: Fix qualified imports
authorJean Privat <jean@pryen.org>
Fri, 28 Aug 2015 20:36:18 +0000 (16:36 -0400)
committerJean Privat <jean@pryen.org>
Fri, 28 Aug 2015 20:36:18 +0000 (16:36 -0400)
Solve #1265 and the includes with examples/nitcorn/src/xymus_net.nit

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

1  2 
src/loader.nit
tests/sav/base_import_alt3.res
tests/sav/base_import_alt5.res
tests/sav/base_import_alt6.res

diff --combined src/loader.nit
@@@ -287,6 -287,20 +287,20 @@@ redef class ModelBuilde
                return res.first
        end
  
+       # Search groups named `name` from paths `lookpaths`.
+       private fun search_group_in_paths(name: String, lookpaths: Collection[String]): ArraySet[MGroup]
+       do
+               var res = new ArraySet[MGroup]
+               for dirname in lookpaths do
+                       # try a single group directory
+                       var mg = get_mgroup(dirname/name)
+                       if mg != null then
+                               res.add mg
+                       end
+               end
+               return res
+       end
        # Cache for `identify_file` by realpath
        private var identified_files_by_path = new HashMap[String, nullable ModulePath]
  
                        mgroup.filepath = path
                        mproject.root = mgroup
                        toolcontext.info("found singleton project `{pn}` at {path}", 2)
 +
 +                      # Attach homonymous `ini` file to the project
 +                      var inipath = path.dirname / "{pn}.ini"
 +                      if inipath.file_exists then
 +                              var ini = new ConfigTree(inipath)
 +                              mproject.ini = ini
 +                      end
                end
  
                var res = new ModulePath(pn, path, mgroup)
                var mgroup
                if parent == null then
                        # no parent, thus new project
 -                      if ini != null and ini.has_key("name") then pn = ini["name"]
 +                      var namekey = "project.name"
 +                      if ini != null and ini.has_key(namekey) then pn = ini[namekey]
                        var mproject = new MProject(pn, model)
                        mgroup = new MGroup(pn, mproject, null) # same name for the root group
                        mproject.root = mgroup
        # This method handles qualified names as used in `AModuleName`.
        fun seach_module_by_amodule_name(n_name: AModuleName, mgroup: nullable MGroup): nullable ModulePath
        do
-               if n_name.n_quad != null then mgroup = null # Start from top level
-               for grp in n_name.n_path do
-                       var path = search_mmodule_by_name(grp, mgroup, grp.text)
-                       if path == null then return null # Forward error
-                       mgroup = path.mgroup
-               end
                var mod_name = n_name.n_id.text
-               return search_mmodule_by_name(n_name, mgroup, mod_name)
+               # If a quad is given, we ignore the starting group (go from path)
+               if n_name.n_quad != null then mgroup = null
+               # If name not qualified, just search the name
+               if n_name.n_path.is_empty then
+                       # Fast search if no n_path
+                       return search_mmodule_by_name(n_name, mgroup, mod_name)
+               end
+               # If qualified and in a group
+               if mgroup != null then
+                       # First search in the project
+                       var r = mgroup.mproject.root
+                       assert r != null
+                       scan_group(r)
+                       # Get all modules with the final name
+                       var res = r.mmodule_paths_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(",")}`")
+                               end
+                               return res.first
+                       end
+               end
+               # If no module yet, then assume that the first element of the path
+               # Is to be searched in the path.
+               var root_name = n_name.n_path.first.text
+               var roots = search_group_in_paths(root_name, paths)
+               if roots.is_empty then
+                       error(n_name, "Error: cannot find `{root_name}`. Tried: {paths.join(", ")}.")
+                       return null
+               end
+               var res = new ArraySet[ModulePath]
+               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)
+                       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(",")}`")
+                       end
+                       return res.first
+               end
+               # If still nothing, just call a basic search that will fail and will produce an error message
+               error(n_name, "Error: cannot find module `{mod_name}` from `{root_name}`. Tried: {paths.join(", ")}.")
+               return null
+       end
+       # Is elements of `n_name` correspond to the group nesting of `m`?
+       #
+       # Basically it check that `bar::foo` matches `bar/foo.nit` and `bar/baz/foo.nit`
+       # 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
+       do
+               var g: nullable MGroup = m.mgroup
+               for grp in n_name.n_path.reverse_iterator do
+                       while g != null and grp.text != g.name do
+                               g = g.parent
+                       end
+               end
+               return g != null
        end
  
        # Analyze the module importation and fill the module_importation_hierarchy
                        mmodule.set_visibility_for(sup, mvisibility)
                end
                if stdimport then
 -                      var mod_name = "standard"
 +                      var mod_name = "core"
                        var sup = self.get_mmodule_by_name(nmodule, null, mod_name)
                        if sup == null then
                                nmodule.mmodule = null # invalidate the module
  
                self.toolcontext.info("{mmodule} imports {mmodule.in_importation.direct_greaters.join(", ")}", 3)
  
 -              # Force standard to be public if imported
 +              # Force `core` to be public if imported
                for sup in mmodule.in_importation.greaters do
 -                      if sup.name == "standard" then
 +                      if sup.name == "core" then
                                mmodule.set_visibility_for(sup, public_visibility)
                        end
                end
@@@ -1,1 -1,1 +1,1 @@@
- alt/base_import_alt3.nit:1,8--21: Error: cannot find module `fail` from `project1`. Tried: ., ../lib/core, ../lib/core/collection, alt, ../lib, ../contrib.
 -alt/base_import_alt3.nit:18,8--21: Error: cannot find module `fail` from `project1`. Tried: ., ../lib/standard, ../lib/standard/collection, alt, ../lib, ../contrib.
++alt/base_import_alt3.nit:18,8--21: Error: cannot find module `fail` from `project1`. Tried: ., ../lib/core, ../lib/core/collection, alt, ../lib, ../contrib.
@@@ -1,1 -1,1 +1,1 @@@
- alt/base_import_alt5.nit:1,8--25: Error: cannot find module `project2` from `project1`. Tried: ., ../lib/core, ../lib/core/collection, alt, ../lib, ../contrib.
 -alt/base_import_alt5.nit:20,8--25: Error: cannot find module `project2` from `project1`. Tried: ., ../lib/standard, ../lib/standard/collection, alt, ../lib, ../contrib.
++alt/base_import_alt5.nit:20,8--25: Error: cannot find module `project2` from `project1`. Tried: ., ../lib/core, ../lib/core/collection, alt, ../lib, ../contrib.
index 0000000,0cc549d..9cdbe5b
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,1 +1,1 @@@
 -alt/base_import_alt6.nit:21,8--20: Error: cannot find `fail`. Tried: ., ../lib/standard, ../lib/standard/collection, alt, ../lib, ../contrib.
++alt/base_import_alt6.nit:21,8--20: Error: cannot find `fail`. Tried: ., ../lib/core, ../lib/core/collection, alt, ../lib, ../contrib.