Skip to content

feat(temporal): resolve executor struct-field dispatch - #86

Merged
zzet merged 1 commit into
zzet:mainfrom
avfirsov:feat/temporal-executor-dispatch
Jun 14, 2026
Merged

feat(temporal): resolve executor struct-field dispatch#86
zzet merged 1 commit into
zzet:mainfrom
avfirsov:feat/temporal-executor-dispatch

Conversation

@avfirsov

Copy link
Copy Markdown
Contributor

What

Resolves Temporal dispatch where the activity/workflow name is a receiver field whose literal is set at struct construction:

type ActivityExecutor struct{ ActivityName string }
func (e ActivityExecutor) Run(ctx workflow.Context) {
    workflow.ExecuteActivity(ctx, e.ActivityName)        // name = receiver field
}
exec := ActivityExecutor{ActivityName: "ChargeCard"}     // the literal lives here

The dispatch only sees e.ActivityName (not a literal), so it never resolved. This joins the dispatch to the construction-site literal by (receiver type, field).

How

Extractor (golang.go / golang_temporal.go):

  • a fieldstr tree-sitter pattern collects Type{Field: "literal"} struct-field assignments, emitted as via=temporal.executor-field marker edges carrying executor_type / executor_field / executor_value;
  • the dispatch stub is stamped temporal_name_field + temporal_recv_type when the dispatch argument is <receiver>.<field>;
  • helper goCompositeLiteralType; a recvTypeByID map; and the call AST node on the deferred-call struct so the calls loop can inspect the dispatch arg.

Resolver (temporal_calls.go):

  • resolveTemporalExecutorFields joins a dispatch's (recv_type, field) to a construction literal and rewrites the dispatching method's stub name to that literal — so the existing sweep lands it on the registered handler and callers of the activity surface the dispatching method (not the constructor). Wired at the top of ResolveTemporalCalls; self-guards; recompute-safe; unique-or-nothing on conflicting literals (matching the const-deref policy).

Scope

  • Only executor-field resolution. Cross-language / const-deref / env-default / wrapper-following are untouched.

Tests

  • resolver unit (field-meta stub + executor-field marker → resolved edge),
  • extractor unit (temporal_name_field/temporal_recv_type on the stub; executor_* on the marker),
  • end-to-end index→resolve test on real Go source.

go build ./... and go test -race ./internal/resolver/... ./internal/parser/languages/... ./internal/indexer/... pass.

🤖 Generated with Claude Code

@avfirsov
avfirsov force-pushed the feat/temporal-executor-dispatch branch from db1b05e to de36f95 Compare June 13, 2026 09:14
@avfirsov
avfirsov marked this pull request as ready for review June 14, 2026 08:44
@zzet

zzet commented Jun 14, 2026

Copy link
Copy Markdown
Owner

@avfirsov needed a rebase against the main branch

Resolve Temporal dispatch where the activity/workflow name is a receiver
field whose literal is set at struct construction:

    type ActivityExecutor struct{ ActivityName string }
    func (e ActivityExecutor) Run(ctx workflow.Context) {
        workflow.ExecuteActivity(ctx, e.ActivityName)        // name = recv field
    }
    exec := ActivityExecutor{ActivityName: "ChargeCard"}     // literal here

The dispatch sees `e.ActivityName` (not a literal) and the literal lives at
the construction site; this joins the two by (receiver type, field).

Extractor (golang.go / golang_temporal.go):
- a `fieldstr` tree-sitter pattern collects `Type{Field: "literal"}` struct
  field assignments; emitted as `via=temporal.executor-field` marker edges
  carrying executor_type / executor_field / executor_value;
- the dispatch stub is stamped `temporal_name_field` + `temporal_recv_type`
  when the dispatch arg is `<receiver>.<field>`;
- helper `goCompositeLiteralType`; `recvTypeByID` map; `callNode` on the
  deferred-call struct so the calls loop can inspect the dispatch arg.

Resolver (temporal_calls.go):
- `resolveTemporalExecutorFields` joins dispatch (recv_type, field) to a
  construction literal and rewrites the method's stub name to that literal,
  so the existing sweep lands it on the registered handler and callers of
  the activity surface the dispatching method. Wired at the top of
  `ResolveTemporalCalls`; self-guards; recompute-safe; unique-or-nothing on
  conflicting construction literals.

Cross-language / const-deref / env-default / wrapper-following untouched.

Tests: resolver unit, extractor unit (field meta + marker), e2e.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@avfirsov
avfirsov force-pushed the feat/temporal-executor-dispatch branch from de36f95 to 5c524a2 Compare June 14, 2026 20:06
@avfirsov

Copy link
Copy Markdown
Contributor Author

@zzet rebased onto current main. Conflicts resolved against the merged #85 (wrapper-dispatch): kept the shared callNode field once and added goExecutorField; both pre-passes now run in ResolveTemporalCalls (resolveTemporalWrapperCalls + resolveTemporalExecutorFields); test files keep both suites. go build ./... + temporal tests (resolver / parser / indexer) green locally. mergeable: MERGEABLE.

@zzet
zzet merged commit f731ba7 into zzet:main Jun 14, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants