solargraph 0.60.4 → 0.62.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 (127) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/linting.yml +24 -2
  3. data/.github/workflows/plugins.yml +19 -1
  4. data/.github/workflows/rspec.yml +17 -2
  5. data/.github/workflows/typecheck.yml +6 -0
  6. data/CHANGELOG.md +54 -0
  7. data/Gemfile +0 -3
  8. data/lib/solargraph/api_map/index.rb +28 -4
  9. data/lib/solargraph/api_map/store.rb +19 -0
  10. data/lib/solargraph/api_map.rb +91 -80
  11. data/lib/solargraph/cache_dir.rb +58 -0
  12. data/lib/solargraph/collection/base.rb +50 -0
  13. data/lib/solargraph/collection/core.rb +17 -0
  14. data/lib/solargraph/collection/gem.rb +59 -0
  15. data/lib/solargraph/collection/stdlib.rb +25 -0
  16. data/lib/solargraph/collection.rb +30 -0
  17. data/lib/solargraph/complex_type/unique_type.rb +13 -0
  18. data/lib/solargraph/complex_type.rb +23 -3
  19. data/lib/solargraph/convention.rb +5 -7
  20. data/lib/solargraph/diagnostics/require_not_found.rb +0 -4
  21. data/lib/solargraph/diagnostics/type_check.rb +0 -1
  22. data/lib/solargraph/external.rb +187 -0
  23. data/lib/solargraph/language_server/host/diagnoser.rb +1 -1
  24. data/lib/solargraph/language_server/host/dispatch.rb +4 -2
  25. data/lib/solargraph/language_server/host.rb +10 -2
  26. data/lib/solargraph/language_server/message/completion_item/resolve.rb +10 -3
  27. data/lib/solargraph/language_server/message/text_document/definition.rb +5 -2
  28. data/lib/solargraph/language_server/message/text_document/hover.rb +1 -2
  29. data/lib/solargraph/library.rb +26 -53
  30. data/lib/solargraph/metagem.rb +80 -0
  31. data/lib/solargraph/parser/node_processor/base.rb +6 -0
  32. data/lib/solargraph/parser/parser_gem/node_chainer.rb +22 -3
  33. data/lib/solargraph/parser/parser_gem/node_methods.rb +29 -6
  34. data/lib/solargraph/parser/parser_gem/node_processors/class_definition/class_assignment_node.rb +98 -0
  35. data/lib/solargraph/parser/parser_gem/node_processors/class_definition.rb +54 -0
  36. data/lib/solargraph/parser/parser_gem/node_processors/cvasgn_node.rb +1 -0
  37. data/lib/solargraph/parser/parser_gem/node_processors/data_definition/data_assignment_node.rb +65 -0
  38. data/lib/solargraph/parser/parser_gem/node_processors/data_definition/data_definition_node.rb +97 -0
  39. data/lib/solargraph/parser/parser_gem/node_processors/data_definition.rb +110 -0
  40. data/lib/solargraph/parser/parser_gem/node_processors/gvasgn_node.rb +1 -0
  41. data/lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb +2 -0
  42. data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +1 -0
  43. data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +185 -0
  44. data/lib/solargraph/parser/parser_gem/node_processors/struct_definition/struct_assignment_node.rb +66 -0
  45. data/lib/solargraph/parser/parser_gem/node_processors/struct_definition/struct_definition_node.rb +108 -0
  46. data/lib/solargraph/parser/parser_gem/node_processors/struct_definition.rb +170 -0
  47. data/lib/solargraph/parser/parser_gem/node_processors.rb +33 -29
  48. data/lib/solargraph/pin/base.rb +25 -1
  49. data/lib/solargraph/pin/base_variable.rb +31 -1
  50. data/lib/solargraph/pin/callable.rb +11 -2
  51. data/lib/solargraph/pin/closure.rb +17 -0
  52. data/lib/solargraph/pin/delegated_method.rb +5 -0
  53. data/lib/solargraph/pin/duck_method.rb +36 -16
  54. data/lib/solargraph/pin/factory_parameter.rb +84 -0
  55. data/lib/solargraph/pin/instance_variable.rb +45 -0
  56. data/lib/solargraph/pin/method.rb +10 -0
  57. data/lib/solargraph/pin/method_alias.rb +4 -0
  58. data/lib/solargraph/pin/parameter.rb +1 -1
  59. data/lib/solargraph/pin/reference/included_mixin.rb +22 -0
  60. data/lib/solargraph/pin/reference.rb +1 -0
  61. data/lib/solargraph/pin.rb +1 -0
  62. data/lib/solargraph/plugin/active_support.rb +100 -0
  63. data/lib/solargraph/plugin/addon.rb +16 -0
  64. data/lib/solargraph/plugin/base.rb +25 -0
  65. data/lib/solargraph/plugin/panel.rb +37 -0
  66. data/lib/solargraph/plugin.rb +26 -0
  67. data/lib/solargraph/rbs_collection.rb +119 -0
  68. data/lib/solargraph/rbs_map/base.rb +31 -0
  69. data/lib/solargraph/rbs_map/conversions.rb +109 -48
  70. data/lib/solargraph/rbs_map/{core_map.rb → core.rb} +18 -39
  71. data/lib/solargraph/rbs_map/core_fills.rb +1 -1
  72. data/lib/solargraph/rbs_map/gem.rb +16 -0
  73. data/lib/solargraph/rbs_map/helpers.rb +61 -0
  74. data/lib/solargraph/rbs_map/path.rb +19 -0
  75. data/lib/solargraph/rbs_map/stdlib.rb +24 -0
  76. data/lib/solargraph/rbs_map.rb +7 -198
  77. data/lib/solargraph/rbs_translator.rb +105 -70
  78. data/lib/solargraph/repo.rb +177 -0
  79. data/lib/solargraph/shell.rb +49 -99
  80. data/lib/solargraph/source/chain/call.rb +58 -14
  81. data/lib/solargraph/source/chain/literal.rb +17 -17
  82. data/lib/solargraph/source/chain/parameter.rb +79 -0
  83. data/lib/solargraph/source/chain.rb +8 -0
  84. data/lib/solargraph/source/node_map.rb +27 -0
  85. data/lib/solargraph/source/source_chainer.rb +40 -44
  86. data/lib/solargraph/source.rb +17 -0
  87. data/lib/solargraph/source_map/call.rb +18 -0
  88. data/lib/solargraph/source_map/clip.rb +5 -3
  89. data/lib/solargraph/source_map/mapper.rb +6 -2
  90. data/lib/solargraph/source_map.rb +28 -9
  91. data/lib/solargraph/type_checker/rules.rb +3 -2
  92. data/lib/solargraph/type_checker.rb +4 -1
  93. data/lib/solargraph/version.rb +1 -1
  94. data/lib/solargraph/workspace.rb +0 -1
  95. data/lib/solargraph/yard_map/cache.rb +1 -1
  96. data/lib/solargraph/yard_map/directives/attribute_directive.rb +1 -1
  97. data/lib/solargraph/yard_map/directives/domain_directive.rb +1 -1
  98. data/lib/solargraph/yard_map/directives/method_directive.rb +1 -1
  99. data/lib/solargraph/yard_map/directives/override_directive.rb +1 -1
  100. data/lib/solargraph/yard_map/directives/parse_directive.rb +1 -1
  101. data/lib/solargraph/yard_map/directives/scope_directive.rb +178 -0
  102. data/lib/solargraph/yard_map/directives/visibility_directive.rb +1 -1
  103. data/lib/solargraph/yard_map/directives.rb +5 -2
  104. data/lib/solargraph/yard_map/helpers.rb +1 -1
  105. data/lib/solargraph/yard_map/macro.rb +85 -6
  106. data/lib/solargraph/yard_map/mapper/to_constant.rb +1 -1
  107. data/lib/solargraph/yard_map/mapper/to_method.rb +1 -1
  108. data/lib/solargraph/yard_map/mapper/to_namespace.rb +1 -1
  109. data/lib/solargraph/yard_map/mapper.rb +2 -2
  110. data/lib/solargraph/yard_map.rb +1 -1
  111. data/lib/solargraph/yardoc.rb +43 -60
  112. data/lib/solargraph.rb +8 -3
  113. data/rbs/fills/boolean/0/boolean.rbs +12 -0
  114. data/solargraph.gemspec +2 -2
  115. metadata +41 -18
  116. data/lib/solargraph/convention/active_support_concern.rb +0 -111
  117. data/lib/solargraph/convention/data_definition/data_assignment_node.rb +0 -61
  118. data/lib/solargraph/convention/data_definition/data_definition_node.rb +0 -93
  119. data/lib/solargraph/convention/data_definition.rb +0 -108
  120. data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +0 -62
  121. data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +0 -103
  122. data/lib/solargraph/convention/struct_definition.rb +0 -168
  123. data/lib/solargraph/doc_map.rb +0 -439
  124. data/lib/solargraph/gem_pins.rb +0 -87
  125. data/lib/solargraph/pin_cache.rb +0 -248
  126. data/lib/solargraph/rbs_map/stdlib_map.rb +0 -71
  127. data/lib/solargraph/workspace/gemspecs.rb +0 -373
