Merge: Added contributing guidelines and link from readme
[nit.git] / src / modelbuilder_base.nit
index 6d78f9e..5cc235f 100644 (file)
@@ -346,20 +346,39 @@ class ModelBuilder
        fun class_not_found(qid: AQclassid, mmodule: MModule)
        do
                var name = qid.n_id.text
+               var qname = qid.full_name
+
+               if bad_class_names[mmodule].has(qname) then
+                       error(qid, "Error: class `{qname}` not found in module `{mmodule}`.")
+                       return
+               end
+               bad_class_names[mmodule].add(qname)
 
                var all_classes = model.get_mclasses_by_name(name)
+               var hints = new Array[String]
+
+               # Look for conflicting classes.
+               if all_classes != null then for c in all_classes do
+                       if not mmodule.is_visible(c.intro_mmodule, c.visibility) then continue
+                       if not qid.accept(c) then continue
+                       hints.add "`{c.full_name}`"
+               end
+               if hints.length > 1 then
+                       error(qid, "Error: ambiguous class name `{qname}` in module `{mmodule}`. Conflicts are between {hints.join(",", " and ")}.")
+                       return
+               end
+               hints.clear
 
                # Look for imported but invisible classes.
                if all_classes != null then for c in all_classes do
                        if not mmodule.in_importation <= c.intro_mmodule then continue
                        if mmodule.is_visible(c.intro_mmodule, c.visibility) then continue
                        if not qid.accept(c) then continue
-                       error(ntype, "Error: class `{c.full_name}` not visible in module `{mmodule}`.")
+                       error(qid, "Error: class `{c.full_name}` not visible in module `{mmodule}`.")
                        return
                end
 
                # Look for not imported but known classes from importable modules
-               var hints = new Array[String]
                if all_classes != null then for c in all_classes do
                        if mmodule.in_importation <= c.intro_mmodule then continue
                        if c.intro_mmodule.in_importation <= mmodule then continue
@@ -368,32 +387,33 @@ class ModelBuilder
                        hints.add "`{c.intro_mmodule.full_name}`"
                end
                if hints.not_empty then
-                       error(ntype, "Error: class `{name}` not found in module `{mmodule}`. Maybe import {hints.join(",", " or ")}?")
+                       error(qid, "Error: class `{qname}` not found in module `{mmodule}`. Maybe import {hints.join(",", " or ")}?")
                        return
                end
 
                # Look for classes with an approximative name.
-               var bestd = name.length / 2 # limit up to 50% name change
+               var bests = new BestDistance[MClass](qname.length - name.length / 2) # limit up to 50% name change
                for c in model.mclasses do
                        if not mmodule.in_importation <= c.intro_mmodule then continue
                        if not mmodule.is_visible(c.intro_mmodule, c.visibility) then continue
-                       var d = name.levenshtein_distance(c.name)
-                       if d <= bestd then
-                               if d < bestd then
-                                       hints.clear
-                                       bestd = d
-                               end
-                               hints.add "`{c.full_name}`"
-                       end
+                       var d = qname.levenshtein_distance(c.name)
+                       bests.update(d, c)
+                       d = qname.levenshtein_distance(c.full_name)
+                       bests.update(d, c)
                end
-               if hints.not_empty then
-                       error(ntype, "Error: class `{name}` not found in module `{mmodule}`. Did you mean {hints.join(",", " or ")}?")
+               if bests.best_items.not_empty then
+                       for c in bests.best_items do hints.add "`{c.full_name}`"
+                       error(qid, "Error: class `{qname}` not found in module `{mmodule}`. Did you mean {hints.join(",", " or ")}?")
                        return
                end
 
-               error(ntype, "Error: class `{name}` not found in module `{mmodule}`.")
+               error(qid, "Error: class `{qname}` not found in module `{mmodule}`.")
        end
 
+       # List of already reported bad class names.
+       # Used to not perform and repeat hints again and again.
+       private var bad_class_names = new MultiHashMap[MModule, String]
+
        # Return the static type associated to the node `ntype`.
        # `mmodule` and `mclassdef` is the context where the call is made (used to understand formal types)
        # In case of problem, an error is displayed on `ntype` and null is returned.
@@ -550,4 +570,19 @@ redef class AQclassid
                end
                return true
        end
+
+       # The pretty name represented by self.
+       fun full_name: String
+       do
+               var res = n_id.text
+               var nqualified = n_qualified
+               if nqualified == null then return res
+               var ncid = nqualified.n_classid
+               if ncid != null then res = ncid.text + "::" + res
+               var nids = nqualified.n_id
+               if nids.not_empty then for n in nids.reverse_iterator do
+                       res = n.text + "::" + res
+               end
+               return res
+       end
 end