solargraph 0.47.2 → 0.54.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 (191) hide show
  1. checksums.yaml +4 -4
  2. data/.github/FUNDING.yml +1 -0
  3. data/.github/workflows/plugins.yml +40 -0
  4. data/.github/workflows/rspec.yml +4 -8
  5. data/.github/workflows/typecheck.yml +34 -0
  6. data/.yardopts +2 -2
  7. data/CHANGELOG.md +166 -3
  8. data/LICENSE +1 -1
  9. data/README.md +19 -16
  10. data/SPONSORS.md +2 -9
  11. data/lib/solargraph/api_map/cache.rb +50 -20
  12. data/lib/solargraph/api_map/source_to_yard.rb +17 -10
  13. data/lib/solargraph/api_map/store.rb +68 -15
  14. data/lib/solargraph/api_map.rb +238 -112
  15. data/lib/solargraph/bench.rb +3 -2
  16. data/lib/solargraph/cache.rb +77 -0
  17. data/lib/solargraph/complex_type/type_methods.rb +116 -35
  18. data/lib/solargraph/complex_type/unique_type.rb +261 -33
  19. data/lib/solargraph/complex_type.rb +149 -30
  20. data/lib/solargraph/convention/rakefile.rb +17 -0
  21. data/lib/solargraph/convention.rb +2 -3
  22. data/lib/solargraph/converters/dd.rb +5 -0
  23. data/lib/solargraph/converters/dl.rb +3 -0
  24. data/lib/solargraph/converters/dt.rb +3 -0
  25. data/lib/solargraph/diagnostics/rubocop.rb +23 -8
  26. data/lib/solargraph/diagnostics/rubocop_helpers.rb +4 -1
  27. data/lib/solargraph/diagnostics/type_check.rb +1 -0
  28. data/lib/solargraph/diagnostics.rb +2 -2
  29. data/lib/solargraph/doc_map.rb +187 -0
  30. data/lib/solargraph/gem_pins.rb +72 -0
  31. data/lib/solargraph/language_server/host/diagnoser.rb +2 -2
  32. data/lib/solargraph/language_server/host/dispatch.rb +22 -5
  33. data/lib/solargraph/language_server/host/message_worker.rb +4 -0
  34. data/lib/solargraph/language_server/host/sources.rb +8 -65
  35. data/lib/solargraph/language_server/host.rb +88 -93
  36. data/lib/solargraph/language_server/message/base.rb +1 -1
  37. data/lib/solargraph/language_server/message/completion_item/resolve.rb +3 -1
  38. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +13 -1
  39. data/lib/solargraph/language_server/message/extended/download_core.rb +1 -5
  40. data/lib/solargraph/language_server/message/initialize.rb +27 -0
  41. data/lib/solargraph/language_server/message/initialized.rb +1 -0
  42. data/lib/solargraph/language_server/message/text_document/document_symbol.rb +4 -1
  43. data/lib/solargraph/language_server/message/text_document/formatting.rb +5 -4
  44. data/lib/solargraph/language_server/message/text_document/hover.rb +2 -0
  45. data/lib/solargraph/language_server/message/text_document/signature_help.rb +1 -6
  46. data/lib/solargraph/language_server/message/text_document/type_definition.rb +24 -0
  47. data/lib/solargraph/language_server/message/text_document.rb +1 -1
  48. data/lib/solargraph/language_server/message/workspace/did_change_configuration.rb +5 -0
  49. data/lib/solargraph/language_server/message/workspace/did_change_watched_files.rb +10 -3
  50. data/lib/solargraph/language_server/message.rb +1 -0
  51. data/lib/solargraph/language_server/progress.rb +118 -0
  52. data/lib/solargraph/language_server/transport/adapter.rb +16 -1
  53. data/lib/solargraph/language_server/transport/data_reader.rb +2 -0
  54. data/lib/solargraph/language_server.rb +1 -0
  55. data/lib/solargraph/library.rb +231 -104
  56. data/lib/solargraph/location.rb +1 -0
  57. data/lib/solargraph/page.rb +6 -0
  58. data/lib/solargraph/parser/comment_ripper.rb +4 -0
  59. data/lib/solargraph/parser/node_methods.rb +47 -7
  60. data/lib/solargraph/parser/node_processor/base.rb +11 -1
  61. data/lib/solargraph/parser/node_processor.rb +1 -0
  62. data/lib/solargraph/parser/{legacy → parser_gem}/class_methods.rb +31 -9
  63. data/lib/solargraph/parser/{legacy → parser_gem}/flawed_builder.rb +3 -1
  64. data/lib/solargraph/parser/{legacy → parser_gem}/node_chainer.rb +57 -41
  65. data/lib/solargraph/parser/parser_gem/node_methods.rb +495 -0
  66. data/lib/solargraph/parser/{rubyvm → parser_gem}/node_processors/alias_node.rb +1 -1
  67. data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +53 -0
  68. data/lib/solargraph/parser/{rubyvm → parser_gem}/node_processors/begin_node.rb +1 -1
  69. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/block_node.rb +3 -2
  70. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/casgn_node.rb +14 -4
  71. data/lib/solargraph/parser/{rubyvm → parser_gem}/node_processors/cvasgn_node.rb +1 -1
  72. data/lib/solargraph/parser/{rubyvm → parser_gem}/node_processors/def_node.rb +7 -20
  73. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/defs_node.rb +2 -2
  74. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/gvasgn_node.rb +1 -1
  75. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/ivasgn_node.rb +2 -2
  76. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/lvasgn_node.rb +2 -2
  77. data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +47 -0
  78. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/namespace_node.rb +2 -2
  79. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/orasgn_node.rb +1 -1
  80. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/resbody_node.rb +3 -3
  81. data/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb +42 -0
  82. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/send_node.rb +7 -5
  83. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/sym_node.rb +1 -1
  84. data/lib/solargraph/parser/parser_gem/node_processors.rb +56 -0
  85. data/lib/solargraph/parser/parser_gem.rb +12 -0
  86. data/lib/solargraph/parser/region.rb +1 -1
  87. data/lib/solargraph/parser/snippet.rb +2 -0
  88. data/lib/solargraph/parser.rb +9 -10
  89. data/lib/solargraph/pin/base.rb +69 -11
  90. data/lib/solargraph/pin/base_variable.rb +40 -7
  91. data/lib/solargraph/pin/block.rb +81 -33
  92. data/lib/solargraph/pin/closure.rb +17 -2
  93. data/lib/solargraph/pin/common.rb +7 -3
  94. data/lib/solargraph/pin/conversions.rb +34 -8
  95. data/lib/solargraph/pin/delegated_method.rb +101 -0
  96. data/lib/solargraph/pin/documenting.rb +25 -32
  97. data/lib/solargraph/pin/instance_variable.rb +4 -0
  98. data/lib/solargraph/pin/local_variable.rb +13 -1
  99. data/lib/solargraph/pin/method.rb +273 -17
  100. data/lib/solargraph/pin/namespace.rb +17 -1
  101. data/lib/solargraph/pin/parameter.rb +40 -28
  102. data/lib/solargraph/pin/reference/override.rb +2 -2
  103. data/lib/solargraph/pin/reference.rb +8 -0
  104. data/lib/solargraph/pin/search.rb +4 -4
  105. data/lib/solargraph/pin/signature.rb +143 -0
  106. data/lib/solargraph/pin.rb +2 -1
  107. data/lib/solargraph/range.rb +4 -6
  108. data/lib/solargraph/rbs_map/conversions.rb +607 -0
  109. data/lib/solargraph/rbs_map/core_fills.rb +50 -0
  110. data/lib/solargraph/rbs_map/core_map.rb +28 -0
  111. data/lib/solargraph/rbs_map/stdlib_map.rb +33 -0
  112. data/lib/solargraph/rbs_map.rb +92 -0
  113. data/lib/solargraph/shell.rb +85 -59
  114. data/lib/solargraph/source/chain/array.rb +32 -0
  115. data/lib/solargraph/source/chain/block_symbol.rb +13 -0
  116. data/lib/solargraph/source/chain/call.rb +125 -61
  117. data/lib/solargraph/source/chain/constant.rb +15 -1
  118. data/lib/solargraph/source/chain/if.rb +23 -0
  119. data/lib/solargraph/source/chain/link.rb +8 -2
  120. data/lib/solargraph/source/chain/or.rb +1 -1
  121. data/lib/solargraph/source/chain/z_super.rb +3 -3
  122. data/lib/solargraph/source/chain.rb +64 -14
  123. data/lib/solargraph/source/change.rb +3 -0
  124. data/lib/solargraph/source/cursor.rb +2 -0
  125. data/lib/solargraph/source/source_chainer.rb +8 -5
  126. data/lib/solargraph/source/updater.rb +1 -0
  127. data/lib/solargraph/source.rb +18 -63
  128. data/lib/solargraph/source_map/clip.rb +31 -23
  129. data/lib/solargraph/source_map/mapper.rb +23 -7
  130. data/lib/solargraph/source_map.rb +36 -11
  131. data/lib/solargraph/type_checker/checks.rb +10 -2
  132. data/lib/solargraph/type_checker.rb +229 -100
  133. data/lib/solargraph/version.rb +1 -1
  134. data/lib/solargraph/views/environment.erb +2 -2
  135. data/lib/solargraph/workspace/config.rb +15 -11
  136. data/lib/solargraph/workspace.rb +41 -17
  137. data/lib/solargraph/yard_map/cache.rb +6 -0
  138. data/lib/solargraph/yard_map/helpers.rb +1 -1
  139. data/lib/solargraph/yard_map/mapper/to_method.rb +23 -7
  140. data/lib/solargraph/yard_map/mapper.rb +1 -1
  141. data/lib/solargraph/yard_map/to_method.rb +11 -4
  142. data/lib/solargraph/yard_map.rb +1 -443
  143. data/lib/solargraph/yard_tags.rb +20 -0
  144. data/lib/solargraph/yardoc.rb +52 -0
  145. data/lib/solargraph.rb +8 -6
  146. data/solargraph.gemspec +19 -8
  147. metadata +164 -99
  148. data/.travis.yml +0 -19
  149. data/lib/solargraph/api_map/bundler_methods.rb +0 -22
  150. data/lib/solargraph/compat.rb +0 -37
  151. data/lib/solargraph/convention/rspec.rb +0 -30
  152. data/lib/solargraph/documentor.rb +0 -76
  153. data/lib/solargraph/language_server/host/cataloger.rb +0 -56
  154. data/lib/solargraph/parser/legacy/node_methods.rb +0 -325
  155. data/lib/solargraph/parser/legacy/node_processors/alias_node.rb +0 -23
  156. data/lib/solargraph/parser/legacy/node_processors/args_node.rb +0 -35
  157. data/lib/solargraph/parser/legacy/node_processors/begin_node.rb +0 -15
  158. data/lib/solargraph/parser/legacy/node_processors/cvasgn_node.rb +0 -23
  159. data/lib/solargraph/parser/legacy/node_processors/def_node.rb +0 -63
  160. data/lib/solargraph/parser/legacy/node_processors/sclass_node.rb +0 -21
  161. data/lib/solargraph/parser/legacy/node_processors.rb +0 -54
  162. data/lib/solargraph/parser/legacy.rb +0 -12
  163. data/lib/solargraph/parser/rubyvm/class_methods.rb +0 -144
  164. data/lib/solargraph/parser/rubyvm/node_chainer.rb +0 -160
  165. data/lib/solargraph/parser/rubyvm/node_methods.rb +0 -315
  166. data/lib/solargraph/parser/rubyvm/node_processors/args_node.rb +0 -85
  167. data/lib/solargraph/parser/rubyvm/node_processors/block_node.rb +0 -42
  168. data/lib/solargraph/parser/rubyvm/node_processors/casgn_node.rb +0 -22
  169. data/lib/solargraph/parser/rubyvm/node_processors/defs_node.rb +0 -57
  170. data/lib/solargraph/parser/rubyvm/node_processors/gvasgn_node.rb +0 -23
  171. data/lib/solargraph/parser/rubyvm/node_processors/ivasgn_node.rb +0 -38
  172. data/lib/solargraph/parser/rubyvm/node_processors/kw_arg_node.rb +0 -39
  173. data/lib/solargraph/parser/rubyvm/node_processors/lit_node.rb +0 -20
  174. data/lib/solargraph/parser/rubyvm/node_processors/lvasgn_node.rb +0 -27
  175. data/lib/solargraph/parser/rubyvm/node_processors/namespace_node.rb +0 -39
  176. data/lib/solargraph/parser/rubyvm/node_processors/opt_arg_node.rb +0 -26
  177. data/lib/solargraph/parser/rubyvm/node_processors/orasgn_node.rb +0 -15
  178. data/lib/solargraph/parser/rubyvm/node_processors/resbody_node.rb +0 -45
  179. data/lib/solargraph/parser/rubyvm/node_processors/sclass_node.rb +0 -21
  180. data/lib/solargraph/parser/rubyvm/node_processors/scope_node.rb +0 -15
  181. data/lib/solargraph/parser/rubyvm/node_processors/send_node.rb +0 -277
  182. data/lib/solargraph/parser/rubyvm/node_processors/sym_node.rb +0 -18
  183. data/lib/solargraph/parser/rubyvm/node_processors.rb +0 -63
  184. data/lib/solargraph/parser/rubyvm.rb +0 -40
  185. data/lib/solargraph/yard_map/core_docs.rb +0 -170
  186. data/lib/solargraph/yard_map/core_fills.rb +0 -208
  187. data/lib/solargraph/yard_map/core_gen.rb +0 -76
  188. data/lib/solargraph/yard_map/rdoc_to_yard.rb +0 -140
  189. data/lib/solargraph/yard_map/stdlib_fills.rb +0 -43
  190. data/lib/yard-solargraph.rb +0 -33
  191. data/yardoc/2.2.2.tar.gz +0 -0
@@ -0,0 +1,77 @@
1
+ require 'fileutils'
2
+ require 'rbs'
3
+
4
+ module Solargraph
5
+ module Cache
6
+ class << self
7
+ # The base directory where cached documentation is installed.
8
+ #
9
+ # @return [String]
10
+ def base_dir
11
+ # The directory is not stored in a variable so it can be overridden
12
+ # in specs.
13
+ ENV['SOLARGRAPH_CACHE'] ||
14
+ (ENV['XDG_CACHE_HOME'] ? File.join(ENV['XDG_CACHE_HOME'], 'solargraph') : nil) ||
15
+ File.join(Dir.home, '.cache', 'solargraph')
16
+ end
17
+
18
+ # The working directory for the current Ruby, RBS, and Solargraph versions.
19
+ #
20
+ # @return [String]
21
+ def work_dir
22
+ # The directory is not stored in a variable so it can be overridden
23
+ # in specs.
24
+ File.join(base_dir, "ruby-#{RUBY_VERSION}", "rbs-#{RBS::VERSION}", "solargraph-#{Solargraph::VERSION}")
25
+ end
26
+
27
+ # Append the given path to the current cache directory (`work_dir`).
28
+ #
29
+ # @example
30
+ # Cache.join('date-3.4.1.ser')
31
+ #
32
+ # @param path [Array<String>]
33
+ # @return [String]
34
+ def join *path
35
+ File.join(work_dir, *path)
36
+ end
37
+
38
+ # @param path [Array<String>]
39
+ # @return [Array<Solargraph::Pin::Base>, nil]
40
+ def load *path
41
+ file = join(*path)
42
+ return nil unless File.file?(file)
43
+ Marshal.load(File.read(file, mode: 'rb'))
44
+ rescue StandardError => e
45
+ Solargraph.logger.warn "Failed to load cached file #{file}: [#{e.class}] #{e.message}"
46
+ FileUtils.rm_f file
47
+ nil
48
+ end
49
+
50
+ def exist? *path
51
+ File.file? join(*path)
52
+ end
53
+
54
+ # @param path [Array<String>]
55
+ # @param pins [Array<Pin::Base>]
56
+ # @return [void]
57
+ def save *path, pins
58
+ file = File.join(work_dir, *path)
59
+ base = File.dirname(file)
60
+ FileUtils.mkdir_p base unless File.directory?(base)
61
+ ser = Marshal.dump(pins)
62
+ File.write file, ser, mode: 'wb'
63
+ end
64
+
65
+ # @return [void]
66
+ # @param path [Array<String>]
67
+ def uncache *path
68
+ FileUtils.rm_rf File.join(work_dir, *path), secure: true
69
+ end
70
+
71
+ # @return [void]
72
+ def clear
73
+ FileUtils.rm_rf base_dir, secure: true
74
+ end
75
+ end
76
+ end
77
+ end
@@ -2,20 +2,38 @@
2
2
 
3
3
  module Solargraph
4
4
  class ComplexType
5
- # Methods for accessing type data.
5
+ # Methods for accessing type data available from
6
+ # both ComplexType and UniqueType.
6
7
  #
8
+ # @abstract This mixin relies on these -
9
+ # instance variables:
10
+ # @name: String
11
+ # @subtypes: Array<ComplexType>
12
+ # @rooted: boolish
13
+ # methods:
14
+ # transform()
7
15
  module TypeMethods
16
+ # @!method transform(new_name = nil, &transform_type)
17
+ # @param new_name [String, nil]
18
+ # @yieldparam t [UniqueType]
19
+ # @yieldreturn [UniqueType]
20
+ # @return [UniqueType, nil]
21
+
8
22
  # @return [String]
9
23
  attr_reader :name
10
24
 
11
- # @return [String]
12
- attr_reader :substring
25
+ # @return [Array<ComplexType>]
26
+ attr_reader :subtypes
13
27
 
14
28
  # @return [String]
15
- attr_reader :tag
29
+ def tag
30
+ @tag ||= "#{name}#{substring}"
31
+ end
16
32
 
17
- # @return [Array<ComplexType>]
18
- attr_reader :subtypes
33
+ # @return [String]
34
+ def rooted_tag
35
+ @rooted_tag ||= rooted_name + rooted_substring
36
+ end
19
37
 
