Rename REAMDE to README.md
[nit.git] / src / neo.nit
index dc9cc86..5e28695 100644 (file)
@@ -43,7 +43,6 @@
 # `MGroup`
 #
 # * labels: `MGroup`, `model_name` and `MEntity`.
-# * `full_name`: fully qualified name.
 # * `(:MGroup)-[:PROJECT]->(:MProject)`: associated project.
 # * `(:MGroup)-[:PARENT]->(:MGroup)`: parent group. Does not exist for the root
 # group.
@@ -55,7 +54,6 @@
 # `MModule`
 #
 # * labels: `MModule`, `model_name` and `MEntity`.
-# * `full_name`: fully qualified name.
 # * `location`: origin of the definition. SEE: `Location.to_s`
 # * `(:MModule)-[:IMPORTS]->(:MModule)`: modules that are imported directly.
 # * `(:MModule)-[:INTRODUCES]->(:MClass)`: all by classes introduced by this
@@ -66,7 +64,6 @@
 # `MClass`
 #
 # * labels: `MClass`, `model_name` and `MEntity`.
-# * `full_name`: fully qualified name.
 # * `kind`: kind of the class (`interface`, `abstract class`, etc.)
 # * `visibility`: visibility of the class.
 # * `parameter_names`: JSON array listing the name of each formal generic
 # `MProperty`
 #
 # * labels: `MProperty`, `model_name` and `MEntity`. Must also have `MMethod`,
-# `MAttribute` or `MVirtualTypeProp`, depending on the class of the represented
-# entity.
-# * `full_name`: fully qualified name.
+# `MAttribute` `MVirtualTypeProp` or `MInnerClass`, depending on the class of
+# the represented entity.
 # * `visibility`: visibility of the property.
 # * `is_init`: Indicates if the property is a constructor. Exists only if the
 # node is a `MMethod`.
 # * `(:MProperty)-[:INTRO_CLASSDEF]->(:MClassDef)`: classdef that introduces
 # the property.
 #
+# Additional relationship for `MInnerClass`:
+#
+# * `(:MInnerClassDef)-[:NESTS]->(:MClass)`: actual inner class.
+#
 # `MPropDef`
 #
 # * labels: `MPropDef`, `model_name` and `MEntity`. Must also have `MMethodDef`,
-# `MAttributeDef` or `MVirtualTypeDef`, depending on the class of the
-# represented entity.
+# `MAttributeDef`, `MVirtualTypeDef` or `MInnerClassDef`, depending on the
+# class of the represented entity.
 # * `location`: origin of the definition. SEE: `Location.to_s`.
 # * `(:MPropDef)-[:DEFINES]->(:MProperty)`: associated property.
 #
 # is bound in this definition. Exists only if this definition bound the virtual
 # type to an effective type.
 #
+# Additional relationship for `MInnerClassDef`:
+#
+# * `(:MInnerClassDef)-[:NESTS]->(:MClassDef)`: actual inner class'
+# definition.
+#
 # `MType`
 #
 # * labels: `MType`, `model_name` and `MEntity`. Must also have `MClassType`,
-# `MNullableType`, `MVirtualType` or `MSignature`, depending on the class of
-# the represented entity.
+# `MNullableType`, `MVirtualType`, `MRawType` or `MSignature`, depending on the
+# class of the represented entity.
 #
 # Additional label and relationships for `MClassType`:
 #
 #
 # * `(:MNullableType)-[:TYPE]->(:MType)`: base type of the nullable type.
 #
+# Additional attribute and relationship for `MRawType`:
+#
+# * `text`: JSON array of the parts’ text.
+# * `(:MRawType)-[:LINK]->(:MTypePart)`: the parts that link to another entity.
+#
 # Additional attribute and relationships for `MSignature`:
 #
 # * `parameter_names`: JSON array representing the list of the parameter names.
 #
 # For example, if the source code contains:
 #
+# ~~~nitish
 #     fun foo(a: A, b: B, c: C)
+# ~~~
 #
 # The `MSignature` node will contain a property
 # `parameter_names = ["a", "b", "c"]` so the MSignature can be reconstructed
 #
 # MParameters are also ranked by their position in the corresponding signature.
 # Rank 0 for the first parameter, 1 for the next one and etc.
+#
+# `MTypePart`
+#
+# * labels: `MTypePart`, `model_name` and `MEntity`.
+# * `rank`: position in the `MRawType`.
+# * `(:MTypePart)-[:TARGET]->(:MEntity)`: the target of the link.
 module neo
 
-import model
+import doc::model_ext
 import neo4j
 import toolcontext
 
@@ -213,27 +231,27 @@ class NeoModel
 
        # Fill `model` using base pointed by `client`.
        fun load(model: Model): Model do
-               toolcontext.info("Locate all mentities...", 1)
-               var nodes = client.nodes_with_label(model_name)
-
-               toolcontext.info("Preload nodes...", 1)
-               pull_all_nodes(nodes)
-               toolcontext.info("Preload edges...", 1)
-               pull_all_edges(nodes)
+               var nodes: Array[NeoNode]
 
-               toolcontext.info("Build model...", 1)
+               toolcontext.info("Loading project node...", 1)
                nodes = client.nodes_with_labels([model_name, "MProject"])
                for node in nodes do to_mproject(model, node)
+               toolcontext.info("Loading groups...", 1)
                nodes = client.nodes_with_labels([model_name, "MGroup"])
                for node in nodes do to_mgroup(model, node)
+               toolcontext.info("Loading modules...", 1)
                nodes = client.nodes_with_labels([model_name, "MModule"])
                for node in nodes do to_mmodule(model, node)
+               toolcontext.info("Loading classes...", 1)
                nodes = client.nodes_with_labels([model_name, "MClass"])
                for node in nodes do to_mclass(model, node)
+               toolcontext.info("Loading class definitions...", 1)
                nodes = client.nodes_with_labels([model_name, "MClassDef"])
                for node in nodes do to_mclassdef(model, node)
+               toolcontext.info("Loading properties...", 1)
                nodes = client.nodes_with_labels([model_name, "MProperty"])
                for node in nodes do to_mproperty(model, node)
+               toolcontext.info("Loading property definitions...", 1)
                nodes = client.nodes_with_labels([model_name, "MPropDef"])
                for node in nodes do to_mpropdef(model, node)
                return model
@@ -270,52 +288,6 @@ class NeoModel
                do_batch(batch)
        end
 
-       # Load content for all `nodes` from base.
-       #
-       # Content corresponds to properties and labels that are loaded in batch mode.
-       private fun pull_all_nodes(nodes: Collection[NeoNode]) do
-               var batch = new NeoBatch(client)
-               var len = nodes.length
-               var sum = 0
-               var i = 1
-               for node in nodes do
-                       batch.load_node(node)
-                       if i == batch_max_size then
-                               do_batch(batch)
-                               sum += batch_max_size
-                               toolcontext.info(" {sum * 100 / len}% done", 1)
-                               batch = new NeoBatch(client)
-                               i = 1
-                       else
-                               i += 1
-                       end
-               end
-               do_batch(batch)
-       end
-
-       # Load all edges from base linked to `nodes`.
-       #
-       # Edges are loaded in batch mode.
-       private fun pull_all_edges(nodes: Collection[NeoNode]) do
-               var batch = new NeoBatch(client)
-               var len = nodes.length
-               var sum = 0
-               var i = 1
-               for node in nodes do
-                       batch.load_node_edges(node)
-                       if i == batch_max_size then
-                               do_batch(batch)
-                               sum += batch_max_size
-                               toolcontext.info(" {sum * 100 / len}% done", 1)
-                               batch = new NeoBatch(client)
-                               i = 1
-                       else
-                               i += 1
-                       end
-               end
-               do_batch(batch)
-       end
-
        # How many operation can be executed in one batch?
        private var batch_max_size = 1000
 
@@ -349,7 +321,9 @@ class NeoModel
        end
 
        # Mentities associated to nodes.
-       private var mentities = new HashMap[NeoNode, MEntity]
+       #
+       # The key is the node’s id.
+       private var mentities = new HashMap[Int, MEntity]
 
        # Nodes associated with MEntities.
        private var nodes = new HashMap[MEntity, NeoNode]
@@ -370,6 +344,20 @@ class NeoModel
                abort
        end
 
+       # Get the `MEntity` associated with `node`.
+       fun to_mentity(model: Model, node: NeoNode): MEntity do
+               if node.labels.has("MProject") then return to_mproject(model, node)
+               if node.labels.has("MGroup") then return to_mgroup(model, node)
+               if node.labels.has("MModule") then return to_mmodule(model, node)
+               if node.labels.has("MClass") then return to_mclass(model, node)
+               if node.labels.has("MClassDef") then return to_mclassdef(model, node)
+               if node.labels.has("MProperty") then return to_mproperty(model, node)
+               if node.labels.has("MPropDef") then return to_mpropdef(model, node)
+               if node.labels.has("MType") then return to_mtype(model, node)
+               if node.labels.has("MParameter") then return to_mparameter(model, node)
+               abort
+       end
+
        # Make a new `NeoNode` based on `mentity`.
        private fun make_node(mentity: MEntity): NeoNode do
                var node = new NeoNode
@@ -377,7 +365,10 @@ class NeoModel
                node.labels.add "MEntity"
                node.labels.add model_name
                node["name"] = mentity.name
-               if mentity.mdoc != null then node["mdoc"] = new JsonArray.from(mentity.mdoc.content)
+               if mentity.mdoc != null then
+                       node["mdoc"] = new JsonArray.from(mentity.mdoc.content)
+                       node["mdoc_location"] = mentity.mdoc.location.to_s
+               end
                return node
        end
 
@@ -396,10 +387,12 @@ class NeoModel
        #
        # REQUIRE `node.labels.has("MProject")`
        private fun to_mproject(model: Model, node: NeoNode): MProject do
-               if mentities.has_key(node) then return mentities[node].as(MProject)
+               var m = mentities.get_or_null(node.id.as(Int))
+               if m isa MProject then return m
+
                assert node.labels.has("MProject")
                var mproject = new MProject(node["name"].to_s, model)
-               mentities[node] = mproject
+               mentities[node.id.as(Int)] = mproject
                set_doc(node, mproject)
                mproject.root = to_mgroup(model, node.out_nodes("ROOT").first)
                return mproject
@@ -409,7 +402,6 @@ class NeoModel
        private fun mgroup_node(mgroup: MGroup): NeoNode do
                var node = make_node(mgroup)
                node.labels.add "MGroup"
-               node["full_name"] = mgroup.full_name
                var parent = mgroup.parent
                node.out_edges.add(new NeoEdge(node, "PROJECT", to_node(mgroup.mproject)))
                if parent != null then
@@ -428,7 +420,9 @@ class NeoModel
        #
        # REQUIRE `node.labels.has("MGroup")`
        private fun to_mgroup(model: Model, node: NeoNode): MGroup do
-               if mentities.has_key(node) then return mentities[node].as(MGroup)
+               var m = mentities.get_or_null(node.id.as(Int))
+               if m isa MGroup then return m
+
                assert node.labels.has("MGroup")
                var mproject = to_mproject(model, node.out_nodes("PROJECT").first)
                var parent: nullable MGroup = null
@@ -437,7 +431,7 @@ class NeoModel
                        parent = to_mgroup(model, out.first)
                end
                var mgroup = new MGroup(node["name"].to_s, mproject, parent)
-               mentities[node] = mgroup
+               mentities[node.id.as(Int)] = mgroup
                set_doc(node, mgroup)
                return mgroup
        end
