Merge: Nitg-g new NativeArray fix
authorJean Privat <jean@pryen.org>
Fri, 29 May 2015 01:40:09 +0000 (21:40 -0400)
committerJean Privat <jean@pryen.org>
Fri, 29 May 2015 01:40:09 +0000 (21:40 -0400)
This is a bug that was discovered during the push of #1403.

What happens is that since NativeArrays work only with boxed values, the copy_to operation could write data beyond its intended boundaries, hence corrupting random memory.

If you execute the test bundled with the PR, on my machine, with `--hardening` on, you get this error:
`BTD BUG: Dynamic type is Sys, static type is Array[Byte]`

What happens here is that the `dest` of the `memmove` was in a emplacement before the `Array[Byte]` itself, due to its length and because the `memmove` used val* as sizeof value, it rewrote the classid of `self`, hence changing its dynamic type effectively from `Array[Byte]` to `Sys`, which produces the typing bug.

If left too long to execute, or in an unlucky memory layout, it simply segfaulted.

The behaviour of NEW_NativeArray henceforth is that it will reserve space for n `val*` instead of the `ctype` of the values.

Pull-Request: #1417
Reviewed-by: Jean Privat <jean@pryen.org>
Reviewed-by: Romain Chanoir <chanoir.romain@courrier.uqam.ca>

54 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]
lib/curl/curl.nit
lib/curl/examples/curl_http.nit
lib/geometry/boxes.nit
lib/geometry/points_and_lines.nit
lib/geometry/quadtree.nit
lib/json/dynamic.nit
lib/neo4j/graph/graph.nit
lib/neo4j/graph/json_graph_store.nit
lib/neo4j/graph/sequential_id.nit
lib/nitcorn/http_response.nit
lib/sax/helpers/attributes_impl.nit
lib/sax/helpers/sax_locator_impl.nit
lib/sax/helpers/xml_filter_impl.nit
lib/saxophonit/saxophonit.nit
lib/saxophonit/testing.nit
lib/socket/socket.nit
lib/sqlite3/native_sqlite3.nit
lib/sqlite3/sqlite3.nit
lib/standard/bytes.nit
lib/standard/collection/abstract_collection.nit
lib/standard/collection/array.nit
lib/standard/collection/range.nit
lib/standard/collection/union_find.nit
lib/standard/file.nit
lib/standard/kernel.nit
lib/standard/ropes.nit
lib/standard/string.nit
lib/string_experimentations/utf8.nit
lib/string_experimentations/utf8_noindex.nit
lib/template/template.nit
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
src/frontend/check_annotation.nit
src/modelize/modelize_property.nit
src/nitpick.nit
tests/base_init_autoinit2.nit
tests/nitpick.args [new file with mode: 0644]
tests/sav/base_init_autoinit2_alt2.res
tests/sav/nitpick_args1.res [new file with mode: 0644]
tests/sav/test_new_native_alt1.res
tests/test_advice_repeated_types.nit [new file with mode: 0644]

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 c087642..dbfc157 100644 (file)
@@ -377,8 +377,7 @@ class CurlResponseSuccess
        var status_code = 0
 
        # Receive body from request due to body callback registering
-       redef fun body_callback(line: String)
-       do
+       redef fun body_callback(line) do
                self.body_str = "{self.body_str}{line}"
        end
 end
index 079f12c..948ba38 100644 (file)
@@ -32,16 +32,16 @@ class MyHttpFetcher
        fun destroy do self.curl.destroy
 
        # Header callback
-       redef fun header_callback(line: String) do
+       redef fun header_callback(line) do
                # We keep this callback silent for testing purposes
                #if not line.has_prefix("Date:") then print "Header_callback : {line}"
        end
 
        # Body callback
-       redef fun body_callback(line: String) do self.our_body = "{self.our_body}{line}"
+       redef fun body_callback(line) do self.our_body = "{self.our_body}{line}"
 
        # Stream callback - Cf : No one is registered
-       redef fun stream_callback(buffer: String, size: Int, count: Int) do print "Stream_callback : {buffer} - {size} - {count}"
+       redef fun stream_callback(buffer, size, count) do print "Stream_callback : {buffer} - {size} - {count}"
 end
 
 
index b8c9798..5ec269d 100644 (file)
@@ -211,7 +211,7 @@ interface Boxed3d[N: Numeric]
                        (self.back <= other.front and other.back <= self.front))
        end
 
-       redef fun padded(dist: N): Box3d[N] do return new Box3d[N].lrtbfb(left - dist, right + dist, top + dist, bottom - dist, front + dist, back - dist)
+       redef fun padded(dist): Box3d[N] do return new Box3d[N].lrtbfb(left - dist, right + dist, top + dist, bottom - dist, front + dist, back - dist)
 end
 
 # A 3d bounded object and an implementation of Boxed
@@ -344,8 +344,8 @@ class BoxedArray[E: Boxed[Numeric]]
 
        private var data: Array[E] = new Array[E]
 
-       redef fun add(item: E) do data.add(item)
-       redef fun items_overlapping(item: Boxed[Numeric]): SimpleCollection[E]
+       redef fun add(item) do data.add(item)
+       redef fun items_overlapping(item): SimpleCollection[E]
        do
                var arr = new Array[E]
                for i in data do
index 303aae1..6c7fda6 100644 (file)
@@ -71,8 +71,8 @@ end
 class Line[N: Numeric]
        super ILine[N]
 
-       redef var point_left: P
-       redef var point_right: P
+       redef var point_left
+       redef var point_right
 
        init
        do
index 089b6ee..0218406 100644 (file)
@@ -55,14 +55,14 @@ abstract class QuadTree[E: Boxed[Numeric]]
                self.parent_node = parent
        end
 
-       redef fun items_overlapping(region :Boxed[Numeric]): SimpleCollection[E] do
+       redef fun items_overlapping(region): SimpleCollection[E] do
                var res = new Array[E]
                items_overlapping_in(region,res)
                return res
        end
 
        # add the item to the tree, create children if the limit is reached
-       redef fun add(item: E) do if self.is_leaf then self.data.add(item) else add_to_children(item)
+       redef fun add(item) do if self.is_leaf then self.data.add(item) else add_to_children(item)
 
        private fun add_to_children(item: Boxed[Numeric])
        do
@@ -93,7 +93,7 @@ abstract class QuadTree[E: Boxed[Numeric]]
                end
        end
 
-       redef fun is_empty: Bool do return data.is_empty and (self.is_leaf or (child0.is_empty and child1.is_empty and child2.is_empty and child3.is_empty))
+       redef fun is_empty do return data.is_empty and (self.is_leaf or (child0.is_empty and child1.is_empty and child2.is_empty and child3.is_empty))
 
        # Return whether or not the Node is a leaf of the tree
        fun is_leaf: Bool do return child0 == null
