Skip to content

fix(pipeline): a bare Go reference can bind a struct Field — READS/WRITES/USAGE attach every local err to whichever Field is named err #1942

Closed

Description

@ilyabrykau-orca

fix(pipeline): a bare Go reference can bind a struct Field — READS/WRITES/USAGE attach every local err to whichever Field is named err

Status: fix open — PR #1944 (cbm_go_suppress_bare_field_ref() at the same four resolver sites as #1937's guard; stacked on #1937 → #1940). Measured: USAGE onto Go fields 21308 → 0, WRITES 5191 → 0; the combined stack ends below main's totals (USAGE 29198 → 21035, WRITES 2681 → 774) while adding 4533 Field nodes. Found while field-validating #1940 — the third leg of its ordering constraint.

What happens

The READS/WRITES resolvers (pass_usages.c:resolve_rw_edges, pass_parallel.c:resolve_file_rw) and the USAGE registry fallback (resolve_usage_edges / resolve_file_usages) hand bare reference text to cbm_registry_resolve(). The registry contains Field nodes, so a dot-less local —

err := doWork()

— resolves by simple name to any Field named err, project-wide.

In Go that binding is impossible by construction: a field is only reachable through a selector expression (x.f), and selector references resolve on the LSP path, never through this fallback. Unlike C#/Java/C++/Python, where a method body legitimately references its own members bare (_count++ — cp_reads_writes_cs_static_field pins that as required), so the veto must be Go-gated, not global.

Damage measured (#1940 stack, ~1150-file Go+C repo)

onto Go Field nodes count
USAGE 21308 (top: a test struct's field T 3013, events 977, err 759, Path 242, config 221)
WRITES 5191

100% of Field-targeted USAGE edges carried dot-less reference text — no legitimate selector-shaped population is mixed in. (The 612/468 refs onto C/C++ fields surviving #1937 are the legitimate own-member-in-method shape plus C-side noise; untouched by this fix.) The remaining field-targeted class is 2466 CALLS — #1906/#1907's selector-guard territory.

Minimal reproduction (requires #1935's extraction)

// state/state.go
package state

type Tracker struct {
    err error
}
// app/app.go
package app

import "errors"

func Run() error {
    err := errors.New("x")
    return err
}

Expected: no edge from Run into Tracker.err. Actual (with #1940 alone): WRITES Run → state.Tracker.err.

Detection recipe

SELECT e.type, count(*) FROM edges e
JOIN nodes t ON t.id = e.target_id
WHERE t.label = 'Field' AND t.file_path LIKE '%.go'
  AND e.type IN ('READS','WRITES','USAGE')
GROUP BY 1;

Any nonzero row is this bug.

Fix (as landed)

Pure predicate next to #1937's: drop the bind when the file is Go, the target label is Field, and the reference has no .. Wired at both sequential resolvers and both parallel twins; reproduce-first probes on both paths (RED on the #1940 stack), plus unit tests (selector-shaped refs may bind; non-Field targets and non-Go languages untouched).

Related: #1927 (same "nothing checks what a name can denote" class on CALLS), #1928/#1937, #1935/#1940; tracked in #1932.

Activity

  1. added a commit that references this issue on Aug 30, 2026
    37c0ef4
  2. added 3 commits that reference this issue on Aug 30, 2026
    c175ca1
    7297139
    cc00a02
  3. DeusData commented on Aug 31, 2026

    @DeusData
    Owner

    Fixed on main by cc00a0275efa37bbcf1a2f27da17b33f9b63add4, merged as 20e86c02b168ed540aa608e222d8af21ed6db2b3 (#1944).

    This completes the three-PR stack, landed in the order the issue's own measurement required:

    PR commit what it does
    #1937 fcd8bfc1 cross-language guard on USAGE/WRITES/READS, so recovered field nodes cannot bind across a language boundary
    #1940 61de19bb Go struct fields actually get extracted — 0 Field nodes existed for ~5055 declarations
    #1944 20e86c02 a bare Go reference never binds a struct Field

    The ordering was load-bearing rather than cosmetic: landing the ~4600 recovered Field nodes without the reference guard underneath would have minted roughly 5,700 WRITES and 22,000 USAGE name-collision edges onto fields named err, ctx and _.

    The suppressor is cbm_go_suppress_bare_field_ref, and it is Go-gated — consistent with the standing decision that call-resolution suppressors stay per-language rather than trading recall globally. cp_reads_writes_cs_static_field pins the C# shape that must keep binding bare member references, and it is untouched.

    Remaining in this family, deliberately not folded in: the 2466 field-targeted CALLS edges, which are #1906/#1907 selector-guard territory. #1932 tracks the family.

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

    parsing/qualityGraph extraction bugs, false positives, missing edges

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions