Skip to content

Remove nil from compact element types - #348

Closed
apiology wants to merge 3 commits into
masterfrom
compact-removes-nil-element-type
Closed

apiology wants to merge 3 commits into
masterfrom
compact-removes-nil-element-type

Conversation

@apiology

Copy link
Copy Markdown
Owner

Claude: This PR was written by Claude Code on behalf of @apiology.

Problem:

Calling compact doesn't remove nil from element types:

# @param a [Array<String, nil>]
# @param h [Hash{Symbol => String, nil}]
def foo a, h
  # Unresolved call to upcase on String, NilClass
  a.compact.each { |x| x.upcase }
  # Unresolved call to upcase on String, NilClass
  h.compact.each_value { |v| v.upcase }
end

Every element call after compact reports a false possible nil.

Solution:

Drop nil from the element types of resolved Array#compact, Enumerable#compact and Hash#compact (values only) pins, because their RBS signatures (() -> ::Array[Elem]) have no block or method type variable for generic binding to narrow, so #202 leaves this unchanged.

Not covered: compact! (still Array<String, NilClass>, nil), and x = x.compact reassignments, which keep the pre-compact type through a separate defect.

Markers cleared: 1

Core RBS declares Array#compact and Enumerable#compact as
() -> ::Array[Elem] and Hash#compact as () -> ::Hash[K, V], so the
result keeps nil from its receiver and every element call reports
"Unresolved call to upcase on String, NilClass". There is no block or
method type variable for generic binding to use, and RBS cannot write
Elem minus nil.

Drop nil and NilClass from the element types (Hash: value types) of
those three resolved pins in Chain::Call#inferred_pins. compact!,
compact methods on other classes and all-nil element types are left
unchanged.

Removes the now-unneeded ignore marker in Pin::Search#do_query.
Replace the hard-coded Array#compact, Enumerable#compact and
Hash#compact paths in Chain::Call with %a{solargraph:removes-nil}.
Conversions records the annotation (on the method, or on every
overload) as Pin::Method#removes_nil?, so a user's own RBS methods can
use it too.

rbs/annotations reopens the core classes with 'def compact: ...'.
RbsMap::Core loads it with the fills, keeps its pins out of the core
pins, and copies their annotations onto the core methods, which keep
their core signatures.
rbs/annotations holds every annotation file RbsMap::Core reads; one
file per annotation keeps them apart as more are added.
apiology added a commit that referenced this pull request Oct 25, 2026
Solargraph-read annotation fills share one directory: move the %a{pure}
markers to rbs/annotations/pure.rbs and load the whole directory in the
fills loader, splitting its pins out by filename and copying their
annotations onto core methods. #348 loads the same
directory the same way, so the two merge into one loader instead of a
second EnvironmentLoader, whose core_root marker goes with it.
@apiology apiology closed this Oct 27, 2026
@apiology

Copy link
Copy Markdown
Owner Author

Claude: Opened upstream as castwide#1458.

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.

1 participant