@@ -1,207 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'digest'
4
- require 'pathname'
5
3
  require 'rbs'
6
4
 
7
5
  module Solargraph
8
- class RbsMap
6
+ module RbsMap
7
+ autoload :Base, 'solargraph/rbs_map/base'
9
8
  autoload :Conversions, 'solargraph/rbs_map/conversions'
10
- autoload :CoreMap, 'solargraph/rbs_map/core_map'
9
+ autoload :Core, 'solargraph/rbs_map/core'
11
10
  autoload :CoreFills, 'solargraph/rbs_map/core_fills'
12
- autoload :StdlibMap, 'solargraph/rbs_map/stdlib_map'
13
-
14
- include Logging
15
-
16
- # @type [Hash{String => RbsMap}]
17
- @@rbs_maps_hash = {}
18
-
19
- attr_reader :library, :rbs_collection_paths, :rbs_collection_config_path
20
-
21
- # @param library [String]
22
- # @param version [String, nil]
23
- # @param rbs_collection_config_path [String, Pathname, nil]
24
- # @param rbs_collection_paths [Array<Pathname, String>]
25
- # @param out [StringIO, IO, nil] where to log messages
26
- def initialize library, version = nil, rbs_collection_config_path: nil, rbs_collection_paths: [], out: $stderr
27
- if rbs_collection_config_path.nil? && !rbs_collection_paths.empty?
28
- raise 'Please provide rbs_collection_config_path if you provide rbs_collection_paths'
29
- end
30
- @library = library
31
- @version = version
32
- @rbs_collection_config_path = rbs_collection_config_path
33
- @rbs_collection_paths = rbs_collection_paths
34
- add_library loader, library, version
35
- end
36
-
37
- CACHE_KEY_GEM_EXPORT = 'gem-export'
38
- CACHE_KEY_UNRESOLVED = 'unresolved'
39
- CACHE_KEY_STDLIB = 'stdlib'
40
- CACHE_KEY_LOCAL = 'local'
41
-
42
- # @param cache_key [String, nil]
43
- # @return [String, nil] a description of the source of the RBS info
44
- def self.rbs_source_desc cache_key
45
- case cache_key
46
- when CACHE_KEY_GEM_EXPORT
47
- 'RBS gem export'
48
- when CACHE_KEY_UNRESOLVED
49
- nil
50
- when CACHE_KEY_STDLIB
51
- 'RBS standard library'
52
- when CACHE_KEY_LOCAL
53
- 'local RBS shims'
54
- else
55
- 'RBS collection'
56
- end
57
- end
58
-
59
- # @return [RBS::EnvironmentLoader]
60
- def loader
61
- @loader ||= RBS::EnvironmentLoader.new(core_root: nil, repository: repository)
62
- end
63
-
64
- # @return [String] representing the version of the RBS info fetched
65
- # for the given library. Must change when the RBS info is
66
- # updated upstream for the same library and version. May change
67
- # if the config for where information comes form changes.
68
- def cache_key
69
- return CACHE_KEY_UNRESOLVED unless resolved?
70
-
71
- @cache_key ||= begin
72
- # @type [String, nil]
73
- data = nil
74
- # @type gem_config [nil, Hash{String => Hash{String => String}}]
75
- gem_config = nil
76
- if rbs_collection_config_path
77
- # @sg-ignore rbs_collection_config_path is not nil here
78
- lockfile_path = RBS::Collection::Config.to_lockfile_path(Pathname.new(rbs_collection_config_path))
79
- if lockfile_path.exist?
80
- collection_config = RBS::Collection::Config.from_path lockfile_path
81
- gem_config = collection_config.gem(library)
82
- data = gem_config&.to_s
83
- end
84
- end
85
- if gem_config.nil?
86
- CACHE_KEY_STDLIB
87
- else
88
- # @type [String]
89
- source = gem_config.dig('source', 'type')
90
- case source
91
- when 'rubygems'
92
- CACHE_KEY_GEM_EXPORT
93
- when 'local'
94
- CACHE_KEY_LOCAL
95
- when 'stdlib'
96
- CACHE_KEY_STDLIB
97
- else
98
- Digest::SHA1.hexdigest(data || '')
99
- end
100
- end
101
- end
102
- end
103
-
104
- # @param gemspec [Gem::Specification, Bundler::LazySpecification]
105
- # @param rbs_collection_path [String, Pathname, nil]
106
- # @param rbs_collection_config_path [String, Pathname, nil]
107
- # @return [RbsMap]
108
- def self.from_gemspec gemspec, rbs_collection_path, rbs_collection_config_path
109
- # prefers stdlib RBS if available
110
- rbs_map = RbsMap::StdlibMap.new(gemspec.name)
111
- return rbs_map if rbs_map.resolved?
112
-
113
- rbs_map = RbsMap.new(gemspec.name, gemspec.version,
114
- rbs_collection_paths: [rbs_collection_path].compact,
115
- rbs_collection_config_path: rbs_collection_config_path)
116
- return rbs_map if rbs_map.resolved?
117
-
118
- # try any version of the gem in the collection
119
- RbsMap.new(gemspec.name, nil,
120
- rbs_collection_paths: [rbs_collection_path].compact,
121
- rbs_collection_config_path: rbs_collection_config_path)
122
- end
123
-
124
- # @param out [IO, nil] where to log messages
125
- # @return [Array<Pin::Base>]
126
- def pins out: $stderr
127
- @pins ||= if resolved?
128
- conversions.pins
129
- else
130
- []
131
- end
132
- end
133
-
134
- # @generic T
135
- # @param path [String]
136
- # @param klass [Class<generic<T>>]
137
- #
138
- # @sg-ignore Need to be able to resolve generics based on a
139
- # Class<generic<T>> param
140
- # @return [generic<T>, nil]
141
- def path_pin path, klass = Pin::Base
142
- pin = pins.find { |p| p.path == path }
143
- pin if pin.is_a?(klass)
144
- end
145
-
146
- # @param path [String]
147
- # @return [Array<Pin::Base>]
148
- def path_pins path
149
- pins.select { |p| p.path == path }
150
- end
151
-
152
- def resolved?
153
- @resolved
154
- end
155
-
156
- # @return [RBS::Repository]
157
- def repository
158
- @repository ||= RBS::Repository.new(no_stdlib: false).tap do |repo|
159
- @rbs_collection_paths.each do |dir|
160
- dir_path = Pathname.new(dir)
161
- repo.add(dir_path) if dir_path.exist? && dir_path.directory?
162
- end
163
- end
164
- end
165
-
166
- # @param library [String]
167
- # @return [RbsMap]
168
- def self.load library
169
- @@rbs_maps_hash[library] ||= RbsMap.new(library)
170
- end
171
-
172
- private
173
-
174
- # @return [Conversions]
175
- def conversions
176
- @conversions ||= Conversions.new(loader: loader)
177
- end
178
-
179
- def resolve_dependencies?
180
- # we need to resolve dependencies via gemfile.lock manually for
181
- # YARD regardless, so use same mechanism here so we don't
182
- # duplicate work generating pins from dependencies
183
- false
184
- end
185
-
186
- # @param loader [RBS::EnvironmentLoader]
187
- # @param library [String]
188
- # @param version [String, nil] the version of the library to load, or nil for any
189
- # @param out [StringIO, IO, nil] where to log messages
190
- # @return [Boolean] true if adding the library succeeded
191
- def add_library loader, library, version, out: $stderr
192
- @resolved = if loader.has_library?(library: library, version: version)
193
- loader.add library: library, version: version, resolve_dependencies: resolve_dependencies?
194
- logger.debug { "#{short_name} successfully loaded library #{library}:#{version}" }
195
- true
196
- else
197
- logger.info { "#{short_name} did not find data for library #{library}:#{version}" }
198
- false
199
- end
200
- end
201
-
202
- # @return [String]
203
- def short_name
204
- self.class.name.split('::').last
205
- end
11
+ autoload :Gem, 'solargraph/rbs_map/gem'
12
+ autoload :Helpers, 'solargraph/rbs_map/helpers'
13
+ autoload :Path, 'solargraph/rbs_map/path'
14
+ autoload :Stdlib, 'solargraph/rbs_map/stdlib'
206
15
  end
