ek9_apply_guard_pattern

Rewrites the construct's head to 'if x <- f()' (or ':=?', or ':=' with an explicit 'then x?', as the source assignment dictates) and deletes the standalone assignment and the redundant 'x?' check; answers status, edits[] and summary, an unsafe or unsupported shape as conflicts[].

Category: Editor · Compare: IDE quick-fix: convert check-then-use into a guard (design-by-contract null safety)

When to use it

Turn 'x <- f()' followed by 'if x?' into the EK9 guard 'if x <- f()' - one call with file and symbol:'x' (the variable), or line on the control line.

When not to use it

Details

No mode parameter - the head operator is DERIVED from the source: '<-' stays '<-', ':=?' stays ':=?' (both gate the body per PreFlowCanSkipBody), a blind ':='/'='/':' stays ':=' with the gate explicit ('if x := f() then x?', 'while x := f() then x? and (cond)') and is refused before switch/try/for/do (kind blind-assignment-cannot-gate). Per-construct fold: switch guards on the MATCH SUBJECT ('v <- getV()'+'switch v' -> 'switch v <- getV()'); try/function has no control so folds bare ('try r <- open()'); loops keep their control/iteration as a 'then' clause ('while x <- src() then cond', 'for x <- src() then item in coll') and the guard is evaluated ONCE at loop entry, not per iteration. A compound 'if x? and cond' (isSet on either side of a two-operand 'and') lifts with the other operand preserved as a 'then' residual; an 'or', a three-way 'and', or nested 'x?' stays a compound-control-not-supported conflict (other kinds: switch-subject-not-the-guard-var, already-a-guard).

Arguments

ArgumentTypeRequiredWhat it is
filestringyesThe EK9 source file (workspace-relative; an absolute path inside the workspace is accepted).
lineintegerIDE refinement: the 1-based line of the control construct's opening line (if/while/do-while/for/switch/try). Omit it and pass symbol.
symbolstringThe variable to lift into the guard ('customer'); the tool finds the control construct that tests it. Prefer this to line.

It also takes the arguments every tool takes.

Recorded calls

Real calls, recorded against small projects and checked on every build - a changed answer fails it. 3 of the 10 recorded are shown.

Lift declaration dryrun

Request

{
  "dryRun" : true,
  "file" : "Guarding.ek9",
  "line" : 16
}

Response

{
  "conflicts" : [ ],
  "edits" : [ {
    "after" : "      if name <- lookup(id)",
    "before" : "      if name?",
    "endLine" : 16,
    "file" : "Guarding.ek9",
    "startLine" : 16,
    "type" : "replace"
  }, {
    "before" : "      name <- lookup(id)",
    "endLine" : 15,
    "file" : "Guarding.ek9",
    "startLine" : 15,
    "type" : "delete"
  } ],
  "meta" : {
    "serverMs" : "<timing>"
  },
  "risk" : {
    "crossesModuleBoundary" : false,
    "estimatedSeverity" : "trivial",
    "fileCount" : 1,
    "lineCount" : 2,
...

Cut short here: 9 more lines. The whole recorded call.

Try resource guard

Request

{
  "dryRun" : true,
  "file" : "Guarding.ek9",
  "line" : 16
}

Response

{
  "conflicts" : [ ],
  "edits" : [ {
    "after" : "      try config <- getConfig(id)",
    "before" : "      try",
    "endLine" : 16,
    "file" : "Guarding.ek9",
    "startLine" : 16,
    "type" : "replace"
  }, {
    "before" : "      config <- getConfig(id)",
    "endLine" : 15,
    "file" : "Guarding.ek9",
    "startLine" : 15,
    "type" : "delete"
  } ],
  "meta" : {
    "serverMs" : "<timing>"
  },
  "risk" : {
    "crossesModuleBoundary" : false,
    "estimatedSeverity" : "trivial",
    "fileCount" : 1,
    "lineCount" : 2,
...

Cut short here: 9 more lines. The whole recorded call.

Do while guard

Request

{
  "dryRun" : true,
  "file" : "Guarding.ek9",
  "line" : 17
}

Response

{
  "conflicts" : [ ],
  "edits" : [ {
    "after" : "      do step <- initial(id)",
    "before" : "      do",
    "endLine" : 17,
    "file" : "Guarding.ek9",
    "startLine" : 17,
    "type" : "replace"
  }, {
    "before" : "      step <- initial(id)",
    "endLine" : 16,
    "file" : "Guarding.ek9",
    "startLine" : 16,
    "type" : "delete"
  } ],
  "meta" : {
    "serverMs" : "<timing>"
  },
  "risk" : {
    "crossesModuleBoundary" : false,
    "estimatedSeverity" : "trivial",
    "fileCount" : 1,
    "lineCount" : 2,
...

Cut short here: 9 more lines. The whole recorded call.

See also