Merge: doc: fixed some typos and other misc. corrections
[nit.git] / src / phase.nit
index d1cc1cb..2a9d7b8 100644 (file)
@@ -21,20 +21,23 @@ import poset
 
 redef class ToolContext
        # The various registered phases to performs
-       # The order in the poset is the dependance of phases
+       # The order in the poset is the dependence of phases
        #
        # While you can directly modify the poset (nodes and edges),
        # it is often simpler to use the constructor in `Phase`
        var phases = new POSet[Phase]
 
        # --disable-phase
-       var opt_disable_phase = new OptionArray("DEBUG: Disable a specific phase; use `list` to get the list.", "--disable-phase")
+       var opt_disable_phase = new OptionArray("Disable a specific phase; use `list` to get the list (debug)", "--disable-phase")
+
+       # --sloppy
+       var opt_sloppy = new OptionBool("Force lazy semantic analysis of the source-code (debug)", "--sloppy")
 
        redef init
        do
                super
 
-               option_context.add_option(opt_disable_phase)
+               option_context.add_option(opt_disable_phase, opt_sloppy)
        end
 
        redef fun process_options(args)
@@ -62,18 +65,32 @@ redef class ToolContext
                        end
                        if not found then fatal_error(null, "Error: no phase named `{v}`. Use `list` to list all phases.")
                end
+
+               if opt_sloppy.value then semantize_is_lazy = true
        end
 
-       fun phases_list: Sequence[Phase]
-       do
+       # The list of registered phases in the application order.
+       var phases_list: Sequence[Phase] is lazy do
                var phases = self.phases.to_a
                self.phases.sort(phases)
                return phases
        end
 
+       # Is `phase_process_npropdef` not called automatically by `run_phases`?
+       #
+       # When set to true, it is the responsibility of the tools
+       #
+       # Is false by default.
+       var semantize_is_lazy = false is writable
+
        # Set of already analyzed modules.
        private var phased_modules = new HashSet[AModule]
 
+       # List of module to process according to `run_phases`
+       #
+       # This allow some new modules to be found and added while analysing the code.
+       var todo_nmodules: Sequence[AModule]
+
        # Run all registered phases on a set of modules
        fun run_phases(nmodules: Collection[AModule])
        do
@@ -87,7 +104,11 @@ redef class ToolContext
                        self.info(" registered phases: {phase}", 2)
                end
 
-               for nmodule in nmodules do
+               var todo_nmodules = nmodules.to_a
+               self.todo_nmodules = todo_nmodules
+
+               while not todo_nmodules.is_empty do
+                       var nmodule = todo_nmodules.shift
                        if phased_modules.has(nmodule) then continue
                        phased_modules.add nmodule
 
@@ -98,34 +119,34 @@ redef class ToolContext
 
                        for phase in phases do
                                if phase.disabled then continue
-                               self.info(" phase: {phase}", 3)
                                assert phase.toolcontext == self
                                var errcount = self.error_count
                                phase.process_nmodule(nmodule)
                                if errcount != self.error_count then
                                        self.check_errors
-                                       break
                                end
                                errcount = self.error_count
                                for nclassdef in nmodule.n_classdefs do
                                        assert phase.toolcontext == self
                                        phase.process_nclassdef(nclassdef)
-                                       for npropdef in nclassdef.n_propdefs do
+                                       if not semantize_is_lazy then for npropdef in nclassdef.n_propdefs do
                                                assert phase.toolcontext == self
                                                phase_process_npropdef(phase, npropdef)
                                        end
                                end
                                if errcount != self.error_count then
                                        self.check_errors
-                                       break
                                end
                                for na in vannot.annotations do
-                                       phase.process_annotated_node(na.parent.parent.as(not null), na)
+                                       var p = na.parent
+                                       if p isa AAnnotations then p = p.parent
+                                       assert p != null
+                                       phase.process_annotated_node(p, na)
                                end
                                if errcount != self.error_count then
                                        self.check_errors
-                                       break
                                end
+                               phase.process_nmodule_after(nmodule)
                        end
                        self.check_errors
                end
@@ -133,13 +154,40 @@ redef class ToolContext
                var time1 = get_time
                self.info("*** END SEMANTIC ANALYSIS: {time1-time0} ***", 2)
 
-               errors_info
+               self.check_errors
        end
 
-       fun phase_process_npropdef(phase: Phase, npropdef: APropdef)
+       # Process the given `phase` on the `npropdef`
+       # Called by `run_phases`
+       protected fun phase_process_npropdef(phase: Phase, npropdef: APropdef)
        do
                phase.process_npropdef(npropdef)
        end
+
+       # Run the phase on the given npropdef.
+       # Does nothing if `semantize_is_lazy` is false.
+       fun run_phases_on_npropdef(npropdef: APropdef)
+       do
+               if not semantize_is_lazy then return
+               if npropdef.is_phased then return
+               npropdef.is_phased = true
+
+               #self.info("Semantic analysis of property {npropdef.location.file.filename}", 0)
+
+               var phases = phases_list
+               for phase in phases do
+                       if phase.disabled then continue
+                       assert phase.toolcontext == self
+                       phase_process_npropdef(phase, npropdef)
+                       self.check_errors
+               end
+       end
+end
+
+redef class APropdef
+       # Is the propdef already analyzed by `run_phases_on_npropdef`.
+       # Unused unless `semantize_is_lazy` is true.
+       private var is_phased = false
 end
 
 # Collect all annotation
@@ -163,13 +211,16 @@ abstract class Phase
        var toolcontext: ToolContext
 
        # The dependence relation of the phase with the other phases
-       var in_hierarchy: POSetElement[Phase]
+       var in_hierarchy: POSetElement[Phase] is noinit
+
+       # The explicit dependences, used to initialize `in_importation`
+       var depends: nullable Collection[Phase]
 
        # Initialize and register a phase to the toolcontext
-       init(toolcontext: ToolContext, depends: nullable Collection[Phase])
+       init
        do
-               self.toolcontext = toolcontext
                in_hierarchy = toolcontext.phases.add_node(self)
+               var depends = self.depends
                if depends != null then
                        for d in depends do
                                toolcontext.phases.add_edge(self, d)
@@ -178,11 +229,13 @@ abstract class Phase
        end
 
        # By default, the name is the lowercased prefix of the classname
-       redef fun to_s do return class_name.strip_extension("Phase").to_lower
+       redef fun to_s do return class_name.strip_extension("Phase").to_snake_case
 
        # Is the phase globally disabled?
        # A disabled phase is not called automatically called by `ToolContext::run_phases` and cie.
-       var disabled = false
+       #
+       # Warning: disabling a phase may cause subsequent phases to work in a degraded way or to fail.
+       var disabled = false is writable
 
        # Specific actions to execute on the whole tree of a module
        # @toimplement
@@ -202,4 +255,10 @@ abstract class Phase
        # Note that the order of the visit is the one of the file
        # @toimplement
        fun process_annotated_node(node: ANode, nat: AAnnotation) do end
+
+       # Specific actions to execute on the whole tree of a module
+       # Called at the end of a phase on a module
+       # Last called hook
+       # @toimplement
+       fun process_nmodule_after(nmodule: AModule) do end
 end