Merge: Enforce namespace rules
authorJean Privat <jean@pryen.org>
Sat, 10 Jan 2015 02:31:30 +0000 (21:31 -0500)
committerJean Privat <jean@pryen.org>
Sat, 10 Jan 2015 02:31:30 +0000 (21:31 -0500)
Enforce rules indicated in #1047.

So, having homonymous public modules and classes in a same project raises an error.
In the current code there was no conflicting modules. It is not surprising because for a long time the compilers refused homonymous modules even in different projects.
There was only 2 conflicting classes (on a total of 3640). This is more a good surprise since I expected a lot of conflicts. It is some kind of prof that the proposed policy is not that crazy. The two pairs of conflicting classes were `UnicodeChar` in `lib/string_experimentations/utf8.nit` and `lib/string_experimentations/utf8_noindex.nit`, and `Frame` in `naive_interpreter` and `abstract_compiler`. Some commits in the PR rename one of each pair to solve the conflicts.

For public properties, the proposed rule is to have a unique full-name "project::class::name".
There was 11 conflicts, again, it is far less than I expected.
Two of these conflict are resolved in some commits.
The other 9 are currently left as is (and the displayed error is in fact a warning).
All these remaining conflicts are a variation of the same pattern: homonymous options in refinements of ToolContext for different tools. Eg `opt_rta` for `nitmetrics` and for `nitc`.
I am not sure what is the correct way to solve these since the conflict is not only in the name but also in the behavior (a refinement of those two modules will have a broken option parsing). Maybe, behind the name conflict, there is also a bad model that misuses class refinement.

Pull-Request: #1069
Reviewed-by: Alexis Laferrière <alexis.laf@xymus.net>
Reviewed-by: Alexandre Terrasa <alexandre@moz-code.org>

contrib/pep8analysis/src/pep8analysis_web.nit
lib/string_experimentations/utf8.nit
src/compiler/abstract_compiler.nit
src/compiler/global_compiler.nit
src/compiler/separate_compiler.nit
src/loader.nit
src/model/mmodule.nit
src/modelize/modelize_class.nit
src/modelize/modelize_property.nit
src/test_docdown.nit

