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>
--- /dev/null
+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
--- /dev/null
+# 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)
# 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.
# 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
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
# 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
module doc_console
import semantize
+import doc_extract
import doc::console_templates
# Nitx handles console I/O.
# 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
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
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
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
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)
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
## 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`.
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
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
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
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
# 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
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
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
# 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
# A DocPage documenting a MEntity.
class MEntityPage
+ autoinit mentity
super DocPage
# Type of MEntity documented by this page.
# 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.
# 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
# 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
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