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

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 Runstate.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.

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