@@ -446,7 +440,6 @@ class NeoModel
        private fun mmodule_node(mmodule: MModule): NeoNode do
                var node = make_node(mmodule)
                node.labels.add "MModule"
-               node["full_name"] = mmodule.full_name
                node["location"] = mmodule.location.to_s
                for parent in mmodule.in_importation.direct_greaters do
                        node.out_edges.add(new NeoEdge(node, "IMPORTS", to_node(parent)))
@@ -464,7 +457,9 @@ class NeoModel
        #
        # REQUIRE `node.labels.has("MModule")`
        private fun to_mmodule(model: Model, node: NeoNode): MModule do
-               if mentities.has_key(node) then return mentities[node].as(MModule)
+               var m = mentities.get_or_null(node.id.as(Int))
+               if m isa MModule then return m
+
                assert node.labels.has("MModule")
                var ins = node.in_nodes("DECLARES")
                var mgroup: nullable MGroup = null
@@ -474,7 +469,7 @@ class NeoModel
                var name = node["name"].to_s
                var location = to_location(node["location"].to_s)
                var mmodule = new MModule(model, mgroup, name, location)
-               mentities[node] = mmodule
+               mentities[node.id.as(Int)] = mmodule
                set_doc(node, mmodule)
                var imported_mmodules = new Array[MModule]
                for smod in node.out_nodes("IMPORTS") do
@@ -488,7 +483,6 @@ class NeoModel
        private fun mclass_node(mclass: MClass): NeoNode do
                var node = make_node(mclass)
                node.labels.add "MClass"
-               node["full_name"] = mclass.full_name
                node["kind"] = mclass.kind.to_s
                node["visibility"] = mclass.visibility.to_s
                if not mclass.mparameters.is_empty then
@@ -504,7 +498,9 @@ class NeoModel
        #
        # REQUIRE `node.labels.has("MClass")`
        private fun to_mclass(model: Model, node: NeoNode): MClass do
-               if mentities.has_key(node) then return mentities[node].as(MClass)
+               var m = mentities.get_or_null(node.id.as(Int))
+               if m isa MClass then return m
+
                assert node.labels.has("MClass")
                var mmodule = to_mmodule(model, node.in_nodes("INTRODUCES").first)
                var name = node["name"].to_s
@@ -517,7 +513,7 @@ class NeoModel
                        end
                end
                var mclass = new MClass(mmodule, name, parameter_names, kind, visibility)
-               mentities[node] = mclass
+               mentities[node.id.as(Int)] = mclass
                set_doc(node, mclass)
                return mclass
        end
@@ -545,13 +541,15 @@ class NeoModel
        #
        # REQUIRE `node.labels.has("MClassDef")`
        private fun to_mclassdef(model: Model, node: NeoNode): MClassDef do
-               if mentities.has_key(node) then return mentities[node].as(MClassDef)
+               var m = mentities.get_or_null(node.id.as(Int))
+               if m isa MClassDef then return m
+
                assert node.labels.has("MClassDef")
                var mmodule = to_mmodule(model, node.in_nodes("DEFINES").first)
                var mtype = to_mtype(model, node.out_nodes("BOUNDTYPE").first).as(MClassType)
                var location = to_location(node["location"].to_s)
                var mclassdef = new MClassDef(mmodule, mtype, location)
-               mentities[node] = mclassdef
+               mentities[node.id.as(Int)] = mclassdef
                set_doc(node, mclassdef)
                var supertypes = new Array[MClassType]
                for sup in node.out_nodes("INHERITS") do
@@ -566,7 +564,6 @@ class NeoModel
        private fun mproperty_node(mproperty: MProperty): NeoNode do
                var node = make_node(mproperty)
                node.labels.add "MProperty"
-               node["full_name"] = mproperty.full_name
                node["visibility"] = mproperty.visibility.to_s
                if mproperty isa MMethod then
                        node.labels.add "MMethod"
@@ -575,6 +572,9 @@ class NeoModel
                        node.labels.add "MAttribute"
                else if mproperty isa MVirtualTypeProp then
                        node.labels.add "MVirtualTypeProp"
+               else if mproperty isa MInnerClass then
+                       node.labels.add "MInnerClass"
+                       node.out_edges.add(new NeoEdge(node, "NESTS", to_node(mproperty.inner)))
                end
                node.out_edges.add(new NeoEdge(node, "INTRO_CLASSDEF", to_node(mproperty.intro_mclassdef)))
                return node
@@ -584,7 +584,9 @@ class NeoModel
        #
        # REQUIRE `node.labels.has("MProperty")`
        private fun to_mproperty(model: Model, node: NeoNode): MProperty do
-               if mentities.has_key(node) then return mentities[node].as(MProperty)
+               var m = mentities.get_or_null(node.id.as(Int))
+               if m isa MProperty then return m
+
                assert node.labels.has("MProperty")
                var intro_mclassdef = to_mclassdef(model, node.out_nodes("INTRO_CLASSDEF").first)
                var name = node["name"].to_s
@@ -597,12 +599,15 @@ class NeoModel
                        mprop = new MAttribute(intro_mclassdef, name, visibility)
                else if node.labels.has("MVirtualTypeProp") then
                        mprop = new MVirtualTypeProp(intro_mclassdef, name, visibility)
+               else if node.labels.has("MInnerClass") then
+                       var inner = to_mclass(model, node.out_nodes("NESTS").first)
+                       mprop = new MInnerClass(intro_mclassdef, name, visibility, inner)
                end
                if mprop == null then
                        print "not yet implemented to_mproperty for {node.labels.join(",")}"
                        abort
                end
-               mentities[node] = mprop
+               mentities[node.id.as(Int)] = mprop
                set_doc(node, mprop)
                return mprop
        end
@@ -634,6 +639,9 @@ class NeoModel
                        if bound != null then
                                node.out_edges.add(new NeoEdge(node, "BOUND", to_node(bound)))
                        end
+               else if mpropdef isa MInnerClassDef then
+                       node.labels.add "MInnerClassDef"
+                       node.out_edges.add(new NeoEdge(node, "NESTS", to_node(mpropdef.inner)))
                end
                return node
        end
@@ -642,7 +650,9 @@ class NeoModel
        #
        # REQUIRE `node.labels.has("MPropDef")`
        private fun to_mpropdef(model: Model, node: NeoNode): MPropDef do
-               if mentities.has_key(node) then return mentities[node].as(MPropDef)
+               var m = mentities.get_or_null(node.id.as(Int))
+               if m isa MPropDef then return m
+
                assert node.labels.has("MPropDef")
                var mclassdef = to_mclassdef(model, node.in_nodes("DECLARES").first)
                var mproperty = to_mproperty(model, node.out_nodes("DEFINES").first)
@@ -653,18 +663,23 @@ class NeoModel
                        mpropdef.is_abstract = node["is_abstract"].as(Bool)
                        mpropdef.is_intern = node["is_intern"].as(Bool)
                        mpropdef.is_extern = node["is_extern"].as(Bool)
-                       mentities[node] = mpropdef
+                       mentities[node.id.as(Int)] = mpropdef
                        mpropdef.msignature = to_mtype(model, node.out_nodes("SIGNATURE").first).as(MSignature)
                else if node.labels.has("MAttributeDef") then
                        mpropdef = new MAttributeDef(mclassdef, mproperty.as(MAttribute), location)
-                       mentities[node] = mpropdef
+                       mentities[node.id.as(Int)] = mpropdef
                        var static_mtype = node.out_nodes("TYPE")
                        if not static_mtype.is_empty then mpropdef.static_mtype = to_mtype(model, static_mtype.first)
                else if node.labels.has("MVirtualTypeDef") then
                        mpropdef = new MVirtualTypeDef(mclassdef, mproperty.as(MVirtualTypeProp), location)
-                       mentities[node] = mpropdef
+                       mentities[node.id.as(Int)] = mpropdef
                        var bound = node.out_nodes("BOUND")
                        if not bound.is_empty then mpropdef.bound = to_mtype(model, bound.first)
+               else if node.labels.has("MInnerClassDef") then
+                       var inner = to_mclassdef(model, node.out_nodes("NESTS").first)
+                       mpropdef = new MInnerClassDef(mclassdef,
+                                       mproperty.as(MInnerClass), location, inner)
+                       mentities[node.id.as(Int)] = mpropdef
                end
                if mpropdef == null then
                        print "not yet implemented to_mpropdef for {node.labels.join(",")}"
@@ -713,6 +728,31 @@ class NeoModel
                        if return_mtype != null then
                                node.out_edges.add(new NeoEdge(node, "RETURNTYPE", to_node(return_mtype)))
                        end
+               else if mtype isa MRawType then
+                       node.labels.add "MRawType"
+                       var text = new JsonArray
+                       var rank = 0
+                       for part in mtype.parts do
+                               text.add part.text
+                               if part.target != null then
+                                       var pnode = mtypepart_node(part)
+                                       pnode["rank"] = rank
+                                       node.out_edges.add(new NeoEdge(node, "LINK", pnode))
+                               end
+                               rank += 1
+                       end
+                       if not text.is_empty then node["text"] = text
+               end
+               return node
+       end
+
+       # Build a `NeoNode` representing `mtypepart`.
+       private fun mtypepart_node(mtypepart: MTypePart): NeoNode do
+               var node = make_node(mtypepart)
+               node.labels.add "MTypePart"
+               if mtypepart.target != null then
+                       var target_node = to_node(mtypepart.target.as(not null))
+                       node.out_edges.add(new NeoEdge(node, "TARGET", target_node))
                end
                return node
        end
@@ -721,7 +761,9 @@ class NeoModel
        #
        # REQUIRE `node.labels.has("MType")`
        private fun to_mtype(model: Model, node: NeoNode): MType do
-               if mentities.has_key(node) then return mentities[node].as(MType)
+               var m = mentities.get_or_null(node.id.as(Int))
+               if m isa MType then return m
+
                assert node.labels.has("MType")
                if node.labels.has("MClassType") then
                        var mclass = to_mclass(model, node.out_nodes("CLASS").first)
@@ -730,24 +772,24 @@ class NeoModel
                                args.add to_mtype(model, narg)
                        end
                        var mtype = mclass.get_mtype(args)
-                       mentities[node] = mtype
+                       mentities[node.id.as(Int)] = mtype
                        return mtype
                else if node.labels.has("MParameterType") then
                        var mclass = to_mclass(model, node.out_nodes("CLASS").first)
                        var rank = node["rank"].to_s.to_i
                        var mtype = mclass.mparameters[rank]
-                       mentities[node] = mtype
+                       mentities[node.id.as(Int)] = mtype
                        return  mtype
                else if node.labels.has("MNullableType") then
                        var intype = to_mtype(model, node.out_nodes("TYPE").first)
                        var mtype = intype.as_nullable
-                       mentities[node] = mtype
+                       mentities[node.id.as(Int)] = mtype
                        return mtype
                else if node.labels.has("MVirtualType") then
                        var mproperty = to_mproperty(model, node.out_nodes("PROPERTY").first)
                        assert mproperty isa MVirtualTypeProp
                        var mtype = mproperty.mvirtualtype
-                       mentities[node] = mtype
+                       mentities[node.id.as(Int)] = mtype
                        return mtype
                else if node.labels.has("MSignature") then
                        # Get all param nodes
@@ -771,7 +813,26 @@ class NeoModel
                                return_mtype = to_mtype(model, ret_nodes.first)
                        end
                        var mtype = new MSignature(mparameters, return_mtype)