index 18ee374..dacc9ff 100644 (file)
@@ -46,7 +46,7 @@ in "C++" `{
 
 redef class AnalysisManager
 
-       fun run(src: String)
+       fun run_web(src: String)
        do
                sys.suggest_garbage_collection
 
@@ -109,7 +109,7 @@ redef class AnalysisManager
 end
 
 redef class NativeString
-       fun run_analysis do manager.run to_s
+       fun run_analysis do manager.run_web to_s
 end
 
 fun dummy_set_callbacks import NativeString.run_analysis in "C++" `{
index 83641d1..f5b8786 100644 (file)
@@ -34,7 +34,7 @@ typedef struct {
 # UTF-8 char as defined in RFC-3629, e.g. 1-4 Bytes
 #
 # A UTF-8 char has its bytes stored in a NativeString (char*)
-extern class UnicodeChar `{ UTF8Char* `}
+extern class UTF8Char `{ UTF8Char* `}
 
        new(pos: Int, ns: NativeString) `{
                UTF8Char* u = malloc(sizeof(UTF8Char));
@@ -83,8 +83,8 @@ extern class UnicodeChar `{ UTF8Char* `}
        # Returns the Unicode code point representing the character
        #
        # Note : A unicode character might not be a visible glyph, but it will be used to determine canonical equivalence
-       fun code_point: Int import UnicodeChar.len `{
-               switch(UnicodeChar_len(recv)){
+       fun code_point: Int import UTF8Char.len `{
+               switch(UTF8Char_len(recv)){
                        case 1:
                                return (long)(0x7F & (unsigned char)recv->ns[recv->pos]);
                        case 2:
@@ -105,8 +105,8 @@ extern class UnicodeChar `{ UTF8Char* `}
        #
        # NOTE : Works only on ASCII chars
        # TODO : Support unicode for to_upper
-       fun to_upper: UnicodeChar import UnicodeChar.code_point `{
-               int cp = UnicodeChar_code_point(recv);
+       fun to_upper: UTF8Char import UTF8Char.code_point `{
+               int cp = UTF8Char_code_point(recv);
                if(cp < 97 || cp > 122){ return recv; }
                char* ns = malloc(2);
                ns[1] = '\0';
@@ -122,8 +122,8 @@ extern class UnicodeChar `{ UTF8Char* `}
        #
        # NOTE : Works only on ASCII chars
        # TODO : Support unicode for to_upper
-       fun to_lower: UnicodeChar import UnicodeChar.code_point `{
-               int cp = UnicodeChar_code_point(recv);
+       fun to_lower: UTF8Char import UTF8Char.code_point `{
+               int cp = UTF8Char_code_point(recv);
                if(cp < 65 || cp > 90){ return recv; }
                char* ns = malloc(2);
                ns[1] = '\0';
@@ -140,15 +140,15 @@ extern class UnicodeChar `{ UTF8Char* `}
                if o isa Char then
                        if len != 1 then return false
                        if code_point == o.ascii then return true
-               else if o isa UnicodeChar then
+               else if o isa UTF8Char then
                        if len != o.len then return false
                        if code_point == o.code_point then return true
                end
                return false
        end
 
-       redef fun output import UnicodeChar.code_point `{
-               switch(UnicodeChar_len(recv)){
+       redef fun output import UTF8Char.code_point `{
+               switch(UTF8Char_len(recv)){
                        case 1:
                                printf("%c", recv->ns[recv->pos]);
                                break;
@@ -165,7 +165,7 @@ extern class UnicodeChar `{ UTF8Char* `}
        `}
 
        redef fun to_s import NativeString.to_s_with_length `{
-               int len = utf8___UnicodeChar_len___impl(recv);
+               int len = utf8___UTF8Char_len___impl(recv);
                char* r = malloc(len + 1);
                r[len] = '\0';
                char* src = (recv->ns + recv->pos);
@@ -182,10 +182,10 @@ private extern class StringIndex `{ UTF8Char* `}
        new(size: Int) `{ return malloc(size*sizeof(UTF8Char)); `}
 
        # Sets the character at `index` as `item`
-       fun []=(index: Int, item: UnicodeChar) `{ recv[index] = *item; `}
+       fun []=(index: Int, item: UTF8Char) `{ recv[index] = *item; `}
 
        # Gets the character at position `id`
-       fun [](id: Int): UnicodeChar `{ return &recv[id]; `}
+       fun [](id: Int): UTF8Char `{ return &recv[id]; `}
 
        # Copies a part of self starting at index `my_from` of length `length` into `other`, starting at `its_from`
        fun copy_to(other: StringIndex, my_from: Int, its_from: Int, length: Int)`{
@@ -259,7 +259,7 @@ redef class FlatString
                        var uchar = index[i]
                        var uchar_len = uchar.len
                        ipos -= uchar_len
-                       new_index[pos_index] = new UnicodeChar(ipos, native)
+                       new_index[pos_index] = new UTF8Char(ipos, native)
                        pos_index -= 1
                        items.copy_to(native, uchar_len, pos, ipos)
                        pos += uchar_len
@@ -373,7 +373,7 @@ redef class NativeString
        # Creates the index for said NativeString
        # `length` is the size of the CString (in bytes, up to the first \0)
        # real_len is just a way to store the length (UTF-8 characters)
-       private fun make_index(length: Int, real_len: Container[Int]): StringIndex import Container[Int].item=, UnicodeChar.len `{
+       private fun make_index(length: Int, real_len: Container[Int]): StringIndex import Container[Int].item=, UTF8Char.len `{
                int pos = 0;
                int index_pos = 0;
                UTF8Char* index = malloc(length*sizeof(UTF8Char));
@@ -381,7 +381,7 @@ redef class NativeString
                        UTF8Char* curr = &index[index_pos];
                        curr->pos = pos;
                        curr->ns = recv;
-                       pos += UnicodeChar_len(curr);
+                       pos += UTF8Char_len(curr);
                        index_pos ++;
                }
                Container_of_Int_item__assign(real_len, index_pos);
index d681036..2312544 100644 (file)
@@ -1040,8 +1040,8 @@ abstract class AbstractCompilerVisitor
        # The current visited AST node
        var current_node: nullable ANode = null is writable
 
-       # The current `Frame`
-       var frame: nullable Frame = null is writable
+       # The current `StaticFrame`
+       var frame: nullable StaticFrame = null is writable
 
        # Alias for self.compiler.mainmodule.object_type
        fun object_type: MClassType do return self.compiler.mainmodule.object_type
@@ -1670,8 +1670,8 @@ class RuntimeVariable
        end
 end
 
-# A frame correspond to a visited property in a `GlobalCompilerVisitor`
-class Frame
+# The static context of a visited property in a `AbstractCompilerVisitor`
+class StaticFrame
 
        type VISITOR: AbstractCompilerVisitor
 
@@ -2270,7 +2270,7 @@ redef class AAttrPropdef
                var oldnode = v.current_node
                v.current_node = self
                var old_frame = v.frame
-               var frame = new Frame(v, self.mpropdef.as(not null), recv.mcasttype.as_notnullable.as(MClassType), [recv])
+               var frame = new StaticFrame(v, self.mpropdef.as(not null), recv.mcasttype.as_notnullable.as(MClassType), [recv])
                v.frame = frame
 
                var value
@@ -2309,7 +2309,7 @@ redef class AAttrPropdef
                var oldnode = v.current_node
                v.current_node = self
                var old_frame = v.frame
-               var frame = new Frame(v, self.mpropdef.as(not null), recv.mtype.as(MClassType), [recv])
+               var frame = new StaticFrame(v, self.mpropdef.as(not null), recv.mtype.as(MClassType), [recv])
                v.frame = frame
                # Force read to check the initialization
                v.read_attribute(self.mpropdef.mproperty, recv)
index e633e36..7a0f162 100644 (file)
@@ -972,7 +972,7 @@ private class CustomizedRuntimeFunction
                        selfvar.is_exact = true
                end
                var arguments = new Array[RuntimeVariable]
-               var frame = new Frame(v, mmethoddef, recv, arguments)
+               var frame = new StaticFrame(v, mmethoddef, recv, arguments)
                v.frame = frame
 
                var sig = new FlatBuffer
@@ -1031,7 +1031,7 @@ private class CustomizedRuntimeFunction
                        ret = v.resolve_for(ret, arguments.first)
                end
                if self.mmethoddef.can_inline(v) then
-                       var frame = new Frame(v, self.mmethoddef, self.recv, arguments)
+                       var frame = new StaticFrame(v, self.mmethoddef, self.recv, arguments)
                        frame.returnlabel = v.get_name("RET_LABEL")
                        if ret != null then
                                frame.returnvar = v.new_var(ret)
index eb2437c..1f1cf67 100644 (file)
@@ -1248,7 +1248,7 @@ class SeparateCompilerVisitor
                        (compiler.modelbuilder.toolcontext.opt_inline_some_methods.value and mmethoddef.can_inline(self)) then
                        compiler.modelbuilder.nb_invok_by_inline += 1
                        if compiler.modelbuilder.toolcontext.opt_invocation_metrics.value then add("count_invoke_by_inline++;")
-                       var frame = new Frame(self, mmethoddef, recvtype, arguments)
+                       var frame = new StaticFrame(self, mmethoddef, recvtype, arguments)
                        frame.returnlabel = self.get_name("RET_LABEL")
                        frame.returnvar = res
                        var old_frame = self.frame
@@ -1298,11 +1298,11 @@ class SeparateCompilerVisitor
                # of the method (ie recv) if the static type is unresolved
                # This is more complex than usual because the unresolved type must not be resolved
                # with the current receiver (ie self).
-               # Therefore to isolate the resolution from self, a local Frame is created.
+               # Therefore to isolate the resolution from self, a local StaticFrame is created.
                # One can see this implementation as an inlined method of the receiver whose only
                # job is to allocate the array
                var old_frame = self.frame
-               var frame = new Frame(self, mpropdef, mpropdef.mclassdef.bound_mtype, [recv])
+               var frame = new StaticFrame(self, mpropdef, mpropdef.mclassdef.bound_mtype, [recv])
                self.frame = frame
                #print "required Array[{elttype}] for recv {recv.inspect}. bound=Array[{self.resolve_for(elttype, recv)}]. selfvar={frame.arguments.first.inspect}"
                var res = self.array_instance(varargs, elttype)
@@ -1820,7 +1820,7 @@ class SeparateRuntimeFunction
                var v = compiler.new_visitor
                var selfvar = new RuntimeVariable("self", recv, recv)
                var arguments = new Array[RuntimeVariable]
-               var frame = new Frame(v, mmethoddef, recv, arguments)
+               var frame = new StaticFrame(v, mmethoddef, recv, arguments)
                v.frame = frame
 
                var msignature = mmethoddef.msignature.resolve_for(mmethoddef.mclassdef.bound_mtype, mmethoddef.mclassdef.bound_mtype, mmethoddef.mclassdef.mmodule, true)
@@ -1892,7 +1892,7 @@ class VirtualRuntimeFunction
                var v = compiler.new_visitor
                var selfvar = new RuntimeVariable("self", v.object_type, recv)
                var arguments = new Array[RuntimeVariable]
-               var frame = new Frame(v, mmethoddef, recv, arguments)
+               var frame = new StaticFrame(v, mmethoddef, recv, arguments)
                v.frame = frame
 
                var sig = new FlatBuffer
index 6149ffa..58d3506 100644 (file)
@@ -508,6 +508,19 @@ redef class ModelBuilder
                        end
                end
 
+               # Check for conflicting module names in the project
+               if mgroup != null then
+                       var others = model.get_mmodules_by_name(mod_name)
+                       if others != null then for other in others do
+                               if other.mgroup!= null and other.mgroup.mproject == mgroup.mproject 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}")
+                                       break
+                               end
+                       end
+               end
+
                # Create the module
                var mmodule = new MModule(model, mgroup, mod_name, nmodule.location)
                nmodule.mmodule = mmodule
index 3e3c497..e66f164 100644 (file)
@@ -129,12 +129,6 @@ class MModule
                                assert mgroup.default_mmodule == null
                                mgroup.default_mmodule = self
                        end
-                       # placebo for old module nesting hierarchy
-                       var direct_owner = mgroup.default_mmodule
-                       if direct_owner == self then
-                               # The potential owner is the default_mmodule of the parent group
-                               if mgroup.parent != null then direct_owner = mgroup.parent.default_mmodule
-                       end
                end
                self.in_importation = model.mmodule_importation_hierarchy.add_node(self)
        end
index 65840a8..8a49296 100644 (file)
@@ -97,6 +97,18 @@ redef class ModelBuilder
                                error(nclassdef, "Redef error: No imported class {name} to refine.")
                                return
                        end
+
+                       # Check for conflicting class full-names in the project
+                       if mmodule.mgroup != null and mvisibility >= protected_visibility then
+                               var mclasses = model.get_mclasses_by_name(name)
+                               if mclasses != null then for other in mclasses do
+                                       if other.intro_mmodule.mgroup != null and other.intro_mmodule.mgroup.mproject == mmodule.mgroup.mproject then
+                                               error(nclassdef, "Error: A class named `{other.full_name}` is already defined in module `{other.intro_mmodule}` at {other.intro.location}.")
+                                               break
+                                       end
+                               end
+                       end
+
                        mclass = new MClass(mmodule, name, names, mkind, mvisibility)
                        #print "new class {mclass}"
                else if nclassdef isa AStdClassdef and nmodule.mclass2nclassdef.has_key(mclass) then
index 0fd7acc..d8fd96d 100644 (file)
@@ -485,6 +485,18 @@ redef class APropdef
                                modelbuilder.error(self, "Redef error: {mclassdef.mclass}::{mprop.name} is an inherited property. To redefine it, add the redef keyword.")
                                return false
                        end
+
+                       # Check for full-name conflicts in the project.
+                       # A public property should have a unique qualified name `project::class::prop`.
+                       if mprop.intro_mclassdef.mmodule.mgroup != null and mprop.visibility >= protected_visibility then
+                               var others = modelbuilder.model.get_mproperties_by_name(mprop.name)
+                               if others != null then for other in others do
+                                       if other != mprop and other.intro_mclassdef.mmodule.mgroup != null and other.intro_mclassdef.mmodule.mgroup.mproject == mprop.intro_mclassdef.mmodule.mgroup.mproject and other.intro_mclassdef.mclass.name == mprop.intro_mclassdef.mclass.name and other.visibility >= protected_visibility then
+                                               modelbuilder.advice(self, "full-name-conflict", "Warning: A property named `{other.full_name}` is already defined in module `{other.intro_mclassdef.mmodule}` for the class `{other.intro_mclassdef.mclass.name}`.")
+                                               break
+                                       end
+                               end
+                       end
                else
                        if not need_redef then
                                modelbuilder.error(self, "Error: No property {mclassdef.mclass}::{mprop.name} is inherited. Remove the redef keyword to define a new property.")
index 71a8669..e092371 100644 (file)
@@ -20,7 +20,7 @@ import highlight
 import docdown
 
 redef class ModelBuilder
-       fun test_markdown(page: HTMLTag, mmodule: MModule)
+       fun do_test_markdown(page: HTMLTag, mmodule: MModule)
        do
                page.add_raw_html "<h3 id='{mmodule}'>module {mmodule}</h1>"
                var mdoc = mmodule.mdoc
@@ -115,13 +115,13 @@ if opt_full.value then
                                page.add mdoc.full_markdown
                        end
                        for m in g.mmodules do
-                               modelbuilder.test_markdown(page, m)
+                               modelbuilder.do_test_markdown(page, m)
                        end
                end
        end
 else
        for m in mmodules do
-               modelbuilder.test_markdown(page, m)
+               modelbuilder.do_test_markdown(page, m)
        end
 end