Merge: Advice on useless repeated types
authorJean Privat <jean@pryen.org>
Fri, 29 May 2015 01:38:22 +0000 (21:38 -0400)
committerJean Privat <jean@pryen.org>
Fri, 29 May 2015 01:38:22 +0000 (21:38 -0400)
With this PR, nitpick and other tools are able to suggest to remove useless types in signature redefinition with the option `-W`.

Example:

~~~nit
class A
fun foo(a: Bool) do end
end

class B
super A

redef fun foo(a: Bool) do end
end
~~~

~~~
$ nitc test.nit -W
test.nit:8,19--22: Warning: useless type repetition on parameter `a` for redefined method `foo` (useless-signature)
redef fun foo(a: Bool) do end
                 ^
Errors: 0. Warnings: 1.
~~~

Pull-Request: #1373
Reviewed-by: Lucas Bajolet <r4pass@hotmail.com>
Reviewed-by: Jean Privat <jean@pryen.org>
Reviewed-by: Romain Chanoir <chanoir.romain@courrier.uqam.ca>

15 files changed:
contrib/objcwrapper/header_static/makefile [new file with mode: 0644]
contrib/objcwrapper/header_static/src/header_static.nit [new file with mode: 0644]
src/doc/doc_base.nit
src/doc/doc_phases/doc_concerns.nit
src/doc/doc_phases/doc_console.nit
src/doc/doc_phases/doc_extract.nit
src/doc/doc_phases/doc_graphs.nit
src/doc/doc_phases/doc_hierarchies.nit
src/doc/doc_phases/doc_html.nit
src/doc/doc_phases/doc_intros_redefs.nit
src/doc/doc_phases/doc_lin.nit
src/doc/doc_phases/doc_pages.nit
src/doc/doc_phases/doc_poset.nit
src/doc/doc_phases/doc_structure.nit
src/doc/html_templates/html_model.nit

diff --git a/contrib/objcwrapper/header_static/makefile b/contrib/objcwrapper/header_static/makefile
new file mode 100644 (file)
index 0000000..e4d3e19
--- /dev/null
@@ -0,0 +1,7 @@
+bin/header_static:
+       mkdir -p bin
+       ../../../bin/nitc --dir bin src/header_static.nit
+
+tests: bin/header_static
+       cat CGGeometry.h | bin/header_static > static_CGGeometry.h
+       cat NSObject.h | bin/header_static > static_NSObject.h
diff --git a/contrib/objcwrapper/header_static/src/header_static.nit b/contrib/objcwrapper/header_static/src/header_static.nit
new file mode 100644 (file)
index 0000000..c43fe2a
--- /dev/null
@@ -0,0 +1,96 @@
+# This file is part of NIT (http://www.nitlanguage.org).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Filters preprocessed C-like header files to remove static code and keep their signatures.
+#
+# This tool is used in the process of parsing header files to extract
+# information on the declared services (the functions and structures).
+# This information is then used to generate bindings for Nit code
+# to access these services.
+#
+# The C header sometimes contains static code. It deletes static code of
+# headers, but keep their signatures. This tool is an extension of
+# header_keeper. It searches the keyword static to identify
+# the static code, and ignores the code into their brackets. The result is
+# printed to sdtout.
+#
+# ~~~sh
+# cat Pre-Processed/CGGeometry.h | header_static > Pre-Processed/static_header.h
+# ~~~
+#
+# This module can also be used as a library.
+# The main service is the method `header_static`
+module header_static
+
+redef class Char
+       private fun is_endline: Bool do return "\};".has(self)
+end
+
+# Filters the preprocessed `input` to keep signatures for static code and write to the `output`
+fun header_static(input: Reader, output: Writer) do
+       var static_target = false
+       var static_attribute_target = false
+       var bracket_counter = 0
+       var previous_letter = ""
+       var instruction = ""
+       var double_underscore = 0
+       var position = 0
+
+       while not input.eof do
+               var line = input.read_line
+               if line.to_s.has("static") then static_target = true
+
+               if static_target then
+                       if line.to_s.has("__attribute__") then static_attribute_target = true
+                       for letter in line do
+                               if letter == '{' then bracket_counter += 1
+                               if letter == '}' then bracket_counter -= 1
+
+                               if letter == '_' and previous_letter == "_" and bracket_counter == 0 then
+                                       double_underscore += 1
+                               end
+
+                               # Sometimes we lost space between return type and signature name,
+                               # because he has a return line between both.
+                               # We add a space before signature name for safety.
+                               if bracket_counter == 0 and letter == '_' and double_underscore >= 1 and not static_attribute_target then
+                                       instruction = instruction.insert_at(" ", position - 2)
+                               end
+                               if bracket_counter == 0 and not letter.is_endline then instruction += letter.to_s
+                               if bracket_counter == 0 and letter.is_endline then
+                                       instruction += ";"
+                                       static_target = false
+                                       static_attribute_target = false
+                               end
+
+                               if bracket_counter == 0 and (letter == '}' and double_underscore >= 1 or letter == ';') then
+                                       output.write instruction + "\n"
+                               end
+
+                               if letter.is_endline and bracket_counter == 0 then
+                                       double_underscore = 0
+                                       position = 0
+                                       instruction = ""
+                               end
+
+                               previous_letter = letter.to_s
+                               position += 1
+                       end
+               else
+                       output.write line + "\n"
+               end
+       end
+end
+
+header_static(sys.stdin, sys.stdout)
index 6cd6083..8f0467d 100644 (file)
@@ -27,16 +27,26 @@ import model_ext
 # It is a placeholder to share data between each phase.
 class DocModel
 