index d0559e0..594aac6 100644 (file)
@@ -131,8 +131,7 @@ class JsonValue
        #     assert "123".to_json_value.to_s == "123"
        #     assert "true".to_json_value.to_s == "true"
        #     assert "[1, 2, 3]".to_json_value.to_s == "123"
-       redef fun to_s: String
-       do
+       redef fun to_s do
                if value == null then return "null"
                return value.to_s
        end
index 2ffa800..12a2b9f 100644 (file)
@@ -105,7 +105,7 @@ abstract class NeoNodeCollection
        #
        # SEE: `create_node`
        # SEE: `register`
-       redef fun add(node: NeoNode) is abstract
+       redef fun add(node) is abstract
 
        # Add a new node to the graph and return it.
        #
index fb5ffd0..898c059 100644 (file)
@@ -297,7 +297,7 @@ redef class NeoNode
        redef fun to_s do return to_json
 
        # Append the JSON representation of the node to the specified buffer.
-       redef fun append_json_for(graph: NeoGraph, buffer: Buffer) do
+       redef fun append_json_for(graph, buffer) do
                append_json(buffer)
        end
 end
@@ -307,7 +307,7 @@ redef class NeoEdge
        # Append the JSON representation of the relationship to the specified buffer.
        #
        # Use the IDs specfied by `graph.nodes`.
-       redef fun append_json_for(graph: NeoGraph, buffer: Buffer) do
+       redef fun append_json_for(graph, buffer) do
                buffer.append "\{\"type\":"
                rel_type.append_json(buffer)
                buffer.append ",\"properties\":"
index fb6129f..939ed88 100644 (file)
@@ -63,7 +63,7 @@ class SequentialNodeCollection
                return nodes[id]
        end
 
-       redef fun has_id(id: Int): Bool do
+       redef fun has_id(id) do
                return id >= 0 and id < nodes.length and nodes[id] isa NeoNode
        end
 
index 666974b..ef87adb 100644 (file)
@@ -50,7 +50,7 @@ class HttpResponse
        end
 
        # Get this reponse as a string according to HTTP protocol
-       redef fun to_s: String
+       redef fun to_s
        do
                finalize
 
index f54ec0f..64691cd 100644 (file)
@@ -31,9 +31,9 @@ class AttributesImpl
        super Attributes
 
        private var data = new Array[String]
-       redef var length: Int = 0
+       redef var length = 0
 
-       redef fun uri(index: Int): nullable String do
+       redef fun uri(index) do
                if index >= 0 and index < length then
                        return data[index * 5]
                else
@@ -41,7 +41,7 @@ class AttributesImpl
                end
        end
 
-       redef fun local_name(index: Int): nullable String do
+       redef fun local_name(index) do
                if index >= 0 and index < length then
                        return data[index * 5 + 1]
                else
@@ -49,7 +49,7 @@ class AttributesImpl
                end
        end
 
-       redef fun qname(index: Int): nullable String do
+       redef fun qname(index) do
                if index >= 0 and index < length then
                        return data[index * 5 + 2]
                else
@@ -85,7 +85,7 @@ class AttributesImpl
        # are not available.
        #
        # SEE: `length`
-       redef fun type_of(index): nullable String do
+       redef fun type_of(index) do
                if index isa Int then
                        if index >= 0 and index < length then
                                return data[index * 5 + 3]
@@ -124,7 +124,7 @@ class AttributesImpl
        # are not available.
        #
        # SEE: `length`
-       redef fun value_of(index): nullable String do
+       redef fun value_of(index) do
                if index isa Int then
                        if index >= 0 and index < length then
                                return data[index * 5 + 4]
@@ -157,7 +157,7 @@ class AttributesImpl
        #
        # The index of the attribute, or -1 if it does not
        # appear in the list.
-       redef fun index_ns(uri: String, local_name: String): Int do
+       redef fun index_ns(uri, local_name) do
                var i = 0
 
                if "" != local_name then
@@ -184,7 +184,7 @@ class AttributesImpl
        #
        # The index of the attribute, or -1 if it does not
        # appear in the list.
-       redef fun index_of(qname: String): Int do
+       redef fun index_of(qname) do
                var i = 0
 
                if "" != qname then
@@ -218,7 +218,7 @@ class AttributesImpl
        # The attribute type as a string, or `null` if the
        # attribute is not in the list or if Namespace
        # processing is not being performed.
-       redef fun type_ns(uri: String, local_name: String): nullable String do
+       redef fun type_ns(uri, local_name) do
                var i = 0
 
                if "" != local_name then
@@ -252,7 +252,7 @@ class AttributesImpl
        # The attribute value as a string, or `null` if the
        # attribute is not in the list or if Namespace
        # processing is not being performed.
-       redef fun value_ns(uri: String, local_name: String): nullable String do
+       redef fun value_ns(uri, local_name) do
                var i = 0
 
                if "" != local_name then
index 99d0228..185f074 100644 (file)
@@ -19,19 +19,21 @@ import sax::sax_locator
 # can use it to make a persistent snapshot of a locator at any
 # point during a document parse:
 #
-#     import sax::helpers::sax_locator_impl
-#     import sax::content_handler
+# ~~~nitish
+# import sax::helpers::sax_locator_impl
+# import sax::content_handler
 #
-#     class Example super ContentHandler
-#      private var locator: SAXLocator
-#      private var start_loc: nullable SAXLocator = null
+# class Example super ContentHandler
+#     private var locator: SAXLocator
+#     private var start_loc: nullable SAXLocator = null
 #
-#      redef fun start_document do
-#              # save the location of the start of the document
-#              # for future use.
-#              start_loc = new SAXLocatorImpl.from(locator)
-#      end
+#     redef fun start_document do
+#         # save the location of the start of the document
+#         # for future use.
+#         start_loc = new SAXLocatorImpl.from(locator)
 #     end
+# end
+# ~~~
 #
 # Normally, parser writers will not use this class, since it
 # is more efficient to provide location information only when
@@ -40,10 +42,10 @@ import sax::sax_locator
 # Note: The original source code and documentation of this class comes, in part,
 # from [SAX 2.0](http://www.saxproject.org).
 class SAXLocatorImpl super SAXLocator
-       redef var public_id: nullable String = null is writable
-       redef var system_id: nullable String = null is writable
-       redef var line_number: Int = -1 is writable
-       redef var column_number: Int = -1 is writable
+       redef var public_id = null is writable
+       redef var system_id = null is writable
+       redef var line_number = -1 is writable
+       redef var column_number = -1 is writable
 
        # Zero-argument constructor.
        #
index 7bcd021..78efef9 100644 (file)
@@ -42,14 +42,14 @@ class XMLFilterImpl
 
        # XMLFilter
 
-       redef var parent: nullable XMLReader = null is writable
+       redef var parent = null is writable
 
        # XMLReader
 
-       redef var entity_resolver: nullable EntityResolver = null is writable
-       redef var dtd_handler: nullable DTDHandler = null is writable
-       redef var content_handler: nullable ContentHandler = null is writable
-       redef var error_handler: nullable ErrorHandler = null is writable
+       redef var entity_resolver = null is writable
+       redef var dtd_handler = null is writable
+       redef var content_handler = null is writable
+       redef var error_handler = null is writable
 
 
        ############################################################################
@@ -73,7 +73,7 @@ class XMLFilterImpl
                parent = parent_reader
        end
 
-       redef fun feature_recognized(name: String): Bool do
+       redef fun feature_recognized(name) do
                if parent == null then
                        return false
                else
@@ -81,7 +81,7 @@ class XMLFilterImpl
                end
        end
 
-       redef fun feature_readable(name: String): Bool do
+       redef fun feature_readable(name) do
                if parent == null then
                        return false
                else
@@ -89,7 +89,7 @@ class XMLFilterImpl
                end
        end
 
-       redef fun feature_writable(name: String): Bool do
+       redef fun feature_writable(name) do
                if parent == null then
                        return false
                else
@@ -112,7 +112,7 @@ class XMLFilterImpl
        # SEE: `feature_recognized`
        #
        # SEE: `feature_readable`
-       redef fun feature(name: String): Bool do
+       redef fun feature(name) do
                assert sax_recognized: parent != null else
                        sys.stderr.write("Feature: {name}\n")
                end
@@ -136,14 +136,14 @@ class XMLFilterImpl
        # SEE: `feature_recognized`
        #
        # SEE: `feature_writable`
-       redef fun feature=(name: String, value: Bool) do
+       redef fun feature=(name, value) do
                assert sax_recognized: parent != null else
                        sys.stderr.write("Feature: {name}\n")
                end
                parent.feature(name) = value
        end
 
-       redef fun property_recognized(name: String): Bool do
+       redef fun property_recognized(name) do
                if parent == null then
                        return false
                else
@@ -151,7 +151,7 @@ class XMLFilterImpl
                end
        end
 
-       redef fun property_readable(name: String): Bool do
+       redef fun property_readable(name) do
                if parent == null then
                        return false
                else
@@ -159,7 +159,7 @@ class XMLFilterImpl
                end
        end
 
-       redef fun property_writable(name: String): Bool do
+       redef fun property_writable(name) do
                if parent == null then
                        return false
                else
@@ -180,7 +180,7 @@ class XMLFilterImpl
        # SEE: `property_recognized`
        #
        # SEE: `property_readable`
-       redef fun property(name: String): nullable Object do
+       redef fun property(name) do
                assert sax_recognized: parent != null else
                        sys.stderr.write("Property: {name}\n")
                end
@@ -204,19 +204,19 @@ class XMLFilterImpl
        # SEE: `property_recognized`
        #
        # SEE: `property_writable`
-       redef fun property=(name: String, value: nullable Object) do
+       redef fun property=(name, value) do
                assert sax_recognized: parent != null else
                        sys.stderr.write("Property: {name}\n")
                end
                parent.property(name) = value
        end
 
-       redef fun parse(input: InputSource) do
+       redef fun parse(input) do
                setup_parse
                parent.parse(input)
        end
 
-       redef fun parse_file(system_id: String) do
+       redef fun parse_file(system_id) do
                var source = new InputSource
 
                source.system_id = system_id
@@ -227,9 +227,7 @@ class XMLFilterImpl
        ############################################################################
        # EntityResolver
 
-       redef fun resolve_entity(public_id: nullable String,
-                       system_id: nullable String):
-                       nullable InputSource do
+       redef fun resolve_entity(public_id, system_id) do
                if entity_resolver == null then
                        return null
                else
@@ -241,15 +239,13 @@ class XMLFilterImpl
        ############################################################################
        # DTDHandler
 
-       redef fun notation_decl(name: String, public_id: String,
-                       system_id: String) do
+       redef fun notation_decl(name, public_id, system_id) do
                if dtd_handler != null then
                        dtd_handler.notation_decl(name, public_id, system_id)
                end
        end
 
-       redef fun unparsed_entity_decl(name: String, public_id: String,
-                       system_id: String) do
+       redef fun unparsed_entity_decl(name, public_id, system_id) do
                if dtd_handler != null then
                        dtd_handler.unparsed_entity_decl(name, public_id, system_id)
                end
@@ -259,7 +255,7 @@ class XMLFilterImpl
        ############################################################################
        # ContentHandler
 
-       redef fun document_locator=(locator: SAXLocator) do
+       redef fun document_locator=(locator) do
                if content_handler != null then
                        content_handler.document_locator = locator
                end
@@ -277,50 +273,49 @@ class XMLFilterImpl
                end
        end
 
-       redef fun start_prefix_mapping(prefix: String, uri: String) do
+       redef fun start_prefix_mapping(prefix, uri) do
                if content_handler != null then
                        content_handler.start_prefix_mapping(prefix, uri)
                end
        end
 
-       redef fun end_prefix_mapping(prefix: String) do
+       redef fun end_prefix_mapping(prefix) do
                if content_handler != null then
                        content_handler.end_prefix_mapping(prefix)
                end
        end
 
-       redef fun start_element(uri: String, local_name: String, qname: String,
-                       atts: Attributes) do
+       redef fun start_element(uri, local_name, qname, atts) do
                if content_handler != null then
                        content_handler.start_element(uri, local_name, qname, atts)
                end
        end
 
-       redef fun end_element(uri: String, local_name: String, qname: String) do
+       redef fun end_element(uri, local_name, qname) do
                if content_handler != null then
                        content_handler.end_element(uri, local_name, qname)
                end
        end
 
-       redef fun characters(str: String) do
+       redef fun characters(str) do
                if content_handler != null then
                        content_handler.characters(str)
                end
        end
 
-       redef fun ignorable_whitespace(str: String) do
+       redef fun ignorable_whitespace(str) do
                if content_handler != null then
                        content_handler.ignorable_whitespace(str)
                end
        end
 
-       redef fun processing_instruction(target: String, data: nullable String) do
+       redef fun processing_instruction(target, data) do
                if content_handler != null then
                        content_handler.processing_instruction(target, data)
                end
        end
 
-       redef fun skipped_entity(name: String) do
+       redef fun skipped_entity(name) do
                if content_handler != null then
                        content_handler.skipped_entity(name)
                end
@@ -330,19 +325,19 @@ class XMLFilterImpl
        ############################################################################
        # ErrorHandler
 
-       redef fun warning(exception: SAXParseException) do
+       redef fun warning(exception) do
                if error_handler != null then
                        error_handler.warning(exception)
                end
        end
 
-       redef fun error(exception: SAXParseException) do
+       redef fun error(exception) do
                if error_handler != null then
                        error_handler.error(exception)
                end
        end
 
-       redef fun fatal_error(exception: SAXParseException) do
+       redef fun fatal_error(exception) do
                if error_handler != null then
                        error_handler.fatal_error(exception)
                else
index 6b3f616..19dbb69 100644 (file)
@@ -68,63 +68,63 @@ class XophonReader
        private var model = new XophonReaderModel
        private var lexer: XophonLexer is noinit
 
-       redef fun entity_resolver: nullable EntityResolver do return model.entity_resolver
-       redef fun entity_resolver=(entity_resolver: nullable EntityResolver) do
+       redef fun entity_resolver do return model.entity_resolver
+       redef fun entity_resolver=(entity_resolver) do
                model.entity_resolver = entity_resolver
        end
 
-       redef fun dtd_handler: nullable DTDHandler do return model.dtd_handler
-       redef fun dtd_handler=(dtd_handler: nullable DTDHandler) do
+       redef fun dtd_handler do return model.dtd_handler
+       redef fun dtd_handler=(dtd_handler) do
                model.dtd_handler = dtd_handler
        end
 
-       redef fun content_handler: nullable ContentHandler do return model.content_handler
-       redef fun content_handler=(content_handler: nullable ContentHandler) do
+       redef fun content_handler do return model.content_handler
+       redef fun content_handler=(content_handler) do
                model.content_handler = content_handler
        end
 
-       redef fun error_handler: nullable ErrorHandler do return model.error_handler
-       redef fun error_handler=(error_handler: nullable ErrorHandler) do
+       redef fun error_handler do return model.error_handler
+       redef fun error_handler=(error_handler) do
                model.error_handler = error_handler
        end
 
 
-       redef fun feature_recognized(name: String): Bool do
+       redef fun feature_recognized(name) do
                return model.feature_recognized(name)
        end
 
-       redef fun feature_readable(name: String): Bool do
+       redef fun feature_readable(name) do
                return model.feature_readable(name)
        end
 
-       redef fun feature_writable(name: String): Bool do
+       redef fun feature_writable(name) do
                return model.feature_readable(name)
        end
 
-       redef fun feature(name: String): Bool do return model.feature(name)
-       redef fun feature=(name: String, value: Bool) do model.feature(name) = value
+       redef fun feature(name) do return model.feature(name)
+       redef fun feature=(name, value) do model.feature(name) = value
 
-       redef fun property_recognized(name: String): Bool do
+       redef fun property_recognized(name) do
                return model.property_recognized(name)
        end
 
-       redef fun property_readable(name: String): Bool do
+       redef fun property_readable(name) do
                return model.property_readable(name)
        end
 
-       redef fun property_writable(name: String): Bool do
+       redef fun property_writable(name) do
                return model.property_writable(name)
        end
 
-       redef fun property(name: String): nullable Object do
+       redef fun property(name) do
                return model.property(name)
        end
 
-       redef fun property=(name: String, value: nullable Object) do
+       redef fun property=(name, value) do
                model.property(name) = value
        end
 
-       redef fun parse(input: InputSource) do
+       redef fun parse(input) do
                var system_id: nullable MaybeError[String, Error] = null
                model.locator = new SAXLocatorImpl
 
@@ -157,7 +157,7 @@ class XophonReader
                end
        end
 
-       redef fun parse_file(system_id: String) do
+       redef fun parse_file(system_id) do
                parse(new InputSource.with_system_id(system_id))
        end
 
index 70db6a5..cc69c83 100644 (file)
@@ -259,7 +259,7 @@ class SAXEventLogger
        ############################################################################
        # XMLReader
 
-       redef fun property(name: String): nullable Object do
+       redef fun property(name) do
                assert sax_recognized: parent != null else
                        sys.stderr.write("Property: {name}\n")
                end
@@ -278,7 +278,7 @@ class SAXEventLogger
                end
        end
 
-       redef fun property=(name: String, value: nullable Object) do
+       redef fun property=(name, value) do
                assert sax_recognized: parent != null else
                        sys.stderr.write("Property: {name}\n")
                end
@@ -297,7 +297,7 @@ class SAXEventLogger
                end
        end
 
-       redef fun parse(input: InputSource) do
+       redef fun parse(input) do
                assert parent_is_not_null: parent != 0 else
                        sys.stderr.write("No parent for filter.")
                end
@@ -314,9 +314,7 @@ class SAXEventLogger
        ############################################################################
        # EntityResolver
 
-       redef fun resolve_entity(public_id: nullable String,
-                       system_id: nullable String):
-                       nullable InputSource do
+       redef fun resolve_entity(public_id, system_id) do
                log.push(["resolve_entity",
                                public_id or else "^NULL",
                                system_id or else "^NULL"])
@@ -327,14 +325,12 @@ class SAXEventLogger
        ############################################################################
        # DTDHandler
 
-       redef fun notation_decl(name: String, public_id: String,
-                       system_id: String) do
+       redef fun notation_decl(name, public_id, system_id) do
                log.push(["notation_decl", name, public_id, system_id])
                super
        end
 
-       redef fun unparsed_entity_decl(name: String, public_id: String,
-                       system_id: String) do
+       redef fun unparsed_entity_decl(name, public_id, system_id) do
                log.push(["unparsed_entity_decl", name, public_id, system_id])
                super
        end
@@ -343,7 +339,7 @@ class SAXEventLogger
        ############################################################################
        # ContentHandler
 
