solargraph 0.60.2 → 0.60.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/plugins.yml +1 -5
- data/CHANGELOG.md +23 -0
- data/lib/solargraph/api_map/constants.rb +1 -1
- data/lib/solargraph/api_map.rb +1 -2
- data/lib/solargraph/complex_type.rb +18 -5
- data/lib/solargraph/library.rb +6 -2
- data/lib/solargraph/parser/parser_gem/node_processors/namespace_node.rb +14 -5
- data/lib/solargraph/pin/method.rb +14 -13
- data/lib/solargraph/rbs_map/conversions.rb +2 -100
- data/lib/solargraph/source/chain/call.rb +165 -67
- data/lib/solargraph/source/chain/instance_variable.rb +1 -6
- data/lib/solargraph/source/chain.rb +36 -5
- data/lib/solargraph/source.rb +15 -11
- data/lib/solargraph/source_map/mapper.rb +2 -2
- data/lib/solargraph/type_checker.rb +12 -3
- data/lib/solargraph/version.rb +1 -1
- data/lib/solargraph/workspace/gemspecs.rb +6 -0
- data/lib/solargraph/yard_map/mapper.rb +36 -3
- data/solargraph.gemspec +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7c7397d5a9bbee07b12baa9372ce14f61216e5a310aa0f68605acabf8a9430e2
|
|
4
|
+
data.tar.gz: aeecf5c5d1ea7b1040fd3ae4bd9d1bff1685e468bd92b30dedfdd8ded2f1cd04
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f4abae37c4fc8b57efe4a7b3550a2edbfc64455e8852f81660220f4a67facf101eafd058805b58cf0be6f9d5b9d16317cd21f6cd3b47f0c666c48418ee0d54c2
|
|
7
|
+
data.tar.gz: e14586769600dd63e2a96b8bcb4a5e4aa9bb045948e87e8c06b6606e711fed6816347051505eb756df55fed375a0419431dd02a44c2082d6d1ec0e2b13c6564f
|
|
@@ -129,12 +129,8 @@ jobs:
|
|
|
129
129
|
- name: clone https://github.com/lekemula/solargraph-rspec/
|
|
130
130
|
run: |
|
|
131
131
|
cd ..
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
# pending https://github.com/lekemula/solargraph-rspec/pull/31
|
|
135
|
-
git clone https://github.com/apiology/solargraph-rspec.git
|
|
132
|
+
git clone https://github.com/lekemula/solargraph-rspec.git
|
|
136
133
|
cd solargraph-rspec
|
|
137
|
-
git checkout test_solargraph_prereleases
|
|
138
134
|
- name: Set up Ruby
|
|
139
135
|
uses: ruby/setup-ruby@v1
|
|
140
136
|
with:
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,26 @@
|
|
|
1
|
+
## 0.60.4 - August 30, 2026
|
|
2
|
+
- Fix @yieldparam type lost on multi-overload block-form methods (#1289) (#1290)
|
|
3
|
+
- Read inline RBS superclass params only on the class line (#1301)
|
|
4
|
+
- Stop recording YARD's implicit Object superclass (#1315)
|
|
5
|
+
- Improve overload resolution and macro handling in Chain::Call (#1247)
|
|
6
|
+
- Fix Chain#nullable? leaking nil from earlier &. into later calls (#1271)
|
|
7
|
+
- Handle YARD bug with open singleton classes (#1279)
|
|
8
|
+
- Fix duck_types_match? to check the inferred type's own duck interface (#1295)
|
|
9
|
+
- Use the original repo (run_solargraph_rspec_specs job) (#1291)
|
|
10
|
+
|
|
11
|
+
## 0.60.3 - August 5, 2026
|
|
12
|
+
- Pin rdoc to ~> 7.0 (#1216)
|
|
13
|
+
- (tag: v0.60.2) Release 0.60.2 (rdoc fix)
|
|
14
|
+
- Fix stale Hash::_Key expectations from upstream RBS core drift (#1224)
|
|
15
|
+
- Bump overcommit to ~> 0.71.0 (#1221)
|
|
16
|
+
- Resolve remote constants (#1234)
|
|
17
|
+
- Fix exponential blowup in Pin::Method#combine_same_type_arity_signatures (#1238)
|
|
18
|
+
- Respect #- yard comment-block separator (#1218)
|
|
19
|
+
- Typecheck attribute return tags against their backing ivar's type (#1219)
|
|
20
|
+
- Fix recursive-mutex deadlock in Library#sync_catalog (#1220)
|
|
21
|
+
- Fix Gemspecs#in_this_bundle? crash when no Gemfile is discoverable (#1225)
|
|
22
|
+
- Remove duplicate dead methods in RbsMap::Conversions (#1253)
|
|
23
|
+
|
|
1
24
|
## 0.60.2 - June 27, 2026
|
|
2
25
|
- Extraneous debug output (#1214)
|
|
3
26
|
- Pin rdoc to ~> 7.0 (#1216)
|
|
@@ -155,7 +155,7 @@ module Solargraph
|
|
|
155
155
|
mixin = resolve(ref.name, ref.reference_gates)
|
|
156
156
|
next unless mixin
|
|
157
157
|
|
|
158
|
-
resolved =
|
|
158
|
+
resolved = resolve(name, mixin)
|
|
159
159
|
return [resolved, gates[(idx + 1)..]] if resolved
|
|
160
160
|
end
|
|
161
161
|
end
|
data/lib/solargraph/api_map.rb
CHANGED
|
@@ -403,11 +403,10 @@ module Solargraph
|
|
|
403
403
|
# @param candidates [Array<Pin::BaseVariable>]
|
|
404
404
|
# @param name [String]
|
|
405
405
|
# @param closure [Pin::Closure]
|
|
406
|
-
# @param location [Location]
|
|
406
|
+
# @param location [Location, nil]
|
|
407
407
|
#
|
|
408
408
|
# @return [Pin::BaseVariable, nil]
|
|
409
409
|
def var_at_location candidates, name, closure, location
|
|
410
|
-
# @todo Location can be nil if clips have trouble finding node recipients
|
|
411
410
|
return unless location
|
|
412
411
|
|
|
413
412
|
with_correct_name = candidates.select { |pin| pin.name == name }
|
|
@@ -227,7 +227,7 @@ module Solargraph
|
|
|
227
227
|
expected = expected.downcast_to_literal_if_possible
|
|
228
228
|
inferred = downcast_to_literal_if_possible
|
|
229
229
|
|
|
230
|
-
return duck_types_match?(api_map, expected, inferred) if expected.duck_type?
|
|
230
|
+
return duck_types_match?(api_map, expected, inferred, rules) if expected.duck_type?
|
|
231
231
|
|
|
232
232
|
if rules.include? :allow_any_match
|
|
233
233
|
inferred.any? do |inf|
|
|
@@ -245,18 +245,31 @@ module Solargraph
|
|
|
245
245
|
# @param api_map [ApiMap]
|
|
246
246
|
# @param expected [ComplexType, UniqueType]
|
|
247
247
|
# @param inferred [ComplexType, UniqueType]
|
|
248
|
+
# @param rules [Array<Symbol>]
|
|
248
249
|
# @return [Boolean]
|
|
249
|
-
def duck_types_match? api_map, expected, inferred
|
|
250
|
+
def duck_types_match? api_map, expected, inferred, rules = []
|
|
250
251
|
raise ArgumentError, 'Expected type must be duck type' unless expected.duck_type?
|
|
252
|
+
allow_any_match = rules.include?(:allow_any_match)
|
|
251
253
|
expected.each do |exp|
|
|
252
254
|
next unless exp.duck_type?
|
|
253
|
-
quack = exp.to_s[1..]
|
|
254
|
-
|
|
255
|
-
return false
|
|
255
|
+
quack = exp.to_s[1..] || ''
|
|
256
|
+
matched = allow_any_match ? inferred.any? { |inf| duck_type_provides?(api_map, inf, quack) } : inferred.all? { |inf| duck_type_provides?(api_map, inf, quack) }
|
|
257
|
+
return false unless matched
|
|
256
258
|
end
|
|
257
259
|
true
|
|
258
260
|
end
|
|
259
261
|
|
|
262
|
+
# @param api_map [ApiMap]
|
|
263
|
+
# @param inf [UniqueType]
|
|
264
|
+
# @param quack [String]
|
|
265
|
+
# @return [Boolean]
|
|
266
|
+
def duck_type_provides? api_map, inf, quack
|
|
267
|
+
return true if inf.duck_type? && inf.to_s[1..] == quack
|
|
268
|
+
|
|
269
|
+
!api_map.get_method_stack(inf.namespace, quack, scope: inf.scope).empty?
|
|
270
|
+
end
|
|
271
|
+
private :duck_type_provides?
|
|
272
|
+
|
|
260
273
|
# @return [String]
|
|
261
274
|
def rooted_tags
|
|
262
275
|
map(&:rooted_tag).join(', ')
|
data/lib/solargraph/library.rb
CHANGED
|
@@ -605,8 +605,12 @@ module Solargraph
|
|
|
605
605
|
queued_gemspec_cache.push(spec)
|
|
606
606
|
return if pending - queued_gemspec_cache.length < 1
|
|
607
607
|
|
|
608
|
-
|
|
609
|
-
sync_catalog
|
|
608
|
+
# Try the next cacheable gemspec. This method is always called
|
|
609
|
+
# from inside sync_catalog's mutex (either directly or via this
|
|
610
|
+
# same recursive call), so recursing through sync_catalog here
|
|
611
|
+
# would try to re-lock a mutex this thread already holds and
|
|
612
|
+
# deadlock (https://github.com/castwide/solargraph/issues/1111).
|
|
613
|
+
cache_next_gemspec
|
|
610
614
|
else
|
|
611
615
|
logger.info "Caching #{spec.name} #{spec.version}"
|
|
612
616
|
Thread.new do
|
|
@@ -27,7 +27,6 @@ module Solargraph
|
|
|
27
27
|
source: :parser
|
|
28
28
|
)
|
|
29
29
|
pins.push nspin
|
|
30
|
-
Solargraph.logger.warn "Superclass: #{superclass_name}" if superclass_name&.start_with?('Array')
|
|
31
30
|
if superclass_name
|
|
32
31
|
pins.push Pin::Reference::Superclass.new(
|
|
33
32
|
location: loc,
|
|
@@ -41,12 +40,22 @@ module Solargraph
|
|
|
41
40
|
|
|
42
41
|
private
|
|
43
42
|
|
|
44
|
-
#
|
|
43
|
+
# Type arguments for a generic superclass in inline RBS syntax, e.g.,
|
|
44
|
+
# `class Foo < Array #[String]`. Only recognized where RBS defines
|
|
45
|
+
# it: directly after the superclass, on the same line, with no space
|
|
46
|
+
# between `#` and `[`. Anything else is an ordinary comment.
|
|
47
|
+
#
|
|
45
48
|
# @return [String, nil]
|
|
46
49
|
def parameters_from_inline_rbs
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
+
superclass = node.children[1]
|
|
51
|
+
return unless superclass
|
|
52
|
+
|
|
53
|
+
source = region.source.code
|
|
54
|
+
pos = get_node_end_position(superclass)
|
|
55
|
+
offset = Position.line_char_to_offset(source, pos.line, pos.character)
|
|
56
|
+
eol = source.index("\n", offset) || source.length
|
|
57
|
+
match = source[offset...eol].to_s.match(/\A\s*#\[([^\]]*)\]/)
|
|
58
|
+
return unless match
|
|
50
59
|
|
|
51
60
|
code = match[1].strip
|
|
52
61
|
return if code.empty?
|
|
@@ -148,12 +148,13 @@ module Solargraph
|
|
|
148
148
|
|
|
149
149
|
# @param parameters [::Array<Parameter>]
|
|
150
150
|
# @param return_type [ComplexType, nil]
|
|
151
|
+
# @param tag_docstring [YARD::Docstring] source of @yieldparam/@yieldreturn tags; pass an @overload tag's own docstring here
|
|
151
152
|
# @return [Signature]
|
|
152
|
-
def generate_signature parameters, return_type
|
|
153
|
+
def generate_signature parameters, return_type, tag_docstring = docstring
|
|
153
154
|
# @type [Pin::Signature, nil]
|
|
154
155
|
block = nil
|
|
155
|
-
yieldparam_tags =
|
|
156
|
-
yieldreturn_tags =
|
|
156
|
+
yieldparam_tags = tag_docstring.tags(:yieldparam)
|
|
157
|
+
yieldreturn_tags = tag_docstring.tags(:yieldreturn)
|
|
157
158
|
generics = docstring.tags(:generic).map(&:name)
|
|
158
159
|
needs_block_param_signature =
|
|
159
160
|
parameters.last&.block? || !yieldreturn_tags.empty? || !yieldparam_tags.empty?
|
|
@@ -509,11 +510,6 @@ module Solargraph
|
|
|
509
510
|
#
|
|
510
511
|
# @return [Array<Pin::Signature>]
|
|
511
512
|
def combine_same_type_arity_signatures same_type_arity_signatures
|
|
512
|
-
# @todo Stubbing this method while we debug an infinite loop bug in Ruby 3.x.
|
|
513
|
-
# The body below is intentionally preserved for when the stub is removed.
|
|
514
|
-
return same_type_arity_signatures
|
|
515
|
-
|
|
516
|
-
# rubocop:disable Lint/UnreachableCode
|
|
517
513
|
# This is an O(n^2) operation, so bail out if n is not small
|
|
518
514
|
return same_type_arity_signatures if same_type_arity_signatures.length > 10
|
|
519
515
|
|
|
@@ -521,7 +517,9 @@ module Solargraph
|
|
|
521
517
|
# @param new_signature [Pin::Signature]
|
|
522
518
|
same_type_arity_signatures.reduce([]) do |old_signatures, new_signature|
|
|
523
519
|
next old_signatures + [new_signature] if old_signatures.empty?
|
|
524
|
-
|
|
520
|
+
|
|
521
|
+
merged = false
|
|
522
|
+
combined = old_signatures.map do |old_signature|
|
|
525
523
|
potential_new_signature = old_signature.combine_with(new_signature)
|
|
526
524
|
|
|
527
525
|
if potential_new_signature.type_arity == old_signature.type_arity
|
|
@@ -534,13 +532,14 @@ module Solargraph
|
|
|
534
532
|
# based on types, not just arity, allowing for type
|
|
535
533
|
# information describing how methods behave based on
|
|
536
534
|
# their input types)
|
|
537
|
-
|
|
535
|
+
merged = true
|
|
536
|
+
potential_new_signature
|
|
538
537
|
else
|
|
539
|
-
|
|
538
|
+
old_signature
|
|
540
539
|
end
|
|
541
540
|
end
|
|
541
|
+
merged ? combined : old_signatures + [new_signature]
|
|
542
542
|
end
|
|
543
|
-
# rubocop:enable Lint/UnreachableCode
|
|
544
543
|
end
|
|
545
544
|
|
|
546
545
|
# @param name [String]
|
|
@@ -749,7 +748,9 @@ module Solargraph
|
|
|
749
748
|
top_type = generate_complex_type
|
|
750
749
|
result = []
|
|
751
750
|
result.push generate_signature(parameters, top_type) if top_type.defined?
|
|
752
|
-
|
|
751
|
+
# @param meth [Pin::Signature]
|
|
752
|
+
# @param tag [YARD::Tags::OverloadTag]
|
|
753
|
+
result.concat(overloads.zip(docstring.tags(:overload).select(&:parameters)).map { |meth, tag| generate_signature(meth.parameters, meth.return_type, tag.docstring) }) unless overloads.empty?
|
|
753
754
|
result.push generate_signature(parameters, @return_type || ComplexType::UNDEFINED) if result.empty?
|
|
754
755
|
result
|
|
755
756
|
end
|
|
@@ -159,27 +159,6 @@ module Solargraph
|
|
|
159
159
|
RBS_TO_CLASS.fetch(ns, ns)
|
|
160
160
|
end
|
|
161
161
|
|
|
162
|
-
# @param type_name [RBS::TypeName]
|
|
163
|
-
# @param type_args [Enumerable<RBS::Types::Bases::Base>]
|
|
164
|
-
# @return [ComplexType::UniqueType]
|
|
165
|
-
def build_type type_name, type_args = []
|
|
166
|
-
# we use .absolute? below to tell the type object what to
|
|
167
|
-
# expect
|
|
168
|
-
rbs_name = type_name.relative!.to_s
|
|
169
|
-
base = RBS_TO_CLASS.fetch(rbs_name, rbs_name)
|
|
170
|
-
|
|
171
|
-
params = type_args.map { |a| RbsTranslator.to_complex_type(a) }
|
|
172
|
-
# @todo Tuples are in flux
|
|
173
|
-
# tuples have their own class and are handled in other_type_to_type
|
|
174
|
-
if base == 'Hash' && params.length == 2
|
|
175
|
-
ComplexType::UniqueType.new(base, [params.first], [params.last], rooted: type_name.absolute?,
|
|
176
|
-
parameters_type: :hash)
|
|
177
|
-
else
|
|
178
|
-
ComplexType::UniqueType.new(base, [], params.reject(&:undefined?), rooted: type_name.absolute?,
|
|
179
|
-
parameters_type: :list)
|
|
180
|
-
end
|
|
181
|
-
end
|
|
182
|
-
|
|
183
162
|
# @param decl [RBS::AST::Declarations::Module::Self]
|
|
184
163
|
# @param closure [Pin::Namespace]
|
|
185
164
|
# @return [void]
|
|
@@ -561,7 +540,8 @@ module Solargraph
|
|
|
561
540
|
def method_def_to_sigs decl, pin
|
|
562
541
|
# rubocop:disable Style/SafeNavigationChainLength
|
|
563
542
|
implicit_nil = decl.overloads.first&.annotations&.map(&:string)&.include?('implicitly-returns-nil') || false
|
|
564
|
-
# rubocop:enable Style/SafeNavigationChainLength
|
|
543
|
+
# rubocop:enable Style/SafeNavigationChainLength
|
|
544
|
+
# @param overload [RBS::AST::Members::MethodDefinition::Overload]
|
|
565
545
|
decl.overloads.map do |overload|
|
|
566
546
|
type_location = location_decl_to_pin_location(overload.method_type.location)
|
|
567
547
|
generics = overload.method_type.type_params.map(&:name).map(&:to_s)
|
|
@@ -587,84 +567,6 @@ module Solargraph
|
|
|
587
567
|
Location.new(location.name.to_s, range)
|
|
588
568
|
end
|
|
589
569
|
|
|
590
|
-
# @param type [RBS::MethodType, RBS::Types::Block]
|
|
591
|
-
# @param pin [Pin::Method]
|
|
592
|
-
# @param implicit_nil [Boolean]
|
|
593
|
-
# @return [Array(Array<Pin::Parameter>, ComplexType)]
|
|
594
|
-
def parts_of_function type, pin, implicit_nil
|
|
595
|
-
type_location = pin.type_location
|
|
596
|
-
if defined?(RBS::Types::UntypedFunction) && type.type.is_a?(RBS::Types::UntypedFunction)
|
|
597
|
-
return [
|
|
598
|
-
[Solargraph::Pin::Parameter.new(decl: :restarg, name: 'arg', closure: pin, source: :rbs,
|
|
599
|
-
type_location: type_location)],
|
|
600
|
-
method_type_to_type(type, implicit_nil)
|
|
601
|
-
]
|
|
602
|
-
end
|
|
603
|
-
|
|
604
|
-
parameters = []
|
|
605
|
-
arg_num = -1
|
|
606
|
-
type.type.required_positionals.each do |param|
|
|
607
|
-
# @sg-ignore Unresolved call to name
|
|
608
|
-
name = param.name ? param.name.to_s : "arg_#{arg_num += 1}"
|
|
609
|
-
parameters.push Solargraph::Pin::Parameter.new(decl: :arg, name: name, closure: pin,
|
|
610
|
-
# @sg-ignore RBS generic type understanding issue
|
|
611
|
-
return_type: other_type_to_type(param.type),
|
|
612
|
-
source: :rbs, type_location: type_location)
|
|
613
|
-
end
|
|
614
|
-
type.type.optional_positionals.each do |param|
|
|
615
|
-
# @sg-ignore Unresolved call to name
|
|
616
|
-
name = param.name ? param.name.to_s : "arg_#{arg_num += 1}"
|
|
617
|
-
parameters.push Solargraph::Pin::Parameter.new(decl: :optarg, name: name, closure: pin,
|
|
618
|
-
# @sg-ignore RBS generic type understanding issue
|
|
619
|
-
return_type: other_type_to_type(param.type),
|
|
620
|
-
type_location: type_location,
|
|
621
|
-
source: :rbs)
|
|
622
|
-
end
|
|
623
|
-
if type.type.rest_positionals
|
|
624
|
-
name = type.type.rest_positionals.name ? type.type.rest_positionals.name.to_s : "arg_#{arg_num += 1}"
|
|
625
|
-
inner_rest_positional_type = other_type_to_type(type.type.rest_positionals.type)
|
|
626
|
-
rest_positional_type = ComplexType::UniqueType.new('Array',
|
|
627
|
-
[],
|
|
628
|
-
[inner_rest_positional_type],
|
|
629
|
-
rooted: true, parameters_type: :list)
|
|
630
|
-
parameters.push Solargraph::Pin::Parameter.new(decl: :restarg, name: name, closure: pin,
|
|
631
|
-
source: :rbs, type_location: type_location,
|
|
632
|
-
return_type: rest_positional_type)
|
|
633
|
-
end
|
|
634
|
-
type.type.trailing_positionals.each do |param|
|
|
635
|
-
# @sg-ignore Unresolved call to name
|
|
636
|
-
name = param.name ? param.name.to_s : "arg_#{arg_num += 1}"
|
|
637
|
-
parameters.push Solargraph::Pin::Parameter.new(decl: :arg, name: name, closure: pin, source: :rbs,
|
|
638
|
-
type_location: type_location)
|
|
639
|
-
end
|
|
640
|
-
type.type.required_keywords.each do |orig, param|
|
|
641
|
-
# @sg-ignore Unresolved call to to_s
|
|
642
|
-
name = orig ? orig.to_s : "arg_#{arg_num += 1}"
|
|
643
|
-
parameters.push Solargraph::Pin::Parameter.new(decl: :kwarg, name: name, closure: pin,
|
|
644
|
-
# @sg-ignore RBS generic type understanding issue
|
|
645
|
-
return_type: other_type_to_type(param.type),
|
|
646
|
-
source: :rbs, type_location: type_location)
|
|
647
|
-
end
|
|
648
|
-
type.type.optional_keywords.each do |orig, param|
|
|
649
|
-
# @sg-ignore Unresolved call to to_s
|
|
650
|
-
name = orig ? orig.to_s : "arg_#{arg_num += 1}"
|
|
651
|
-
parameters.push Solargraph::Pin::Parameter.new(decl: :kwoptarg, name: name, closure: pin,
|
|
652
|
-
# @sg-ignore RBS generic type understanding issue
|
|
653
|
-
return_type: other_type_to_type(param.type),
|
|
654
|
-
type_location: type_location,
|
|
655
|
-
source: :rbs)
|
|
656
|
-
end
|
|
657
|
-
if type.type.rest_keywords
|
|
658
|
-
name = type.type.rest_keywords.name ? type.type.rest_keywords.name.to_s : "arg_#{arg_num += 1}"
|
|
659
|
-
parameters.push Solargraph::Pin::Parameter.new(decl: :kwrestarg,
|
|
660
|
-
name: type.type.rest_keywords.name.to_s, closure: pin,
|
|
661
|
-
source: :rbs, type_location: type_location)
|
|
662
|
-
end
|
|
663
|
-
|
|
664
|
-
return_type = method_type_to_type(type, implicit_nil)
|
|
665
|
-
[parameters, return_type]
|
|
666
|
-
end
|
|
667
|
-
|
|
668
570
|
# @param type [RBS::MethodType,RBS::Types::Block]
|
|
669
571
|
# @param pin [Pin::Method]
|
|
670
572
|
# @param implicit_nil [Boolean]
|
|
@@ -14,7 +14,7 @@ module Solargraph
|
|
|
14
14
|
# @return [String]
|
|
15
15
|
attr_reader :word
|
|
16
16
|
|
|
17
|
-
# @return [Location]
|
|
17
|
+
# @return [Location, nil]
|
|
18
18
|
attr_reader :location
|
|
19
19
|
|
|
20
20
|
# @return [::Array<Chain>]
|
|
@@ -71,6 +71,95 @@ module Solargraph
|
|
|
71
71
|
|
|
72
72
|
private
|
|
73
73
|
|
|
74
|
+
# Checks whether a single overload signature matches the call's
|
|
75
|
+
# arguments/block and, if so, resolves its return type. Threaded
|
|
76
|
+
# through the caller's accumulator so behavior matches the
|
|
77
|
+
# original inline loop: if the overload doesn't match, the
|
|
78
|
+
# incoming type/signature are returned unchanged.
|
|
79
|
+
#
|
|
80
|
+
# @param overload [Pin::Signature]
|
|
81
|
+
# @param pin [Pin::Method]
|
|
82
|
+
# @param api_map [ApiMap]
|
|
83
|
+
# @param name_pin [Pin::Base]
|
|
84
|
+
# @param locals [::Array<Solargraph::Pin::LocalVariable, Solargraph::Pin::Parameter>]
|
|
85
|
+
# @param type [ComplexType]
|
|
86
|
+
# @param new_signature_pin [Pin::Signature, nil]
|
|
87
|
+
# @return [::Array(ComplexType, Pin::Signature)]
|
|
88
|
+
def match_overload_type overload, pin, api_map, name_pin, locals, type, new_signature_pin
|
|
89
|
+
return [type, new_signature_pin] unless overload.arity_matches?(arguments, with_block?)
|
|
90
|
+
|
|
91
|
+
match = true
|
|
92
|
+
atypes = []
|
|
93
|
+
arguments.each_with_index do |arg, idx|
|
|
94
|
+
param = overload.parameters[idx]
|
|
95
|
+
if param.nil?
|
|
96
|
+
match = overload.parameters.any?(&:restarg?)
|
|
97
|
+
break
|
|
98
|
+
end
|
|
99
|
+
arg_name_pin = Pin::ProxyType.anonymous(name_pin.context,
|
|
100
|
+
closure: name_pin.closure,
|
|
101
|
+
gates: name_pin.gates,
|
|
102
|
+
source: :chain)
|
|
103
|
+
atype = atypes[idx] ||= arg.infer(api_map, arg_name_pin, locals)
|
|
104
|
+
# @sg-ignore flow sensitive typing should handle is_a? and next
|
|
105
|
+
unless param.compatible_arg?(atype, api_map) || param.restarg?
|
|
106
|
+
match = false
|
|
107
|
+
break
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
return [type, new_signature_pin] unless match
|
|
111
|
+
|
|
112
|
+
if overload.block && with_block?
|
|
113
|
+
block_atypes = overload.block.parameters.map(&:return_type)
|
|
114
|
+
# @todo Need to add nil check here
|
|
115
|
+
# @sg-ignore Need to add nil check here
|
|
116
|
+
blocktype = if block.links.map(&:class) == [BlockSymbol]
|
|
117
|
+
# like the bar in foo(&:bar)
|
|
118
|
+
block_symbol_call_type(api_map, name_pin.context, block_atypes, locals)
|
|
119
|
+
else
|
|
120
|
+
block_call_type(api_map, name_pin, locals)
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
new_signature_pin = overload.resolve_generics_from_context_until_complete(overload.generics, atypes, nil, nil,
|
|
124
|
+
blocktype)
|
|
125
|
+
# @todo It shouldn't be necessary to choose either generics or macros
|
|
126
|
+
# @sg-ignore Need to add nil check here
|
|
127
|
+
new_return_type = if new_signature_pin.return_type.defined?
|
|
128
|
+
# @sg-ignore Need to add nil check here
|
|
129
|
+
new_signature_pin.return_type
|
|
130
|
+
else
|
|
131
|
+
# @sg-ignore Need to add nil check here
|
|
132
|
+
named_types = pin.parameter_names.zip(arguments.map { |arg| ComplexType.try_parse(simple_convert(arg.node).to_s) }).to_h
|
|
133
|
+
pin.typify(api_map).expand(named_types)
|
|
134
|
+
end
|
|
135
|
+
self_type = if head?
|
|
136
|
+
# If we're at the head of the chain, we called a
|
|
137
|
+
# method somewhere that marked itself as returning
|
|
138
|
+
# self. Given we didn't invoke this on an object,
|
|
139
|
+
# this must be a method in this same class - so we
|
|
140
|
+
# use our own self type
|
|
141
|
+
name_pin.context
|
|
142
|
+
else
|
|
143
|
+
# if we're past the head in the chain, whatever the
|
|
144
|
+
# type of the lhs side is what 'self' will be in its
|
|
145
|
+
# declaration - we can't just use the type of the
|
|
146
|
+
# method pin, as this might be a subclass of the
|
|
147
|
+
# place where the method is defined
|
|
148
|
+
name_pin.binder
|
|
149
|
+
end
|
|
150
|
+
# This same logic applies to the YARD work done by
|
|
151
|
+
# 'with_params()'.
|
|
152
|
+
#
|
|
153
|
+
# qualify(), however, happens in the namespace where
|
|
154
|
+
# the docs were written - from the method pin.
|
|
155
|
+
# @todo Need to add nil check here
|
|
156
|
+
if new_return_type.defined?
|
|
157
|
+
type = with_params(new_return_type.self_to_type(self_type), self_type).qualify(api_map, *pin.gates)
|
|
158
|
+
end
|
|
159
|
+
type ||= ComplexType::UNDEFINED
|
|
160
|
+
[type, new_signature_pin]
|
|
161
|
+
end
|
|
162
|
+
|
|
74
163
|
# @param pins [::Enumerable<Pin::Base>]
|
|
75
164
|
# @param api_map [ApiMap]
|
|
76
165
|
# @param name_pin [Pin::Base]
|
|
@@ -96,76 +185,20 @@ module Solargraph
|
|
|
96
185
|
# @sg-ignore flow sensitive typing should handle is_a? and next
|
|
97
186
|
# @param ol [Pin::Signature]
|
|
98
187
|
sorted_overloads.each do |ol|
|
|
99
|
-
|
|
100
|
-
match = true
|
|
101
|
-
|
|
102
|
-
atypes = []
|
|
103
|
-
arguments.each_with_index do |arg, idx|
|
|
104
|
-
param = ol.parameters[idx]
|
|
105
|
-
if param.nil?
|
|
106
|
-
match = ol.parameters.any?(&:restarg?)
|
|
107
|
-
break
|
|
108
|
-
end
|
|
109
|
-
arg_name_pin = Pin::ProxyType.anonymous(name_pin.context,
|
|
110
|
-
closure: name_pin.closure,
|
|
111
|
-
gates: name_pin.gates,
|
|
112
|
-
source: :chain)
|
|
113
|
-
atype = atypes[idx] ||= arg.infer(api_map, arg_name_pin, locals)
|
|
114
|
-
unless param.compatible_arg?(atype, api_map) || param.restarg?
|
|
115
|
-
match = false
|
|
116
|
-
break
|
|
117
|
-
end
|
|
118
|
-
end
|
|
119
|
-
if match
|
|
120
|
-
if ol.block && with_block?
|
|
121
|
-
block_atypes = ol.block.parameters.map(&:return_type)
|
|
122
|
-
# @todo Need to add nil check here
|
|
123
|
-
blocktype = if block.links.map(&:class) == [BlockSymbol]
|
|
124
|
-
# like the bar in foo(&:bar)
|
|
125
|
-
block_symbol_call_type(api_map, name_pin.context, block_atypes, locals)
|
|
126
|
-
else
|
|
127
|
-
block_call_type(api_map, name_pin, locals)
|
|
128
|
-
end
|
|
129
|
-
end
|
|
130
|
-
new_signature_pin = ol.resolve_generics_from_context_until_complete(ol.generics, atypes, nil, nil,
|
|
131
|
-
blocktype)
|
|
132
|
-
# @todo It shouldn't be necessary to choose either generics or macros
|
|
133
|
-
new_return_type = if new_signature_pin.return_type.defined?
|
|
134
|
-
new_signature_pin.return_type
|
|
135
|
-
else
|
|
136
|
-
named_types = p.parameter_names.zip(arguments.map { |arg| ComplexType.try_parse(simple_convert(arg.node).to_s) }).to_h
|
|
137
|
-
p.typify(api_map).expand(named_types)
|
|
138
|
-
end
|
|
139
|
-
self_type = if head?
|
|
140
|
-
# If we're at the head of the chain, we called a
|
|
141
|
-
# method somewhere that marked itself as returning
|
|
142
|
-
# self. Given we didn't invoke this on an object,
|
|
143
|
-
# this must be a method in this same class - so we
|
|
144
|
-
# use our own self type
|
|
145
|
-
name_pin.context
|
|
146
|
-
else
|
|
147
|
-
# if we're past the head in the chain, whatever the
|
|
148
|
-
# type of the lhs side is what 'self' will be in its
|
|
149
|
-
# declaration - we can't just use the type of the
|
|
150
|
-
# method pin, as this might be a subclass of the
|
|
151
|
-
# place where the method is defined
|
|
152
|
-
name_pin.binder
|
|
153
|
-
end
|
|
154
|
-
# This same logic applies to the YARD work done by
|
|
155
|
-
# 'with_params()'.
|
|
156
|
-
#
|
|
157
|
-
# qualify(), however, happens in the namespace where
|
|
158
|
-
# the docs were written - from the method pin.
|
|
159
|
-
# @todo Need to add nil check here
|
|
160
|
-
if new_return_type.defined?
|
|
161
|
-
type = with_params(new_return_type.self_to_type(self_type), self_type).qualify(api_map, *p.gates)
|
|
162
|
-
end
|
|
163
|
-
type ||= ComplexType::UNDEFINED
|
|
164
|
-
end
|
|
188
|
+
type, new_signature_pin = match_overload_type(ol, p, api_map, name_pin, locals, type, new_signature_pin)
|
|
165
189
|
break if type.defined?
|
|
166
190
|
end
|
|
167
191
|
p = p.with_single_signature(new_signature_pin) unless new_signature_pin.nil?
|
|
168
192
|
next p.proxy(type) if type.defined?
|
|
193
|
+
if !p.macros.empty?
|
|
194
|
+
result = process_macro(p, api_map, name_pin.context, locals)
|
|
195
|
+
# @sg-ignore flow sensitive typing should be able to handle redefinition
|
|
196
|
+
next result unless result.return_type.undefined?
|
|
197
|
+
elsif !p.directives.empty?
|
|
198
|
+
result = process_directive(p, api_map, name_pin.context, locals)
|
|
199
|
+
# @sg-ignore flow sensitive typing should be able to handle redefinition
|
|
200
|
+
next result unless result.return_type.undefined?
|
|
201
|
+
end
|
|
169
202
|
p
|
|
170
203
|
end
|
|
171
204
|
logger.debug do
|
|
@@ -186,6 +219,71 @@ module Solargraph
|
|
|
186
219
|
end
|
|
187
220
|
end
|
|
188
221
|
|
|
222
|
+
# @param pin [Pin::Base]
|
|
223
|
+
# @param api_map [ApiMap]
|
|
224
|
+
# @param context [ComplexType, ComplexType::UniqueType]
|
|
225
|
+
# @param locals [::Array<Solargraph::Pin::LocalVariable, Solargraph::Pin::Parameter>]
|
|
226
|
+
# @return [Pin::Base]
|
|
227
|
+
def process_macro pin, api_map, context, locals
|
|
228
|
+
pin.macros.each do |macro|
|
|
229
|
+
# @todo 'Wrong argument type for
|
|
230
|
+
# Solargraph::Source::Chain::Call#inner_process_macro:
|
|
231
|
+
# macro expected YARD::Tags::MacroDirective, received
|
|
232
|
+
# generic<Elem>' is because we lose 'rooted' information
|
|
233
|
+
# in the 'Chain::Array' class internally, leaving
|
|
234
|
+
# ::Array#each shadowed when it shouldn't be.
|
|
235
|
+
# @sg-ignore macro is Solargraph::YardMap::Macro, wraps a YARD::Tags::MacroDirective
|
|
236
|
+
result = inner_process_macro(pin, macro, api_map, context, locals)
|
|
237
|
+
return result unless result.return_type.undefined?
|
|
238
|
+
end
|
|
239
|
+
Pin::ProxyType.anonymous(ComplexType::UNDEFINED, source: :chain)
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
# @param pin [Pin::Method]
|
|
243
|
+
# @param api_map [ApiMap]
|
|
244
|
+
# @param context [ComplexType, ComplexType::UniqueType]
|
|
245
|
+
# @param locals [::Array<Solargraph::Pin::LocalVariable, Solargraph::Pin::Parameter>]
|
|
246
|
+
# @return [Pin::ProxyType]
|
|
247
|
+
def process_directive pin, api_map, context, locals
|
|
248
|
+
pin.directives.each do |dir|
|
|
249
|
+
macro = api_map.named_macro(dir.tag.name)
|
|
250
|
+
next if macro.nil?
|
|
251
|
+
# @sg-ignore macro is Solargraph::YardMap::Macro, wraps a YARD::Tags::MacroDirective
|
|
252
|
+
result = inner_process_macro(pin, macro, api_map, context, locals)
|
|
253
|
+
return result unless result.return_type.undefined?
|
|
254
|
+
end
|
|
255
|
+
Pin::ProxyType.anonymous ComplexType::UNDEFINED, source: :chain
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
# @param pin [Pin::Base]
|
|
259
|
+
# @param macro [YARD::Tags::MacroDirective]
|
|
260
|
+
# @param api_map [ApiMap]
|
|
261
|
+
# @param context [ComplexType, ComplexType::UniqueType]
|
|
262
|
+
# @param locals [::Array<Pin::LocalVariable, Pin::Parameter>]
|
|
263
|
+
# @return [Pin::ProxyType]
|
|
264
|
+
def inner_process_macro pin, macro, api_map, context, locals
|
|
265
|
+
vals = arguments.map { |c| Pin::ProxyType.anonymous(c.infer(api_map, pin, locals), source: :chain) }
|
|
266
|
+
txt = macro.tag.text.clone
|
|
267
|
+
# @sg-ignore Need to add nil check here
|
|
268
|
+
if txt.empty? && macro.tag.name
|
|
269
|
+
named = api_map.named_macro(macro.tag.name)
|
|
270
|
+
txt = named.tag.text.clone if named
|
|
271
|
+
end
|
|
272
|
+
i = 1
|
|
273
|
+
vals.each do |v|
|
|
274
|
+
# @sg-ignore Need to add nil check here
|
|
275
|
+
txt.gsub!(/\$#{i}/, v.context.namespace)
|
|
276
|
+
i += 1
|
|
277
|
+
end
|
|
278
|
+
# @sg-ignore Need to add nil check here
|
|
279
|
+
docstring = Solargraph::Source.parse_docstring(txt).to_docstring
|
|
280
|
+
tag = docstring.tag(:return)
|
|
281
|
+
unless tag.nil? || tag.types.nil?
|
|
282
|
+
return Pin::ProxyType.anonymous(ComplexType.try_parse(*tag.types), source: :chain)
|
|
283
|
+
end
|
|
284
|
+
Pin::ProxyType.anonymous(ComplexType::UNDEFINED, source: :chain)
|
|
285
|
+
end
|
|
286
|
+
|
|
189
287
|
# @param docstring [YARD::Docstring]
|
|
190
288
|
# @param context [ComplexType]
|
|
191
289
|
# @return [ComplexType, nil]
|
|
@@ -13,10 +13,6 @@ module Solargraph
|
|
|
13
13
|
@location = location
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
-
# @sg-ignore Declared return type
|
|
17
|
-
# ::Array<::Solargraph::Pin::Base> does not match inferred
|
|
18
|
-
# type ::Array<::Solargraph::Pin::BaseVariable, ::NilClass>
|
|
19
|
-
# for Solargraph::Source::Chain::InstanceVariable#resolve
|
|
20
16
|
def resolve api_map, name_pin, locals
|
|
21
17
|
ivars = api_map.get_instance_variable_pins(name_pin.context.namespace, name_pin.context.scope).select do |p|
|
|
22
18
|
p.name == word
|
|
@@ -27,8 +23,7 @@ module Solargraph
|
|
|
27
23
|
|
|
28
24
|
private
|
|
29
25
|
|
|
30
|
-
# @
|
|
31
|
-
# @return [Location]
|
|
26
|
+
# @return [Location, nil]
|
|
32
27
|
attr_reader :location
|
|
33
28
|
end
|
|
34
29
|
end
|
|
@@ -168,7 +168,7 @@ module Solargraph
|
|
|
168
168
|
return ComplexType::UNDEFINED
|
|
169
169
|
end
|
|
170
170
|
type = infer_from_definitions(pins, links.last.last_context, api_map, locals)
|
|
171
|
-
out = maybe_nil(type)
|
|
171
|
+
out = maybe_nil(type, api_map)
|
|
172
172
|
logger.debug do
|
|
173
173
|
"Chain#infer_uncached(links=#{links.map(&:desc)}, locals=#{locals.map(&:desc)}, " \
|
|
174
174
|
"name_pin=#{name_pin}, name_pin.closure=#{name_pin&.closure&.inspect}, " \
|
|
@@ -199,8 +199,38 @@ module Solargraph
|
|
|
199
199
|
@splat
|
|
200
200
|
end
|
|
201
201
|
|
|
202
|
-
|
|
203
|
-
|
|
202
|
+
# Whether this chain's inferred type should have nil added to it
|
|
203
|
+
# because a `&.` earlier in the chain might have short-circuited
|
|
204
|
+
# evaluation to nil.
|
|
205
|
+
#
|
|
206
|
+
# A `&.` only makes the *immediate* call nil-or-normal-result; a
|
|
207
|
+
# later, non-safe-navigated call in the same chain is still
|
|
208
|
+
# invoked on whatever that produced. If receiver is nil at that
|
|
209
|
+
# point, Ruby calls the method on nil itself instead of skipping
|
|
210
|
+
# it - so nil only continues to flow through subsequent links
|
|
211
|
+
# that NilClass defines as returning `self` (e.g., #tap,
|
|
212
|
+
# #itself). Any other subsequent call resolves to a concrete,
|
|
213
|
+
# non-nil-including type (e.g. NilClass#== returns Boolean), and
|
|
214
|
+
# nil no longer needs to be tracked past that point (unless a
|
|
215
|
+
# later `&.` reintroduces it).
|
|
216
|
+
#
|
|
217
|
+
# @param api_map [ApiMap]
|
|
218
|
+
# @return [Boolean]
|
|
219
|
+
def nullable? api_map
|
|
220
|
+
currently_nullable = false
|
|
221
|
+
links.each do |link|
|
|
222
|
+
if link.nullable?
|
|
223
|
+
currently_nullable = true
|
|
224
|
+
next
|
|
225
|
+
end
|
|
226
|
+
next unless currently_nullable
|
|
227
|
+
next unless link.is_a?(Chain::Call)
|
|
228
|
+
|
|
229
|
+
pin = api_map.get_method_stack('NilClass', link.word, scope: :instance).first
|
|
230
|
+
# @sg-ignore Need to add nil check here
|
|
231
|
+
currently_nullable = pin.nil? || pin.return_type.tag == 'self'
|
|
232
|
+
end
|
|
233
|
+
currently_nullable
|
|
204
234
|
end
|
|
205
235
|
|
|
206
236
|
include Logging
|
|
@@ -286,10 +316,11 @@ module Solargraph
|
|
|
286
316
|
end
|
|
287
317
|
|
|
288
318
|
# @param type [ComplexType, ComplexType::UniqueType]
|
|
319
|
+
# @param api_map [ApiMap]
|
|
289
320
|
# @return [ComplexType, ComplexType::UniqueType]
|
|
290
|
-
def maybe_nil type
|
|
321
|
+
def maybe_nil type, api_map
|
|
291
322
|
return type if type.undefined? || type.void? || type.nullable?
|
|
292
|
-
return type unless nullable?
|
|
323
|
+
return type unless nullable?(api_map)
|
|
293
324
|
ComplexType.new(type.items + [ComplexType::NIL])
|
|
294
325
|
end
|
|
295
326
|
|
data/lib/solargraph/source.rb
CHANGED
|
@@ -209,7 +209,7 @@ module Solargraph
|
|
|
209
209
|
stringified_comments[rng.start.line] ||= begin
|
|
210
210
|
# @sg-ignore Need to add nil check here
|
|
211
211
|
buff = associated_comments[rng.start.line]
|
|
212
|
-
|
|
212
|
+
stringify_comment_array(buff)
|
|
213
213
|
end
|
|
214
214
|
end
|
|
215
215
|
|
|
@@ -249,20 +249,20 @@ module Solargraph
|
|
|
249
249
|
|
|
250
250
|
# Get a hash of comments grouped by the line numbers of the associated code.
|
|
251
251
|
#
|
|
252
|
-
# @return [Hash{Integer => String
|
|
252
|
+
# @return [Hash{Integer => Array<String>}]
|
|
253
253
|
def associated_comments
|
|
254
254
|
@associated_comments ||= begin
|
|
255
255
|
# @type [Hash{Integer => String}]
|
|
256
256
|
result = {}
|
|
257
|
-
buffer =
|
|
257
|
+
buffer = []
|
|
258
258
|
# @type [Integer, nil]
|
|
259
259
|
last = nil
|
|
260
260
|
comments.each_pair do |num, snip|
|
|
261
261
|
if !last || num == last + 1
|
|
262
|
-
buffer.
|
|
262
|
+
buffer.push "#{snip.text}\n"
|
|
263
263
|
else
|
|
264
264
|
result[first_not_empty_from(last + 1)] = buffer.clone
|
|
265
|
-
buffer.replace "#{snip.text}\n"
|
|
265
|
+
buffer.replace ["#{snip.text}\n"]
|
|
266
266
|
end
|
|
267
267
|
last = num
|
|
268
268
|
end
|
|
@@ -305,27 +305,31 @@ module Solargraph
|
|
|
305
305
|
|
|
306
306
|
# Get a string representation of an array of comments.
|
|
307
307
|
#
|
|
308
|
-
# @param comments [String]
|
|
308
|
+
# @param comments [Array<String>, nil]
|
|
309
309
|
# @return [String]
|
|
310
310
|
def stringify_comment_array comments
|
|
311
|
-
ctxt =
|
|
311
|
+
ctxt = []
|
|
312
312
|
started = false
|
|
313
313
|
skip = nil
|
|
314
|
-
comments
|
|
314
|
+
comments&.each do |l|
|
|
315
|
+
if l =~ /^#-\R/
|
|
316
|
+
ctxt.clear
|
|
317
|
+
next
|
|
318
|
+
end
|
|
315
319
|
# Trim the comment and minimum leading whitespace
|
|
316
320
|
p = l.force_encoding('UTF-8').encode('UTF-8', invalid: :replace, replace: '?').gsub(/^#+/, '')
|
|
317
321
|
if p.strip.empty?
|
|
318
322
|
next unless started
|
|
319
|
-
ctxt.
|
|
323
|
+
ctxt.push p
|
|
320
324
|
else
|
|
321
325
|
here = p.index(/[^ \t]/)
|
|
322
326
|
# @sg-ignore flow sensitive typing should be able to handle redefinition
|
|
323
327
|
skip = here if skip.nil? || here < skip
|
|
324
|
-
ctxt.
|
|
328
|
+
ctxt.push p[skip..]
|
|
325
329
|
end
|
|
326
330
|
started = true
|
|
327
331
|
end
|
|
328
|
-
ctxt
|
|
332
|
+
ctxt.join('')
|
|
329
333
|
end
|
|
330
334
|
|
|
331
335
|
# A hash of line numbers and their associated comments.
|
|
@@ -145,8 +145,8 @@ module Solargraph
|
|
|
145
145
|
)
|
|
146
146
|
end
|
|
147
147
|
# @sg-ignore Need to add nil check here
|
|
148
|
-
com_pos = Position.new(line + 1 - comments.
|
|
149
|
-
process_comment(src_pos, com_pos, comments)
|
|
148
|
+
com_pos = Position.new(line + 1 - comments.length, 0)
|
|
149
|
+
process_comment(src_pos, com_pos, comments.join(''))
|
|
150
150
|
end
|
|
151
151
|
rescue StandardError => e
|
|
152
152
|
raise e.class, "Error processing comment directives in #{@filename}: #{e.message}"
|
|
@@ -162,10 +162,19 @@ module Solargraph
|
|
|
162
162
|
result.push Problem.new(pin.location, "Untyped method #{pin.path} could not be inferred")
|
|
163
163
|
end
|
|
164
164
|
elsif rules.validate_tags?
|
|
165
|
-
|
|
165
|
+
# Attribute pins never have a node (attr_reader/attr_writer/attr_accessor are
|
|
166
|
+
# synthesized, not parsed method bodies), but their inferred type is still
|
|
167
|
+
# meaningful: #probe walks the backing ivar's assignments via infer_from_iv.
|
|
168
|
+
# Without this carve-out, a manually-written @return tag on an attribute is
|
|
169
|
+
# never cross-checked against how the ivar is actually assigned (e.g. a nilable
|
|
170
|
+
# default in the constructor), so a bad tag silently passes at every level.
|
|
171
|
+
unless (pin.node.nil? && !pin.attribute?) || declared.void? || virtual_pin?(pin) || abstract?(pin)
|
|
166
172
|
inferred = pin.probe(api_map).self_to_type(pin.full_context)
|
|
167
173
|
if inferred.undefined?
|
|
168
|
-
|
|
174
|
+
# An attribute with no locally-discoverable ivar assignment (set via a
|
|
175
|
+
# mixin, metaprogramming, etc.) isn't a tag/inference mismatch - just
|
|
176
|
+
# unproven. Only methods with an actual body get flagged for this.
|
|
177
|
+
unless rules.ignore_all_undefined? || external?(pin) || pin.attribute?
|
|
169
178
|
result.push Problem.new(pin.location, "#{pin.path} return type could not be inferred", pin: pin)
|
|
170
179
|
end
|
|
171
180
|
else
|
|
@@ -848,7 +857,7 @@ module Solargraph
|
|
|
848
857
|
def all_sg_ignore_lines
|
|
849
858
|
source.associated_comments.select do |_line, text|
|
|
850
859
|
# @sg-ignore Need to add nil check here
|
|
851
|
-
text.include?('@sg-ignore')
|
|
860
|
+
text.any? { |t| t.include?('@sg-ignore') }
|
|
852
861
|
end.keys.to_set
|
|
853
862
|
end
|
|
854
863
|
|
data/lib/solargraph/version.rb
CHANGED
|
@@ -220,6 +220,12 @@ module Solargraph
|
|
|
220
220
|
# @sg-ignore need boolish support for ? methods
|
|
221
221
|
def in_this_bundle?
|
|
222
222
|
Bundler.definition&.lockfile&.to_s&.start_with?(directory)
|
|
223
|
+
rescue Bundler::GemfileNotFound
|
|
224
|
+
# Solargraph itself isn't running under a discoverable Gemfile
|
|
225
|
+
# (e.g. installed and invoked as a standalone gem), so it can't
|
|
226
|
+
# be "this bundle" - fall back to treating the workspace as an
|
|
227
|
+
# external bundle.
|
|
228
|
+
false
|
|
223
229
|
end
|
|
224
230
|
|
|
225
231
|
# @return [Array<Gem::Specification, Bundler::LazySpecification, Bundler::StubSpecification>]
|
|
@@ -11,7 +11,7 @@ module Solargraph
|
|
|
11
11
|
# @param spec [Gem::Specification, nil]
|
|
12
12
|
def initialize code_objects, spec = nil
|
|
13
13
|
@code_objects = code_objects
|
|
14
|
-
@macro_code_objects = code_objects.
|
|
14
|
+
@macro_code_objects = code_objects.grep(YARD::CodeObjects::MacroObject)
|
|
15
15
|
@spec = spec
|
|
16
16
|
# @type [Array<Solargraph::Pin::Base>]
|
|
17
17
|
@pins = []
|
|
@@ -32,13 +32,17 @@ module Solargraph
|
|
|
32
32
|
|
|
33
33
|
private
|
|
34
34
|
|
|
35
|
+
def core_store
|
|
36
|
+
@core_store ||= ApiMap::Store.new(RbsMap::CoreMap.new.pins)
|
|
37
|
+
end
|
|
38
|
+
|
|
35
39
|
# @param code_object [YARD::CodeObjects::Base]
|
|
36
40
|
# @return [Array<Pin::Base>]
|
|
37
41
|
def generate_pins code_object
|
|
38
42
|
result = []
|
|
39
43
|
case code_object
|
|
40
44
|
when YARD::CodeObjects::NamespaceObject
|
|
41
|
-
nspin =
|
|
45
|
+
nspin = namespace_with_bug_fix(code_object)
|
|
42
46
|
@namespace_pins[code_object.path] = nspin
|
|
43
47
|
result.push nspin
|
|
44
48
|
if code_object.is_a?(YARD::CodeObjects::ClassObject) && !code_object.superclass.nil?
|
|
@@ -50,7 +54,16 @@ module Solargraph
|
|
|
50
54
|
else
|
|
51
55
|
code_object.superclass.to_s
|
|
52
56
|
end
|
|
53
|
-
|
|
57
|
+
# YARD fills in `Object` for every class whose definition it never
|
|
58
|
+
# saw a superclass clause on - including a bare `class Foo`
|
|
59
|
+
# reopening, which asserts nothing about ancestry - and keeps no
|
|
60
|
+
# record of which case it was. A class with no superclass reference
|
|
61
|
+
# already resolves to Object, so recording this one adds nothing and
|
|
62
|
+
# can shadow a superclass declared in another source.
|
|
63
|
+
unless superclass.delete_prefix('::') == 'Object'
|
|
64
|
+
result.push Solargraph::Pin::Reference::Superclass.new(name: superclass, closure: nspin,
|
|
65
|
+
source: :yard_map)
|
|
66
|
+
end
|
|
54
67
|
end
|
|
55
68
|
# @sg-ignore flow sensitive typing ought to be able to handle 'when ClassName'
|
|
56
69
|
code_object.class_mixins.each do |m|
|
|
@@ -72,6 +85,8 @@ module Solargraph
|
|
|
72
85
|
# @todo Check the visibility of <Class>.new
|
|
73
86
|
result.push ToMethod.make(code_object, 'new', :class, :public, closure, @spec)
|
|
74
87
|
result.push ToMethod.make(code_object, 'initialize', :instance, :private, closure, @spec)
|
|
88
|
+
elsif closure&.source == :yard_map_hack
|
|
89
|
+
result.push ToMethod.make(code_object, nil, :instance, nil, closure, @spec)
|
|
75
90
|
else
|
|
76
91
|
result.push ToMethod.make(code_object, nil, nil, nil, closure, @spec)
|
|
77
92
|
end
|
|
@@ -97,6 +112,24 @@ module Solargraph
|
|
|
97
112
|
def macros_for_method_object method_object
|
|
98
113
|
attached_macros_by_method_object[method_object]
|
|
99
114
|
end
|
|
115
|
+
|
|
116
|
+
# Handle a bug in YARD where code that opens a constant's singleton class
|
|
117
|
+
# (e.g., `class << ENV`) creates a pin that incorrectly overrides the
|
|
118
|
+
# original constant's value.
|
|
119
|
+
#
|
|
120
|
+
# @param code_object [YARD::CodeObjects::NamespaceObject]
|
|
121
|
+
# @return [Pin::Namespace]
|
|
122
|
+
def namespace_with_bug_fix code_object
|
|
123
|
+
core_pin = core_store.get_path_pins(code_object.path)
|
|
124
|
+
.find { |pin| pin.is_a?(Pin::Constant) }
|
|
125
|
+
if core_pin
|
|
126
|
+
location = Helpers.object_location(code_object, @spec)
|
|
127
|
+
Solargraph.logger.warn "Adjusting YARD pin that conflicts with RBS core (#{code_object.inspect} #{location.inspect})"
|
|
128
|
+
Pin::Namespace.new(name: core_pin.return_type.namespace, location: location, source: :yard_map_hack)
|
|
129
|
+
else
|
|
130
|
+
ToNamespace.make(code_object, @spec, @namespace_pins[code_object.namespace.to_s])
|
|
131
|
+
end
|
|
132
|
+
end
|
|
100
133
|
end
|
|
101
134
|
end
|
|
102
135
|
end
|
data/solargraph.gemspec
CHANGED
|
@@ -65,7 +65,7 @@ Gem::Specification.new do |s|
|
|
|
65
65
|
#
|
|
66
66
|
# even more specific on RuboCop itself, which is written into _todo
|
|
67
67
|
# file.
|
|
68
|
-
s.add_development_dependency 'overcommit', '~> 0.
|
|
68
|
+
s.add_development_dependency 'overcommit', '~> 0.71.0'
|
|
69
69
|
s.add_development_dependency 'rubocop', '~> 1.80.0.0'
|
|
70
70
|
s.add_development_dependency 'rubocop-rake', '~> 0.7.1'
|
|
71
71
|
s.add_development_dependency 'rubocop-rspec', '~> 3.6.0'
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: solargraph
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.60.
|
|
4
|
+
version: 0.60.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Fred Snyder
|
|
@@ -419,14 +419,14 @@ dependencies:
|
|
|
419
419
|
requirements:
|
|
420
420
|
- - "~>"
|
|
421
421
|
- !ruby/object:Gem::Version
|
|
422
|
-
version: 0.
|
|
422
|
+
version: 0.71.0
|
|
423
423
|
type: :development
|
|
424
424
|
prerelease: false
|
|
425
425
|
version_requirements: !ruby/object:Gem::Requirement
|
|
426
426
|
requirements:
|
|
427
427
|
- - "~>"
|
|
428
428
|
- !ruby/object:Gem::Version
|
|
429
|
-
version: 0.
|
|
429
|
+
version: 0.71.0
|
|
430
430
|
- !ruby/object:Gem::Dependency
|
|
431
431
|
name: rubocop
|
|
432
432
|
requirement: !ruby/object:Gem::Requirement
|