Class: PinkSpoon::ConstantResolver

Inherits:
Object
  • Object
show all
Defined in:
lib/pink_spoon/constant_resolver.rb

Overview

Given a file + cursor position, figures out:

1. What constant the receiver resolves to (e.g. EnabledFeatureGauge  Prometheus::Client::Gauge)
2. What method name is under the cursor

Returns { type: “Prometheus::Client::Gauge”, method: “init_label_set” } or nil.

Resolution strategy (in order):

a. Walk up the AST to find the call node under the cursor.
b. If the receiver is a constant, look it up as an assignment in the same file.
c. If the receiver is a local variable, resolve its type from assignments or block params.
d. Follow the RHS chain through the RBI index (register_gauge → Gauge, .freeze → passthrough).
e. For Foo.new(...), the type is Foo directly (Sorbet rarely types .new explicitly).

Defined Under Namespace

Classes: AssignmentFinder, BlockParamFinder, EnclosingDefFinder, IvarTypeFinder, LocalAssignmentFinder

Constant Summary collapse

PASSTHROUGH_METHODS =
%w[freeze dup clone tap then yield_self].freeze
RSPEC_EXAMPLE_GROUP_METHODS =
%w[
  it example specify
  context describe
  let let!
  subject subject!
  before after around
  pending skip
  include_context include_examples include_shared_examples_for
  shared_examples shared_context shared_examples_for
  aggregate_failures
].freeze
RSPEC_MODULE_METHODS =
%w[describe shared_examples shared_context shared_examples_for].freeze
ENUMERATION_METHODS =

Methods that yield each element of their receiver collection.

%w[
  each map select reject flat_map filter_map find detect
  each_with_index each_with_object collect sum min_by max_by
  sort_by group_by
].freeze

Instance Method Summary collapse

Constructor Details

#initialize(root_path, rbi_index) ⇒ ConstantResolver

Returns a new instance of ConstantResolver.



42
43
44
45
# File 'lib/pink_spoon/constant_resolver.rb', line 42

def initialize(root_path, rbi_index)
  @root_path = root_path
  @rbi_index = rbi_index
end

Instance Method Details

#resolve_at(file, line, col) ⇒ Object

Standalone server entry point. Returns { type:, method: } or nil.



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/pink_spoon/constant_resolver.rb', line 81

def resolve_at(file, line, col)
  return nil unless file && File.exist?(file)

  source = File.read(file)
  result = Prism.parse(source)

  call = innermost_call_at(result.value, line, col)
  return nil unless call

  method_name = call.name.to_s
  receiver    = call.receiver

  unless receiver
    type = infer_rspec_receiver(method_name)
    return type ? { type: type, method: method_name } : nil
  end

  type = resolve_receiver(receiver, result.value, source)
  return nil unless type

  { type: type, method: method_name }
end

#resolve_from_call(call_node, program_node, nesting = nil) ⇒ Object

Entry point for the ruby-lsp addon. Returns { type:, method: } or nil.



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/pink_spoon/constant_resolver.rb', line 49

def resolve_from_call(call_node, program_node, nesting = nil)
  return nil unless call_node

  method_name = call_node.name.to_s
  receiver    = call_node.receiver

  unless receiver
    type = infer_rspec_receiver(method_name)
    return type ? { type: type, method: method_name } : nil
  end

  @_call_line = call_node.location.start_line
  @_nesting   = nesting

  type = resolve_receiver(receiver, program_node, nil)
  return nil unless type

  { type: type, method: method_name }
ensure
  @_call_line = nil
  @_nesting   = nil
end

#resolve_receiver_type(receiver_node, program_node, nesting = nil) ⇒ Object

Public entry point for completion: returns the type string of a receiver node.



73
74
75
76
77
78
# File 'lib/pink_spoon/constant_resolver.rb', line 73

def resolve_receiver_type(receiver_node, program_node, nesting = nil)
  @_nesting = nesting
  resolve_receiver(receiver_node, program_node, nil)
ensure
  @_nesting = nil
end