20
38
  # @return [Boolean]
21
39
  def duck_type?
@@ -24,8 +42,7 @@ module Solargraph
24
42
 
25
43
  # @return [Boolean]
26
44
  def nil_type?
27
- @nil_type = (name.casecmp('nil') == 0) if @nil_type.nil?
28
- @nil_type
45
+ @nil_type ||= (name.casecmp('nil') == 0)
29
46
  end
30
47
 
31
48
  # @return [Boolean]
@@ -33,6 +50,10 @@ module Solargraph
33
50
  !substring.empty?
34
51
  end
35
52
 
53
+ def tuple?
54
+ @tuple_type ||= (name == 'Tuple') || (name == 'Array' && subtypes.length >= 1 && fixed_parameters?)
55
+ end
56
+
36
57
  def void?
37
58
  name == 'void'
38
59
  end
@@ -45,19 +66,44 @@ module Solargraph
45
66
  name == 'undefined'
46
67
  end
47
68
 
69
+ # @param generics_to_erase [Enumerable<String>]
70
+ # @return [self]
71
+ def erase_generics(generics_to_erase)
72
+ transform do |type|
73
+ if type.name == ComplexType::GENERIC_TAG_NAME
74
+ if type.all_params.length == 1 && generics_to_erase.include?(type.all_params.first.to_s)
75
+ ComplexType::UNDEFINED
76
+ else
77
+ type
78
+ end
79
+ else
80
+ type
81
+ end
82
+ end
83
+ end
84
+
85
+ # @return [Symbol, nil]
86
+ attr_reader :parameters_type
87
+
88
+ PARAMETERS_TYPE_BY_STARTING_TAG = {
89
+ '{' => :hash,
90
+ '(' => :fixed,
91
+ '<' => :list
92
+ }.freeze
93
+
48
94
  # @return [Boolean]
49
95
  def list_parameters?
50
- substring.start_with?('<')
96
+ parameters_type == :list
51
97
  end
52
98
 
53
99
  # @return [Boolean]
54
100
  def fixed_parameters?
55
- substring.start_with?('(')
101
+ parameters_type == :fixed
56
102
  end
57
103
 
58
104
  # @return [Boolean]
59
105
  def hash_parameters?
60
- substring.start_with?('{')
106
+ parameters_type == :hash
61
107
  end
62
108
 
63
109
  # @return [Array<ComplexType>]
@@ -80,12 +126,52 @@ module Solargraph
80
126
  end.call
81
127
  end
82
128
 
83
- # @return [Symbol] :class or :instance
129
+ # @return [String]
130
+ def rooted_namespace
131
+ return namespace unless rooted?
132
+ "::#{namespace}"
133
+ end
134
+
135
+ # @return [String]
136
+ def rooted_name
137
+ return name unless rooted?
138
+ "::#{name}"
139
+ end
140
+
141
+ # @return [String]
142
+ def substring
143
+ @substring ||= generate_substring_from(&:tags)
144
+ end
145
+
146
+ # @return [String]
147
+ def rooted_substring
148
+ @rooted_substring = generate_substring_from(&:rooted_tags)
149
+ end
150
+
151
+ # @return [String]
152
+ def generate_substring_from(&to_str)
153
+ key_types_str = key_types.map(&to_str).join(', ')
154
+ subtypes_str = subtypes.map(&to_str).join(', ')
155
+ if key_types.none?(&:defined?) && subtypes.none?(&:defined?)
156
+ ''
157
+ elsif key_types.empty? && subtypes.empty?
158
+ ''
159
+ elsif hash_parameters?
160
+ "{#{key_types_str} => #{subtypes_str}}"
161
+ elsif fixed_parameters?
162
+ "(#{subtypes_str})"
163
+ else
164
+ "<#{subtypes_str}>"
165
+ end
166
+ end
167
+
168
+ # @return [::Symbol] :class or :instance
84
169
  def scope
85
170
  @scope ||= :instance if duck_type? || nil_type?
86
171
  @scope ||= (name == 'Class' || name == 'Module') && !subtypes.empty? ? :class : :instance
87
172
  end
88
173
 
174
+ # @param other [Object]
89
175
  def == other
90
176
  return false unless self.class == other.class
91
177
  tag == other.tag
@@ -99,32 +185,27 @@ module Solargraph
99
185
  #
100
186
  # @param api_map [ApiMap] The ApiMap that performs qualification
101
187
  # @param context [String] The namespace from which to resolve names
102
- # @return [ComplexType] The generated ComplexType
188
+ # @return [self, ComplexType, UniqueType] The generated ComplexType
103
189
  def qualify api_map, context = ''
104
- return ComplexType.new([self]) if duck_type? || void? || undefined?
105
- recon = (rooted? ? '' : context)
106
- fqns = api_map.qualify(name, recon)
107
- if fqns.nil?
108
- return UniqueType::BOOLEAN if tag == 'Boolean'
109
- return UniqueType::UNDEFINED
110
- end
111
- fqns = "::#{fqns}" # Ensure the resulting complex type is rooted
112
- ltypes = key_types.map do |t|
113
- t.qualify api_map, context
114
- end
115
- rtypes = value_types.map do |t|
116
- t.qualify api_map, context
117
- end
118
- if list_parameters?
119
- Solargraph::ComplexType.parse("#{fqns}<#{rtypes.map(&:tag).join(', ')}>")
120
- elsif fixed_parameters?
121
- Solargraph::ComplexType.parse("#{fqns}(#{rtypes.map(&:tag).join(', ')})")
122
- elsif hash_parameters?
123
- Solargraph::ComplexType.parse("#{fqns}{#{ltypes.map(&:tag).join(', ')} => #{rtypes.map(&:tag).join(', ')}}")
124
- else
125
- Solargraph::ComplexType.parse(fqns)
190
+ transform do |t|
191
+ next t if t.name == GENERIC_TAG_NAME
192
+ next t if t.duck_type? || t.void? || t.undefined?
193
+ recon = (t.rooted? ? '' : context)
194
+ fqns = api_map.qualify(t.name, recon)
195
+ if fqns.nil?
196
+ next UniqueType::BOOLEAN if t.tag == 'Boolean'
197
+ next UniqueType::UNDEFINED
198
+ end
199
+ t.recreate(new_name: fqns, make_rooted: true)
126
200
  end
127
201
  end
202
+
203
+ # @yieldparam [UniqueType]
204
+ # @return [Enumerator<UniqueType>]
205
+ def each_unique_type &block
206
+ return enum_for(__method__) unless block_given?
207
+ yield self
208
+ end
128
209
  end
129
210
  end
130
211
  end
@@ -8,6 +8,8 @@ module Solargraph
8
8
  class UniqueType
9
9
  include TypeMethods
10
10
 
11
+ attr_reader :all_params, :subtypes, :key_types
12
+
11
13
  # Create a UniqueType with the specified name and an optional substring.
12
14
  # The substring is the parameter section of a parametrized type, e.g.,
13
15
  # for the type `Array<String>`, the name is `Array` and the substring is
@@ -15,52 +17,278 @@ module Solargraph
15
17
  #
16
18
  # @param name [String] The name of the type
17
19
  # @param substring [String] The substring of the type
18
- def initialize name, substring = ''
20
+ # @param make_rooted [Boolean, nil]
21
+ # @return [UniqueType]
22
+ def self.parse name, substring = '', make_rooted: nil
23
+ if name.start_with?(':::')
24
+ raise "Illegal prefix: #{name}"
25
+ end
19
26
  if name.start_with?('::')
20
- @name = name[2..-1]
21
- @rooted = true
27
+ name = name[2..-1]
28
+ rooted = true
22
29
  else
23
- @name = name
24
- @rooted = false
30
+ rooted = false
25
31
  end
26
- @substring = substring
27
- @tag = @name + substring
28
- @key_types = []
29
- @subtypes = []
30
- return unless parameters?
31
- if @substring.start_with?('<(') && @substring.end_with?(')>')
32
- subs = ComplexType.parse(substring[2..-3], partial: true)
33
- else
32
+ rooted = make_rooted unless make_rooted.nil?
33
+
34
+ # @type [Array<ComplexType>]
35
+ key_types = []
36
+ # @type [Array<ComplexType>]
37
+ subtypes = []
38
+ parameters_type = nil
39
+ unless substring.empty?
34
40
  subs = ComplexType.parse(substring[1..-2], partial: true)
41
+ parameters_type = PARAMETERS_TYPE_BY_STARTING_TAG.fetch(substring[0])
42
+ if parameters_type == :hash
43
+ 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)
44
+ # @todo should be able to resolve map; both types have it
45
+ # with same return type
46
+ # @sg-ignore
47
+ key_types.concat(subs[0].map { |u| ComplexType.new([u]) })
48
+ # @sg-ignore
49
+ subtypes.concat(subs[1].map { |u| ComplexType.new([u]) })
50
+ else
51
+ subtypes.concat subs
52
+ end
35
53
  end
36
- if hash_parameters?
37
- 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)
38
- @key_types.concat subs[0].map { |u| ComplexType.new([u]) }
39
- @subtypes.concat subs[1].map { |u| ComplexType.new([u]) }
40
- else
41
- @subtypes.concat subs
54
+ new(name, key_types, subtypes, rooted: rooted, parameters_type: parameters_type)
55
+ end
56
+
57
+ # @param name [String]
58
+ # @param key_types [Array<ComplexType>]
59
+ # @param subtypes [Array<ComplexType>]
60
+ # @param rooted [Boolean]
61
+ # @param parameters_type [Symbol, nil]
62
+ def initialize(name, key_types = [], subtypes = [], rooted:, parameters_type: nil)
63
+ if parameters_type.nil?
64
+ raise "You must supply parameters_type if you provide parameters" unless key_types.empty? && subtypes.empty?
42
65
  end
66
+ raise "Please remove leading :: and set rooted instead - #{name}" if name.start_with?('::')
67
+ @name = name
68
+ @key_types = key_types
69
+ @subtypes = subtypes
70
+ @rooted = rooted
71
+ @all_params = []
72
+ @all_params.concat key_types
73
+ @all_params.concat subtypes
74
+ @parameters_type = parameters_type
43
75
  end
44
76
 
45
77
  def to_s
46
78
  tag
47
79
  end
48
80
 
