Skip to content

Flow-sensitive typing doesn't update a variable's type after reassignment to a different (non-literal) type #1250

Description

@apiology

Summary

When a local variable or parameter is reassigned to the result of a call that narrows or changes its type (e.g. a Position, Array(Integer, Integer) union normalized down to just Position), the type checker keeps using the original declared/inferred type for later uses of that variable instead of the type of the value it was just reassigned to.

This is distinct from #1196/#1223 (which covers literal-value tracking through reassignment for array/tuple indexing) — this is about the checker not picking up a plain reassignment's new static type at all, for non-literal types.

Repro

# typecheck --level strong
class Position
  # @return [Integer]
  def line; end
end

module PositionNormalizer
  # @param position [Position, Array(Integer, Integer)]
  # @return [Position]
  def self.normalize(position); end
end

# @param position [Position, Array(Integer, Integer)]
def describe(position)
  position = PositionNormalizer.normalize(position)
  # `position` is still typed as `Position, Array(Integer, Integer)`
  # here, even though it was just reassigned to the return value of
  # `normalize`, which is declared `@return [Position]`. `Array` has
  # no `#line` method, so this is flagged as unresolved.
  position.line
end

Context

Found while auditing @sg-ignore accumulation in #1240 (strong-level typecheck cleanup) — 20 occurrences across source.rb, api_map.rb, range.rb, shell.rb, type_checker.rb, pin/parameter.rb, and others. Representative real example (lib/solargraph/range.rb):

# @param position [Position, Array(Integer, Integer)]
# @return [Boolean]
def contain? position
  position = Position.normalize(position)
  # @sg-ignore flow sensitive typing should be able to handle redefinition
  return false if position.line < start.line || position.line > ending.line
  ...

and (lib/solargraph/api_map.rb):

# @sg-ignore flow-sensitive typing should be able to handle redefinition
return false if sup.literal? && sub.literal? && sup.to_s != sub.to_s
sup = sup.simplify_literals.to_s
# @sg-ignore flow sensitive typing should be able to handle redefinition
sub = sub.simplify_literals.to_s
return true if sup == sub

Here sup/sub are reassigned from ComplexType/UniqueType to String via .to_s, but the checker keeps treating them as their pre-reassignment type afterward.

Activity

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

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions