From ff795f71e5ae25ec9e0b0bfdea08abd74c4e5578 Mon Sep 17 00:00:00 2001 From: Henrique Costa Date: Sat, 2 May 2026 14:53:03 +0200 Subject: [PATCH] fix: emit merge flow for explicitly empty ELSE bodies Symptom: an IF statement with a continuing THEN branch and an explicit but empty ELSE branch could build a split/merge graph without a configured false outgoing flow, causing Studio Pro CE0079. Root cause: the IF builder only connected non-returning ELSE branches to the merge when the ELSE body produced a last activity ID. An explicitly empty ELSE has HasElse=true but no lastElseID, so the false case was dropped. Fix: when an explicit empty ELSE still requires a merge, emit a direct split-to-merge flow with case false, mirroring the existing empty-THEN handling. Tests: go test ./mdl/executor -run TestIfEmptyElseBodyWithContinuingThenEmitsFalseFlowToMerge -v; make build; make test; make lint-go. --- .../cmd_microflows_builder_control.go | 6 ++ ...icroflows_builder_empty_else_merge_test.go | 63 +++++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 mdl/executor/cmd_microflows_builder_empty_else_merge_test.go diff --git a/mdl/executor/cmd_microflows_builder_control.go b/mdl/executor/cmd_microflows_builder_control.go index 11b3fd5a..99f825cc 100644 --- a/mdl/executor/cmd_microflows_builder_control.go +++ b/mdl/executor/cmd_microflows_builder_control.go @@ -272,6 +272,12 @@ func (fb *flowBuilder) addIfStatement(s *ast.IfStmt) model.ID { } applyUserAnchors(flow, originAnchor, destAnchor) fb.flows = append(fb.flows, flow) + } else { + // Explicit empty ELSE body: the split still needs a configured + // false case when both branches converge at the merge. + flow := newDownwardFlowWithCase(splitID, mergeID, "false") + applyUserAnchors(flow, falseBranchAnchor, falseBranchAnchor) + fb.flows = append(fb.flows, flow) } } else if elseReturns && needMerge { fb.addPendingErrorHandlerFlowTo(mergeID) diff --git a/mdl/executor/cmd_microflows_builder_empty_else_merge_test.go b/mdl/executor/cmd_microflows_builder_empty_else_merge_test.go new file mode 100644 index 00000000..3fc405c7 --- /dev/null +++ b/mdl/executor/cmd_microflows_builder_empty_else_merge_test.go @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: Apache-2.0 + +package executor + +import ( + "testing" + + "github.com/mendixlabs/mxcli/mdl/ast" + "github.com/mendixlabs/mxcli/sdk/microflows" +) + +// TestIfEmptyElseBodyWithContinuingThenEmitsFalseFlowToMerge is a regression +// guard for CE0079 ("The 'false' condition value should be configured in +// properties for an outgoing flow"). +// +// Pattern: an IF with THEN that continues and an explicitly empty ELSE body. +// Both branches must feed a merge, but an empty ELSE has no lastElseID to wire. +func TestIfEmptyElseBodyWithContinuingThenEmitsFalseFlowToMerge(t *testing.T) { + fb := &flowBuilder{ + spacing: HorizontalSpacing, + measurer: &layoutMeasurer{}, + } + + fb.addIfStatement(&ast.IfStmt{ + Condition: &ast.VariableExpr{Name: "Flag"}, + ThenBody: []ast.MicroflowStatement{ + &ast.LogStmt{Level: ast.LogInfo, Message: &ast.LiteralExpr{Kind: ast.LiteralString, Value: "in-then"}}, + }, + // The source had an explicit ELSE token, but no statements in that + // branch. That must still build a false split->merge flow. + HasElse: true, + }) + + var split *microflows.ExclusiveSplit + var merge *microflows.ExclusiveMerge + for _, obj := range fb.objects { + switch o := obj.(type) { + case *microflows.ExclusiveSplit: + split = o + case *microflows.ExclusiveMerge: + merge = o + } + } + if split == nil { + t.Fatal("expected ExclusiveSplit to be created") + } + if merge == nil { + t.Fatal("expected ExclusiveMerge to be created") + } + + var hasFalseFlow bool + for _, flow := range fb.flows { + if flow.OriginID != split.ID || flow.DestinationID != merge.ID { + continue + } + if ec, ok := flow.CaseValue.(microflows.EnumerationCase); ok && ec.Value == "false" { + hasFalseFlow = true + } + } + if !hasFalseFlow { + t.Fatal("missing split->merge flow with case \"false\"") + } +}