Class GuardCouplingReader

java.lang.Object
org.ek9lang.compiler.symbolic.GuardCouplingReader

public final class GuardCouplingReader extends Object
THE reader of guard CO-OCCURRENCE β€” which of a callable's parameters, or which FIELDS of one parameter, the code tests together. Use this, do not re-derive one.

πŸ”‘ A different signal from RequirePredicateReader, and the distinction is the whole point. That reader takes a guard's literal and answers which VALUE matters. This one ignores values entirely and answers which TUPLE matters. Same source, orthogonal signal, and only the second says anything about combination.

Why co-occurrence is worth reading at all. A coupling is often neither declared by any type nor derivable by reasoning about the domain. Production code that checks a widget's brittleness and its colour together looks arbitrary until colour turns out to drive sunlight exposure. Worse, some couplings have no mechanism at all β€” an insurer prices a fitted tow bar higher not because the vehicle tows, but because that population claims more often. No amount of domain understanding produces that. It exists in exactly one place: the condition somebody wrote. The code is the domain expert.

πŸ”‘ Where this sits in the combinatorial-testing model. Nie & Leung's CT survey (ACM Computing Surveys 43(2), 2011) formalises the model as Model_SUT(P, V, R, C) β€” parameters, value sets, the interaction relations R, and the constraints C. This class computes R; C comes from constrained types and require.

πŸ”‘ R is the COVERING REQUIREMENT SET, not merely an observation β€” Def. 2.2.3: "Let R denote the set of the interaction relation set. R is the covering requirements for the SUT … If CA covers all the combinations required by R, we call CA a covering array." When R's elements have differing sizes the result is a variable strength covering array, which is precisely what a coupling of arity 3 asks for. So the couplings this returns are requirements the array must satisfy, not hints it may use.

πŸ›‘ Claim nothing novel for the idea. That same survey names the automated route β€” "static analysis, such as program slicing, can also be used to identify the interactions between parameters" β€” citing Schroeder & Korel (2000) on input–output analysis and Cheng, Dumitrescu & Schroeder (2003). Deriving R rather than eliciting it from documents and interviews is established prior art. What differs here is the signal and its cost: theirs is dataflow-based input–output analysis, ours is syntactic co-occurrence in a control condition, read off the parse tree with no execution β€” and our C is enforced by the compiler rather than modelled.

πŸ”‘ Driven by a parse CONTEXT, never by name β€” as with RequirePredicateReader, the caller supplies the construct's own subtree, so functions, methods, constructors, operators and dynamic functions are all covered by one traversal.

⚠️ ADDITIVE ONLY, and that is what makes it blindness-safe. A coupling guarantees combinations the generator would otherwise leave to a covering array's discretion (SPEC-test-data-generation.md §0's A_use ADDITIVE row). It must never prune: "nothing couples these two today" is precisely the implementation-under-test assumption use-directed reduction is warned against, because the omission being hunted looks identical to an absent coupling.

⚠️ The relation is NOT transitively closed, deliberately. If a–b couple and b–c couple, (a,c) is not a coupling. Taking the closure makes everything couple everything, the array degenerates to exhaustive, and the signal disappears exactly where the code is richest. Only literal observed sets are reported.

⚠️ Anchors are PERISHABLE, hence GuardCouplingReader.GuardCoupling.line and GuardCouplingReader.GuardCoupling.guardText. A declared constraint changes only when someone edits the declaration, and the compiler revalidates every generated test on the next build. A guard is not like that: it moves, narrows or disappears when the world changes β€” a paint defect is fixed, an actuarial model re-fitted β€” and nothing notices. Recording where a coupling came from is what lets a later staleness check ask whether it is still there.

  • Constructor Details

    • GuardCouplingReader

      public GuardCouplingReader()
  • Method Details

    • read

      public List<GuardCouplingReader.GuardCoupling> read(org.antlr.v4.runtime.tree.ParseTree constructCtx, List<ISymbol> callParameters, ParsedModule module)
      Read every coupling expressed by a control condition in constructCtx.

      Conditions are taken from the grammar's labelled control=expression β€” covering if / when (via preFlowAndControl), switch / given (which uses the same rule) and both while forms.

      πŸ”‘ Nesting counts, and reading only single conditions was a real gap. if a > 3 containing if b < 7 is the same discrimination as if a > 3 and b < 7 β€” the block is reached only when both hold β€” so each condition is read together with its ENCLOSING chain. Branch polarity is irrelevant here: reaching an inner condition inside an else still required the outer one to be evaluated, so the coupling holds either way. That is why this needs none of the dominator care SymbolicEngine takes, where the polarity decides soundness.

      Parameters:
      constructCtx - the construct's parse subtree
      callParameters - the construct's parameters, in declaration order, for index mapping
      module - the parsed module, to resolve identifier references to symbols
      Returns:
      the couplings found, de-duplicated, in first-appearance order; never null