207
16
  end
@@ -10,36 +10,43 @@ module Solargraph
10
10
  'int' => 'Integer',
11
11
  'untyped' => '',
12
12
  'NilClass' => 'nil'
13
- }
13
+ }.freeze
14
14
 
15
- # @param type [RBS::Types::Bases::Base]
15
+ # Relative type names (e.g. `Bar` in an inline `#: () -> Bar`) stay
16
+ # unrooted so they resolve against the pin's namespace like YARD tags
17
+ # do. Absolute names (`::Bar`, or any name from an RBS environment
18
+ # after `resolve_type_names`) and RBS builtins stay rooted.
19
+ #
20
+ # @param type [RBS::Types::t]
21
+ # @param type_alias_decls [Hash{String => RBS::AST::Declarations::TypeAlias}]
16
22
  # @return [ComplexType]
17
- def self.to_complex_type(type)
18
- tag = type_to_tag(type)
19
- ComplexType.try_parse(tag).force_rooted
23
+ def self.to_complex_type type, type_alias_decls: {}
24
+ ComplexType.try_parse(type_to_tag(type, type_alias_decls))
20
25
  end
21
26
 
22
27
  # @param param_type [RBS::Types::Function::Param]
23
28
  # @param name [String]
24
29
  # @param decl [Symbol]
25
30
  # @param closure [Pin::Closure]
31
+ # @param type_alias_decls [Hash{String => RBS::AST::Declarations::TypeAlias}]
26
32
  # @return [Pin::Parameter]
27
- def self.to_parameter_pin(param_type, name, decl, closure)
33
+ def self.to_parameter_pin param_type, name, decl, closure, type_alias_decls: {}
28
34
  return_type = if decl == :restarg
29
- ComplexType.parse('Array')
30
- elsif decl == :kwrestarg
31
- ComplexType.parse('Hash{Symbol => Object}')
32
- else
33
- RbsTranslator.to_complex_type(param_type.type)
34
- end
35
+ ComplexType.parse('Array')
36
+ elsif decl == :kwrestarg
37
+ ComplexType.parse('Hash{Symbol => Object}')
38
+ else
39
+ RbsTranslator.to_complex_type(param_type.type, type_alias_decls: type_alias_decls)
40
+ end
35
41
  Solargraph::Pin::Parameter.new(decl: decl, name: name, closure: closure, return_type: return_type, source: :rbs, type_location: to_sg_location(param_type.location) || closure.type_location)
36
42
  end
37
43
 
38
- # @param method_type [RBS::MethodType]
44
+ # @param method_type [RBS::MethodType, RBS::Types::Block]
39
45
  # @param closure [Pin::Closure]
40
46
  # @param parameter_names [Array<String>]
47
+ # @param type_alias_decls [Hash{String => RBS::AST::Declarations::TypeAlias}]
41
48
  # @return [Array<Pin::Parameter>]
42
- def self.to_parameter_pins method_type, closure, parameter_names = []
49
+ def self.to_parameter_pins method_type, closure, parameter_names = [], type_alias_decls: {}
43
50
  if defined?(RBS::Types::UntypedFunction) && method_type.type.is_a?(RBS::Types::UntypedFunction)