49
- def self_to dst
50
- return self unless selfy?
51
- new_name = (@name == 'self' ? dst : @name)
52
- new_key_types = @key_types.map { |t| t.self_to dst }
53
- new_subtypes = @subtypes.map { |t| t.self_to dst }
54
- if hash_parameters?
55
- UniqueType.new(new_name, "{#{new_key_types.join(', ')} => #{new_subtypes.join(', ')}}")
56
- elsif parameters?
57
- if @substring.start_with?'<('
58
- UniqueType.new(new_name, "<(#{new_subtypes.join(', ')})>")
81
+ # @return [Array<UniqueType>]
82
+ def items
83
+ [self]
84
+ end
85
+
86
+ # @return [String]
87
+ def rbs_name
88
+ if name == 'undefined'
89
+ 'untyped'
90
+ else
91
+ rooted_name
92
+ end
93
+ end
94
+
95
+ # @return [String]
96
+ def to_rbs
97
+ if duck_type?
98
+ 'untyped'
99
+ elsif name == 'Boolean'
100
+ 'bool'
101
+ elsif name.downcase == 'nil'
102
+ 'nil'
103
+ elsif name == GENERIC_TAG_NAME
104
+ all_params.first.name
105
+ elsif ['Class', 'Module'].include?(name)
106
+ rbs_name
107
+ elsif ['Tuple', 'Array'].include?(name) && fixed_parameters?
108
+ # tuples don't have a name; they're just [foo, bar, baz].
109
+ if substring == '()'
110
+ # but there are no zero element tuples, so we go with an array
111
+ 'Array[]'
112
+ else
113
+ # already generated surrounded by []
114
+ parameters_as_rbs
115
+ end
116
+ else
117
+ "#{rbs_name}#{parameters_as_rbs}"
118
+ end
119
+ end
120
+
121
+ # @return [Boolean]
122
+ def parameters?
123
+ !all_params.empty?
124
+ end
125
+
126
+ # @param types [Array<UniqueType, ComplexType>]
127
+ # @return [String]
128
+ def rbs_union(types)
129
+ if types.length == 1
130
+ types.first.to_rbs
131
+ else
132
+ "(#{types.map(&:to_rbs).join(' | ')})"
133
+ end
134
+ end
135
+
136
+ # @return [String]
137
+ def parameters_as_rbs
138
+ return '' unless parameters?
139
+
140
+ return "[#{all_params.map(&:to_rbs).join(', ')}]" if key_types.empty?
141
+
142
+ # handle, e.g., Hash[K, V] case
143
+ key_types_str = rbs_union(key_types)
144
+ subtypes_str = rbs_union(subtypes)
145
+ "[#{key_types_str}, #{subtypes_str}]"
146
+ end
147
+
148
+ def generic?
149
+ name == GENERIC_TAG_NAME || all_params.any?(&:generic?)
150
+ end
151
+
152
+ # @param generics_to_resolve [Enumerable<String>]
153
+ # @param context_type [UniqueType, nil]
154
+ # @param resolved_generic_values [Hash{String => ComplexType}] Added to as types are encountered or resolved
155
+ # @return [UniqueType, ComplexType]
156
+ def resolve_generics_from_context generics_to_resolve, context_type, resolved_generic_values: {}
157
+ if name == ComplexType::GENERIC_TAG_NAME
158
+ type_param = subtypes.first&.name
159
+ return self unless generics_to_resolve.include? type_param
160
+ unless context_type.nil? || !resolved_generic_values[type_param].nil?
161
+ new_binding = true
162
+ resolved_generic_values[type_param] = context_type
163
+ end
164
+ if new_binding
165
+ resolved_generic_values.transform_values! do |complex_type|
166
+ complex_type.resolve_generics_from_context(generics_to_resolve, nil, resolved_generic_values: resolved_generic_values)
167
+ end
168
+ end
169
+ return resolved_generic_values[type_param] || self
170
+ end
171
+
172
+ # @todo typechecking should complain when the method being called has no @yieldparam tag
173
+ new_key_types = resolve_param_generics_from_context(generics_to_resolve, context_type, resolved_generic_values, &:key_types)
174
+ new_subtypes = resolve_param_generics_from_context(generics_to_resolve, context_type, resolved_generic_values, &:subtypes)
175
+ recreate(new_key_types: new_key_types, new_subtypes: new_subtypes)
176
+ end
177
+
178
+ # @param generics_to_resolve [Enumerable<String>]
179
+ # @param context_type [UniqueType]
180
+ # @param resolved_generic_values [Hash{String => ComplexType}]
181
+ # @yieldreturn [Array<ComplexType>]
182
+ # @return [Array<ComplexType>]
183
+ def resolve_param_generics_from_context(generics_to_resolve, context_type, resolved_generic_values)
184
+ types = yield self
185
+ types.each_with_index.flat_map do |ct, i|
186
+ ct.items.flat_map do |ut|
187
+ context_params = yield context_type if context_type
188
+ if context_params && context_params[i]
189
+ type_arg = context_params[i]
190
+ type_arg.map do |new_unique_context_type|
191
+ ut.resolve_generics_from_context generics_to_resolve, new_unique_context_type, resolved_generic_values: resolved_generic_values
192
+ end
193
+ else
194
+ ut.resolve_generics_from_context generics_to_resolve, nil, resolved_generic_values: resolved_generic_values
195
+ end
196
+ end
197
+ end
198
+ end
199
+
200
+ # Probe the concrete type for each of the generic type
201
+ # parameters used in this type, and return a new type if
202
+ # possible.
203
+ #
204
+ # @param definitions [Pin::Namespace, Pin::Method] The module/class/method which uses generic types
205
+ # @param context_type [ComplexType] The receiver type
206
+ # @return [UniqueType, ComplexType]
207
+ def resolve_generics definitions, context_type
208
+ return self if definitions.nil? || definitions.generics.empty?
209
+
210
+ transform(name) do |t|
211
+ if t.name == GENERIC_TAG_NAME
212
+ idx = definitions.generics.index(t.subtypes.first&.name)
213
+ next t if idx.nil?
214
+ context_type.all_params[idx] || ComplexType::UNDEFINED
59
215
  else
60
- UniqueType.new(new_name, "<#{new_subtypes.join(', ')}>")
216
+ t
61
217
  end
218
+ end
219
+ end
220
+
221
+ # @yieldparam t [self]
222
+ # @yieldreturn [self]
223
+ # @return [Array<self>]
224
+ def map &block
225
+ [block.yield(self)]
226
+ end
227
+
228
+ # @return [Array<UniqueType>]
229
+ def to_a
230
+ [self]
231
+ end
232
+
233
+ # @param new_name [String, nil]
234
+ # @param make_rooted [Boolean, nil]
235
+ # @param new_key_types [Array<UniqueType>, nil]
236
+ # @param rooted [Boolean, nil]
237
+ # @param new_subtypes [Array<UniqueType>, nil]
238
+ # @return [self]
239
+ def recreate(new_name: nil, make_rooted: nil, new_key_types: nil, new_subtypes: nil)
240
+ raise "Please remove leading :: and set rooted instead - #{new_name}" if new_name&.start_with?('::')
241
+ new_name ||= name
242
+ new_key_types ||= @key_types
243
+ new_subtypes ||= @subtypes
244
+ make_rooted = @rooted if make_rooted.nil?
245
+ UniqueType.new(new_name, new_key_types, new_subtypes, rooted: make_rooted, parameters_type: parameters_type)
246
+ end
247
+
248
+ # @return [String]
249
+ def rooted_tags
250
+ rooted_tag
251
+ end
252
+
253
+ # @return [String]
254
+ def tags
255
+ tag
256
+ end
257
+
258
+ # @return [self]
259
+ def force_rooted
260
+ transform do |t|
261
+ t.recreate(make_rooted: true)
262
+ end
263
+ end
264
+
265
+ # Apply the given transformation to each subtype and then finally to this type
266
+ #
267
+ # @param new_name [String, nil]
268
+ # @yieldparam t [UniqueType]
269
+ # @yieldreturn [self]
270
+ # @return [self]
271
+ def transform(new_name = nil, &transform_type)
272
+ raise "Please remove leading :: and set rooted with recreate() instead - #{new_name}" if new_name&.start_with?('::')
273
+ if name == ComplexType::GENERIC_TAG_NAME
274
+ # doesn't make sense to manipulate the name of the generic
275
+ new_key_types = @key_types
276
+ new_subtypes = @subtypes
62
277
  else
63
- UniqueType.new(new_name)
278
+ new_key_types = @key_types.flat_map { |ct| ct.items.map { |ut| ut.transform(&transform_type) } }
279
+ new_subtypes = @subtypes.flat_map { |ct| ct.items.map { |ut| ut.transform(&transform_type) } }
280
+ end
281
+ new_type = recreate(new_name: new_name || name, new_key_types: new_key_types, new_subtypes: new_subtypes)
282
+ yield new_type
283
+ end
284
+
285
+ # Transform references to the 'self' type to the specified concrete namespace
286
+ # @param dst [String]
287
+ # @return [UniqueType]
288
+ def self_to dst
289
+ transform do |t|
290
+ next t if t.name != 'self'
291
+ t.recreate(new_name: dst, new_key_types: [], new_subtypes: [])
64
292
  end
65
293
  end
66
294
 
@@ -68,8 +296,8 @@ module Solargraph
68
296
  @name == 'self' || @key_types.any?(&:selfy?) || @subtypes.any?(&:selfy?)
69
297
  end
70
298
 
71
- UNDEFINED = UniqueType.new('undefined')
72
- BOOLEAN = UniqueType.new('Boolean')
299
+ UNDEFINED = UniqueType.new('undefined', rooted: false)
300
+ BOOLEAN = UniqueType.new('Boolean', rooted: true)
73
301
  end
74
302
  end
75
303
  end