Skip to content

Commit

Permalink
Introduce Entry::Signature to support overloading (#2221)
Browse files Browse the repository at this point in the history
* Introduce Entry::Signature

* Memoize

* Extract 'format' method

* Add description
  • Loading branch information
andyw8 committed Jun 21, 2024
1 parent 3aa0430 commit f8a7d89
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 16 deletions.
4 changes: 2 additions & 2 deletions lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ def on_def_node_enter(node)
node.location,
node.name_loc,
comments,
list_params(node.parameters),
[Entry::Signature.new(list_params(node.parameters))],
current_visibility,
@owner_stack.last,
))
Expand All @@ -322,7 +322,7 @@ def on_def_node_enter(node)
node.location,
node.name_loc,
comments,
list_params(node.parameters),
[Entry::Signature.new(list_params(node.parameters))],
current_visibility,
singleton,
))
Expand Down
58 changes: 44 additions & 14 deletions lib/ruby_indexer/lib/ruby_indexer/entry.rb
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,11 @@ class Member < Entry
sig { returns(T.nilable(Entry::Namespace)) }
attr_reader :owner

sig { returns(T::Array[RubyIndexer::Entry::Parameter]) }
def parameters
T.must(signatures.first).parameters
end

sig do
params(
name: String,
Expand All @@ -310,32 +315,36 @@ def initialize(name, file_path, location, comments, visibility, owner) # rubocop
@owner = owner
end

sig { abstract.returns(T::Array[Parameter]) }
def parameters; end
sig { abstract.returns(T::Array[Entry::Signature]) }
def signatures; end

# Returns a string with the decorated names of the parameters of this member. E.g.: `(a, b = 1, c: 2)`
sig { returns(String) }
def decorated_parameters
"(#{parameters.map(&:decorated_name).join(", ")})"
"(#{T.must(signatures.first).format})"
end
end

class Accessor < Member
extend T::Sig

sig { override.returns(T::Array[Parameter]) }
def parameters
params = []
params << RequiredParameter.new(name: name.delete_suffix("=").to_sym) if name.end_with?("=")
params
sig { override.returns(T::Array[Signature]) }
def signatures
@signatures ||= T.let(
begin
params = []
params << RequiredParameter.new(name: name.delete_suffix("=").to_sym) if name.end_with?("=")
[Entry::Signature.new(params)]
end,
T.nilable(T::Array[Signature]),
)
end
end

class Method < Member
extend T::Sig

sig { override.returns(T::Array[Parameter]) }
attr_reader :parameters
sig { override.returns(T::Array[Signature]) }
attr_reader :signatures

# Returns the location of the method name, excluding parameters or the body
sig { returns(Location) }
Expand All @@ -348,14 +357,14 @@ class Method < Member
location: T.any(Prism::Location, RubyIndexer::Location),
name_location: T.any(Prism::Location, Location),
comments: T::Array[String],
parameters: T::Array[Parameter],
signatures: T::Array[Signature],
visibility: Visibility,
owner: T.nilable(Entry::Namespace),
).void
end
def initialize(name, file_path, location, name_location, comments, parameters, visibility, owner) # rubocop:disable Metrics/ParameterLists
def initialize(name, file_path, location, name_location, comments, signatures, visibility, owner) # rubocop:disable Metrics/ParameterLists
super(name, file_path, location, comments, visibility, owner)
@parameters = parameters
@signatures = signatures
@name_location = T.let(
if name_location.is_a?(Prism::Location)
Location.new(
Expand Down Expand Up @@ -514,5 +523,26 @@ def decorated_parameters
@target.decorated_parameters
end
end

# Ruby doesn't support method overloading, so a method will have only one signature.
# However RBS can represent the concept of method overloading, with different return types based on the arguments
# passed, so we need to store all the signatures.
class Signature
extend T::Sig

sig { returns(T::Array[Parameter]) }
attr_reader :parameters

sig { params(parameters: T::Array[Parameter]).void }
def initialize(parameters)
@parameters = parameters
end

# Returns a string with the decorated names of the parameters of this member. E.g.: `(a, b = 1, c: 2)`
sig { returns(String) }
def format
@parameters.map(&:decorated_name).join(", ")
end
end
end
end

0 comments on commit f8a7d89

Please sign in to comment.