# typed: true
# frozen_string_literal: true
require_relative "test_case"
module RubyIndexer
class MethodTest < TestCase
def test_method_with_no_parameters
index(<<~RUBY)
class Foo
def bar
end
end
RUBY
assert_entry("bar", Entry::Method, "/fake/path/foo.rb:1-2:2-5")
end
def test_conditional_method
index(<<~RUBY)
class Foo
def bar
end if condition
end
RUBY
assert_entry("bar", Entry::Method, "/fake/path/foo.rb:1-2:2-5")
end
def test_method_with_multibyte_characters
index(<<~RUBY)
class Foo
def こんにちは; end
end
RUBY
assert_entry("こんにちは", Entry::Method, "/fake/path/foo.rb:1-2:1-16")
end
def test_singleton_method_using_self_receiver
index(<<~RUBY)
class Foo
def self.bar
end
end
RUBY
assert_entry("bar", Entry::Method, "/fake/path/foo.rb:1-2:2-5")
entry = @index["bar"]&.first #: as Entry::Method
owner = entry.owner
assert_equal("Foo::<Class:Foo>", owner&.name)
assert_instance_of(Entry::SingletonClass, owner)
end
def test_singleton_method_using_other_receiver_is_not_indexed
index(<<~RUBY)
class Foo
def String.bar
end
end
RUBY
assert_no_entry("bar")
end
def test_method_under_dynamic_class_or_module
index(<<~RUBY)
module Foo
class self::Bar
def bar
end
end
end
module Bar
def bar
end
end
RUBY
assert_equal(2, @index["bar"]&.length)
first_entry = @index["bar"]&.first #: as Entry::Method
assert_equal("Foo::self::Bar", first_entry.owner&.name)
second_entry = @index["bar"]&.last #: as Entry::Method
assert_equal("Bar", second_entry.owner&.name)
end
def test_visibility_tracking
index(<<~RUBY)
class Foo
private def foo
end
def bar; end
protected
def baz; end
end
RUBY
assert_entry("foo", Entry::Method, "/fake/path/foo.rb:1-10:2-5", visibility: :private)
assert_entry("bar", Entry::Method, "/fake/path/foo.rb:4-2:4-14", visibility: :public)
assert_entry("baz", Entry::Method, "/fake/path/foo.rb:8-2:8-14", visibility: :protected)
end
def test_visibility_tracking_with_nested_class_or_modules
index(<<~RUBY)
class Foo
private
def foo; end
class Bar
def bar; end
end
def baz; end
end
RUBY
assert_entry("foo", Entry::Method, "/fake/path/foo.rb:3-2:3-14", visibility: :private)
assert_entry("bar", Entry::Method, "/fake/path/foo.rb:6-4:6-16", visibility: :public)
assert_entry("baz", Entry::Method, "/fake/path/foo.rb:9-2:9-14", visibility: :private)
end
def test_visibility_tracking_with_module_function
index(<<~RUBY)
module Test
def foo; end
def bar; end
module_function :foo, "bar"
end
RUBY
["foo", "bar"].each do |keyword|
entries = @index[keyword] #: as Array[Entry::Method]
# should receive two entries because module_function creates a singleton method
# for the Test module and a private method for classes include the Test module
assert_equal(entries.size, 2)
first_entry, second_entry = *entries
# The first entry points to the location of the module_function call
assert_equal("Test", first_entry&.owner&.name)
assert_instance_of(Entry::Module, first_entry&.owner)
assert_predicate(first_entry, :private?)
# The second entry points to the public singleton method
assert_equal("Test::<Class:Test>", second_entry&.owner&.name)
assert_instance_of(Entry::SingletonClass, second_entry&.owner)
assert_equal(:public, second_entry&.visibility)
end
end
def test_private_class_method_visibility_tracking_string_symbol_arguments
index(<<~RUBY)
class Test
def self.foo
end
def self.bar
end
private_class_method("foo", :bar)
def self.baz
end
end
RUBY
["foo", "bar"].each do |keyword|
entries = @index[keyword] #: as Array[Entry::Method]
assert_equal(1, entries.size)
entry = entries.first
assert_predicate(entry, :private?)
end
entries = @index["baz"] #: as Array[Entry::Method]
assert_equal(1, entries.size)
entry = entries.first
assert_predicate(entry, :public?)
end
def test_private_class_method_visibility_tracking_array_argument
index(<<~RUBY)
class Test
def self.foo
end
def self.bar
end
private_class_method(["foo", :bar])
def self.baz
end
end
RUBY
["foo", "bar"].each do |keyword|
entries = @index[keyword] #: as Array[Entry::Method]
assert_equal(1, entries.size)
entry = entries.first
assert_predicate(entry, :private?)
end
entries = @index["baz"] #: as Array[Entry::Method]
assert_equal(1, entries.size)
entry = entries.first
assert_predicate(entry, :public?)
end
def test_private_class_method_visibility_tracking_method_argument
index(<<~RUBY)
class Test
private_class_method def self.foo
end
def self.bar
end
end
RUBY
entries = @index["foo"] #: as Array[Entry::Method]
assert_equal(1, entries.size)
entry = entries.first
assert_predicate(entry, :private?)
entries = @index["bar"] #: as Array[Entry::Method]
assert_equal(1, entries.size)
entry = entries.first
assert_predicate(entry, :public?)
end
def test_comments_documentation
index(<<~RUBY)
# Documentation for Foo
class Foo
# ####################
# Documentation for bar
# ####################
#
def bar
end
# test
# Documentation for baz
def baz; end
def ban; end
end
RUBY
foo = @index["Foo"]&.first #: as !nil
assert_equal("Documentation for Foo", foo.comments)
bar = @index["bar"]&.first #: as !nil
assert_equal("####################\nDocumentation for bar\n####################\n", bar.comments)
baz = @index["baz"]&.first #: as !nil
assert_equal("Documentation for baz", baz.comments)
ban = @index["ban"]&.first #: as !nil
assert_empty(ban.comments)
end
def test_method_with_parameters
index(<<~RUBY)
class Foo
def bar(a)
end
end
RUBY
assert_entry("bar", Entry::Method, "/fake/path/foo.rb:1-2:2-5")
entry = @index["bar"]&.first #: as Entry::Method
parameters = entry.signatures.first&.parameters #: as Array[Entry::Parameter]
assert_equal(1, parameters.length)
parameter = parameters.first
assert_equal(:a, parameter&.name)
assert_instance_of(Entry::RequiredParameter, parameter)
end
def test_method_with_destructed_parameters
index(<<~RUBY)
class Foo
def bar((a, (b, )))
end
end
RUBY
assert_entry("bar", Entry::Method, "/fake/path/foo.rb:1-2:2-5")
entry = @index["bar"]&.first #: as Entry::Method
parameters = entry.signatures.first&.parameters #: as Array[Entry::Parameter]
assert_equal(1, parameters.length)
parameter = parameters.first
assert_equal(:"(a, (b, ))", parameter&.name)
assert_instance_of(Entry::RequiredParameter, parameter)
end
def test_method_with_optional_parameters
index(<<~RUBY)
class Foo
def bar(a = 123)
end
end
RUBY
assert_entry("bar", Entry::Method, "/fake/path/foo.rb:1-2:2-5")
entry = @index["bar"]&.first #: as Entry::Method
parameters = entry.signatures.first&.parameters #: as Array[Entry::Parameter]
assert_equal(1, parameters.length)
parameter = parameters.first
assert_equal(:a, parameter&.name)
assert_instance_of(Entry::OptionalParameter, parameter)
end
def test_method_with_keyword_parameters
index(<<~RUBY)
class Foo
def bar(a:, b: 123)
end
end
RUBY
assert_entry("bar", Entry::Method, "/fake/path/foo.rb:1-2:2-5")
entry = @index["bar"]&.first #: as Entry::Method
parameters = entry.signatures.first&.parameters #: as Array[Entry::Parameter]
assert_equal(2, parameters.length)
a, b = parameters
assert_equal(:a, a&.name)
assert_instance_of(Entry::KeywordParameter, a)
assert_equal(:b, b&.name)
assert_instance_of(Entry::OptionalKeywordParameter, b)
end
def test_method_with_rest_and_keyword_rest_parameters
index(<<~RUBY)
class Foo
def bar(*a, **b)
end
end
RUBY
assert_entry("bar", Entry::Method, "/fake/path/foo.rb:1-2:2-5")
entry = @index["bar"]&.first #: as Entry::Method
parameters = entry.signatures.first&.parameters #: as Array[Entry::Parameter]
assert_equal(2, parameters.length)
a, b = parameters
assert_equal(:a, a&.name)
assert_instance_of(Entry::RestParameter, a)
assert_equal(:b, b&.name)
assert_instance_of(Entry::KeywordRestParameter, b)
end
def test_method_with_post_parameters
index(<<~RUBY)
class Foo
def bar(*a, b)
end
def baz(**a, b)
end
def qux(*a, (b, c))
end
RUBY
assert_entry("bar", Entry::Method, "/fake/path/foo.rb:1-2:2-5")
entry = @index["bar"]&.first #: as Entry::Method
parameters = entry.signatures.first&.parameters #: as Array[Entry::Parameter]
assert_equal(2, parameters.length)
a, b = parameters
assert_equal(:a, a&.name)
assert_instance_of(Entry::RestParameter, a)
assert_equal(:b, b&.name)
assert_instance_of(Entry::RequiredParameter, b)
entry = @index["baz"]&.first #: as Entry::Method
parameters = entry.signatures.first&.parameters #: as Array[Entry::Parameter]
assert_equal(2, parameters.length)
a, b = parameters
assert_equal(:a, a&.name)
assert_instance_of(Entry::KeywordRestParameter, a)
assert_equal(:b, b&.name)
assert_instance_of(Entry::RequiredParameter, b)
entry = @index["qux"]&.first #: as Entry::Method
parameters = entry.signatures.first&.parameters #: as Array[Entry::Parameter]
assert_equal(2, parameters.length)
_a, second = parameters
assert_equal(:"(b, c)", second&.name)
assert_instance_of(Entry::RequiredParameter, second)
end
def test_method_with_destructured_rest_parameters
index(<<~RUBY)
class Foo
def bar((a, *b))
end
end
RUBY
assert_entry("bar", Entry::Method, "/fake/path/foo.rb:1-2:2-5")
entry = @index["bar"]&.first #: as Entry::Method
parameters = entry.signatures.first&.parameters #: as Array[Entry::Parameter]
assert_equal(1, parameters.length)
param = parameters.first #: as Entry::Parameter
assert_equal(:"(a, *b)", param.name)
assert_instance_of(Entry::RequiredParameter, param)
end
def test_method_with_block_parameters
index(<<~RUBY)
class Foo
def bar(&block)
end
def baz(&)
end
end
RUBY
entry = @index["bar"]&.first #: as Entry::Method
parameters = entry.signatures.first&.parameters #: as Array[Entry::Parameter]
param = parameters.first #: as Entry::Parameter
assert_equal(:block, param.name)
assert_instance_of(Entry::BlockParameter, param)
entry = @index["baz"]&.first #: as Entry::Method
parameters = entry.signatures.first&.parameters #: as Array[Entry::Parameter]
assert_equal(1, parameters.length)
param = parameters.first #: as Entry::Parameter
assert_equal(Entry::BlockParameter::DEFAULT_NAME, param.name)
assert_instance_of(Entry::BlockParameter, param)
end
def test_method_with_anonymous_rest_parameters
index(<<~RUBY)
class Foo
def bar(*, **)
end
end
RUBY
assert_entry("bar", Entry::Method, "/fake/path/foo.rb:1-2:2-5")
entry = @index["bar"]&.first #: as Entry::Method
parameters = entry.signatures.first&.parameters #: as Array[Entry::Parameter]
assert_equal(2, parameters.length)
first, second = parameters
assert_equal(Entry::RestParameter::DEFAULT_NAME, first&.name)
assert_instance_of(Entry::RestParameter, first)
assert_equal(Entry::KeywordRestParameter::DEFAULT_NAME, second&.name)
assert_instance_of(Entry::KeywordRestParameter, second)
end
def test_method_with_forbidden_keyword_splat_parameter
index(<<~RUBY)
class Foo
def bar(**nil)
end
end
RUBY
assert_entry("bar", Entry::Method, "/fake/path/foo.rb:1-2:2-5")
entry = @index["bar"]&.first #: as Entry::Method
parameters = entry.signatures.first&.parameters #: as Array[Entry::Parameter]
assert_empty(parameters)
end
def test_methods_with_argument_forwarding
index(<<~RUBY)
class Foo
def bar(...)
end
def baz(a, ...)
end
end
RUBY
entry = @index["bar"]&.first #: as Entry::Method
assert_instance_of(Entry::Method, entry, "Expected `bar` to be indexed")
parameters = entry.signatures.first&.parameters #: as Array[Entry::Parameter]
assert_equal(1, parameters.length)
assert_instance_of(Entry::ForwardingParameter, parameters.first)
entry = @index["baz"]&.first #: as Entry::Method
assert_instance_of(Entry::Method, entry, "Expected `baz` to be indexed")
parameters = entry.signatures.first&.parameters #: as Array[Entry::Parameter]
assert_equal(2, parameters.length)
assert_instance_of(Entry::RequiredParameter, parameters[0])
assert_instance_of(Entry::ForwardingParameter, parameters[1])
end
def test_keeps_track_of_method_owner
index(<<~RUBY)
class Foo
def bar
end
end
RUBY
entry = @index["bar"]&.first #: as Entry::Method
owner_name = entry.owner&.name
assert_equal("Foo", owner_name)
end
def test_keeps_track_of_attributes
index(<<~RUBY)
class Foo
# Hello there
attr_reader :bar, :other
attr_writer :baz
attr_accessor :qux
end
RUBY
assert_entry("bar", Entry::Accessor, "/fake/path/foo.rb:2-15:2-18")
assert_equal("Hello there", @index["bar"]&.first&.comments)
assert_entry("other", Entry::Accessor, "/fake/path/foo.rb:2-21:2-26")
assert_equal("Hello there", @index["other"]&.first&.comments)
assert_entry("baz=", Entry::Accessor, "/fake/path/foo.rb:3-15:3-18")
assert_entry("qux", Entry::Accessor, "/fake/path/foo.rb:4-17:4-20")
assert_entry("qux=", Entry::Accessor, "/fake/path/foo.rb:4-17:4-20")
end
def test_ignores_attributes_invoked_on_constant
index(<<~RUBY)
class Foo
end
Foo.attr_reader :bar
RUBY
assert_no_entry("bar")
end
def test_properly_tracks_multiple_levels_of_nesting
index(<<~RUBY)
module Foo
def first_method; end
module Bar
def second_method; end
end
def third_method; end
end
RUBY
entry = @index["first_method"]&.first #: as Entry::Method
assert_equal("Foo", entry.owner&.name)
entry = @index["second_method"]&.first #: as Entry::Method
assert_equal("Foo::Bar", entry.owner&.name)
entry = @index["third_method"]&.first #: as Entry::Method
assert_equal("Foo", entry.owner&.name)
end
def test_keeps_track_of_aliases
index(<<~RUBY)
class Foo
alias whatever to_s
alias_method :foo, :to_a
alias_method "bar", "to_a"
# These two are not indexed because they are dynamic or incomplete
alias_method baz, :to_a
alias_method :baz
end
RUBY
assert_entry("whatever", Entry::UnresolvedMethodAlias, "/fake/path/foo.rb:1-8:1-16")
assert_entry("foo", Entry::UnresolvedMethodAlias, "/fake/path/foo.rb:2-15:2-19")
assert_entry("bar", Entry::UnresolvedMethodAlias, "/fake/path/foo.rb:3-15:3-20")
# Foo plus 3 valid aliases
assert_equal(4, @index.length - @default_indexed_entries.length)
end
def test_singleton_methods
index(<<~RUBY)
class Foo
def self.bar; end
class << self
def baz; end
end
end
RUBY
assert_entry("bar", Entry::Method, "/fake/path/foo.rb:1-2:1-19")
assert_entry("baz", Entry::Method, "/fake/path/foo.rb:4-4:4-16")
bar = @index["bar"]&.first #: as Entry::Method
baz = @index["baz"]&.first #: as Entry::Method
assert_instance_of(Entry::SingletonClass, bar.owner)
assert_instance_of(Entry::SingletonClass, baz.owner)
# Regardless of whether the method was added through `self.something` or `class << self`, the owner object must be
# the exact same
assert_same(bar.owner, baz.owner)
end
def test_name_location_points_to_method_identifier_location
index(<<~RUBY)
class Foo
def bar
a = 123
a + 456
end
end
RUBY
entry = @index["bar"]&.first #: as Entry::Method
refute_equal(entry.location, entry.name_location)
name_location = entry.name_location
assert_equal(2, name_location.start_line)
assert_equal(2, name_location.end_line)
assert_equal(6, name_location.start_column)
assert_equal(9, name_location.end_column)
end
def test_signature_matches_for_a_method_with_positional_params
index(<<~RUBY)
class Foo
def bar(a, b = 123)
end
end
RUBY
entry = @index["bar"]&.first #: as Entry::Method
# Matching calls
assert_signature_matches(entry, "bar()")
assert_signature_matches(entry, "bar(1)")
assert_signature_matches(entry, "bar(1, 2)")
assert_signature_matches(entry, "bar(...)")
assert_signature_matches(entry, "bar(1, ...)")
assert_signature_matches(entry, "bar(*a)")
assert_signature_matches(entry, "bar(1, *a)")
assert_signature_matches(entry, "bar(1, *a, 2)")
assert_signature_matches(entry, "bar(*a, 2)")
assert_signature_matches(entry, "bar(1, **a)")
assert_signature_matches(entry, "bar(1) {}")
# This call is impossible to analyze statically because it depends on whether there are elements inside `a` or
# not. If there's nothing, the call will fail. But if there's anything inside, the hash will become the first
# positional argument
assert_signature_matches(entry, "bar(**a)")
# Non matching calls
refute_signature_matches(entry, "bar(1, 2, 3)")
refute_signature_matches(entry, "bar(1, b: 2)")
refute_signature_matches(entry, "bar(1, 2, c: 3)")
end
def test_signature_matches_for_a_method_with_argument_forwarding
index(<<~RUBY)
class Foo
def bar(...)
end
end
RUBY
entry = @index["bar"]&.first #: as Entry::Method
# All calls match a forwarding parameter
assert_signature_matches(entry, "bar(1)")
assert_signature_matches(entry, "bar(1, 2)")
assert_signature_matches(entry, "bar(...)")
assert_signature_matches(entry, "bar(1, ...)")
assert_signature_matches(entry, "bar(*a)")
assert_signature_matches(entry, "bar(1, *a)")
assert_signature_matches(entry, "bar(1, *a, 2)")
assert_signature_matches(entry, "bar(*a, 2)")
assert_signature_matches(entry, "bar(1, **a)")
assert_signature_matches(entry, "bar(1) {}")
assert_signature_matches(entry, "bar()")
assert_signature_matches(entry, "bar(1, 2, 3)")
assert_signature_matches(entry, "bar(1, 2, a: 1, b: 5) {}")
end
def test_signature_matches_for_post_forwarding_parameter
index(<<~RUBY)
class Foo
def bar(a, ...)
end
end
RUBY
entry = @index["bar"]&.first #: as Entry::Method
# All calls with at least one positional argument match
assert_signature_matches(entry, "bar(1)")
assert_signature_matches(entry, "bar(1, 2)")
assert_signature_matches(entry, "bar(...)")
assert_signature_matches(entry, "bar(1, ...)")
assert_signature_matches(entry, "bar(*a)")
assert_signature_matches(entry, "bar(1, *a)")
assert_signature_matches(entry, "bar(1, *a, 2)")
assert_signature_matches(entry, "bar(*a, 2)")
assert_signature_matches(entry, "bar(1, **a)")
assert_signature_matches(entry, "bar(1) {}")
assert_signature_matches(entry, "bar(1, 2, 3)")
assert_signature_matches(entry, "bar(1, 2, a: 1, b: 5) {}")
assert_signature_matches(entry, "bar()")
end
def test_signature_matches_for_destructured_parameters
index(<<~RUBY)
class Foo
def bar(a, (b, c))
end
end
RUBY
entry = @index["bar"]&.first #: as Entry::Method
# All calls with at least one positional argument match
assert_signature_matches(entry, "bar()")
assert_signature_matches(entry, "bar(1)")
assert_signature_matches(entry, "bar(1, 2)")
assert_signature_matches(entry, "bar(...)")
assert_signature_matches(entry, "bar(1, ...)")
assert_signature_matches(entry, "bar(*a)")
assert_signature_matches(entry, "bar(1, *a)")
assert_signature_matches(entry, "bar(*a, 2)")
# This matches because `bar(1, *[], 2)` would result in `bar(1, 2)`, which is a valid call
assert_signature_matches(entry, "bar(1, *a, 2)")
assert_signature_matches(entry, "bar(1, **a)")
assert_signature_matches(entry, "bar(1) {}")
refute_signature_matches(entry, "bar(1, 2, 3)")
refute_signature_matches(entry, "bar(1, 2, a: 1, b: 5) {}")
end
def test_signature_matches_for_post_parameters
index(<<~RUBY)
class Foo
def bar(*splat, a)
end
end
RUBY
entry = @index["bar"]&.first #: as Entry::Method
# All calls with at least one positional argument match
assert_signature_matches(entry, "bar(1)")
assert_signature_matches(entry, "bar(1, 2)")
assert_signature_matches(entry, "bar(...)")
assert_signature_matches(entry, "bar(1, ...)")
assert_signature_matches(entry, "bar(*a)")
assert_signature_matches(entry, "bar(1, *a)")
assert_signature_matches(entry, "bar(*a, 2)")
assert_signature_matches(entry, "bar(1, *a, 2)")
assert_signature_matches(entry, "bar(1, **a)")
assert_signature_matches(entry, "bar(1, 2, 3)")
assert_signature_matches(entry, "bar(1) {}")
assert_signature_matches(entry, "bar()")
refute_signature_matches(entry, "bar(1, 2, a: 1, b: 5) {}")
end
def test_signature_matches_for_keyword_parameters
index(<<~RUBY)
class Foo
def bar(a:, b: 123)
end
end
RUBY
entry = @index["bar"]&.first #: as Entry::Method
assert_signature_matches(entry, "bar(...)")
assert_signature_matches(entry, "bar()")
assert_signature_matches(entry, "bar(a: 1)")
assert_signature_matches(entry, "bar(a: 1, b: 32)")
refute_signature_matches(entry, "bar(a: 1, c: 2)")
refute_signature_matches(entry, "bar(1, ...)")
refute_signature_matches(entry, "bar(1) {}")
refute_signature_matches(entry, "bar(1, *a)")
refute_signature_matches(entry, "bar(*a, 2)")
refute_signature_matches(entry, "bar(1, *a, 2)")
refute_signature_matches(entry, "bar(1, **a)")
refute_signature_matches(entry, "bar(*a)")
refute_signature_matches(entry, "bar(1)")
refute_signature_matches(entry, "bar(1, 2)")
refute_signature_matches(entry, "bar(1, 2, a: 1, b: 5) {}")
end
def test_signature_matches_for_keyword_splats
index(<<~RUBY)
class Foo
def bar(a, b:, **kwargs)
end
end
RUBY
entry = @index["bar"]&.first #: as Entry::Method
assert_signature_matches(entry, "bar(...)")
assert_signature_matches(entry, "bar()")
assert_signature_matches(entry, "bar(1)")
assert_signature_matches(entry, "bar(1, b: 2)")
assert_signature_matches(entry, "bar(1, b: 2, c: 3, d: 4)")
refute_signature_matches(entry, "bar(1, 2, b: 2)")
end
def test_partial_signature_matches
# It's important to match signatures partially, because we want to figure out which signature we should show while
# the user is in the middle of typing
index(<<~RUBY)
class Foo
def bar(a:, b:)
end
def baz(a, b)
end
end
RUBY
entry = @index["bar"]&.first #: as Entry::Method
assert_signature_matches(entry, "bar(a: 1)")
entry = @index["baz"]&.first #: as Entry::Method
assert_signature_matches(entry, "baz(1)")
end
def test_module_function_with_no_arguments
index(<<~RUBY)
module Foo
def bar; end
module_function
def baz; end
attr_reader :attribute
public
def qux; end
end
RUBY
entry = @index["bar"]&.first #: as Entry::Method
assert_predicate(entry, :public?)
assert_equal("Foo", entry.owner&.name)
instance_baz, singleton_baz = @index["baz"] #: as Array[Entry::Method]
assert_predicate(instance_baz, :private?)
assert_equal("Foo", instance_baz&.owner&.name)
assert_predicate(singleton_baz, :public?)
assert_equal("Foo::<Class:Foo>", singleton_baz&.owner&.name)
# After invoking `public`, the state of `module_function` is reset
instance_qux, singleton_qux = @index["qux"] #: as Array[Entry::Method]
assert_nil(singleton_qux)
assert_predicate(instance_qux, :public?)
assert_equal("Foo", instance_baz&.owner&.name)
# Attributes are not turned into class methods, they do become private
instance_attribute, singleton_attribute = @index["attribute"] #: as Array[Entry::Method]
assert_nil(singleton_attribute)
assert_equal("Foo", instance_attribute&.owner&.name)
assert_predicate(instance_attribute, :private?)
end
def test_module_function_does_nothing_in_classes
# Invoking `module_function` in a class raises an error. We simply ignore it
index(<<~RUBY)
class Foo
def bar; end
module_function
def baz; end
end
RUBY
entry = @index["bar"]&.first #: as Entry::Method
assert_predicate(entry, :public?)
assert_equal("Foo", entry.owner&.name)
entry = @index["baz"]&.first #: as Entry::Method
assert_predicate(entry, :public?)
assert_equal("Foo", entry.owner&.name)
end
def test_making_several_class_methods_private
index(<<~RUBY)
class Foo
def self.bar; end
def self.baz; end
def self.qux; end
private_class_method :bar, :baz, :qux
def initialize
end
end
RUBY
end
def test_changing_visibility_post_definition
index(<<~RUBY)
class Foo
def bar; end
private :bar
def baz; end
protected :baz
private
def qux; end
public :qux
end
RUBY
entry = @index["bar"]&.first #: as Entry::Method
assert_predicate(entry, :private?)
entry = @index["baz"]&.first #: as Entry::Method
assert_predicate(entry, :protected?)
entry = @index["qux"]&.first #: as Entry::Method
assert_predicate(entry, :public?)
end
def test_handling_attr
index(<<~RUBY)
class Foo
attr :bar
attr :baz, true
attr :qux, false
end
RUBY
assert_entry("bar", Entry::Accessor, "/fake/path/foo.rb:1-8:1-11")
assert_no_entry("bar=")
assert_entry("baz", Entry::Accessor, "/fake/path/foo.rb:2-8:2-11")
assert_entry("baz=", Entry::Accessor, "/fake/path/foo.rb:2-8:2-11")
assert_entry("qux", Entry::Accessor, "/fake/path/foo.rb:3-8:3-11")
assert_no_entry("qux=")
end
private
#: (Entry::Method entry, String call_string) -> void
def assert_signature_matches(entry, call_string)
sig = entry.signatures.first #: as !nil
arguments = parse_prism_args(call_string)
assert(sig.matches?(arguments), "Expected #{call_string} to match #{entry.name}#{entry.decorated_parameters}")
end
#: (Entry::Method entry, String call_string) -> void
def refute_signature_matches(entry, call_string)
sig = entry.signatures.first #: as !nil
arguments = parse_prism_args(call_string)
refute(sig.matches?(arguments), "Expected #{call_string} to not match #{entry.name}#{entry.decorated_parameters}")
end
def parse_prism_args(s)
Array(Prism.parse(s).value.statements.body.first.arguments&.arguments)
end
end
end