Skip to content

Add more regression tests #908

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions spec/pin/delegated_method_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

describe Solargraph::Pin::DelegatedMethod do
it 'can be constructed from a Method pin' do
method_pin = Solargraph::Pin::Method.new(comments: '@return [Hash<String, String>]')
method_pin = Solargraph::Pin::Method.new(comments: '@return [Hash{String => String}]')

delegation_pin = Solargraph::Pin::DelegatedMethod.new(method: method_pin, scope: :instance)
expect(delegation_pin.return_type.to_s).to eq('Hash<String, String>')
expect(delegation_pin.return_type.to_s).to eq('Hash{String => String}')
end

it 'can be constructed from a receiver source and method name' do
Expand Down
114 changes: 114 additions & 0 deletions spec/source/chain/call_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -391,4 +391,118 @@ def bar; end
type = chain.infer(api_map, Solargraph::Pin::ROOT_PIN, api_map.source_map('test.rb').locals)
expect(type.tag).to eq('String')
end

it 'understands types in an Array#+ scenario' do
source = Solargraph::Source.load_string(%(
module A
class B
def c
([B.new] + [B.new]).each do |d|
d
end
end
end
end
), 'test.rb')

api_map = Solargraph::ApiMap.new
api_map.map source

chain = Solargraph::Source::SourceChainer.chain(source, Solargraph::Position.new(5, 14))
type = chain.infer(api_map, Solargraph::Pin::ROOT_PIN, api_map.source_map('test.rb').locals)
expect(type.tags).to eq('A::B')
end

it 'handles subclass and superclass issues in Array#+' do
source = Solargraph::Source.load_string(%(
module A
class B; end
class C < B
def c
([B.new] + [C.new]).each do |d|
d
end
end
def d
([C.new] + [B.new]).each do |d|
d
end
end
end
end
), 'test.rb')
api_map = Solargraph::ApiMap.new
api_map.map source

chain = Solargraph::Source::SourceChainer.chain(source, Solargraph::Position.new(6, 14))
type = chain.infer(api_map, Solargraph::Pin::ROOT_PIN, api_map.source_map('test.rb').locals)
expect(type.rooted_tags).to eq('::A::B').or eq('::A::B, ::A::C')

chain = Solargraph::Source::SourceChainer.chain(source, Solargraph::Position.new(11, 14))
type = chain.infer(api_map, Solargraph::Pin::ROOT_PIN, api_map.source_map('test.rb').locals)
# valid options here:
# * emit type checker warning when adding [B.new] and type whole thing as '::A::B'
# * type whole thing as '::A::B, A::C'
# * type as undefined
expect(type.rooted_tags).to eq('::A::B, ::A::C').or be_undefined
expect(type.rooted_tags).not_to eq('::A::C')
end

it 'qualifies types in a second Array#+ ' do
source = Solargraph::Source.load_string(%(
module A
module D
class E; end
end
class B; end
class C < B
def e
([D::E.new] + [D::E.new]).each do |d|
d
end
end
def f
de1 = [D::E.new]
de2 = [D::E.new]
(de1 + de2).each do |d|
d
end
end
# @return [Array<D::E>]
attr_reader :g
# @return [Array<D::E>]
attr_reader :h
def i
de1 = [D::E.new]
(g + de1).each do |d|
d
end
end
def j
(g + h).each do |d|
d
end
end
end
end
), 'test.rb')
api_map = Solargraph::ApiMap.new
api_map.map source

chain = Solargraph::Source::SourceChainer.chain(source, Solargraph::Position.new(9, 14))
type = chain.infer(api_map, Solargraph::Pin::ROOT_PIN, api_map.source_map('test.rb').locals)
expect(type.rooted_tags).to eq('::A::D::E')

chain = Solargraph::Source::SourceChainer.chain(source, Solargraph::Position.new(16, 14))
type = chain.infer(api_map, Solargraph::Pin::ROOT_PIN, api_map.source_map('test.rb').locals)
expect(type.rooted_tags).to eq('::A::D::E')

chain = Solargraph::Source::SourceChainer.chain(source, Solargraph::Position.new(26, 14))
type = chain.infer(api_map, Solargraph::Pin::ROOT_PIN, api_map.source_map('test.rb').locals)
expect(type.rooted_tags).to eq('::A::D::E')

