Skip to content

fix: match bound method values by package path, not bare type name - #40

Merged
jackielii merged 1 commit into
mainfrom
fix/bound-method-pkg-path
Oct 26, 2026
Merged

jackielii merged 1 commit into
mainfrom
fix/bound-method-pkg-path

Conversation

@jackielii

Copy link
Copy Markdown
Owner

Fixes #39.

Problem

ID, IDTarget and RenderTarget.Is matched a bound method value (p.List) to its page by the receiver's bare type name. The package path was dropped from runtime.FuncForPC. With same-named page types in different packages, such as a Page type per package:

  • with no current page, a bound value errored as ambiguous;
  • when the current page was the other package's type, it silently took that page's id.

The issue names ID/IDTarget. The same name-only match was in two more places:

  • methodRenderTarget.Is: Is(b.List) was true for the other package's Widget.List.
  • htmxUnmatchedTarget.Is: a request targeting the other package's same-named region never matched.

Fix

  • Keep the package path. boundReceiverFromFuncName returns the package path along with the type name. Go writes a . in the last path element as %2e (sub%2ev2, confirmed against a real module), so the path is unescaped before comparing.
  • One check everywhere. methodInfo.receiverIs(t) replaces sameTypeName at all four call sites. Bound values match on package path and type name, ignoring type arguments. Method expressions compare exactly as before.
  • Generic receivers parse correctly. Gen[...].M-fm now parses to Gen[...], not Gen[. This doesn't change any match, because genericBase already cut the name at [; only the stored name and messages are now right.
  • Matching error format. Not-found errors name the type by package path for both forms.
  • Older bug, found in review. isBound was strings.Contains(name, "-fm"), so a standalone function in a package path containing -fm (go-fmt…) failed with "cannot extract receiver type". It's HasSuffix now, with an internal/fn-fmt fixture to cover it.

Not done: the issue's step 3 (accepting pages that embed the type). A promoted bound value names the embedded type that declares the method, and structpages already skips promoted methods as components. So exact matching keeps today's behavior.

Tests

  • id_bound_cross_package_test.go:

    • the issue's repro for ID and IDTarget, with no current page and with each Widget as the current page;
    • methodRenderTarget.Is and htmxUnmatchedTarget.Is across packages;
    • the -fm package path case.

    All of these fail on main.

  • TestBoundReceiverFromFuncName: adds the package path to every case, plus the escaped dot, a dot earlier in the path, generic types and a malformed escape.

  • TestID_BoundMethodAmbiguous: it asserted the bug, a bound idconflicta.Widget value being ambiguous with idconflictb.Widget. It now covers the real ambiguity: one type mounted under two parents.

An independent review checked the FuncForPC name forms on go1.27.1:

  • pointer and generic receivers, including type arguments containing dots and slashes;
  • packages named main (under both go run and go test) and external _test packages;
  • interface method values, promoted methods and local types.

It found no correctness bugs. Interface method values now never match a page; before, they matched any page type with the same name.

go test ./... passes and golangci-lint is clean.

https://claude.ai/code/session_01NpjJeP32ycvAHNFXRgTviG

ID, IDTarget and RenderTarget.Is matched a bound method value (p.List)
to its page by the receiver's bare type name, dropping the package. With
same-named page types in two packages, as with a Page type per package,
a bound value errored as ambiguous with no current page, and silently
took the current page's id when that was the other package's type. The
htmx and method render targets had the same confusion in Is.

boundReceiverFromFuncName now keeps the package path from the function
name, unescaping its last element ("yaml%2ev3"), and methodInfo.receiverIs
matches bound values on package path and type name, so they resolve as
exactly as method expressions. A generic value receiver "Gen[...].M-fm"
now parses to "Gen[...]" rather than "Gen[". Not-found errors name the
type by package path for both forms.

A function counted as a bound method value whenever its name contained
"-fm", so a standalone function in a package path such as "go-fmt..."
failed. Only the "-fm" suffix counts now.

Fixes #39

Claude-Session: https://claude.ai/code/session_01NpjJeP32ycvAHNFXRgTviG
@codecov

codecov Bot commented Oct 26, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.11111% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 95.45%. Comparing base (c9d3fa7) to head (ece5c87).

Files with missing lines Patch % Lines
reflect.go 87.87% 2 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main      #40      +/-   ##
==========================================
+ Coverage   95.22%   95.45%   +0.22%     
==========================================
  Files          17       18       +1     
  Lines        1761     1759       -2     
==========================================
+ Hits         1677     1679       +2     
+ Misses         50       48       -2     
+ Partials       34       32       -2     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@jackielii
jackielii merged commit eb01e55 into main Oct 26, 2026
16 checks passed
@jackielii
jackielii deleted the fix/bound-method-pkg-path branch October 26, 2026 08:12
joeblew999 added a commit to joeblew999/structpages that referenced this pull request Oct 28, 2026
TinyGo branch: catch up with jackielii/main (jackielii#40), fix what it exposed, skill and CI for TinyGo
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.

ID: bound method value resolves by bare type name, so same-named page types in other packages get the wrong id

1 participant