44
51
  return [
45
52
  Solargraph::Pin::Parameter.new(decl: :restarg, name: 'arg', closure: closure, source: :rbs)
@@ -49,27 +56,37 @@ module Solargraph
49
56
  arg_num = 0
50
57
  params = []
51
58
  method_type.type.required_positionals.each do |param|
52
- params.push RbsTranslator.to_parameter_pin(param, param.name&.to_s || parameter_names[arg_num] || "arg_#{arg_num}", :arg, closure)
59
+ # @sg-ignore Unresolved call to name on RBS::Types::Function::Param
60
+ params.push RbsTranslator.to_parameter_pin(param, param.name&.to_s || parameter_names[arg_num] || "arg_#{arg_num}", :arg, closure, type_alias_decls: type_alias_decls)
53
61
  arg_num += 1
54
62
  end
55
63
  method_type.type.optional_positionals.each do |param|
56
- params.push RbsTranslator.to_parameter_pin(param, param.name&.to_s || parameter_names[arg_num] || "arg_#{arg_num}", :optarg, closure)
64
+ # @sg-ignore Unresolved call to name on RBS::Types::Function::Param
65
+ params.push RbsTranslator.to_parameter_pin(param, param.name&.to_s || parameter_names[arg_num] || "arg_#{arg_num}", :optarg, closure, type_alias_decls: type_alias_decls)
57
66
  arg_num += 1
58
67
  end
59
68
  if method_type.type.rest_positionals
60
- params.push RbsTranslator.to_parameter_pin(method_type.type.rest_positionals, method_type.type.rest_positionals.name&.to_s || parameter_names[arg_num] || "arg_#{arg_num}", :restarg, closure)
69
+ rest_positionals = method_type.type.rest_positionals
70
+ # @sg-ignore Unresolved call to name on RBS::Types::Function::Param
71
+ rest_name = rest_positionals.name&.to_s || parameter_names[arg_num] || "arg_#{arg_num}"
72
+ params.push RbsTranslator.to_parameter_pin(rest_positionals, rest_name, :restarg, closure, type_alias_decls: type_alias_decls)
61
73
  arg_num += 1
62
74
  end
63
75
  method_type.type.required_keywords.each do |param|
64
- params.push RbsTranslator.to_parameter_pin(param.last, param.first.to_s, :kwarg, closure)
76
+ # @sg-ignore Unresolved calls to last, first on generic<D>
77
+ params.push RbsTranslator.to_parameter_pin(param.last, param.first.to_s, :kwarg, closure, type_alias_decls: type_alias_decls)
65
78
  arg_num += 1
66
79
  end
67
80
  method_type.type.optional_keywords.each do |param|
68
- params.push RbsTranslator.to_parameter_pin(param.last, param.first.to_s, :kwoptarg, closure)
81
+ # @sg-ignore Unresolved calls to last, first on generic<D>
82
+ params.push RbsTranslator.to_parameter_pin(param.last, param.first.to_s, :kwoptarg, closure, type_alias_decls: type_alias_decls)
69
83
  arg_num += 1
70
84
  end
71
85
  if method_type.type.rest_keywords
72
- params.push RbsTranslator.to_parameter_pin(method_type.type.rest_keywords, method_type.type.rest_keywords.name&.to_s || parameter_names[arg_num] || "arg_#{arg_num}", :kwrestarg, closure)
86
+ rest_keywords = method_type.type.rest_keywords
87
+ # @sg-ignore Unresolved call to name on RBS::Types::Function::Param
88
+ rest_keywords_name = rest_keywords.name&.to_s || parameter_names[arg_num] || "arg_#{arg_num}"
89
+ params.push RbsTranslator.to_parameter_pin(rest_keywords, rest_keywords_name, :kwrestarg, closure, type_alias_decls: type_alias_decls)
73
90
  end
74
91
  params
75
92
  end
@@ -77,41 +94,29 @@ module Solargraph
77
94
  # @param method_type [RBS::MethodType]
78
95
  # @param closure [Pin::Closure]
79
96
  # @param parameter_names [Array<String>]
97
+ # @param type_alias_decls [Hash{String => RBS::AST::Declarations::TypeAlias}]
80
98
  # @return [Pin::Signature]
81
- def self.to_signature method_type, closure, parameter_names = []
99
+ def self.to_signature method_type, closure, parameter_names = [], type_alias_decls: {}
82
100
  # There may be edge cases here around different signatures
83
101
  # having different type params / orders - we may need to match
84
102
  # this data model and have generics live in signatures to
85
103
  # handle those correctly
86
104
  generics = method_type.type_params.map(&:name).map(&:to_s).uniq
87
- parameters = to_parameter_pins(method_type, closure, parameter_names)
88
- return_type = to_complex_type(method_type.type.return_type)
105
+ parameters = to_parameter_pins(method_type, closure, parameter_names, type_alias_decls: type_alias_decls)
106
+ return_type = to_complex_type(method_type.type.return_type, type_alias_decls: type_alias_decls)
89
107
  block = if method_type.block
90
- block_parameters = to_parameter_pins(method_type.block, closure)
91
- block_return_type = to_complex_type(method_type.block.type.return_type)
92
- Pin::Signature.new(generics: generics, parameters: block_parameters, return_type: block_return_type, source: :rbs, type_location: closure.location, closure: closure)
93
- end
94
- Pin::Signature.new(generics: generics, parameters: parameters, return_type: return_type, block: block, source: :rbs, type_location: closure.location, closure: closure)
95
- end
96
-
97
- # @param type_name [RBS::TypeName]
98
- # @param type_args [Enumerable<RBS::Types::Bases::Base>]
99
- # @return [ComplexType::UniqueType]
100
- def self.build_unique_type(type_name, type_args = [])
101
- base = RBS_TO_YARD_TYPE[type_name.relative!.to_s] || type_name.relative!.to_s
102
- params = type_args.map do |a|
103
- RbsTranslator.to_complex_type(a)
104
- end
105
- if base == 'Hash' && params.length == 2
106
- ComplexType::UniqueType.new(base, [params.first], [params.last], rooted: true, parameters_type: :hash)
107
- else
108
- ComplexType::UniqueType.new(base, [], params.reject(&:undefined?), rooted: true, parameters_type: :list)
109
- end
108
+ # @sg-ignore https://github.com/castwide/solargraph/issues/1249
109
+ block_parameters = to_parameter_pins(method_type.block, closure, type_alias_decls: type_alias_decls)
110
+ block_return_type = to_complex_type(method_type.block.type.return_type, type_alias_decls: type_alias_decls)
111
+ Pin::Signature.new(generics: generics, parameters: block_parameters, return_type: block_return_type, source: :rbs, type_location: closure.location, closure: closure)
112
+ end
113
+ Pin::Signature.new(generics: generics, parameters: parameters, return_type: return_type, block: block,
114
+ block_required: method_type.block&.required || false, source: :rbs, type_location: closure.location, closure: closure)
110
115
  end
111
116
 
112
117
  # @param location [RBS::Location, nil]
113
118
  # @return [Solargraph::Location, nil]
114
- def self.to_sg_location(location)
119
+ def self.to_sg_location location
115
120
  return nil if location&.name.nil?
116
121
 
117
122
  start_pos = Position.new(location.start_line - 1, location.start_column)
@@ -123,50 +128,74 @@ module Solargraph
123
128
  class << self
124
129
  private
125
130
 
126
- # @param type [RBS::Types::Bases::Base]
131
+ # @param type [RBS::Types::t]
132
+ # @param type_alias_decls [Hash{String => RBS::AST::Declarations::TypeAlias}]
133
+ # @param expanding_aliases [Array<String>] Names of aliases already
134
+ # being expanded in this call chain, to detect recursive aliases
127
135
  # @return [String]
128
- def type_to_tag type
136
+ def type_to_tag type, type_alias_decls = {}, expanding_aliases = []
129
137
  case type
130
138
  when RBS::Types::Optional
131
- "#{type_to_tag(type.type)}, nil"
139
+ # @sg-ignore flow sensitive typing should support case/when
140
+ "#{type_to_tag(type.type, type_alias_decls, expanding_aliases)}, nil"
132
141
  when RBS::Types::Bases::Bool
133
- 'Boolean'
142
+ '::Boolean'
134
143
  when RBS::Types::Tuple
135
- "Array(#{type.types.map { |t| type_to_tag(t) }.join(', ')})"
144
+ # @sg-ignore flow sensitive typing should support case/when
145
+ "::Array(#{type.types.map { |t| type_to_tag(t, type_alias_decls, expanding_aliases) }.join(', ')})"
136
146
  when RBS::Types::Literal
147
+ # @sg-ignore flow sensitive typing should support case/when
137
148
  type.literal.inspect
138
149
  when RBS::Types::Union
139
- type.types.map { |t| type_to_tag(t) }.join(', ')
150
+ # @sg-ignore flow sensitive typing should support case/when
151
+ type.types.map { |t| type_to_tag(t, type_alias_decls, expanding_aliases) }.join(', ')
140
152
  when RBS::Types::Record
141
153
  # @todo Better record support
142
- 'Hash'
154
+ '::Hash'
143
155
  when RBS::Types::Bases::Nil
144
156
  'nil'
145
157
  when RBS::Types::Bases::Void
146
158
  'void'
147
159
  when RBS::Types::Variable
160
+ # @sg-ignore flow sensitive typing should support case/when
148
161
  "#{Solargraph::ComplexType::GENERIC_TAG_NAME}<#{type.name}>"
149
162
  when RBS::Types::Bases::Self, RBS::Types::Bases::Instance
150
163
  'self'
151
164
  when RBS::Types::Bases::Top
152
165
  # `Top` is the most super superclass
153
- 'BasicObject'
166
+ '::BasicObject'
154
167
  when RBS::Types::Intersection
155
- type.types.map { |member| type_to_tag(member) }.join(', ')
168
+ # @sg-ignore flow sensitive typing should support case/when
169
+ type.types.map { |member| type_to_tag(member, type_alias_decls, expanding_aliases) }.join(', ')
156
170
  when RBS::Types::Proc
157
- 'Proc'
158
- when RBS::Types::ClassInstance, RBS::Types::Alias, RBS::Types::Interface
159
- # `Alias` is a top-level type alias, e.g., 'bool' in "type bool = true | false"
160
- # @todo ensure these get resolved after processing all aliases
161
- # @todo handle recursive aliases
162
- #
163
- # `Interface represents a mix-in module which can be considered a
171
+ '::Proc'
172
+ when RBS::Types::Alias
173
+ # Expand so conformance checks see the members, not the name. A
174
+ # generic alias falls back: args are not substituted, so expanding
175
+ # would leak an unbound generic<T>.
176
+ # @sg-ignore flow sensitive typing should support case/when
177
+ alias_name = type.name.to_s
178
+ alias_decl = type_alias_decls[alias_name]
179
+ # @sg-ignore flow sensitive typing should support case/when
180
+ if alias_decl.nil? || expanding_aliases.include?(alias_name) || !type.args.empty?
181
+ # @sg-ignore flow sensitive typing should support case/when
182
+ type_tag(type.name, type.args, type_alias_decls, expanding_aliases)
183
+ else
184
+ # The alias body belongs to the alias's declaration, not the
185
+ # use site, so its names stay rooted even when a core-less
186
+ # environment left them relative.
187
+ expanded = type_to_tag(alias_decl.type, type_alias_decls, expanding_aliases + [alias_name])
188
+ ComplexType.try_parse(expanded).force_rooted.rooted_tags
189
+ end
190
+ when RBS::Types::ClassInstance, RBS::Types::Interface
191
+ # `Interface` represents a mix-in module which can be considered a
164
192
  # subtype of a consumer of it
165
- #
166
- type_tag(type.name, type.args)
193
+ # @sg-ignore flow sensitive typing should support case/when
194
+ type_tag(type.name, type.args, type_alias_decls, expanding_aliases)
167
195
  when RBS::Types::ClassSingleton
168
196
  # e.g., singleton(String)
169
- type_tag(type.name)
197
+ # @sg-ignore flow sensitive typing should support case/when
198
+ type_tag(type.name, [], type_alias_decls, expanding_aliases)
170
199
  when RBS::Types::Bases::Any, RBS::Types::Bases::Bottom
171
200
  # `Bottom`` is used in contexts where nothing will ever return
172
201
  # - e.g., it could be the return type of 'exit()' or 'raise'
@@ -182,23 +211,29 @@ module Solargraph
182
211
 
183
212
  # @param type_name [RBS::TypeName]
184
213
  # @param type_args [Enumerable<RBS::Types::Bases::Base>]
214
+ # @param type_alias_decls [Hash{String => RBS::AST::Declarations::TypeAlias}]
215
+ # @param expanding_aliases [Array<String>]
185
216
  # @return [String]
186
- def type_tag(type_name, type_args = [])
187
- build_type(type_name, type_args).tags
217
+ def type_tag type_name, type_args = [], type_alias_decls = {}, expanding_aliases = []
218
+ build_type(type_name, type_args, type_alias_decls, expanding_aliases).rooted_tags
188
219
  end
189
220
 
190
221
  # @param type_name [RBS::TypeName]
191
222
  # @param type_args [Enumerable<RBS::Types::Bases::Base>]
223
+ # @param type_alias_decls [Hash{String => RBS::AST::Declarations::TypeAlias}]
224
+ # @param expanding_aliases [Array<String>]
192
225
  # @return [ComplexType::UniqueType]
193
- def build_type(type_name, type_args = [])
194
- base = RBS_TO_YARD_TYPE[type_name.relative!.to_s] || type_name.relative!.to_s
195
- params = type_args.map { |a| type_to_tag(a) }.map do |t|
226
+ def build_type type_name, type_args = [], type_alias_decls = {}, expanding_aliases = []
227
+ yard_base = RBS_TO_YARD_TYPE[type_name.relative!.to_s]
228
+ base = yard_base || type_name.relative!.to_s
229
+ rooted = !yard_base.nil? || type_name.absolute?
230
+ params = type_args.map { |a| type_to_tag(a, type_alias_decls, expanding_aliases) }.map do |t|
196
231
  ComplexType.try_parse(t)
197
232
  end
198
233
  if base == 'Hash' && params.length == 2
199
- ComplexType::UniqueType.new(base, [params.first], [params.last], rooted: true, parameters_type: :hash)
234
+ ComplexType::UniqueType.new(base, [params.first], [params.last], rooted: rooted, parameters_type: :hash)
200
235
  else
201
- ComplexType::UniqueType.new(base, [], params.reject(&:undefined?), rooted: true, parameters_type: :list)
236
+ ComplexType::UniqueType.new(base, [], params.reject(&:undefined?), rooted: rooted, parameters_type: :list)
202
237
  end
203
238
  end
204
239
  end
@@ -0,0 +1,177 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'open3'
4
+ require 'json'
5
+
6
+ module Solargraph
7
+ # A repository for the gems available in the specified directory. If the
8
+ # directory has a Gemfile, Repo will use its gem definitions. Otherwise it
9
+ # uses the system gems.
10
+ #
11
+ class Repo
12
+ # @return [String, nil]
13
+ attr_reader :directory
14
+
15
+ # @param directory [String, nil]
16
+ def initialize directory
17
+ @directory = File.expand_path(directory) if directory
18
+ build_from_directory
19
+ end
20
+
21
+ # Find a metagem by path from the directory's bundle definition or the
22
+ # system gems.
23
+ #
24
+ # @param path [String]
25
+ # @return [Metagem, nil]
26
+ def find_by_path path
27
+ return system_find_by_path(path) unless bundled?
28
+
29
+ bundled_metagem_path_map[path]
30
+ end
31
+
32
+ # Find a metagem by name from the directory's bundle definition or the
33
+ # system gems.
34
+ #
35
+ # @param name [String]
36
+ # @return [Metagem, nil]
37
+ def find_by_name name
38
+ return system_find_by_name(name) unless bundled?
39
+
40
+ bundled_metagem_name_map[name]
41
+ end
42
+
43
+ # Get an array of metagems by its bundle group name. Returns an empty array
44
+ # if the repo isn't bundled or the group doesn't exist.
45
+ #
46
+ # @param group [Symbol]
47
+ # @return [Array<Metagem>]
48
+ def find_by_group group
49
+ return system_find_by_group(group) unless bundled?
50
+
51
+ bundled_group_map[group] || []
52
+ end
53
+
54
+ # True if the directory has a bundle definition.
55
+ #
56
+ # @note If Solargraph itself is running in this directory's bundled
57
+ # environment, `bundled?` is false.
58
+ #
59
+ def bundled?
60
+ !!@metagems
61
+ end
62
+ alias bundle? bundled?
63
+
64
+ # Metagems from the directory's bundle definition, or an empty array if a
65
+ # bundle definition does not exist.
66
+ #
67
+ # @return [Array<Solargraph::Metagem>]
68
+ def bundled
69
+ @metagems || []
70
+ end
71
+
72
+ private
73
+
74
+ # @return [String, nil]
75
+ def gemfile
76
+ @gemfile ||= File.expand_path('Gemfile', directory) if directory
77
+ end
78
+
79
+ # @return [String, nil]
80
+ def lockfile
81
+ @lockfile ||= File.expand_path('Gemfile.lock', directory) if directory
82
+ end
83
+
84
+ def bundled_directory?
85
+ directory && File.file?(gemfile) && File.file?(lockfile)
86
+ end
87
+
88
+ # Load metagems from the directory's bundle definition if available.
89
+ #
90
+ # This method performs gem collection in a separate process to suppress
91
+ # output from the `Bundler.definition.specs` call.
92
+ #
93
+ # @return [Array<Metagem>, nil]
94
+ def build_from_directory
95
+ return unless bundled_directory? && ENV['BUNDLE_GEMFILE'] != gemfile
96
+
97
+ Solargraph.with_clean_env do
98
+ cmd = ['ruby', '-e', bundle_script]
99
+ o, e, s = Open3.capture3(*cmd, chdir: directory)
100
+ if s.success?
101
+ json = o && !o.empty? ? JSON.parse(o.strip.split("\n").last, symbolize_names: true) : []
102
+ @metagems = json[:metagems].map { |data| Metagem.new(**data) }
103
+ @bundled_group_map = json[:groups].transform_values { |names| names.map { |name| bundled_metagem_name_map[name] } }
104
+ else
105
+ Solargraph.logger.warn "Failed to load gems from bundle at #{directory}: #{e}"
106
+ nil
107
+ end
108
+ end
109
+ end
110
+
111
+ def bundle_script
112
+ "
113
+ require 'bundler/setup'
114
+ require 'json'
115
+
116
+ puts Bundler.definition.specs.length
117
+ metagems = Bundler.definition.specs.map do |spec|
118
+ {
119
+ name: spec.name,
120
+ full_path: spec.full_gem_path,
121
+ spec_file: spec.spec_file,
122
+ source: spec.source.to_s,
123
+ version: spec.version,
124
+ require_paths: spec.require_paths,
125
+ dependencies: spec.runtime_dependencies.map(&:name)
126
+ }
127
+ end
128
+ groups = Bundler.definition.groups.to_h do |group|
129
+ [group, Bundler.definition.specs_for([group]).map(&:name)]
130
+ end
131
+ puts({ groups: groups, metagems: metagems }.to_json)
132
+ "
133
+ end
134
+
135
+ # @param path [String]
136
+ # @return [Metagem, nil]
137
+ def system_find_by_path path
138
+ gem = Gem::Specification.find_by_path(path)
139
+ gem && Metagem.from_specification(gem)
140
+ end
141
+
142
+ # @param name [String]
143
+ # @return [Metagem, nil]
144
+ def system_find_by_name name
145
+ gem = Gem::Specification.find_by_name(name)
146
+ gem && Metagem.from_specification(gem)
147
+ rescue Gem::MissingSpecError => _e
148
+ nil
149
+ end
150
+
151
+ # @param group [Symbol]
152
+ # @return [Array<Metagem>]
153
+ def system_find_by_group group
154
+ return [] unless ENV['BUNDLE_GEMFILE']
155
+
156
+ Bundler.definition
157
+ .specs_for([group])
158
+ .map { |spec| Metagem.from_specification(spec) }
159
+ end
160
+
161
+ def bundled_metagem_name_map
162
+ @bundled_metagem_name_map ||= bundled.to_set
163
+ .classify(&:name)
164
+ .transform_values(&:first)
165
+ end
166
+
167
+ def bundled_metagem_path_map
168
+ @bundled_metagem_path_map ||= Hash.new do |hash, path|
169
+ hash[path] = bundled.find { |mg| mg.require?(path) }
170
+ end
171
+ end
172
+
173
+ def bundled_group_map
174
+ @bundled_group_map ||= {}
175
+ end
176
+ end
177
+ end