Package org.ek9lang.compiler.phase5


package org.ek9lang.compiler.phase5
I - PRE Intermediate Representation generation checks.

At this point, before the compiler builds the intermediate representation it completes some more checks.

PreIntermediateRepresentationChecks is the main entry point for this phase. Most of the processing is delegated to PreIRListener.

This now follows the same pattern as most of the other Listeners, using SymbolsAndScopes for handling scope traversals and locating recorded symbols and scopes.

There is a 'complexity' calculator built into this phase, it is quite tolerant of high complexity. It does emit compiler errors if a single method/function has complexity at level 50 or higher or if an aggregate has a total complexity of 500 or higher. The rules for calculating complexity are based on a modified McCabe region algorithm. It is quite simple in its approach, as code elements are encountered a level of complexity is incremented.

There are additional checks around simple evaluation of whether variables have been initialised or not. In some cases properties on aggregates may need to be defined in uninitialised for i.e.

  prop1 as String?

In such cases the EK9 compiler will mandate that the EK9 developer create all necessary coded constructors, it will not mandate that 'prop1' is then initialised - it will leave that to the Ek9 developer to 'take the risk' and understand what may or may not go wrong in their code.

Ideally, all properties and variables are always declared in initialised form (even if the value is 'un-set'). But this cannot always be mandated, so the EK9 compiler has some tolerance for these uninitialised variables.

  • Class
    Description
    Does checks on the control part of a while loop to see if Optional/Result/Iterator are now safe via method access.
    Once an expression has been assessed as being simple enough to process, this consumer is called to mark the appropriate symbol(s) used in the expression as safe if the appropriate methods are called.
    Validates that application blocks do not have too many registrations (E11042).
    Emit error if there are too many arguments.
    Validates class design patterns for Construct Misuse Detection (E11022-E11025).
    Checks cognitive complexity at function/method/operator exit and emits error if too high.
    Checks LCOM4 cohesion against thresholds and emits errors if exceeded.
    Checks the combined complexity and statement count at function/method/operator exit.
    Looks for the complexity value in the complexityCounter and adds the value to the appropriate symbol.
    Checks efferent coupling (Ce) against thresholds and emits errors if exceeded.
    Checks expression complexity when a coalescing expression tree completes.
    Checks inheritance depth (DIT) against thresholds and emits errors if exceeded.
    Pops the injection count from the counter and stores it as squirrelled data on the symbol.
    Validates DI injection design quality for components (E11040).
    Validates DI injection design quality for a single callable (E11043).
    Validates module-level cohesion against threshold and emits E11017 error if exceeded.
    Functional interface to get the appropriate ErrorListener for a ParsedModule.
    Validates module-level efferent coupling against threshold and emits E11016 error if exceeded.
    Functional interface to get the appropriate ErrorListener for a ParsedModule.
    Validates the number of constructs declared in a module against MetricLimits.MODULE_SIZE and emits E11027 if exceeded.
    Checks nesting depth at function/method/operator exit and emits error if too deep.
    Checks statement count at function/method/operator exit and emits error if too many.
    Enforces 'allow only' constraints on traits and classes (sealed type pattern).
    Checks call sites for ambiguous positional arguments.
    Detects assign-if-unset statements (:=?) that can never apply (E08095).
    Check the assignment to a variable can be a single assignment or a deep copy type assignment.
    Checks that gated system types are only used in publishable packages when the corresponding capability has been declared.
    Capture ownership by FRESHNESS (R-E, Steve 2026-10-06) - never by the named/unnamed syntax of the capture.
    THE record of which constructor parameters a constructor KEEPS as a closeable property, and the deferred verdict on every construction that hands such a parameter a value that is not new (R4 of the fresh-store rule, E81062).
    R7 of the closeable ownership rules (Steve, 2026-10-05): closeability cannot be erased by a store (E81059).
    If you hold it, you close it (E81067, Steve 2026-10-06, D8/C5): a class, record, component or dynamic class with a closeable property - or a capture it owns (R-E) - must DECLARE its own operator close: default operator close or one it writes.
    The NAMED half of the closeable ownership rule (Steve, 2026-10-05): "A closeable value you never NAME is owned by the statement that created it and closed when that statement finishes; a closeable value you NAME must be declared as a try-with-resources resource." (E81056).
    R5 (Steve, 2026-10-05): a method, operator, function or dynamic function whose DECLARED return type is closeable must return a NEW instance (E81057).
    THE fresh-store rule for closeable SLOTS (R-A, Steve 2026-10-06, decisions D3 and D9, with R-D): every closeable object has exactly ONE owner, so a closeable value may enter a slot that will close it only when nothing else already owns it.
    Tracks cognitive complexity within functions/methods/operators.
    Tracks method-field relationships to calculate LCOM4 (Lack of Cohesion of Methods v4).
    Is the value of an expression KNOWN WHEN THE PROGRAM IS COMPILED - that is, made only from text written in the source? Used by UrlTextKnownAtCompileTimeOrError (E08275, decision U3 of docs/architecture/EK9_URL_TYPE_DESIGN.md §0).
    Designed to give a broad indication of complexity, this is not true pure Cyclometric Complexity.
    One popped region: its total complexity, and how much of that came from expressions.
    Detects tautological and contradictory compound conditions using range algebra (E08086/E08087).
    Finds all concrete (non-abstract) subtypes of a given abstract aggregate type.
    Propagates the CONCURRENT_ENTRY_POINT = "TRUE" squirrelled marker transitively along the call graph (Step 6b, slice 1).
    Detects confusingly similar variable, parameter, and field names within the same scope.
    Detects arithmetic expressions that always produce a constant result (E08083).
    Detects coalescing expressions whose result is predetermined (E08094).
    Detects comparisons where both operands are literals (E08082).
    A constrained-type construction whose argument value is KNOWN at compile time and provably violates the constraint is an error (E08260), not a runtime Panic.
    THE shared resolver for a constrained-type construction site — a constructor call whose constructed type is a CLASS_CONSTRAINED aggregate carrying a constraint, together with its single base-type argument symbol.
     
    The per-construct obligation rollup accumulated during the phase-5 walk of one callable body, then squirrelled as OBLIGATIONS_TOTAL/DISCHARGED/UNDISCHARGED at construct exit (docs/tooling/EK9_SYMBOLIC_SCANNER_IMPLEMENTATION_SPEC.md §2.1/§2.4).
    Walk 0 of PRE_IR_CHECKS — the "all-constructors initialise the field" pre-pass.
    The per-construct statement budget and human-readable construct label, keyed off the symbol kind.
    Collects CONTAINER_UNWRAP obligations from an object-access chain for the census — one per Optional.get / Result.ok/error / Iterator.next call, discharged by GUARD when the access is safe.
    Tracks efferent coupling (Ce) - the number of external types a construct depends on.
    Validates that a module does not contain data clumps (E11053).
    Tracks function/method/constructor parameter signatures for data clump detection (E11053).
    An immutable (type, name) pair from a parameter, ordered for deterministic comparison.
    One callable's parameter signature for data clump analysis.
    Walk 3 of phase 5: dedicated AST walk for the data-race check (slice 1, step 7).
    PRE_IR_CHECKS confirm-or-retract for the BARE default operator comparison family.
    The generated copy of a closeable holder is refused (R-A, Steve 2026-10-06, decision D9; E81065).
    After a this(...) call, the properties the delegated-to constructor sets have a value for the rest of the delegating constructor - and only those.
    Reports DI/AOP analysis to stderr when the -di flag is set.
    Shared helpers over an application's dependency-injection registrations, so the several DI checks and the DI reporter derive the same view of what is registered rather than each re-spelling the 'filter component registrations, map to interface FQN' stream.
    Detects discarded return values in bare statement calls (E11050, E11051, E11052, E11055).
    Why an obligation is considered discharged (proven safe) by an existing analysis, or DischargeReason.UNDISCHARGED when no analysis proves it — the discharge-detection order of docs/tooling/EK9_SYMBOLIC_SCANNER_IMPLEMENTATION_SPEC.md §2.3.
    Validates that no reachable type in a dispatcher's hierarchy resolves ambiguously between two or more handlers at equal cost.
    Validates that dispatchers on sealed traits have handlers for all permitted types.
    Reports a dispatcher handler whose parameter type has no concrete form anywhere in the program, so no value can ever have that runtime type and the handler is dead code that looks live.
    Validates that dispatcher handler parameter types are reachable from the base dispatch type's hierarchy.
    Deals with handling any returning values in a dynamic function.
    Detects call chains deeper than one level (E11069).
    Checks for excessive type traversal in method chains (Law of Demeter).
    Tracks complexity of coalescing expression trees with depth-based multiplier.
    Classifies fields in an aggregate as service-type (abstract/interface) or data-type (concrete).
    Result of field classification for an aggregate.
    Detects conditions that are always true or always false based on data flow analysis (E08086/E08087).
    Does checks on both for loops and for range in terms of pre-flow and expressions (for loop).
    Predicate to determine if an expression is a Boolean logical operator (and/or) with Boolean type operands.
    When looking to calculate complexity of methods/functions it is important to work out if parts of an expression are in some way conditional.
    Checks the for structure via the code analysers.
    Typically checks any returning values to see if they have now been initialised on a function.
    Widens a GENERIC construct's input-variety denominator to span the types it is actually parameterised with (design D11/W5b).
    Extracts the EXISTING variable a pre-flow assigns: the target of a blind assignment (v := e()) or an assign-if-unset (v :=? e()).
    E08270 (Steve, 2026-10-07, Q4): reading a <- guard's variable in a LATER else if / else block of the same if chain, before anything assigns it there.
     
    Given an identifier, this consumer checks if it is a property of some aggregate.
    THE check of a READ of a local or returning variable that may still be uninitialised (E08020): every grammar position that loads a variable's value hands its read node here - this is the one consumer, do not write another.
    Does basic checks on the if condition to see if a Result/Optional access has been made safe.
    Deals with checking if all paths through if/else/else-if/else result in variables meeting criteria.
    A construction call that reaches a constructor which leaves properties without a value (E08264).
    A stream whose source the compiler can PROVE infinite may not reach a stage that waits for the end of the stream (E10050) - decision 15(a), phase 1.
    Calculates the Depth of Inheritance Tree (DIT) for aggregate symbols.
    Tracks injection counts within functions/methods/operators.
    Detects circular dependencies in component injection chains per application (E08190).
    THE components of an application that sit on a circular injection chain (E08190), as InjectionCycleOrError found them - and THE rule that E08190 suppresses E08200 for them (Steve, 2026-10-11): in a cycle no registration order works, so "reorder registrations" is advice that cannot be followed.
    Validates ordering in application blocks (E08200).
    Validates DI injection design quality across a construct's methods (E11041).
    Parallel to InjectionCounter, but tracks the DISTINCT injected component TYPE (fully-qualified names) per scope rather than just the site count.
    Records the input-variation figures — the 1-wise FLOOR (headline) and the pairwise DENOMINATOR (ceiling) plus the per-leaf confidence — on each callable (method / function / operator), squirrelled for the metrics / MCP surface next to COMPLEXITY.
    A stack of integer counters, one per nested function/method/operator scope, accumulated during AST traversal.
    Walk 4 of phase 5: data-collection walk for slice-2 deadlock detection (Phase B.1 — intra-function scope).
    Detects logical tautologies and contradictions (E08085).
    Validates that a module does not contain repeated magic literals (E11065).
    Detects bare literals used as operands in comparison, relational, matching, and range expressions (E11064).
    Tracks literal occurrences across files for repeated magic literal detection (E11065).
    One recorded occurrence of a literal.
    A bit of a catch 22, need to issue errors and the like when traversing an identifier in some circumstances.
    Typically checks any returning values to see if they have now been initialised on a method.
    Tracks module-level cohesion by recording constructs and their intra-module type references.
    Tracks module-level efferent coupling (Ce) - the number of distinct external modules that a module depends on.
    Item 5, step 1: the E08090 (NOT_REFERENCED) verdict FULL_RESOLUTION deferred, on a local initialised by a call and afterwards only mutated in place, never read (view <- config.get("server"); view :~: overrides).
    What PRE_IR_CHECKS needs, collected during Walk 1 (by PreIRListener, every source in parallel), to decide the E08090 verdicts FULL_RESOLUTION deferred (item 5): every local marked NOT_REFERENCED_DEFERRED - initialised by a call, afterwards only mutated in place, never read - with the expression it was initialised by (MutatedOnlyVariableOrError); every user method, operator, function and dynamic function with a returning variable, with each expression that variable is made to REFER to (its initialiser, every pointer assignment (pre-flows included), and through an arm-shaped value the returning variable it yields), so ReceiverInteriorReturns can derive whether a method returns (part of) its receiver and CloseableReturnOrError can require a closeable return to be new - both reading ReturnSourceKind. Shared across the parallel walk, so the queues are concurrent; the per-source state is in a MutatedOnlyVariables.Collector.
    A deferred local and the expression it was initialised by.
    A user method, operator, function or dynamic function with a returning variable, and every expression that variable is made to refer to.
    Detects `throw` statements whose expression is a nested call (E11070).
    Tracks nesting depth within functions/methods/operators.
    The phase-5 pre-pass that marks iteration the compiler can PROVE never ends (decision 15(a), phase 1: the infinite half).
    Detects non-descriptive variable names (E11031).
    In the case of specific variables of type like 'Result' and 'Optional' it is important to check if the method being called needs a previous call to isOk or isError or just '?' before accessing specific methods.
    An object cannot be used before it is fully constructed (E08061).
    A single contract/partiality obligation found in a construct body, tagged with the analysis (if any) that discharges it (docs/tooling/EK9_SYMBOLIC_SCANNER_IMPLEMENTATION_SPEC.md §2.1).
    Layer 1 of the Symbolic/Contract Scanner — the every-build obligation census (docs/tooling/EK9_SYMBOLIC_SCANNER_IMPLEMENTATION_SPEC.md §2).
    The taxonomy of contract/partiality obligations the census counts, from what already exists in the language and analysis (docs/tooling/EK9_SYMBOLIC_SCANNER_IMPLEMENTATION_SPEC.md §2.2).
    Detects variable names that shadow operator keywords (E11032).
    Typically checks any returning values to see if they have now been initialised on an operator.
    Used as a base for a range of constructs that can be expressions.
    Detects string concatenation chains with 3 or more parts and recommends string interpolation instead (E11068).
    At this point all templates should be present and all symbols resolved.
    Designed to do additional logic checks once everything has been resolved.
    Checks that reveal() on Sensitive is only called from within a class or trait that is Privileged.
    Deals with handling any returning values in a dynamic function.
    Validates that program HTTP wiring is consistent with application service registrations.
    THE reading of a constructor's CommonValues.LEAVES_PROPERTIES_UNSET mark: the properties (declared without a value) it leaves unset, following its this(...) chain.
    Marks the types whose objects can reach themselves through their properties, so phase 7 guards only THEIR generated operators against a cyclic object graph (Steve, 2026-10-05: the hybrid - a compile error on a potentially cyclic type would reject every legitimate tree, a runtime guard on every type would tax the hottest operator in the language).
    THE property-type graph: which types an object of a given type can reach through its properties.
    The three kinds of node - see the class javadoc.
    One node, keyed by kind and fully qualified type name (value equality, as the shared Tarjan requires).
    The size of the part of the graph that was reached - what the linearity test asserts on.
    The two whole-program facts the graph needs: the concrete subtypes of a type, and every aggregate.
    Central home for the code-quality metric limits that are SHARED across more than one check in this phase.
    THE "what does running this callable construct, and what does that construction run with no edge of its own" for the dependency-injection checks that walk CallGraph.getReachableIncludingReferences(Set, Function): DI completeness (ReachableInjectionsOrError, E08210/E08220) and DI ordering (TransitiveInjectionOrderingOrError, E08200).
    THE dependency-injection completeness check for programs (E08220, E08210): every injection that can run when a program runs must be satisfiable - the program needs an application, and that application must register every injected abstract component the program can reach.
    Item 5, step 2: DERIVE, for user methods and operators, the RETAINS marker the built-ins declare - so a user method that hands back one of its own properties follows the same rule as JSON.get.
    Detects comparisons of a Boolean expression with a Boolean literal (E08084).
    Detects empty checks that are always true or always false based on data flow analysis (E08092/E08093).
    Detects isSet checks that are always true or always false based on data flow analysis (E08088/E08089).
    Detects when an override method/operator body is textually identical to the super (E11044).
    PRE_IR_CHECKS use-site check for a RETRACTED bare default comparison operator.
    Checks if return variables are initialised.
    Enforces guard-first return ordering (E08055; docs/language-design/EK9_REQUIRE_RETURN_ORDERING_SPEC.md).
    THE classification of what a callable's returning variable is made to refer to - one of the MutatedOnlyVariables.ReturningCallable.sources() collected in Walk 1.
    THE conjunct walk for Optional/Result/Iterator safe access (Steve, 2026-10-07, decision Q3) - use it, never re-derive a variant.
    One fact a conjunct proves: access (ok/error/get/next) on symbol is safe.
    THE E08040 rule: an Optional/Result/Iterator that is proven safe to unwrap in the current scope must not be re-pointed there, by ANY store - an assignment statement (o: other, o :=? other) or a construct's assignment pre-flow (if o := other then o?).
    Marks specific types of variable of specific types (generic Result/Option) in a manner that enables the compiler to check if access to certain methods is safe.
    Tests specific types of variable of specific types (generic Result/Option) in a manner that enables the compiler to check if access to certain methods is safe.
     
    It may be that before calling this you have determined that 'isSet' has been called or you 'know' that the value of the variable is present and set.
    Walks a callable's parse tree to collect the variable symbols accessible within it - the local variables declared in its (possibly nested) instruction blocks, and the property fields of its enclosing aggregate.
    Detects hardcoded secrets in string literals at compile time (E11080-E11086).
    Detects self-comparison expressions where both sides resolve to the same symbol (E08081).
    Typically checks any returning values to see if they have now been initialised on a service operation.
    Links the escaping-return-value contract of service operations into the call graph.
    Validates that every service declaration has a security posture: either 'open' or 'constrain by X [and Y]'.
    The principal data-race check (slice 1, step 7): every mutation of shared state inside a function/method marked CONCURRENT_ENTRY_POINT must occur inside a MutexLock.enter(...) callback.
    THE "may this slot already hold a value here?" test for a slot that may be assigned AT MOST ONCE ON ANY PATH: a closeable property in its own constructor, and a closeable returning variable declared unset (D3 extended to every closeable slot, 2026-10-06).
    Tracks statement counts within functions/methods/operators.
    THE decision of which calls make a closeable value that the block statement making it OWNS - the unnamed half of the closeable ownership rule (Steve, 2026-10-05): "A closeable value you never NAME is owned by the statement that created it and closed when that statement finishes." The named half is CloseableLocalOrError (E81056).
    THE phase-5 reading of a STORE: a statement that makes a variable, property or returning variable refer to the value an expression evaluates to.
    Checks a stream pipeline whose source is a for carrying a pre-flow guard.
    Used with aggregates to check that if they have any properties that are not initialised at declaration the developer has taken appropriate steps to ensure initialisation.
     
    Deals with checking if all paths through switch/case/default (return) result in variables meeting criteria.
    Only triggers the call the retrieve the appropriate symbol for the node.
    Similar to the if block check, but this is focused on the true part of the ternary expression.
    Analyzes how traits are implemented in an aggregate to detect E11023 pattern (missing 'by' delegation).
    Result of analyzing trait implementation for potential missing 'by' delegation.
    Detects when multiple traits implemented by an aggregate have methods with the same signature.
    Post-parallel detector for transitive injection ordering validation (E08200), run once the call graph is built.
     
    Checks the try/catch/finally structure via the code analysers.
    Union-Find (Disjoint Set Union) data structure for O(n * alpha(n)) connected components.
    Reports dependency-injection fields that cannot be satisfied at a given point, walking an aggregate's own injection fields plus those of any aggregate types reachable through its property field types and supers.
    Detects unused captured variables in dynamic functions and classes (E11018).
    Detects unused parameters in functions, methods, constructors, and service operations (E08091).
    E08275: Networking().urlOf(text) given text the compiler can know - decision U3 of docs/architecture/EK9_URL_TYPE_DESIGN.md §0.
    Accepts a variable only declaration and ensures the variable is recorded if appropriate, for transient flow checks.
    Does checks on the control part of a while loop to see if Optional/Result/Iterator are now safe via method access.
    Checks the while statement via the code analysers.