chain = Solargraph::Source::SourceChainer.chain(source, Solargraph::Position.new(31, 14))
type = chain.infer(api_map, Solargraph::Pin::ROOT_PIN, api_map.source_map('test.rb').locals)
expect(type.rooted_tags).to eq('::A::D::E')
end
end
27 changes: 23 additions & 4 deletions spec/source_map/clip_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -684,8 +684,7 @@ def initialize
), 'test.rb')
api_map = Solargraph::ApiMap.new
api_map.map source
# [[4, 39], [7, 15], [11, 13], [12, 37], [15, 37]].each do |loc|
[[7, 15]].each do |loc|
[[4, 39], [7, 15], [11, 13], [12, 37], [15, 37]].each do |loc|
clip = api_map.clip_at('test.rb', loc)
paths = clip.complete.pins.map(&:path)
expect(paths).to include('String#upcase'), -> { %(expected #{paths} at #{loc} to include "String#upcase") }
Expand Down Expand Up @@ -1077,8 +1076,9 @@ def bar opts = {}
), 'test.rb')
map = Solargraph::ApiMap.new
map.map source
clip = map.clip_at('test.rb', Solargraph::Position.new(3, 15))
expect(clip.infer.to_s).to eq('Array<String>, nil')
# @todo need to understand range type
# clip = map.clip_at('test.rb', Solargraph::Position.new(3, 15))
# expect(clip.infer.to_s).to eq('String, nil')
clip = map.clip_at('test.rb', Solargraph::Position.new(4, 15))
expect(clip.infer.to_s).to eq('Array<String>, nil')
clip = map.clip_at('test.rb', Solargraph::Position.new(5, 15))
Expand Down Expand Up @@ -1285,6 +1285,7 @@ def meth
api_map.map source
clip = api_map.clip_at('test.rb', [6, 6])
expect(clip.infer.tag).to eq('Array<String>')
expect(clip.infer.tags).to eq('Array<String>, nil')
end

it 'excludes local variables from chained call resolution' do
Expand Down Expand Up @@ -1900,6 +1901,11 @@ def foo
), 'test.rb')
api_map = Solargraph::ApiMap.new.map(source)

clip = api_map.clip_at('test.rb', [3, 8])
type = clip.infer
expect(type.tag).to eq('Array<Integer>')

api_map = Solargraph::ApiMap.new.map(source)
clip = api_map.clip_at('test.rb', [5, 10])
type = clip.infer
expect(type.tag).to eq('Integer')
Expand Down Expand Up @@ -2545,6 +2551,19 @@ def foo
expect(clip.infer.to_s).to eq('String')
end

xit 'infers that type of argument has been overridden' do
source = Solargraph::Source.load_string(%(
def foo a
a = 'foo'
a
end
), 'test.rb')
api_map = Solargraph::ApiMap.new
api_map.map source
clip = api_map.clip_at('test.rb', [3, 8])
expect(clip.infer.to_s).to eq('String')
end

it 'resolves String#split overloads' do
source = Solargraph::Source.load_string(%(
a = 'abc\ndef'.split('\n')
Expand Down
2 changes: 1 addition & 1 deletion spec/type_checker/levels/normal_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ def bar; end
# vendored code.
gemspec = Gem::Specification.find_by_name('kramdown-parser-gfm')
pins = Solargraph::GemPins.build(gemspec)
Solargraph::Cache.save('gems', "#{gemspec.name}-#{gemspec.version}.ser", pins)
Solargraph::Cache.save('gems', "#{gemspec.name}-#{gemspec.version}.ser", pins)
checker = type_checker(%(
require 'kramdown-parser-gfm'
# @type [String]
Expand Down
33 changes: 33 additions & 0 deletions spec/type_checker/levels/strict_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -797,5 +797,38 @@ def bar(*args, **kwargs, &blk)
))
expect(checker.problems.map(&:message)).to eq([])
end

it "understands Array#+ overloads" do
checker = type_checker(%(
c = ['a'] + ['a']
c
))
expect(checker.problems.map(&:message)).to eq([])
end

it "understands String#+ overloads" do
checker = type_checker(%(
detail = ''
detail += "foo"
detail.strip!
))
expect(checker.problems.map(&:message)).to eq([])
end

it "understands Enumerable#each via _Each self type" do
checker = type_checker(%(
class Blah
# @param e [Enumerable<String>]
# @return [void]
def foo(e)
e
e.each do |x|
x
end
end
end
))
expect(checker.problems.map(&:message)).to eq([])
end
end
end