Skip to content

IF/ELSIF/ELSE silently drops every ELSIF arm on write (both engines) #745

Description

@mlehane1

Symptom

A microflow authored with IF … THEN … ELSIF … THEN … ELSE … END IF loses every ELSIF arm when written to the .mpr. Only the first IF condition/body and the trailing ELSE body reach the model. No error, no warning — mxcli check passes, exec reports success, and mx check on the resulting app is clean, so the data loss is completely silent.

Reproduced on v0.13.0 and v0.14.0, on both engines (modelsdk and legacy), Windows, against a fresh mxcli new 11.11.0 app.

Repro

CREATE MICROFLOW MyFirstModule.TestElsif ($In: Integer)
RETURNS String AS $Out
BEGIN
  DECLARE $Out String = 'none';
  IF $In = 1 THEN
    SET $Out = 'one';
  ELSIF $In = 2 THEN
    SET $Out = 'two';
  ELSE
    SET $Out = 'other';
  END IF;
  RETURN $Out;
END;
mxcli exec probe-elsif.mdl -p App.mpr        # "Created microflow" — no error
mxcli -p App.mpr -c "DESCRIBE MICROFLOW MyFirstModule.TestElsif"

Round-trip output — the $In = 2 → 'two' arm is gone:

if $In = 1 then
  set $Out = 'one';
else
  set $Out = 'other';
end if;

The drop is on write, not a DESCRIBE artifact: DESCRIBE renders what is stored in the .mpr, and with only the visitor (write-side AST build) fixed — the read path untouched — the same DESCRIBE shows every arm present. Before the fix the stored model itself lacked the arm.

Root cause

buildIfStatement in mdl/visitor/visitor_microflow_statements.go reads only exprs[0] / bodies[0] and treats the last body as ELSE. The middle (ELSIF expression THEN microflowBody)* pairs from the grammar rule are never visited:

exprs := ifCtx.AllExpression()
if len(exprs) > 0 { stmt.Condition = buildSourceExpression(exprs[0]) }   // only [0]
bodies := ifCtx.AllMicroflowBody()
if len(bodies) > 0 { stmt.ThenBody = buildMicroflowBody(bodies[0]) }     // only [0]
if len(bodies) > len(exprs) { stmt.ElseBody = ...bodies[len(bodies)-1] } // last = ELSE

Proposed fix

Mendix has no native elsif construct, so lower each ELSIF arm into a nested IfStmt in the ELSE branch of the arm before it (built innermost-first) — if A then X elsif B then Y else Z becomes if A then X else { if B then Y else Z }, which is the canonical nested-exclusive-split shape and what Studio Pro users build by hand.

Fix + visitor unit tests (elsif chain, elsif-without-else, plain if/else regression guard) + mdl-examples/bug-tests/elsif-arms-dropped.mdl + fix-issue.md symptom row are ready on a branch; I can open the PR immediately on sign-off.

(Found while re-validating mxcli 0.14.0 for our internal Mendix build tooling — we've been carrying a "never emit ELSIF, always nest IF/ELSE" workaround since 0.12.0.)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions