Synthesize a Struct.new constructor pin for gem-sourced constants - #1328
Open
apiology wants to merge 5 commits into
Open
Synthesize a Struct.new constructor pin for gem-sourced constants#1328apiology wants to merge 5 commits into
apiology wants to merge 5 commits into
Conversation
YARD's Ruby handler for `Foo = Struct.new(:bar, :baz)` (and the inheritance form `class Foo < Struct.new(...)`) registers the struct as a real ClassObject with accessor methods for each member, but never documents an initialize method. Solargraph::YardMap::Mapper then has no constructor pin to combine for the resulting class, so `Foo.new(...)` falls back to resolving against Struct.new's own RBS signature -- the nearest ancestor method it can find -- and reports a wrong-argument-type error for ordinary positional Struct construction. Convention::StructDefinition already covers this for workspace source by building its own initialize pin from the parsed Struct.new node. ToStructInitializer covers the equivalent gem/yardoc case, where there is no such node -- only the member names YARD kept in ClassObject#attributes. keyword_init: true is detected by rereading the source line YARD points at, since YARD itself discards that flag. File.readlines defaults to Encoding.default_external and does not honor a Ruby magic encoding comment the way compiling the file would, so the reread first detects a declared encoding the same way Ruby's own parser does (shebang-aware, first one or two lines, ASCII-safe) and rereads with it -- correctly decoding, for example, a gem source line declared ISO-8859-1. keyword_init?'s rescue is narrowed to ArgumentError, for the case that's still genuinely unrecoverable: a magic comment whose declared encoding doesn't match the file's actual bytes. What was caught is logged via Solargraph.logger.info, matching to_class_definition.rb's sibling rescue. The Fixnum/Integer RBS union-shadowing finding this surfaced (a diff-lcs-shipped Fixnum shim gets YARD-indexed as a near-empty class that shadows Integer in a union type) is recorded in type_checker/rules.rb's existing "pending code fixes" catalog rather than as an inline @sg-ignore, per this repo's own convention for tracking known-but-unfixed typechecker gaps.
"See ToStructInitializer for why a Struct.new definition needs this" adds nothing at the call site a reader would not already get from the method call itself - ToStructInitializer.make already says what it does, and its own module docstring already has the why.
undercover flagged lib/solargraph/yard_map/mapper/to_struct_initializer.rb:104-118 as uncovered against castwide/solargraph:master. The rescue that falls back to UTF-8 when a magic comment names an encoding Ruby doesn't recognize had no test.
Fold the two encoding-fallback specs' "why" out of buried mid-test comments and into their `it` descriptions, per this repo's spec-comment convention. Back the `Encoding.find` @sg-ignore's claim about its nil-return case with a real Ruby docs link instead of asserting it without a citation.
apiology
marked this pull request as ready for review
September 6, 2026 16:10
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Foo = Struct.new(:bar, :baz)(andclass Foo < Struct.new(...)) sourced from a gem, not workspace code -- YARD's Ruby handler registersFooas a realClassObjectwith accessor pins for each member, but never documents aninitialize.Solargraph::YardMap::Mapperhas no constructor pin to combine, soFoo.new(...)falls back to resolving againstStruct.new's own RBS signature -- the nearest ancestor method it can find -- and reports a wrong-argument-type error for ordinary positional construction.Convention::StructDefinitionalready covers this for workspace source, building aninitializepin from the parsedStruct.newnode. This adds the gem/yardoc equivalent (ToStructInitializer), where there's no such node -- only the member names YARD kept inClassObject#attributes.keyword_init: trueis detected by rereading the source line YARD points at (YARD itself discards the flag), first detecting the file's declared magic-comment encoding the way Ruby's own parser does, so a gem source line declared e.g.ISO-8859-1decodes correctly rather than being misread as the default external encoding.Test plan
spec/yard_map/mapper/to_struct_initializer_spec.rb, including theISO-8859-1encoding case and one genuinely-unrecoverable case (declared encoding doesn't match actual bytes)bundle exec rspec(full suite): 1633 examples, 1 pre-existing failure (confirmed unrelated via stash A/B)bundle exec rubocop: no offensesClaude: filed on behalf of the repo owner while auditing
@sg-ignoresuppressions in a downstream project.