-       redef fun document_locator=(locator: SAXLocator) do
+       redef fun document_locator=(locator) do
                log.push(["document_locator=",
                                locator.public_id or else "^NULL",
                                locator.system_id or else "^NULL",
@@ -362,18 +358,17 @@ class SAXEventLogger
                super
        end
 
-       redef fun start_prefix_mapping(prefix: String, uri: String) do
+       redef fun start_prefix_mapping(prefix, uri) do
                log.push(["start_prefix_mapping", prefix, uri])
                super
        end
 
-       redef fun end_prefix_mapping(prefix: String) do
+       redef fun end_prefix_mapping(prefix) do
                log.push(["end_prefix_mapping", prefix])
                super
        end
 
-       redef fun start_element(uri: String, local_name: String, qname: String,
-                       atts: Attributes) do
+       redef fun start_element(uri, local_name, qname, atts) do
                var entry = new Array[String]
                var i = 0
                var length = atts.length
@@ -394,27 +389,27 @@ class SAXEventLogger
                super
        end
 
-       redef fun end_element(uri: String, local_name: String, qname: String) do
+       redef fun end_element(uri, local_name, qname) do
                log.push(["end_element", uri, local_name, qname])
                super
        end
 
-       redef fun characters(str: String) do
+       redef fun characters(str) do
                log.push(["characters", str])
                super
        end
 
-       redef fun ignorable_whitespace(str: String) do
+       redef fun ignorable_whitespace(str) do
                log.push(["ignorable_witespace", str])
                super
        end
 
-       redef fun processing_instruction(target: String, data: nullable String) do
+       redef fun processing_instruction(target, data) do
                log.push(["processing_instruction", target, data or else "^NULL"])
                super
        end
 
-       redef fun skipped_entity(name: String) do
+       redef fun skipped_entity(name) do
                log.push(["skipped_entity", name])
                super
        end
@@ -423,17 +418,17 @@ class SAXEventLogger
        ############################################################################
        # ErrorHandler
 
-       redef fun warning(exception: SAXParseException) do
+       redef fun warning(exception) do
                log.push(["warning", exception.full_message])
                super
        end
 
-       redef fun error(exception: SAXParseException) do
+       redef fun error(exception) do
                log.push(["error", exception.full_message])
                super
        end
 
-       redef fun fatal_error(exception: SAXParseException) do
+       redef fun fatal_error(exception) do
                log.push(["fatal_error", exception.full_message])
                if error_handler != null then
                        error_handler.fatal_error(exception)
@@ -444,18 +439,14 @@ class SAXEventLogger
        ############################################################################
        # DeclHandler
 
-       redef fun element_decl(name: String, model: String) do
+       redef fun element_decl(name, model) do
                log.push(["element_decl", name, model])
                if decl_handler != null then
                        decl_handler.element_decl(name, model)
                end
        end
 
-       redef fun attribute_decl(element_name: String,
-                       attribute_name: String,
-                       attribute_type: String,
-                       mode: nullable String,
-                       value: nullable String) do
+       redef fun attribute_decl(element_name, attribute_name, attribute_type, mode, value) do
                log.push(["attribute_decl",
                                element_name,
                                attribute_name,
@@ -468,14 +459,14 @@ class SAXEventLogger
                end
        end
 
-       redef fun internal_entity_decl(name: String, value: String) do
+       redef fun internal_entity_decl(name, value) do
                log.push(["internal_entity_decl", name, value])
                if decl_handler != null then
                        decl_handler.internal_entity_decl(name, value)
                end
        end
 
-       redef fun external_entity_decl(name: String, value: String) do
+       redef fun external_entity_decl(name, value) do
                log.push(["external_entity_decl", name, value])
                if decl_handler != null then
                        decl_handler.external_entity_decl(name, value)
@@ -486,8 +477,7 @@ class SAXEventLogger
        ############################################################################
        # LexicalHandler
 
-       redef fun start_dtd(name: String, public_id: nullable String,
-                       system_id: nullable String) do
+       redef fun start_dtd(name, public_id, system_id) do
                log.push(["start_dtd", name,
                                public_id or else "^NULL",
                                system_id or else "^NULL"])
@@ -503,14 +493,14 @@ class SAXEventLogger
                end
        end
 
-       redef fun start_entity(name: String) do
+       redef fun start_entity(name) do
                log.push(["start_entity", name])
                if lexical_handler != null then
                        lexical_handler.start_entity(name)
                end
        end
 
-       redef fun end_entity(name: String) do
+       redef fun end_entity(name) do
                log.push(["end_entity", name])
                if lexical_handler != null then
                        lexical_handler.end_entity(name)
@@ -531,7 +521,7 @@ class SAXEventLogger
                end
        end
 
-       redef fun comment(str: String) do
+       redef fun comment(str) do
                log.push(["comment", str])
                if lexical_handler != null then
                        lexical_handler.comment(str)
index d553540..a665b41 100644 (file)
@@ -141,7 +141,7 @@ class TCPStream
        end
 
        # If socket.end_reached, nothing will happen
-       redef fun write(msg: Text)
+       redef fun write(msg)
        do
                if closed then return
                socket.write(msg.to_s)
index beeb162..dd45211 100644 (file)
@@ -66,7 +66,7 @@ extern class Sqlite3Code `{int`}
        new done `{ return SQLITE_DONE; `} #       101  /* sqlite3_step() has finished executing */
        fun is_done: Bool `{ return self == SQLITE_DONE; `}
 
-       redef fun to_s: String import NativeString.to_s `{
+       redef fun to_s import NativeString.to_s `{
 #if SQLITE_VERSION_NUMBER >= 3007015
                char *err = (char *)sqlite3_errstr(self);
 #else
index 0fe8050..5f90801 100644 (file)
@@ -257,7 +257,7 @@ class StatementIterator
 
        redef var item: StatementRow is noinit
 
-       redef var is_ok: Bool is noinit
+       redef var is_ok is noinit
 
        # require: `self.statement.is_open`
        redef fun next
index b300298..b9b39fd 100644 (file)
@@ -31,7 +31,7 @@ class Bytes
        private var items: NativeString
 
        # Number of bytes in the array
-       redef var length: Int
+       redef var length
 
        # Capacity of the array
        private var capacity: Int
@@ -58,7 +58,7 @@ class Bytes
        #     var b = new Bytes.empty
        #     b.add 101
        #     assert b[0] == 101
-       redef fun [](i: Int): Int do
+       redef fun [](i) do
                assert i >= 0
                assert i < length
                return items[i].ascii
@@ -67,7 +67,7 @@ class Bytes
        #     var b = new Bytes.with_capacity(1)
        #     b[0] = 101
        #     assert b.to_s == "e"
-       redef fun []=(i: Int, v: Int) do
+       redef fun []=(i, v) do
                if persisted then regen
                assert i >= 0
                assert i <= length
@@ -78,7 +78,7 @@ class Bytes
        #     var b = new Bytes.empty
        #     b.add 101
        #     assert b.to_s == "e"
-       redef fun add(c: Int) do
+       redef fun add(c) do
                if persisted then regen
                if length >= capacity then
                        enlarge(length)
@@ -90,7 +90,7 @@ class Bytes
        #     var b = new Bytes.empty
        #     b.append([104, 101, 108, 108, 111])
        #     assert b.to_s == "hello"
-       redef fun append(arr: Collection[Int]) do
+       redef fun append(arr) do
                if arr isa Bytes then
                        append_ns(arr.items, arr.length)
                else
@@ -147,7 +147,7 @@ private class BytesIterator
 
        var tgt: NativeString
 
-       redef var index: Int
+       redef var index
 
        var max: Int
 
index 8b01780..16e4b1e 100644 (file)
@@ -317,7 +317,7 @@ private class ContainerIterator[E]
 
        redef fun next do is_ok = false
 
-       redef var is_ok: Bool = true
+       redef var is_ok = true
 
        var container: Container[E]
 end
@@ -1100,7 +1100,7 @@ end
 private class CoupleMapIterator[K, V]
        super MapIterator[K, V]
        redef fun item do return _iter.item.second
-       
+
        #redef fun item=(e) do _iter.item.second = e
 
        redef fun key do return _iter.item.first
index b3ec382..de66e21 100644 (file)
@@ -5,7 +5,7 @@
 #
 # This file is free software, which comes along with NIT.  This software is
 # distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
-# without  even  the implied warranty of  MERCHANTABILITY or  FITNESS FOR A 
+# without  even  the implied warranty of  MERCHANTABILITY or  FITNESS FOR A
 # PARTICULAR PURPOSE.  You can modify it is you want,  provided this header
 # is kept unaltered, and a notification of the changes is added.
 # You  are  allowed  to  redistribute it and sell it, alone or is a part of
@@ -65,8 +65,7 @@ abstract class AbstractArrayRead[E]
 
        redef fun last_index_of(item) do return last_index_of_from(item, length-1)
 
-       redef fun index_of_from(item, pos)
-       do
+       redef fun index_of_from(item, pos) do
                var i = pos
                var len = length
                while i < len do
@@ -78,8 +77,7 @@ abstract class AbstractArrayRead[E]
                return -1
        end
 
-       redef fun last_index_of_from(item, pos)
-       do
+       redef fun last_index_of_from(item, pos) do
                var i = pos
                while i >= 0 do
                        if self[i] == item then
@@ -242,8 +240,7 @@ abstract class AbstractArray[E]
                self[0] = item
        end
 
-       redef fun insert(item: E, pos: Int)
-       do
+       redef fun insert(item, pos) do
                enlarge(length + 1)
                copy_to(pos, length-pos, self, pos + 1)
                self[pos] = item
index c611ec1..29fa17e 100644 (file)
@@ -19,7 +19,7 @@ import abstract_collection
 class Range[E: Discrete]
        super Collection[E]
 
-       redef var first: E
+       redef var first
 
        # Get the last element.
        var last: E
index f4c6baa..b44b7f9 100644 (file)
@@ -115,8 +115,7 @@ class DisjointSet[E]
        #     s.add(1)
        #     assert s.has(1)
        #     assert not s.has(2)
-       redef fun has(e)
-       do
+       redef fun has(e) do
                return nodes.has_key(e)
        end
 
@@ -126,8 +125,7 @@ class DisjointSet[E]
        # Initially it is in its own disjoint subset
        #
        # ENSURE: `has(e)`
-       redef fun add(e:E)
-       do
+       redef fun add(e) do
                if nodes.has_key(e) then return
                var ne = new DisjointSetNode
                nodes[e] = ne
index 5a89d10..3bad3f3 100644 (file)
@@ -135,7 +135,7 @@ class FileReader
        end
 
        # End of file?
-       redef var end_reached: Bool = false
+       redef var end_reached = false
 
        # Open the file at `path` for reading.
        #
@@ -299,7 +299,7 @@ class Stdin
                prepare_buffer(1)
        end
 
-       redef fun poll_in: Bool is extern "file_stdin_poll_in"
+       redef fun poll_in is extern "file_stdin_poll_in"
 end
 
 # Standard output stream.
index 85386b4..ffd684a 100644 (file)
@@ -5,7 +5,7 @@
 #
 # This file is free software, which comes along with NIT.  This software is
 # distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
-# without  even  the implied warranty of  MERCHANTABILITY or  FITNESS FOR A 
+# without  even  the implied warranty of  MERCHANTABILITY or  FITNESS FOR A
 # PARTICULAR PURPOSE.  You can modify it is you want,  provided this header
 # is kept unaltered, and a notification of the changes is added.
 # You  are  allowed  to  redistribute it and sell it, alone or is a part of
@@ -128,7 +128,7 @@ interface Comparable
        type OTHER: Comparable
 
        # Is `self` lesser than `other`?
-       fun <(other: OTHER): Bool is abstract 
+       fun <(other: OTHER): Bool is abstract
 
        # not `other` < `self`
        # Note, the implementation must ensure that: `(x<=y) == (x<y or x==y)`
@@ -331,10 +331,10 @@ universal Float
        redef fun !=(i) is intern
        redef fun output is intern
 
-       redef fun <=(i): Bool is intern
-       redef fun <(i): Bool is intern
-       redef fun >=(i): Bool is intern
-       redef fun >(i): Bool is intern
+       redef fun <=(i) is intern
+       redef fun <(i) is intern
+       redef fun >=(i) is intern
+       redef fun >(i) is intern
 
        redef fun +(i) is intern
        redef fun - is intern
@@ -483,9 +483,9 @@ universal Int
 
        redef fun is_between(c, d)
        do
-               if self < c or d < self then 
+               if self < c or d < self then
                        return false
-               else 
+               else
                        return true
                end
        end
@@ -536,7 +536,7 @@ universal Int
                # count digits
                while n > 0 do
                        d += 1
-                       n = n / b       # euclidian division /
+                       n = n / b       # euclidian division /
                end
                return d
        end
index 5ac6ee2..902649a 100644 (file)
@@ -74,7 +74,7 @@ end
 private class Concat
        super RopeString
 
-       redef var length: Int is noinit
+       redef var length is noinit
 
        redef fun substrings do return new RopeSubstrings(self)
 
@@ -159,7 +159,6 @@ private class Concat
        end
 
        redef fun copy_to_native(dest, n, src_offset, dest_offset) do
-               var remlen = n
                var subs = new RopeSubstrings.from(self, src_offset)
                var st = src_offset - subs.pos
                var off = dest_offset
@@ -827,7 +826,7 @@ class RopeBufferIter
        # Maximum position iterable.
        var maxpos: Int
 
-       redef var index: Int
+       redef var index
 
        # Init the iterator from a RopeBuffer.
        init(t: RopeBuffer) is old_style_init do
@@ -877,7 +876,7 @@ class RopeBufferReviter
        # Current position in `ns`.
        var pns: Int
 
-       redef var index: Int
+       redef var index
 
        # Init the iterator from a RopeBuffer.
        init(tgt: RopeBuffer) is old_style_init do
index 61af948..60f3af9 100644 (file)
@@ -898,8 +898,8 @@ abstract class Text
        #
        # REQUIRE: `n` must be large enough to contain `len` bytes
        #
-       #       var ns = new NativeString(8)
-       #       "Text is String".copy_to_native(ns, 8, 2, 0)
+       #       var ns = new NativeString(8)
+       #       "Text is String".copy_to_native(ns, 8, 2, 0)
        #       assert ns.to_s_with_length(8) == "xt is St"
        #
        fun copy_to_native(dest: NativeString, n, src_offset, dest_offset: Int) do
@@ -945,7 +945,7 @@ abstract class FlatText
        # copy locally the char* as Nit Strings are immutable.
        private fun fast_cstring: NativeString is abstract
 
-       redef var length: Int = 0
+       redef var length = 0
 
        redef fun output
        do
@@ -1194,7 +1194,7 @@ class FlatString
        # Indes in _items of the last item of the string
        private var index_to: Int is noinit
 
-       redef var chars: SequenceRead[Char] = new FlatStringCharView(self) is lazy
+       redef var chars = new FlatStringCharView(self) is lazy
 
        redef fun [](index)
        do
@@ -1319,8 +1319,7 @@ class FlatString
                index_to = to
        end
 
-       redef fun to_cstring: NativeString
-       do
+       redef fun to_cstring do
                if real_items != null then
                        return real_items.as(not null)
                else
@@ -1738,8 +1737,7 @@ class FlatBuffer
                capacity = c
        end
 
-       redef fun to_s: String
-       do
+       redef fun to_s do
                written = true
                if length == 0 then items = new NativeString(1)
                return new FlatString.with_infos(items, length, 0, length - 1)
index b2d9372..b3a2450 100644 (file)
@@ -394,7 +394,7 @@ redef class NativeString
                return to_s_with_length(len)
        end
 
-       redef fun to_s_with_length(len: Int): FlatString
+       redef fun to_s_with_length(len)
        do
                var real_len = new Container[Int](0)
                var x = make_index(len, real_len)
index 08757aa..8756838 100644 (file)
@@ -271,7 +271,7 @@ redef class FlatString
        redef type OTHER: FlatString
 
        # Length in bytes of the string (e.g. the length of the C string)
-       redef var bytelen: Int
+       redef var bytelen
 
        # Cache for the last accessed character in the char
        var cache = new CharCache(-1,-1)
@@ -475,7 +475,7 @@ redef class FlatString
        end
 
        # O(n)
-       redef fun substring(from: Int, count: Int) do
+       redef fun substring(from, count) do
                assert count >= 0
 
                if from < 0 then
@@ -518,7 +518,7 @@ end
 
 redef class FlatBuffer
 
-       redef var bytelen: Int
+       redef var bytelen
 
        redef init from(s) do
                if s isa Concat then
@@ -717,7 +717,7 @@ redef class NativeString
                return to_s_with_length(len)
        end
 
-       redef fun to_s_with_length(len: Int): FlatString
+       redef fun to_s_with_length(len)
        do
                return new FlatString.with_bytelen(self, 0, len - 1, len)
        end
index b67df17..fc37617 100644 (file)
@@ -200,7 +200,7 @@ class Template
        end
 
        # Do the full rendering and write the final content to a stream
-       redef fun write_to(stream: Writer)
+       redef fun write_to(stream)
        do
                assert not is_writing
                is_writing = true
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
 
index 6d9bb60..eb05a14 100644 (file)
@@ -84,6 +84,7 @@ readonly
 writable
 autoinit
 noautoinit
+lateinit
 nosuper
 old_style_init
 abstract
index 5242cdc..f1cce80 100644 (file)
@@ -206,9 +206,9 @@ redef class ModelBuilder
                                var mreadpropdef = npropdef.mreadpropdef
                                if mreadpropdef == null or mreadpropdef.msignature == null then return # Skip broken attribute
                                if npropdef.noinit then continue # Skip noinit attribute
-                               var atautoinit = npropdef.get_single_annotation("autoinit", self)
-                               if atautoinit != null then
-                                       # For autoinit attributes, call the reader to force
+                               var atlateinit = npropdef.get_single_annotation("lateinit", self)
+                               if atlateinit != null then
+                                       # For lateinit attributes, call the reader to force
                                        # the lazy initialization of the attribute.
                                        initializers.add(mreadpropdef.mproperty)
                                        mreadpropdef.mproperty.is_autoinit = true
@@ -660,6 +660,8 @@ redef class APropdef
                return true
        end
 
+       # Checks for useless type in redef signatures.
+       private fun check_repeated_types(modelbuilder: ModelBuilder) do end
 end
 
 redef class ASignature
@@ -1055,6 +1057,28 @@ redef class AMethPropdef
                        var nt = nsig.n_type
                        if nt != null then modelbuilder.check_visibility(nt, nt.mtype.as(not null), mpropdef)
                end
+               check_repeated_types(modelbuilder)
+       end
+
+       # For parameters, type is always useless in a redef.
+       # For return type, type is useless if not covariant with introduction.
+       redef fun check_repeated_types(modelbuilder) do
+               if mpropdef.is_intro or n_signature == null then return
+               # check params
+               for param in n_signature.n_params do
+                       if param.n_type != null then
+                               modelbuilder.advice(param.n_type, "useless-signature", "Warning: useless type repetition on parameter `{param.n_id.text}` for redefined method `{mpropdef.name}`")
+                       end
+               end
+               # get intro
+               var intro = mpropdef.mproperty.intro
+               var n_intro = modelbuilder.mpropdef2npropdef.get_or_null(intro)
+               if n_intro == null or not n_intro isa AMethPropdef then return
+               # check return type
+               var ret_type = n_signature.ret_type
+               if ret_type != null and ret_type == n_intro.n_signature.ret_type then
+                       modelbuilder.advice(n_signature.n_type, "useless-signature", "Warning: useless return type repetition for redefined method `{mpropdef.name}`")
+               end
        end
 end
 
@@ -1191,17 +1215,17 @@ redef class AAttrPropdef
                end
 
                var atlazy = self.get_single_annotation("lazy", modelbuilder)
-               var atautoinit = self.get_single_annotation("autoinit", modelbuilder)
-               if atlazy != null or atautoinit != null then
-                       if atlazy != null and atautoinit != null then
-                               modelbuilder.error(atlazy, "Error: `lazy` incompatible with `autoinit`.")
+               var atlateinit = self.get_single_annotation("lateinit", modelbuilder)
+               if atlazy != null or atlateinit != null then
+                       if atlazy != null and atlateinit != null then
+                               modelbuilder.error(atlazy, "Error: `lazy` incompatible with `lateinit`.")
                                return
                        end
                        if not has_value then
                                if atlazy != null then
                                        modelbuilder.error(atlazy, "Error: `lazy` attributes need a value.")
-                               else if atautoinit != null then
-                                       modelbuilder.error(atautoinit, "Error: `autoinit` attributes need a value.")
+                               else if atlateinit != null then
+                                       modelbuilder.error(atlateinit, "Error: `lateinit` attributes need a value.")
                                end
                                has_value = true
                                return
@@ -1350,6 +1374,7 @@ redef class AAttrPropdef
                if mlazypropdef != null then
                        mlazypropdef.static_mtype = modelbuilder.model.get_mclasses_by_name("Bool").first.mclass_type
                end
+               check_repeated_types(modelbuilder)
        end
 
        redef fun check_signature(modelbuilder)
@@ -1454,6 +1479,25 @@ redef class AAttrPropdef
                        end
                end
        end
+
+       # Type is useless if the attribute type is the same thant the intro.
+       redef fun check_repeated_types(modelbuilder) do
+               if mreadpropdef.is_intro or n_type == null then return
+               # get intro
+               var intro = mreadpropdef.mproperty.intro
+               var n_intro = modelbuilder.mpropdef2npropdef.get_or_null(intro)
+               if n_intro == null then return
+               # get intro type
+               var ntype = null
+               if n_intro isa AMethPropdef then
+                       ntype = n_intro.n_signature.ret_type
+               else if n_intro isa AAttrPropdef and n_intro.n_type != null then
+                       ntype = n_intro.n_type.mtype
+               end
+               # check
+               if ntype ==null or ntype != n_type.mtype then return
+               modelbuilder.advice(n_type, "useless-signature", "Warning: useless type repetition on redefined attribute `{mpropdef.name}`")
+       end
 end
 
 redef class ATypePropdef
index 44cab72..4d00945 100644 (file)
@@ -49,7 +49,7 @@ var model = new Model
 # A model builder to parse files
 var modelbuilder = new ModelBuilder(model, toolcontext)
 
-# Here we load an process all modules passed on the command line
+# Here we load and process all modules passed on the command line
 var mmodules = modelbuilder.parse_full(arguments)
 toolcontext.mmodules_to_check.add_all mmodules
 
index 5930588..081b84b 100644 (file)
@@ -19,8 +19,8 @@ class A
        var b: Object is noautoinit
        #alt1#var b2: Object = get(-4) is noautoinit
        var c: Object is noautoinit
-       var d: Object = get(2) is autoinit
-       #alt2#var d2: Object = get(-2) is autoinit, lazy
+       var d: Object = get(2) is lateinit
+       #alt2#var d2: Object = get(-2) is lateinit, lazy
        var e: Object = get(1)
        fun setc(v: Object) is autoinit do self.c = get(v)
        init do
diff --git a/tests/nitpick.args b/tests/nitpick.args
new file mode 100644 (file)
index 0000000..3ae7a2a
--- /dev/null
@@ -0,0 +1 @@
+--no-color -W test_advice_repeated_types.nit
index 91e3496..db23606 100644 (file)
@@ -1 +1 @@
-alt/base_init_autoinit2_alt2.nit:23,40--43: Error: `lazy` incompatible with `autoinit`.
+alt/base_init_autoinit2_alt2.nit:23,40--43: Error: `lazy` incompatible with `lateinit`.
diff --git a/tests/sav/nitpick_args1.res b/tests/sav/nitpick_args1.res
new file mode 100644 (file)
index 0000000..38f9166
--- /dev/null
@@ -0,0 +1,12 @@
+../lib/standard/bytes.nit:51,7--19: Documentation warning: Undocumented property `with_capacity`
+../lib/standard/bytes.nit:164,6--13: Documentation warning: Undocumented property `to_bytes`
+../lib/standard/stream.nit:425,6--17: Documentation warning: Undocumented property `buffer_reset`
+../lib/standard/file.nit:444,6--19: Documentation warning: Undocumented property `read_all_bytes`
+test_advice_repeated_types.nit:36,15--20: Warning: useless type repetition on redefined attribute `_a`
+test_advice_repeated_types.nit:37,18--20: Warning: useless type repetition on parameter `b1` for redefined method `b`
+test_advice_repeated_types.nit:38,18--20: Warning: useless type repetition on parameter `c1` for redefined method `c`
+test_advice_repeated_types.nit:38,27--29: Warning: useless type repetition on parameter `c2` for redefined method `c`
+test_advice_repeated_types.nit:39,15--20: Warning: useless return type repetition for redefined method `d`
+test_advice_repeated_types.nit:40,18--20: Warning: useless type repetition on parameter `e1` for redefined method `e`
+test_advice_repeated_types.nit:40,24--29: Warning: useless return type repetition for redefined method `e`
+test_advice_repeated_types.nit:49,18--20: Warning: useless type repetition on parameter `e1` for redefined method `e`
index 43e3636..209563a 100644 (file)
@@ -1,4 +1,4 @@
-Runtime error: Cast failed. Expected `E`, got `Bool` (../lib/standard/collection/array.nit:960)
+Runtime error: Cast failed. Expected `E`, got `Bool` (../lib/standard/collection/array.nit:957)
 NativeString
 N
 Nit
diff --git a/tests/test_advice_repeated_types.nit b/tests/test_advice_repeated_types.nit
new file mode 100644 (file)
index 0000000..21f1a04
--- /dev/null
@@ -0,0 +1,56 @@
+# 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.
+
+class A
+       var a: Object
+       fun b(b1: Int) is abstract
+       fun c(c1: Int, c2: Int) is abstract
+       fun d: Object is abstract
+       fun e(e1: Int): Object is abstract
+end
+
+class B
+       super A
+
+       redef var a
+       redef fun b(b1) do end
+       redef fun c(c1, c2) do end
+       redef fun d do return ""
+       redef fun e(e1) do return ""
+end
+
+class C
+       super A
+
+       redef var a: Object
+       redef fun b(b1: Int) do end
+       redef fun c(c1: Int, c2: Int) do end
+       redef fun d: Object do return ""
+       redef fun e(e1: Int): Object do return ""
+end
+
+class D
+       super A
+
+       redef fun b(b1) do end
+       redef fun c(c1, c2) do end
+       redef fun d: Int do return 1
+       redef fun e(e1: Int): Numeric do return 1
+end
+
+class E
+       super A
+
+       redef var d: Int = 1
+end