solargraph 0.54.1 → 0.56.0

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.
Files changed (135) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/plugins.yml +2 -0
  3. data/.github/workflows/typecheck.yml +3 -1
  4. data/.gitignore +2 -0
  5. data/CHANGELOG.md +70 -0
  6. data/README.md +13 -3
  7. data/lib/solargraph/api_map/cache.rb +10 -1
  8. data/lib/solargraph/api_map/index.rb +175 -0
  9. data/lib/solargraph/api_map/store.rb +79 -126
  10. data/lib/solargraph/api_map.rb +247 -93
  11. data/lib/solargraph/bench.rb +17 -1
  12. data/lib/solargraph/complex_type/type_methods.rb +13 -1
  13. data/lib/solargraph/complex_type/unique_type.rb +118 -9
  14. data/lib/solargraph/complex_type.rb +48 -18
  15. data/lib/solargraph/convention/base.rb +3 -3
  16. data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +60 -0
  17. data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +100 -0
  18. data/lib/solargraph/convention/struct_definition.rb +101 -0
  19. data/lib/solargraph/convention.rb +5 -3
  20. data/lib/solargraph/doc_map.rb +274 -56
  21. data/lib/solargraph/equality.rb +33 -0
  22. data/lib/solargraph/gem_pins.rb +53 -37
  23. data/lib/solargraph/language_server/host/message_worker.rb +31 -11
  24. data/lib/solargraph/language_server/host.rb +24 -13
  25. data/lib/solargraph/language_server/message/base.rb +19 -12
  26. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +2 -0
  27. data/lib/solargraph/language_server/message/extended/document.rb +5 -2
  28. data/lib/solargraph/language_server/message/extended/document_gems.rb +3 -3
  29. data/lib/solargraph/language_server/message/initialize.rb +3 -1
  30. data/lib/solargraph/language_server/message/text_document/completion.rb +0 -3
  31. data/lib/solargraph/language_server/message/text_document/formatting.rb +4 -0
  32. data/lib/solargraph/library.rb +13 -11
  33. data/lib/solargraph/location.rb +28 -0
  34. data/lib/solargraph/logging.rb +1 -0
  35. data/lib/solargraph/parser/comment_ripper.rb +12 -6
  36. data/lib/solargraph/parser/flow_sensitive_typing.rb +227 -0
  37. data/lib/solargraph/parser/node_methods.rb +15 -1
  38. data/lib/solargraph/parser/node_processor.rb +3 -1
  39. data/lib/solargraph/parser/parser_gem/class_methods.rb +16 -14
  40. data/lib/solargraph/parser/parser_gem/node_chainer.rb +17 -23
  41. data/lib/solargraph/parser/parser_gem/node_methods.rb +5 -3
  42. data/lib/solargraph/parser/parser_gem/node_processors/alias_node.rb +2 -1
  43. data/lib/solargraph/parser/parser_gem/node_processors/and_node.rb +21 -0
  44. data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +4 -2
  45. data/lib/solargraph/parser/parser_gem/node_processors/block_node.rb +4 -2
  46. data/lib/solargraph/parser/parser_gem/node_processors/casgn_node.rb +23 -2
  47. data/lib/solargraph/parser/parser_gem/node_processors/cvasgn_node.rb +2 -1
  48. data/lib/solargraph/parser/parser_gem/node_processors/def_node.rb +6 -3
  49. data/lib/solargraph/parser/parser_gem/node_processors/defs_node.rb +2 -1
  50. data/lib/solargraph/parser/parser_gem/node_processors/gvasgn_node.rb +2 -1
  51. data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +21 -0
  52. data/lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb +4 -2
  53. data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +4 -3
  54. data/lib/solargraph/parser/parser_gem/node_processors/namespace_node.rb +29 -6
  55. data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +41 -0
  56. data/lib/solargraph/parser/parser_gem/node_processors/resbody_node.rb +2 -1
  57. data/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb +4 -3
  58. data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +28 -16
  59. data/lib/solargraph/parser/parser_gem/node_processors/sym_node.rb +2 -1
  60. data/lib/solargraph/parser/parser_gem/node_processors/until_node.rb +29 -0
  61. data/lib/solargraph/parser/parser_gem/node_processors/while_node.rb +29 -0
  62. data/lib/solargraph/parser/parser_gem/node_processors.rb +10 -0
  63. data/lib/solargraph/parser/region.rb +1 -1
  64. data/lib/solargraph/parser.rb +1 -0
  65. data/lib/solargraph/pin/base.rb +326 -43
  66. data/lib/solargraph/pin/base_variable.rb +18 -10
  67. data/lib/solargraph/pin/block.rb +3 -3
  68. data/lib/solargraph/pin/breakable.rb +9 -0
  69. data/lib/solargraph/pin/callable.rb +77 -6
  70. data/lib/solargraph/pin/closure.rb +18 -1
  71. data/lib/solargraph/pin/common.rb +5 -0
  72. data/lib/solargraph/pin/delegated_method.rb +20 -1
  73. data/lib/solargraph/pin/documenting.rb +16 -0
  74. data/lib/solargraph/pin/keyword.rb +7 -2
  75. data/lib/solargraph/pin/local_variable.rb +15 -6
  76. data/lib/solargraph/pin/method.rb +172 -42
  77. data/lib/solargraph/pin/namespace.rb +17 -9
  78. data/lib/solargraph/pin/parameter.rb +61 -11
  79. data/lib/solargraph/pin/proxy_type.rb +12 -6
  80. data/lib/solargraph/pin/reference/override.rb +10 -6
  81. data/lib/solargraph/pin/reference/require.rb +2 -2
  82. data/lib/solargraph/pin/signature.rb +42 -0
  83. data/lib/solargraph/pin/singleton.rb +1 -1
  84. data/lib/solargraph/pin/symbol.rb +3 -2
  85. data/lib/solargraph/pin/until.rb +18 -0
  86. data/lib/solargraph/pin/while.rb +18 -0
  87. data/lib/solargraph/pin.rb +4 -1
  88. data/lib/solargraph/pin_cache.rb +185 -0
  89. data/lib/solargraph/position.rb +16 -0
  90. data/lib/solargraph/range.rb +16 -0
  91. data/lib/solargraph/rbs_map/conversions.rb +199 -66
  92. data/lib/solargraph/rbs_map/core_fills.rb +32 -16
  93. data/lib/solargraph/rbs_map/core_map.rb +34 -11
  94. data/lib/solargraph/rbs_map/stdlib_map.rb +15 -5
  95. data/lib/solargraph/rbs_map.rb +74 -16
  96. data/lib/solargraph/shell.rb +19 -18
  97. data/lib/solargraph/source/chain/array.rb +12 -8
  98. data/lib/solargraph/source/chain/block_symbol.rb +1 -1
  99. data/lib/solargraph/source/chain/block_variable.rb +1 -1
  100. data/lib/solargraph/source/chain/call.rb +62 -25
  101. data/lib/solargraph/source/chain/constant.rb +1 -1
  102. data/lib/solargraph/source/chain/hash.rb +9 -3
  103. data/lib/solargraph/source/chain/head.rb +1 -1
  104. data/lib/solargraph/source/chain/if.rb +6 -1
  105. data/lib/solargraph/source/chain/link.rb +19 -5
  106. data/lib/solargraph/source/chain/literal.rb +27 -2
  107. data/lib/solargraph/source/chain/or.rb +1 -1
  108. data/lib/solargraph/source/chain/z_super.rb +1 -1
  109. data/lib/solargraph/source/chain.rb +106 -62
  110. data/lib/solargraph/source/cursor.rb +1 -11
  111. data/lib/solargraph/source/source_chainer.rb +2 -2
  112. data/lib/solargraph/source.rb +2 -1
  113. data/lib/solargraph/source_map/clip.rb +4 -2
  114. data/lib/solargraph/source_map/mapper.rb +9 -5
  115. data/lib/solargraph/source_map.rb +0 -17
  116. data/lib/solargraph/type_checker/checks.rb +4 -0
  117. data/lib/solargraph/type_checker.rb +41 -14
  118. data/lib/solargraph/version.rb +1 -1
  119. data/lib/solargraph/views/_method.erb +10 -10
  120. data/lib/solargraph/views/_namespace.erb +3 -3
  121. data/lib/solargraph/views/document.erb +10 -10
  122. data/lib/solargraph/workspace/config.rb +7 -3
  123. data/lib/solargraph/workspace.rb +16 -6
  124. data/lib/solargraph/yard_map/mapper/to_constant.rb +5 -2
  125. data/lib/solargraph/yard_map/mapper/to_method.rb +55 -15
  126. data/lib/solargraph/yard_map/mapper/to_namespace.rb +5 -2
  127. data/lib/solargraph/yard_map/mapper.rb +5 -3
  128. data/lib/solargraph/yard_map/to_method.rb +4 -2
  129. data/lib/solargraph/yardoc.rb +6 -9
  130. data/lib/solargraph.rb +20 -1
  131. data/rbs/fills/tuple.rbs +150 -0
  132. data/rbs_collection.yaml +19 -0
  133. data/solargraph.gemspec +6 -5
  134. metadata +56 -29
  135. data/lib/solargraph/cache.rb +0 -77
@@ -7,9 +7,15 @@ module Solargraph
7
7
  #
8
8
  class UniqueType
9
9
  include TypeMethods
10
+ include Equality
10
11
 
11
12
  attr_reader :all_params, :subtypes, :key_types
12
13
 
14
+ # @sg-ignore Fix "Not enough arguments to Module#protected"
15
+ protected def equality_fields
16
+ [@name, @all_params, @subtypes, @key_types]
17
+ end
18
+
13
19
  # Create a UniqueType with the specified name and an optional substring.
14
20
  # The substring is the parameter section of a parametrized type, e.g.,
15
21
  # for the type `Array<String>`, the name is `Array` and the substring is
@@ -21,7 +27,7 @@ module Solargraph
21
27
  # @return [UniqueType]
22
28
  def self.parse name, substring = '', make_rooted: nil
23
29
  if name.start_with?(':::')
24
- raise "Illegal prefix: #{name}"
30
+ raise ComplexTypeError, "Illegal prefix: #{name}"
25
31
  end
26
32
  if name.start_with?('::')
27
33
  name = name[2..-1]
@@ -42,13 +48,20 @@ module Solargraph
42
48
  subs = ComplexType.parse(substring[1..-2], partial: true)
43
49
  parameters_type = PARAMETERS_TYPE_BY_STARTING_TAG.fetch(substring[0])
44
50
  if parameters_type == :hash
45
- raise ComplexTypeError, "Bad hash type" unless !subs.is_a?(ComplexType) and subs.length == 2 and !subs[0].is_a?(UniqueType) and !subs[1].is_a?(UniqueType)
51
+ raise ComplexTypeError, "Bad hash type: name=#{name}, substring=#{substring}" unless !subs.is_a?(ComplexType) and subs.length == 2 and !subs[0].is_a?(UniqueType) and !subs[1].is_a?(UniqueType)
46
52
  # @todo should be able to resolve map; both types have it
47
53
  # with same return type
48
54
  # @sg-ignore
49
55
  key_types.concat(subs[0].map { |u| ComplexType.new([u]) })
50
56
  # @sg-ignore
51
57
  subtypes.concat(subs[1].map { |u| ComplexType.new([u]) })
58
+ elsif parameters_type == :list && name == 'Hash'
59
+ # Treat Hash<A, B> as Hash{A => B}
60
+ if subs.length != 2
61
+ raise ComplexTypeError, "Bad hash type: name=#{name}, substring=#{substring} - must have exactly two parameters"
62
+ end
63
+ key_types.concat(subs[0].map { |u| ComplexType.new([u]) })
64
+ subtypes.concat(subs[1].map { |u| ComplexType.new([u]) })
52
65
  else
53
66
  subtypes.concat subs
54
67
  end
@@ -67,19 +80,63 @@ module Solargraph
67
80
  end
68
81
  raise "Please remove leading :: and set rooted instead - #{name.inspect}" if name.start_with?('::')
69
82
  @name = name
70
- @key_types = key_types
71
- @subtypes = subtypes
83
+ @parameters_type = parameters_type
84
+ if implicit_union?
85
+ @key_types = key_types.uniq
86
+ @subtypes = subtypes.uniq
87
+ else
88
+ @key_types = key_types
89
+ @subtypes = subtypes
90
+ end
72
91
  @rooted = rooted
73
92
  @all_params = []
74
- @all_params.concat key_types
75
- @all_params.concat subtypes
76
- @parameters_type = parameters_type
93
+ @all_params.concat @key_types
94
+ @all_params.concat @subtypes
95
+ end
96
+
97
+ def implicit_union?
98
+ # @todo use api_map to establish number of generics in type;
99
+ # if only one is allowed but multiple are passed in, treat
100
+ # those as implicit unions
101
+ ['Hash', 'Array', 'Set', '_ToAry', 'Enumerable', '_Each'].include?(name) && parameters_type != :fixed
77
102
  end
78
103
 
79
104
  def to_s
80
105
  tag
81
106
  end
82
107
 
108
+ def simplify_literals
109
+ transform do |t|
110
+ next t unless t.literal?
111
+ t.recreate(new_name: t.non_literal_name)
112
+ end
113
+ end
114
+
115
+ def literal?
116
+ non_literal_name != name
117
+ end
118
+
119
+ def non_literal_name
120
+ @non_literal_name ||= determine_non_literal_name
121
+ end
122
+
123
+ def determine_non_literal_name
124
+ # https://github.com/ruby/rbs/blob/master/docs/syntax.md
125
+ #
126
+ # _literal_ ::= _string-literal_
127
+ # | _symbol-literal_
128
+ # | _integer-literal_
129
+ # | `true`
130
+ # | `false`
131
+ return name if name.empty?
132
+ return 'NilClass' if name == 'nil'
133
+ return 'Boolean' if ['true', 'false'].include?(name)
134
+ return 'Symbol' if name[0] == ':'
135
+ return 'String' if ['"', "'"].include?(name[0])
136
+ return 'Integer' if name.match?(/^-?\d+$/)
137
+ name
138
+ end
139
+
83
140
  def eql?(other)
84
141
  self.class == other.class &&
85
142
  @name == other.name &&
@@ -107,11 +164,17 @@ module Solargraph
107
164
  def rbs_name
108
165
  if name == 'undefined'
109
166
  'untyped'
167
+ elsif literal?
168
+ name
110
169
  else
111
170
  rooted_name
112
171
  end
113
172
  end
114
173
 
174
+ def desc
175
+ rooted_tags
176
+ end
177
+
115
178
  # @return [String]
116
179
  def to_rbs
117
180
  if duck_type?
@@ -173,6 +236,23 @@ module Solargraph
173
236
  name == GENERIC_TAG_NAME || all_params.any?(&:generic?)
174
237
  end
175
238
 
239
+ # @param api_map [ApiMap] The ApiMap that performs qualification
240
+ # @param atype [ComplexType] type which may be assigned to this type
241
+ def can_assign?(api_map, atype)
242
+ logger.debug { "UniqueType#can_assign?(self=#{rooted_tags.inspect}, atype=#{atype.rooted_tags.inspect})" }
243
+ downcasted_atype = atype.downcast_to_literal_if_possible
244
+ out = downcasted_atype.all? do |autype|
245
+ autype.name == name || api_map.super_and_sub?(name, autype.name)
246
+ end
247
+ logger.debug { "UniqueType#can_assign?(self=#{rooted_tags.inspect}, atype=#{atype.rooted_tags.inspect}) => #{out}" }
248
+ out
249
+ end
250
+
251
+ # @return [UniqueType]
252
+ def downcast_to_literal_if_possible
253
+ SINGLE_SUBTYPE.fetch(rooted_tag, self)
254
+ end
255
+
176
256
  # @param generics_to_resolve [Enumerable<String>]
177
257
  # @param context_type [UniqueType, nil]
178
258
  # @param resolved_generic_values [Hash{String => ComplexType}] Added to as types are encountered or resolved
@@ -233,9 +313,26 @@ module Solargraph
233
313
 
234
314
  transform(name) do |t|
235
315
  if t.name == GENERIC_TAG_NAME
236
- idx = definitions.generics.index(t.subtypes.first&.name)
316
+ generic_name = t.subtypes.first&.name
317
+ idx = definitions.generics.index(generic_name)
237
318
  next t if idx.nil?
238
- context_type.all_params[idx] || ComplexType::UNDEFINED
319
+ if context_type.parameters_type == :hash
320
+ if idx == 0
321
+ next ComplexType.new(context_type.key_types)
322
+ elsif idx == 1
323
+ next ComplexType.new(context_type.subtypes)
324
+ else
325
+ next ComplexType::UNDEFINED
326
+ end
327
+ elsif context_type.all?(&:implicit_union?)
328
+ if idx == 0 && !context_type.all_params.empty?
329
+ ComplexType.new(context_type.all_params)
330
+ else
331
+ ComplexType::UNDEFINED
332
+ end
333
+ else
334
+ context_type.all_params[idx] || definitions.generic_defaults[generic_name] || ComplexType::UNDEFINED
335
+ end
239
336
  else
240
337
  t
241
338
  end
@@ -361,6 +458,18 @@ module Solargraph
361
458
 
362
459
  UNDEFINED = UniqueType.new('undefined', rooted: false)
363
460
  BOOLEAN = UniqueType.new('Boolean', rooted: true)
461
+ TRUE = UniqueType.new('true', rooted: true)
462
+ FALSE = UniqueType.new('false', rooted: true)
463
+ NIL = UniqueType.new('nil', rooted: true)
464
+ # @type [Hash{String => UniqueType}]
465
+ SINGLE_SUBTYPE = {
466
+ '::TrueClass' => UniqueType::TRUE,
467
+ '::FalseClass' => UniqueType::FALSE,
468
+ '::NilClass' => UniqueType::NIL
469
+ }.freeze
470
+
471
+
472
+ include Logging
364
473
  end
365
474
  end
366
475
  end
@@ -7,6 +7,7 @@ module Solargraph
7
7
  GENERIC_TAG_NAME = 'generic'.freeze
8
8
  # @!parse
9
9
  # include TypeMethods
10
+ include Equality
10
11
 
11
12
  autoload :TypeMethods, 'solargraph/complex_type/type_methods'
12
13
  autoload :UniqueType, 'solargraph/complex_type/unique_type'
@@ -15,20 +16,18 @@ module Solargraph
15
16
  def initialize types = [UniqueType::UNDEFINED]
16
17
  # @todo @items here should not need an annotation
17
18
  # @type [Array<UniqueType>]
18
- @items = types.flat_map(&:items).uniq(&:to_s)
19
- end
20
-
21
- def eql?(other)
22
- self.class == other.class &&
23
- @items == other.items
24
- end
25
-
26
- def ==(other)
27
- self.eql?(other)
19
+ items = types.flat_map(&:items).uniq(&:to_s)
20
+ if items.any? { |i| i.name == 'false' } && items.any? { |i| i.name == 'true' }
21
+ items.delete_if { |i| i.name == 'false' || i.name == 'true' }
22
+ items.unshift(ComplexType::BOOLEAN)
23
+ end
24
+ items = [UniqueType::UNDEFINED] if items.any?(&:undefined?)
25
+ @items = items
28
26
  end
29
27
 
30
- def hash
31
- [self.class, @items].hash
28
+ # @sg-ignore Fix "Not enough arguments to Module#protected"
29
+ protected def equality_fields
30
+ [self.class, items]
32
31
  end
33
32
 
34
33
  # @param api_map [ApiMap]
@@ -77,7 +76,7 @@ module Solargraph
77
76
  end
78
77
 
79
78
  # @yieldparam [UniqueType]
80
- # @return [Array]
79
+ # @return [Array<UniqueType>]
81
80
  def map &block
82
81
  @items.map &block
83
82
  end
@@ -100,6 +99,12 @@ module Solargraph
100
99
  end
101
100
  end
102
101
 
102
+ # @param atype [ComplexType] type which may be assigned to this type
103
+ # @param api_map [ApiMap] The ApiMap that performs qualification
104
+ def can_assign?(api_map, atype)
105
+ any? { |ut| ut.can_assign?(api_map, atype) }
106
+ end
107
+
103
108
  # @return [Integer]
104
109
  def length
105
110
  @items.length
@@ -110,10 +115,6 @@ module Solargraph
110
115
  @items
111
116
  end
112
117
 
113
- def tags
114
- @items.map(&:tag).join(', ')
115
- end
116
-
117
118
  # @param index [Integer]
118
119
  # @return [UniqueType]
119
120
  def [](index)
@@ -154,14 +155,39 @@ module Solargraph
154
155
  map(&:tag).join(', ')
155
156
  end
156
157
 
158
+ def tags
159
+ map(&:tag).join(', ')
160
+ end
161
+
162
+ def simple_tags
163
+ simplify_literals.tags
164
+ end
165
+
166
+ def literal?
167
+ @items.any?(&:literal?)
168
+ end
169
+
170
+ # @return [ComplexType]
171
+ def downcast_to_literal_if_possible
172
+ ComplexType.new(items.map(&:downcast_to_literal_if_possible))
173
+ end
174
+
175
+ def desc
176
+ rooted_tags
177
+ end
178
+
157
179
  def rooted_tags
158
180
  map(&:rooted_tag).join(', ')
159
181
  end
160
182
 
183
+ # @yieldparam [UniqueType]
161
184
  def all? &block
162
185
  @items.all? &block
163
186
  end
164
187
 
188
+ # @yieldparam [UniqueType]
189
+ # @yieldreturn [Boolean]
190
+ # @return [Boolean]
165
191
  def any? &block
166
192
  @items.compact.any? &block
167
193
  end
@@ -174,6 +200,10 @@ module Solargraph
174
200
  any?(&:generic?)
175
201
  end
176
202
 
203
+ def simplify_literals
204
+ ComplexType.new(map(&:simplify_literals))
205
+ end
206
+
177
207
  # @param new_name [String, nil]
178
208
  # @yieldparam t [UniqueType]
179
209
  # @yieldreturn [UniqueType]
@@ -263,7 +293,7 @@ module Solargraph
263
293
  # Consumers should not need to use this parameter; it should only be
264
294
  # used internally.
265
295
  #
266
- # @param *strings [Array<String>] The type definitions to parse
296
+ # @param strings [Array<String>] The type definitions to parse
267
297
  # @return [ComplexType]
268
298
  # # @overload parse(*strings, partial: false)
269
299
  # # @todo Need ability to use a literal true as a type below
@@ -20,12 +20,12 @@ module Solargraph
20
20
  EMPTY_ENVIRON
21
21
  end
22
22
 
23
- # The Environ for a YARD map.
23
+ # The Environ for a DocMap.
24
24
  # Subclasses can override this method.
25
25
  #
26
- # @param yard_map [YardMap]
26
+ # @param doc_map [DocMap]
27
27
  # @return [Environ]
28
- def global yard_map
28
+ def global doc_map
29
29
  EMPTY_ENVIRON
30
30
  end
31
31
  end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ module Convention
5
+ module StructDefinition
6
+ # A node wrapper for a Struct definition via const assignment.
7
+ # @example
8
+ # MyStruct = Struct.new(:bar, :baz) do
9
+ # def foo
10
+ # end
11
+ # end
12
+ class StructAssignmentNode < StructDefintionNode
13
+ class << self
14
+ # @example
15
+ # s(:casgn, nil, :Foo,
16
+ # s(:block,
17
+ # s(:send,
18
+ # s(:const, nil, :Struct), :new,
19
+ # s(:sym, :bar),
20
+ # s(:sym, :baz)),
21
+ # s(:args),
22
+ # s(:def, :foo,
23
+ # s(:args),
24
+ # s(:send, nil, :bar))))
25
+ def valid?(node)
26
+ return false unless node&.type == :casgn
27
+ return false if node.children[2].nil?
28
+
29
+ struct_node = if node.children[2].type == :block
30
+ node.children[2].children[0]
31
+ else
32
+ node.children[2]
33
+ end
34
+
35
+ struct_definition_node?(struct_node)
36
+ end
37
+ end
38
+
39
+ def class_name
40
+ if node.children[0]
41
+ Parser::NodeMethods.unpack_name(node.children[0]) + "::#{node.children[1]}"
42
+ else
43
+ node.children[1].to_s
44
+ end
45
+ end
46
+
47
+ private
48
+
49
+ # @return [Parser::AST::Node]
50
+ def struct_node
51
+ if node.children[2].type == :block
52
+ node.children[2].children[0]
53
+ else
54
+ node.children[2]
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ module Convention
5
+ module StructDefinition
6
+ # A node wrapper for a Struct definition via inheritance.
7
+ # @example
8
+ # class MyStruct < Struct.new(:bar, :baz)
9
+ # def foo
10
+ # end
11
+ # end
12
+ class StructDefintionNode
13
+ class << self
14
+ # @example
15
+ # s(:class,
16
+ # s(:const, nil, :Foo),
17
+ # s(:send,
18
+ # s(:const, nil, :Struct), :new,
19
+ # s(:sym, :bar),
20
+ # s(:sym, :baz)),
21
+ # s(:hash,
22
+ # s(:pair,
23
+ # s(:sym, :keyword_init),
24
+ # s(:true)))),
25
+ # s(:def, :foo,
26
+ # s(:args),
27
+ # s(:send, nil, :bar)))
28
+ def valid?(node)
29
+ return false unless node&.type == :class
30
+
31
+ struct_definition_node?(node.children[1])
32
+ end
33
+
34
+ private
35
+
36
+ # @param struct_node [Parser::AST::Node]
37
+ # @return [Boolean]
38
+ def struct_definition_node?(struct_node)
39
+ return false unless struct_node.is_a?(::Parser::AST::Node)
40
+ return false unless struct_node&.type == :send
41
+ return false unless struct_node.children[0]&.type == :const
42
+ return false unless struct_node.children[0].children[1] == :Struct
43
+ return false unless struct_node.children[1] == :new
44
+
45
+ true
46
+ end
47
+ end
48
+
49
+ # @return [Parser::AST::Node]
50
+ def initialize(node)
51
+ @node = node
52
+ end
53
+
54
+ # @return [String]
55
+ def class_name
56
+ Parser::NodeMethods.unpack_name(node)
57
+ end
58
+
59
+ # @return [Array<Array(Parser::AST::Node, String)>]
60
+ def attributes
61
+ struct_attribute_nodes.map do |struct_def_param|
62
+ next unless struct_def_param.type == :sym
63
+ [struct_def_param, struct_def_param.children[0].to_s]
64
+ end.compact
65
+ end
66
+
67
+ def keyword_init?
68
+ keyword_init_param = struct_attribute_nodes.find do |struct_def_param|
69
+ struct_def_param.type == :hash && struct_def_param.children[0].type == :pair &&
70
+ struct_def_param.children[0].children[0].children[0] == :keyword_init
71
+ end
72
+
73
+ return false if keyword_init_param.nil?
74
+
75
+ keyword_init_param.children[0].children[1].type == :true
76
+ end
77
+
78
+ # @return [Parser::AST::Node]
79
+ def body_node
80
+ node.children[2]
81
+ end
82
+
83
+ private
84
+
85
+ # @return [Parser::AST::Node]
86
+ attr_reader :node
87
+
88
+ # @return [Parser::AST::Node]
89
+ def struct_node
90
+ node.children[1]
91
+ end
92
+
93
+ # @return [Array<Parser::AST::Node>]
94
+ def struct_attribute_nodes
95
+ struct_node.children[2..-1]
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ module Convention
5
+ module StructDefinition
6
+ autoload :StructDefintionNode, 'solargraph/convention/struct_definition/struct_definition_node'
7
+ autoload :StructAssignmentNode, 'solargraph/convention/struct_definition/struct_assignment_node'
8
+
9
+ module NodeProcessors
10
+ class StructNode < Parser::NodeProcessor::Base
11
+ def process
12
+ return if struct_definition_node.nil?
13
+
14
+ loc = get_node_location(node)
15
+ nspin = Solargraph::Pin::Namespace.new(
16
+ type: :class,
17
+ location: loc,
18
+ closure: region.closure,
19
+ name: struct_definition_node.class_name,
20
+ comments: comments_for(node),
21
+ visibility: :public,
22
+ gates: region.closure.gates.freeze
23
+ )
24
+ pins.push nspin
25
+
26
+ # define initialize method
27
+ initialize_method_pin = Pin::Method.new(
28
+ name: 'initialize',
29
+ parameters: [],
30
+ scope: :instance,
31
+ location: get_node_location(node),
32
+ closure: nspin,
33
+ visibility: :private,
34
+ comments: comments_for(node)
35
+ )
36
+
37
+ pins.push initialize_method_pin
38
+
39
+ struct_definition_node.attributes.map do |attribute_node, attribute_name|
40
+ initialize_method_pin.parameters.push(
41
+ Pin::Parameter.new(
42
+ name: attribute_name,
43
+ decl: struct_definition_node.keyword_init? ? :kwarg : :arg,
44
+ location: get_node_location(attribute_node),
45
+ closure: initialize_method_pin
46
+ )
47
+ )
48
+ end
49
+
50
+ # define attribute accessors and instance variables
51
+ struct_definition_node.attributes.each do |attribute_node, attribute_name|
52
+ [attribute_name, "#{attribute_name}="].each do |name|
53
+ method_pin = Pin::Method.new(
54
+ name: name,
55
+ parameters: [],
56
+ scope: :instance,
57
+ location: get_node_location(attribute_node),
58
+ closure: nspin,
59
+ comments: attribute_comments(attribute_node, attribute_name),
60
+ visibility: :public
61
+ )
62
+
63
+ pins.push method_pin
64
+
65
+ next unless name.include?('=') # setter
66
+ pins.push Pin::InstanceVariable.new(name: "@#{attribute_name}",
67
+ closure: method_pin,
68
+ location: get_node_location(attribute_node),
69
+ comments: attribute_comments(attribute_node, attribute_name))
70
+ end
71
+ end
72
+
73
+ process_children region.update(closure: nspin, visibility: :public)
74
+ end
75
+
76
+ private
77
+
78
+ # @return [StructDefintionNode, nil]
79
+ def struct_definition_node
80
+ @struct_definition_node ||= if StructDefintionNode.valid?(node)
81
+ StructDefintionNode.new(node)
82
+ elsif StructAssignmentNode.valid?(node)
83
+ StructAssignmentNode.new(node)
84
+ end
85
+ end
86
+
87
+ # @param attribute_node [Parser::AST::Node]
88
+ # @return [String, nil]
89
+ def attribute_comments(attribute_node, attribute_name)
90
+ struct_comments = comments_for(attribute_node)
91
+ return if struct_comments.nil? || struct_comments.empty?
92
+
93
+ struct_comments.split("\n").find do |row|
94
+ row.include?(attribute_name)
95
+ end&.gsub('@param', '@return')&.gsub(attribute_name, '')
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -10,7 +10,9 @@ module Solargraph
10
10
  autoload :Gemfile, 'solargraph/convention/gemfile'
11
11
  autoload :Gemspec, 'solargraph/convention/gemspec'
12
12
  autoload :Rakefile, 'solargraph/convention/rakefile'
13
+ autoload :StructDefinition, 'solargraph/convention/struct_definition'
13
14
 
15
+ # @type [Set<Convention::Base>]
14
16
  @@conventions = Set.new
15
17
 
16
18
  # @param convention [Class<Convention::Base>]
@@ -29,12 +31,12 @@ module Solargraph
29
31
  result
30
32
  end
31
33
 
32
- # @param yard_map [YardMap]
34
+ # @param yard_map [DocMap]
33
35
  # @return [Environ]
34
- def self.for_global(yard_map)
36
+ def self.for_global(doc_map)
35
37
  result = Environ.new
36
38
  @@conventions.each do |conv|
37
- result.merge conv.global(yard_map)
39
+ result.merge conv.global(doc_map)
38
40
  end
39
41
  result
40
42
  end