Skip to content

Expose identifier provenance for variable and subroutine declarations and uses #324

Description

@e54-bot

Problem

Consumers cannot locate the identifier of a variable or subroutine declaration or use through the public Program API. workshop-rs already records these spans but keeps them crate-private.

Recorded internally (all pub(crate)):

  • declaration identifier spans: name_span on global variables, player variables, and subroutines (wir/rule.rs, carried into ProgramProvenance/DeclarationProvenance in program.rs);
  • write-target spans: target_span on the set/modify-variable action forms (wir/action.rs);
  • call-target spans: callee_span on subroutine calls (wir/action.rs);
  • value spans: span on WIR value nodes (wir/value.rs).

Public (1.0): rule_span, condition_span, action_span, and action_argument_span. declaration_provenance is private, and the declaration span setters have no getters.

Consumer impact

Wright has to recover identifier positions by searching the source text for the name inside the enclosing action or argument span (crates/wright-analyzer/src/canonical/symbols.rs, occurrence_in_sources / find_occurrence). Its current output shows the recovery failing. On tests/fixtures/workshop/real-world/overpy-cake.ws in wrightkit/wright, global variable cakePos has one write and 16 reads. Every reported reference span covers the enclosing function name (Set Global Variable for the write, Add for all 16 reads) instead of cakePos, and the declaration on line 3 gets no span at all (symbols reports span: null for all three globals).

This blocks two Wright capabilities:

Source provenance is Workshop-owned (AGENTS.md: "Workshop-owned source/provenance contracts needed by consumers"). A consumer that re-derives identifier positions by text search duplicates parser knowledge above the layer that owns it.

Scope

Additive public accessors on Program, semver-compatible for 1.x:

  • the identifier span of each global variable, player variable, and subroutine declaration;
  • for each variable write (set and modify forms), the span of the target variable identifier;
  • for each subroutine call, the span of the callee identifier;
  • for each variable read, the span of the variable identifier, reachable from the rule/action/value position that consumers already address.

Exact method names and addressing are this repository's design choice. Accessors return Option<Span> and give None when provenance was not recorded, following the existing span accessors.

Non-goals

  • New spans the parser does not already have. Where a listed span is not recorded today, say so on the issue instead of widening the parser in this change.
  • Rename or any other edit semantics. Those stay with consumers, built on edit_source.
  • OPY or DEL provenance. Source-language providers own their own mapping.
  • Changing existing accessors or the WIR structure.

Acceptance criteria

  • For a raw Workshop program that declares a global variable, a player variable, and a subroutine, and writes, reads, and calls each of them, every listed accessor returns a span whose source text is exactly the identifier. Tests assert the sliced text.
  • A variable read nested inside another value, such as Add(First Of(Global.cakePos), …), returns the cakePos identifier span, not the enclosing value's span. Covered by a test.
  • A program built without source provenance returns None from every new accessor. Covered by a test.
  • The public API gate classifies the change as additive, with no breaking marker.
  • docs/ describes the new provenance surface where the existing span accessors are documented.

Dependencies / ownership

  • Owner: workshop-rs.
  • Consumer: wrightkit/wright. It will integrate these accessors separately and remove its text-search recovery.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions