Skip to content

Commit 9aa2248

Browse files
committed
fix: unwrap empty source expressions during association resolution
Symptom: resolveAssociationPaths could return a SourceExpr wrapper with an empty Source field after resolving the inner expression. Root cause: the SourceExpr branch rebuilt the wrapper even when there was no source text to preserve. That made the wrapper semantically redundant and left future callers dependent on expressionToString peeling it away. Fix: keep non-empty SourceExpr values verbatim for whitespace-preserving roundtrips, but unwrap empty SourceExpr values to the resolved inner expression. Tests: make build; focused SourceExpr association-resolution tests; make test.
1 parent 38d28b9 commit 9aa2248

2 files changed

Lines changed: 39 additions & 4 deletions

File tree

mdl/executor/bugfix_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,40 @@ func TestResolveAssociationPaths(t *testing.T) {
515515
}
516516
}
517517

518+
func TestResolveAssociationPathsUnwrapsEmptySourceExpr(t *testing.T) {
519+
fb := &flowBuilder{}
520+
resolved := fb.resolveAssociationPaths(&ast.SourceExpr{
521+
Expression: &ast.VariableExpr{Name: "CurrentObject"},
522+
})
523+
524+
if _, ok := resolved.(*ast.SourceExpr); ok {
525+
t.Fatalf("empty SourceExpr should unwrap to resolved inner expression, got %T", resolved)
526+
}
527+
if got := expressionToString(resolved); got != "$CurrentObject" {
528+
t.Fatalf("resolved expression = %q, want $CurrentObject", got)
529+
}
530+
}
531+
532+
func TestResolveAssociationPathsKeepsNonEmptySourceExprVerbatim(t *testing.T) {
533+
source := "$CurrentObject/Module.Assoc/Name\n"
534+
fb := &flowBuilder{}
535+
resolved := fb.resolveAssociationPaths(&ast.SourceExpr{
536+
Expression: &ast.AttributePathExpr{
537+
Variable: "CurrentObject",
538+
Path: []string{"Module.Assoc", "Name"},
539+
},
540+
Source: source,
541+
})
542+
543+
sourceExpr, ok := resolved.(*ast.SourceExpr)
544+
if !ok {
545+
t.Fatalf("non-empty SourceExpr should remain SourceExpr, got %T", resolved)
546+
}
547+
if sourceExpr.Source != source {
548+
t.Fatalf("source = %q, want %q", sourceExpr.Source, source)
549+
}
550+
}
551+
518552
// TestExprToStringNoSpaces verifies that association navigation expressions
519553
// produce no extra spaces around separators after parsing.
520554
// Issue #120: generated $Order / Module.Assoc / Name instead of $Order/Module.Assoc/Name

mdl/executor/cmd_microflows_builder.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -331,12 +331,13 @@ func (fb *flowBuilder) resolveAssociationPaths(expr ast.Expression) ast.Expressi
331331
}
332332
case *ast.SourceExpr:
333333
if e.Source != "" {
334+
// Non-empty Source is the exact expression text to write back.
335+
// Rebuilding it here would defeat the whitespace-preservation
336+
// purpose of SourceExpr, so keep the parsed tree only for callers
337+
// that need semantic inspection.
334338
return e
335339
}
336-
return &ast.SourceExpr{
337-
Expression: fb.resolveAssociationPaths(e.Expression),
338-
Source: e.Source,
339-
}
340+
return fb.resolveAssociationPaths(e.Expression)
340341
default:
341342
return expr
342343
}

0 commit comments

Comments
 (0)