-       # `DocPage` composing the documentation.
+       # `DocPage` composing the documentation associated to their ids.
        #
        # This is where `DocPhase` store and access pages to produce documentation.
-       var pages = new Array[DocPage]
+       #
+       # See `add_page`.
+       var pages: Map[String, DocPage] = new HashMap[String, DocPage]
 
        # Nit `Model` from which we extract the documentation.
        var model: Model is writable
 
        # The entry point of the `model`.
        var mainmodule: MModule is writable
+
+       # Add a `page` to this documentation.
+       fun add_page(page: DocPage) do
+               if pages.has_key(page.id) then
+                       print "Warning: multiple page with the same id `{page.id}`"
+               end
+               pages[page.id] = page
+       end
 end
 
 # A documentation page abstraction.
@@ -45,6 +55,17 @@ end
 # the page.
 class DocPage
 
+       # Page uniq id.
+       #
+       # The `id` is used as name for the generated file corresponding to the page
+       # (if any).
+       # Because multiple pages can be generated in the same directory it should be
+       # uniq.
+       #
+       # The `id` can also be used to establish links between pages (HTML links,
+       # HTML anchors, vim links, etc.).
+       var id: String is writable
+
        # Title of this page.
        var title: String is writable
 
@@ -229,10 +250,30 @@ class PropertyGroup[E: MProperty]
 end
 
 redef class MEntity
+       # ID used as a unique ID and in file names.
+       #
+       # **Must** match the following (POSIX ERE) regular expression:
+       #
+       # ~~~POSIX ERE
+       # ^[A-Za-z_][A-Za-z0-9._-]*$
+       # ~~~
+       #
+       # That way, the ID is always a valid URI component and a valid XML name.
+       fun nitdoc_id: String do return full_name.to_cmangle
+
        # Name displayed in console for debug and tests.
        fun nitdoc_name: String do return name.html_escape
 end
 
+redef class MModule
+
+       # Avoid id conflict with group
+       redef fun nitdoc_id do
+               if mgroup == null then return super
+               return "{mgroup.full_name}::{full_name}".to_cmangle
+       end
+end
+
 redef class MClassDef
        redef fun nitdoc_name do return mclass.nitdoc_name
 end
index d1ada76..cca6346 100644 (file)
@@ -23,7 +23,7 @@ class ConcernsPhase
 
        # Populates the given DocModel.
        redef fun apply do
-               for page in doc.pages do page.build_concerns(doc)
+               for page in doc.pages.values do page.build_concerns(doc)
        end
 end
 
index dbd1d39..f927fd9 100644 (file)
@@ -19,6 +19,7 @@
 module doc_console
 
 import semantize
+import doc_extract
 import doc::console_templates
 
 # Nitx handles console I/O.
@@ -138,7 +139,7 @@ interface NitxQuery
 
        # Pretty prints the results for the console.
        fun make_results(nitx: Nitx, results: Array[NitxMatch]): DocPage do
-               var page = new DocPage("Results")
+               var page = new DocPage("results", "Results")
                page.root.add_child(new QueryResultArticle(self, results))
                return page
        end
@@ -203,7 +204,7 @@ class CommentQuery
        redef fun perform(nitx, doc) do
                var name = args.first
                var res = new Array[NitxMatch]
-               for mentity in doc.search_mentities(name) do
+               for mentity in doc.mentities_by_name(name) do
                        res.add new MEntityMatch(self, mentity)
                end
                return res
@@ -214,7 +215,7 @@ class CommentQuery
                if len == 1 then
                        var res = results.first.as(MEntityMatch)
                        var mentity = res.mentity
-                       var page = new DocPage("Results")
+                       var page = new DocPage("resultats", "Results")
                        var article = new DefinitionArticle(mentity)
                        article.cs_title = mentity.name
                        article.cs_subtitle = mentity.cs_declaration
@@ -317,7 +318,7 @@ class DocQuery
        redef fun perform(nitx, doc) do
                var res = new Array[NitxMatch]
                var name = args.first
-               for page in doc.pages do
+               for page in doc.pages.values do
                        if name == "*" then # FIXME dev only
                                res.add new PageMatch(self, page)
                        else if page.title == name then
@@ -377,7 +378,7 @@ class CodeQuery
                        return res
                end
                # else, lookup the model by name
-               for mentity in doc.search_mentities(name) do
+               for mentity in doc.mentities_by_name(name) do
                        if mentity isa MClass then continue
                        if mentity isa MProperty then continue
                        res.add new CodeMatch(self, mentity.cs_location, mentity.cs_source_code)
@@ -386,7 +387,7 @@ class CodeQuery
        end
 
        redef fun make_results(nitx, results) do
-               var page = new DocPage("Code Results")
+               var page = new DocPage("results", "Code Results")
                for res in results do
                        page.add new CodeQueryArticle(self, res.as(CodeMatch))
                end
@@ -435,32 +436,6 @@ end
 
 ## exploration
 
-redef class DocModel
-
-       # Lists all MEntities in the model.
-       private var mentities: Collection[MEntity] is lazy do
-               var res = new HashSet[MEntity]
-               res.add_all mprojects
-               res.add_all mgroups
-               res.add_all mmodules
-               res.add_all mclasses
-               res.add_all mclassdefs
-               res.add_all mproperties
-               res.add_all mpropdefs
-               return res
-       end
-
-       # Search MEntities that match `name` by their name or namespace.
-       private fun search_mentities(name: String): Array[MEntity] do
-               var res = new Array[MEntity]
-               for mentity in mentities do
-                       if mentity.name != name and mentity.cs_namespace != name then continue
-                       res.add mentity
-               end
-               return res
-       end
-end
-
 # Visitor looking for initialized `MType` (new T).
 #
 # See `NewQuery`.
index 53adeac..456d545 100644 (file)
@@ -146,4 +146,78 @@ redef class DocModel
                        end
                end
        end
+
+       # Lists all MEntities in the model.
+       #
+       # FIXME invalidate cache if `self` is modified.
+       var mentities: Collection[MEntity] is lazy do
+               var res = new HashSet[MEntity]
+               res.add_all mprojects
+               res.add_all mgroups
+               res.add_all mmodules
+               res.add_all mclasses
+               res.add_all mclassdefs
+               res.add_all mproperties
+               res.add_all mpropdefs
+               return res
+       end
+
+       # Searches MEntities that match `name`.
+       fun mentities_by_name(name: String): Array[MEntity] do
+               var res = new Array[MEntity]
+               for mentity in mentities do
+                       if mentity.name != name then continue
+                       res.add mentity
+               end
+               return res
+       end
+
+       # Looks up a MEntity by its `namespace`.
+       #
+       # Usefull when `mentities_by_name` by return conflicts.
+       #
+       # Path can be the shortest possible to disambiguise like `Class::property`.
+       # In case of larger conflicts, a more complex namespace can be given like
+       # `project::module::Class::prop`.
+       fun mentities_by_namespace(namespace: String): Array[MEntity] do
+               var res = new Array[MEntity]
+               for mentity in mentities do
+                       mentity.mentities_by_namespace(namespace, res)
+               end
+               return res
+       end
+end
+
+redef class MEntity
+       # Looks up a MEntity by its `namespace` from `self`.
+       private fun mentities_by_namespace(namespace: String, res: Array[MEntity]) do end
+
+       private fun lookup_in(mentities: Collection[MEntity], namespace: String, res: Array[MEntity]) do
+               var parts = namespace.split_once_on("::")
+               var name = parts.shift
+               for mentity in mentities do
+                       if mentity.name != name then continue
+                       if parts.is_empty then
+                               res.add mentity
+                       else
+                               mentity.mentities_by_namespace(parts.first, res)
+                       end
+               end
+       end
+end
+
+redef class MProject
+       redef fun mentities_by_namespace(namespace, res) do lookup_in(mgroups, namespace, res)
+end
+
+redef class MGroup
+       redef fun mentities_by_namespace(namespace, res) do lookup_in(mmodules, namespace, res)
+end
+
+redef class MModule
+       redef fun mentities_by_namespace(namespace, res) do lookup_in(mclassdefs, namespace, res)
+end
+
+redef class MClassDef
+       redef fun mentities_by_namespace(namespace, res) do lookup_in(mpropdefs, namespace, res)
 end
index 07b7c99..1339d09 100644 (file)
@@ -36,7 +36,7 @@ class GraphPhase
 
        redef fun apply do
                if ctx.opt_nodot.value then return
-               for page in doc.pages do
+               for page in doc.pages.values do
                        var article = page.build_graph(self, doc)
                        if article == null then continue
                        # FIXME avoid diff
index 5bfbfb1..eecc95b 100644 (file)
@@ -26,7 +26,7 @@ class InheritanceListsPhase
        var name_sorter = new MEntityNameSorter
 
        redef fun apply do
-               for page in doc.pages do
+               for page in doc.pages.values do
                        if page isa MEntityPage then page.build_inh_list(self, doc)
                end
        end
index 156a650..0c843bb 100644 (file)
@@ -104,7 +104,7 @@ class RenderHTMLPhase
 
        redef fun apply do
                init_output_dir
-               for page in doc.pages do
+               for page in doc.pages.values do
                        page.render(self, doc).write_to_file("{ctx.output_dir.to_s}/{page.html_url}")
                end
        end
@@ -187,7 +187,7 @@ redef class DocPage
        # all properties below are roughly copied from `doc_pages`
 
        # Build page title string
-       fun init_title(v: RenderHTMLPhase, doc: DocModel) is abstract
+       fun init_title(v: RenderHTMLPhase, doc: DocModel) do end
 
        # Build top menu template if any.
        fun init_topmenu(v: RenderHTMLPhase, doc: DocModel) do
index 54f7c18..5ed6f78 100644 (file)
@@ -24,7 +24,7 @@ class IntroRedefListPhase
        super DocPhase
 
        redef fun apply do
-               for page in doc.pages do
+               for page in doc.pages.values do
                        if not page isa MEntityPage then continue
                        page.root.build_intro_redef_list(self, doc, page)
                end
index eda76ba..c36756c 100644 (file)
@@ -25,7 +25,7 @@ class LinListPhase
        private var lin_sorter = new MEntityNameSorter
 
        redef fun apply do
-               for page in doc.pages do page.apply_linearization(self, doc)
+               for page in doc.pages.values do page.apply_linearization(self, doc)
        end
 end
 
index e468cb6..3f83001 100644 (file)
@@ -23,19 +23,19 @@ class MakePagePhase
 
        # Instanciates documentation pages for the given DocModel.
        redef fun apply do
-               doc.pages.add new OverviewPage("Overview")
-               doc.pages.add new SearchPage("Index")
+               doc.add_page new OverviewPage("overview", "Overview")
+               doc.add_page new SearchPage("search", "Index")
                for mgroup in doc.mgroups do
-                       doc.pages.add new MGroupPage(mgroup.nitdoc_name, mgroup)
+                       doc.add_page new MGroupPage(mgroup)
                end
                for mmodule in doc.mmodules do
-                       doc.pages.add new MModulePage(mmodule.nitdoc_name, mmodule)
+                       doc.add_page new MModulePage(mmodule)
                end
                for mclass in doc.mclasses do
-                       doc.pages.add new MClassPage(mclass.nitdoc_name, mclass)
+                       doc.add_page new MClassPage(mclass)
                end
                for mproperty in doc.mproperties do
-                       doc.pages.add new MPropertyPage(mproperty.nitdoc_name, mproperty)
+                       doc.add_page new MPropertyPage(mproperty)
                end
        end
 end
@@ -52,6 +52,7 @@ end
 
 # A DocPage documenting a MEntity.
 class MEntityPage
+       autoinit mentity
        super DocPage
 
        # Type of MEntity documented by this page.
@@ -59,6 +60,9 @@ class MEntityPage
 
        # MEntity documented by this page.
        var mentity: MENTITY
+
+       redef var id is lazy do return mentity.nitdoc_id
+       redef var title is lazy do return mentity.nitdoc_name
 end
 
 # A documentation page about a MGroup.
index fd38e0f..0c610d4 100644 (file)
@@ -23,7 +23,7 @@ class POSetPhase
 
        # Populates the given DocModel.
        redef fun apply do
-               for page in doc.pages do
+               for page in doc.pages.values do
                        if page isa MEntityPage then page.build_poset(self, doc)
                end
        end
index 507a0a4..7491e4b 100644 (file)
@@ -33,7 +33,7 @@ class StructurePhase
 
        # Populates the given DocModel.
        redef fun apply do
-               for page in doc.pages do page.apply_structure(self, doc)
+               for page in doc.pages.values do page.apply_structure(self, doc)
        end
 end
 
index cd6974c..bc8ccbe 100644 (file)
@@ -22,17 +22,6 @@ import html::bootstrap
 import ordered_tree
 
 redef class MEntity
-       # ID used as a HTML unique ID and in file names.
-       #
-       # **Must** match the following (POSIX ERE) regular expression:
-       #
-       # ~~~POSIX ERE
-       # ^[A-Za-z_][A-Za-z0-9._-]*$
-       # ~~~
-       #
-       # That way, the ID is always a valid URI component and a valid XML name.
-       fun nitdoc_id: String is abstract
-
        # URL of this entity’s Nitdoc page.
        fun nitdoc_url: String is abstract