-                       mentities[node] = mtype
+                       mentities[node.id.as(Int)] = mtype
+                       return mtype
+               else if node.labels.has("MRawType") then
+                       var mtype = new MRawType(model)
+                       var parts = node["text"]
+                       if parts isa JsonArray then
+                               for p in parts do
+                                       mtype.parts.add(new MTypePart(model, p.to_s, null))
+                               end
+                               for pnode in node.out_nodes("LINK") do
+                                       assert pnode.labels.has("MTypePart")
+                                       if not pnode.out_nodes("TARGET").is_empty then
+                                               var rank = pnode["rank"]
+                                               var target = to_mentity(model, pnode.out_nodes("TARGET").first)
+                                               assert rank isa Int
+                                               mtype.parts[rank] = mtype.parts[rank].link_to(target)
+                                       end
+                               end
+                       end
+                       mentities[node.id.as(Int)] = mtype
                        return mtype
                end
                print "not yet implemented to_mtype for {node.labels.join(",")}"
@@ -784,6 +845,7 @@ class NeoModel
                node.labels.add "MParameter"
                node["name"] = mparameter.name
                node["is_vararg"] = mparameter.is_vararg
+               node["is_default"] = mparameter.is_default
                node.out_edges.add(new NeoEdge(node, "TYPE", to_node(mparameter.mtype)))
                return node
        end
@@ -792,30 +854,22 @@ class NeoModel
        #
        # REQUIRE `node.labels.has("MParameter")`
        private fun to_mparameter(model: Model, node: NeoNode): MParameter do
-               if mentities.has_key(node) then return mentities[node].as(MParameter)
+               var m = mentities.get_or_null(node.id.as(Int))
+               if m isa MParameter then return m
+
                assert node.labels.has("MParameter")
                var name = node["name"].to_s
                var mtype = to_mtype(model, node.out_nodes("TYPE").first)
                var is_vararg = node["is_vararg"].as(Bool)
-               var mparameter = new MParameter(name, mtype, is_vararg)
-               mentities[node] = mparameter
+               var is_default = node["is_default"].as(Bool)
+               var mparameter = new MParameter(name, mtype, is_vararg, is_default)
+               mentities[node.id.as(Int)] = mparameter
                return mparameter
        end
 
        # Get a `Location` from its string representation.
        private fun to_location(loc: String): Location do
-               #TODO filepath
-               var parts = loc.split_with(":")
-               var file = new SourceFile.from_string(parts[0], "")
-               var pos = parts[1].split_with("--")
-               var pos1 = pos[0].split_with(",")
-               var pos2 = pos[1].split_with(",")
-               var line_s = pos1[0].to_i
-               var line_e = pos2[0].to_i
-               var column_s = pos1[1].to_i
-               var column_e = 0
-               if pos2.length == 2 then pos2[1].to_i
-               return new Location(file, line_s, line_e, column_s, column_e)
+               return new Location.from_string(loc)
        end
 
        # Get a `MVisibility` from its string representation.
@@ -828,6 +882,8 @@ class NeoModel
                        return protected_visibility
                else if vis == private_visibility.to_s then
                        return private_visibility
+               else if vis == package_visibility.to_s then
+                       return package_visibility
                else
                        return none_visibility
                end
@@ -845,8 +901,9 @@ class NeoModel
                        return enum_kind
                else if kind == extern_kind.to_s then
                        return extern_kind
+               else
+                       return raw_kind(kind)
                end
-               abort
        end
 
        # Extract the `MDoc` from `node` and link it to `mentity`.
@@ -856,7 +913,8 @@ class NeoModel
                        for e in node["mdoc"].as(JsonArray) do
                                lines.add e.to_s#.replace("\n", "\\n")
                        end
-                       var mdoc = new MDoc
+                       var location = to_location(node["mdoc_location"].to_s)
+                       var mdoc = new MDoc(location)
                        mdoc.content.add_all(lines)
                        mdoc.original_mentity = mentity
                        mentity.mdoc = mdoc