nitdoc: Remove the project’s name from the groups’ IDs.
[nit.git] / src / phase.nit
index df766e1..2011cd8 100644 (file)
@@ -27,6 +27,43 @@ redef class ToolContext
        # 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")
+
+       redef init
+       do
+               super
+
+               option_context.add_option(opt_disable_phase)
+       end
+
+       redef fun process_options(args)
+       do
+               super
+
+               for v in opt_disable_phase.value do
+                       if v == "list" then
+                               for p in phases_list do
+                                       var deps = p.in_hierarchy.direct_greaters
+                                       if deps.is_empty then
+                                               print p
+                                       else
+                                               print "{p} (dep: {deps.join(", ")})"
+                                       end
+                               end
+                               exit 0
+                       end
+
+                       var found = false
+                       for p in phases do
+                               if v != p.to_s then continue
+                               found = true
+                               p.disabled = true
+                       end
+                       if not found then fatal_error(null, "Error: no phase named `{v}`. Use `list` to list all phases.")
+               end
+       end
+
        fun phases_list: Sequence[Phase]
        do
                var phases = self.phases.to_a
@@ -34,6 +71,9 @@ redef class ToolContext
                return phases
        end
 
+       # Set of already analyzed modules.
+       private var phased_modules = new HashSet[AModule]
+
        # Run all registered phases on a set of modules
        fun run_phases(nmodules: Collection[AModule])
        do
@@ -44,13 +84,21 @@ redef class ToolContext
                var phases = phases_list
 
                for phase in phases do
-                       self.info(" registered phases: {phase.class_name}", 2)
+                       self.info(" registered phases: {phase}", 2)
                end
 
                for nmodule in nmodules do
+                       if phased_modules.has(nmodule) then continue
+                       phased_modules.add nmodule
+
                        self.info("Semantic analysis module {nmodule.location.file.filename}", 2)
+
+                       var vannot = new AnnotationPhaseVisitor
+                       vannot.enter_visit(nmodule)
+
                        for phase in phases do
-                               self.info(" phase: {phase.class_name}", 3)
+                               if phase.disabled then continue
+                               self.info(" phase: {phase}", 3)
                                assert phase.toolcontext == self
                                var errcount = self.error_count
                                phase.process_nmodule(nmodule)
@@ -60,17 +108,20 @@ redef class ToolContext
                                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
                                                assert phase.toolcontext == self
-                                               phase.process_npropdef(npropdef)
+                                               phase_process_npropdef(phase, npropdef)
                                        end
                                end
                                if errcount != self.error_count then
                                        self.check_errors
                                        break
                                end
-                               var v = new AnnotationPhaseVisitor(phase)
-                               v.enter_visit(nmodule)
+                               for na in vannot.annotations do
+                                       phase.process_annotated_node(na.parent.parent.as(not null), na)
+                               end
                                if errcount != self.error_count then
                                        self.check_errors
                                        break
@@ -81,20 +132,27 @@ redef class ToolContext
 
                var time1 = get_time
                self.info("*** END SEMANTIC ANALYSIS: {time1-time0} ***", 2)
+
+               errors_info
+       end
+
+       fun phase_process_npropdef(phase: Phase, npropdef: APropdef)
+       do
+               phase.process_npropdef(npropdef)
        end
 end
 
+# Collect all annotation
 private class AnnotationPhaseVisitor
        super Visitor
 
-       var phase: Phase
-
-       init(phase: Phase) do self.phase = phase
+       # The collected annotations
+       var annotations = new Array[AAnnotation]
 
        redef fun visit(n)
        do
                n.visit_all(self)
-               if n isa AAnnotation then phase.process_annotated_node(n.parent.parent.as(not null), n)
+               if n isa AAnnotation then annotations.add n
        end
 end
 
@@ -105,13 +163,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)
@@ -119,10 +180,24 @@ abstract class Phase
                end
        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
+
+       # Is the phase globally disabled?
+       # A disabled phase is not called automatically called by `ToolContext::run_phases` and cie.
+       #
+       # 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
        fun process_nmodule(nmodule: AModule) do end
 
+       # Specific actions to execute on the tree of a class definition
+       # Note that the order of the visit is the one of the file
+       # @toimplement
+       fun process_nclassdef(nclassdef: AClassdef) do end
+
        # Specific actions to execute on the tree of a property
        # Note that the order of the visit is the one of the file
        # @toimplement