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
@@ -576,14 +576,8 @@ module Solargraph
576
576
  return unless source
577
577
  # @sg-ignore Wrong argument type for Solargraph::Workspace#has_file?: filename expected String, received String, nil
578
578
  return unless @current == source || workspace.has_file?(source.filename)
579
- if source_map_hash.key?(source.filename)
580
- new_map = Solargraph::SourceMap.map(source)
581
- # @sg-ignore OK if source.filename is nil
582
- source_map_hash[source.filename] = new_map
583
- else
584
- # @sg-ignore OK if source.filename is nil
585
- source_map_hash[source.filename] = Solargraph::SourceMap.map(source)
586
- end
579
+ # @sg-ignore OK if source.filename is nil
580
+ source_map_hash[source.filename] = Solargraph::SourceMap.map(source)
587
581
  end
588
582
 
589
583
  # @return [Set<Gem::Specification>]
@@ -595,57 +589,35 @@ module Solargraph
595
589
  def cache_next_gemspec
596
590
  return if @cache_progress
597
591
 
598
- spec = cacheable_specs.first
592
+ spec = (api_map.unloaded_gems - cache_errors).first
599
593
  return end_cache_progress unless spec
600
594
 
601
- pending = api_map.uncached_gemspecs.length - cache_errors.length - 1
595
+ pending = api_map.unloaded_gems.length - cache_errors.length - 1
602
596
 
603
- if Yardoc.processing?(spec)
604
- logger.info "Enqueuing cache of #{spec.name} #{spec.version} (already being processed)"
605
- queued_gemspec_cache.push(spec)
606
- return if pending - queued_gemspec_cache.length < 1
597
+ logger.info "Caching #{spec.name} #{spec.version}"
598
+ Thread.new do
599
+ report_cache_progress spec.name, pending
607
600
 
608
- # Try the next cacheable gemspec. This method is always called
609
- # from inside sync_catalog's mutex (either directly or via this
610
- # same recursive call), so recursing through sync_catalog here
611
- # would try to re-lock a mutex this thread already holds and
612
- # deadlock (https://github.com/castwide/solargraph/issues/1111).
613
- cache_next_gemspec
614
- else
615
- logger.info "Caching #{spec.name} #{spec.version}"
616
- Thread.new do
617
- report_cache_progress spec.name, pending
618
- _o, e, s = Open3.capture3(workspace.command_path, 'cache', spec.name, spec.version.to_s)
619
- if s.success?
620
- logger.info "Cached #{spec.name} #{spec.version}"
621
- else
622
- cache_errors.add spec
623
- logger.warn "Error caching gemspec #{spec.name} #{spec.version}"
624
- logger.warn e
625
- end
626
- end_cache_progress
627
- catalog
628
- sync_catalog
601
+ cmd = [workspace.command_path, 'cache', spec.name]
602
+ cmd.push('--directory', workspace.directory) if workspace.directory
603
+ begin
604
+ _o, e, s = Open3.capture3(*cmd)
605
+ rescue SystemCallError => e
606
+ # The command could not start, e.g. Errno::ENOENT - handled below
629
607
  end
608
+ if s&.success?
609
+ logger.info "Cached #{spec.name} #{spec.version}"
610
+ else
611
+ cache_errors.add spec
612
+ logger.warn "Error caching gemspec #{spec.name} #{spec.version}"
613
+ logger.warn e
614
+ end
615
+ end_cache_progress
616
+ catalog
617
+ sync_catalog
630
618
  end
631
619
  end
632
620
 
633
- # @return [Array<Gem::Specification>]
634
- def cacheable_specs
635
- cacheable = api_map.uncached_yard_gemspecs +
636
- api_map.uncached_rbs_collection_gemspecs -
637
- queued_gemspec_cache -
638
- cache_errors.to_a
639
- return cacheable unless cacheable.empty?
640
-
641
- queued_gemspec_cache
642
- end
643
-
644
- # @return [Array<Gem::Specification>]
645
- def queued_gemspec_cache
646
- @queued_gemspec_cache ||= []
647
- end
648
-
649
621
  # @param gem_name [String]
650
622
  # @param pending [Integer]
651
623
  # @return [void]
@@ -696,8 +668,9 @@ module Solargraph
696
668
  source_map_hash.each_value { |map| find_external_requires(map) }
697
669
  api_map.catalog bench
698
670
  logger.info "Catalog complete (#{api_map.source_maps.length} files, #{api_map.pins.length} pins)"
699
- logger.info "#{api_map.uncached_yard_gemspecs.length} uncached YARD gemspecs"
700
- logger.info "#{api_map.uncached_rbs_collection_gemspecs.length} uncached RBS collection gemspecs"
671
+ # @todo This needs work
672
+ # logger.info "#{api_map.uncached_yard_gemspecs.length} uncached YARD gemspecs"
673
+ # logger.info "#{api_map.uncached_rbs_collection_gemspecs.length} uncached RBS collection gemspecs"
701
674
  cache_next_gemspec
702
675
  @sync_count = 0
703
676
  end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ class Metagem
5
+ include Equality
6
+
7
+ # @return [String]
8
+ attr_reader :name
9
+
10
+ # @return [String]
11
+ attr_reader :full_path
12
+
13
+ # @return [String]
14
+ attr_reader :spec_file
15
+
16
+ # @return [String]
17
+ attr_reader :source
18
+
19
+ # @return [String]
20
+ attr_reader :version
21
+
22
+ # @return [Array<String>]
23
+ attr_reader :require_paths
24
+
25
+ # @return [Array<String>]
26
+ attr_reader :dependencies
27
+
28
+ # @return [String]
29
+ attr_reader :cache_name
30
+
31
+ def initialize name:, full_path:, spec_file:, source:, version:, require_paths:, dependencies:
32
+ @name = name
33
+ @full_path = full_path
34
+ @spec_file = spec_file
35
+ @source = source
36
+ @version = version
37
+ @require_paths = require_paths
38
+ @dependencies = dependencies
39
+ # @todo Possible change to cache names to make them more specific to sources
40
+ # @cache_name = "#{File.basename(full_path)}__#{source.gsub(/[^a-z0-9\-_]/i, '_')}" unless source.end_with?('Path')
41
+ # @todo Better path source detection
42
+ @cache_name = File.basename(full_path) unless source.start_with?('source at') || source.to_s.end_with?('::Path')
43
+ end
44
+
45
+ def cacheable?
46
+ !!@cache_name
47
+ end
48
+
49
+ def require? path
50
+ require_paths.any? do |req|
51
+ File.file?(File.join(full_path, req, "#{path}.rb"))
52
+ end
53
+ end
54
+
55
+ def to_specification
56
+ Gem::Specification.load(spec_file)
57
+ end
58
+
59
+ alias full_gem_path full_path
60
+
61
+ # @return [Array]
62
+ def equality_fields
63
+ # A version arrives as a String from a bundle definition and as a
64
+ # Gem::Version from a specification, so compare its string form.
65
+ [name, version.to_s, full_path, source]
66
+ end
67
+
68
+ # @param gem [Gem::Specification]
69
+ # @return [Metagem]
70
+ def self.from_specification gem
71
+ Metagem.new(name: gem.name,
72
+ full_path: gem.full_gem_path,
73
+ spec_file: gem.spec_file,
74
+ source: gem.source.to_s,
75
+ version: gem.version,
76
+ require_paths: gem.require_paths,
77
+ dependencies: gem.runtime_dependencies.map(&:name))
78
+ end
79
+ end
80
+ end
@@ -91,6 +91,12 @@ module Solargraph
91
91
  region.source.comments_for(node)
92
92
  end
93
93
 
94
+ # @param node [Parser::AST::Node]
95
+ # @return [String, nil]
96
+ def type_assertion_for node
97
+ region.source.type_assertion_for(node)
98
+ end
99
+
94
100
  # @param position [Solargraph::Position]
95
101
  # @return [Pin::Closure, nil]
96
102
  def named_path_pin position
@@ -13,10 +13,12 @@ module Solargraph
13
13
  # @param node [Parser::AST::Node]
14
14
  # @param filename [String, nil]
15
15
  # @param parent [Parser::AST::Node, nil]
16
- def initialize node, filename = nil, parent = nil
16
+ # @param tree [Array<Parser::AST::Node>, nil]
17
+ def initialize node, filename = nil, parent = nil, tree = nil
17
18
  @node = node
18
19
  @filename = filename
19
20
  @parent = parent
21
+ @tree = tree
20
22
  end
21
23
 
22
24
  # @return [Source::Chain]
@@ -29,9 +31,10 @@ module Solargraph
29
31
  # @param node [Parser::AST::Node]
30
32
  # @param filename [String, nil]
31
33
  # @param parent [Parser::AST::Node, nil]
34
+ # @param tree [Parser::AST::Node, nil]
32
35
  # @return [Source::Chain]
33
- def chain node, filename = nil, parent = nil
34
- NodeChainer.new(node, filename, parent).chain
36
+ def chain node, filename = nil, parent = nil, tree = nil
37
+ NodeChainer.new(node, filename, parent, tree).chain
35
38
  end
36
39
 
37
40
  # @param code [String]
@@ -150,6 +153,13 @@ module Solargraph
150
153
  elsif n.type == :array
151
154
  chained_children = n.children.map { |c| NodeChainer.chain(c) }
152
155
  result.push Source::Chain::Array.new(chained_children, n)
156
+ elsif inside_method_call? # If the node is inside a method call, it may be a parameter.
157
+ lit = infer_literal_node_type(n)
158
+ if lit
159
+ method_call_chain = NodeChainer.chain(send_node, @filename, nil, [])
160
+ literal = Chain::Literal.new(lit, n)
161
+ result.push Chain::Parameter.new(literal, method_call_chain)
162
+ end
153
163
  else
154
164
  lit = infer_literal_node_type(n)
155
165
  result.push(lit ? Chain::Literal.new(lit, n) : Chain::Link.new)
@@ -158,6 +168,15 @@ module Solargraph
158
168
  result
159
169
  end
160
170
 
171
+ def inside_method_call?
172
+ !send_node.nil? && send_node != @node
173
+ end
174
+
175
+ # @return [Parser::AST::Node, nil]
176
+ def send_node
177
+ @send_node ||= @tree&.find { |n| n.type == :send }
178
+ end
179
+
161
180
  # @param node [Parser::AST::Node]
162
181
  def hash_is_splatted? node
163
182
  return false unless Parser.is_ast_node?(node) && node.type == :hash
@@ -197,6 +197,18 @@ module Solargraph
197
197
  nodes.any? { |n| splatted_call?(n) }
198
198
  end
199
199
 
200
+ # Anonymous `*`, `**` and `...` arguments pass on whatever the
201
+ # enclosing method received, so neither count nor types are known.
202
+ #
203
+ # @param nodes [Enumerable<Parser::AST::Node>]
204
+ def any_forwarded_args? nodes
205
+ nodes.any? do |n|
206
+ next false unless Parser.is_ast_node?(n)
207
+ next true if %i[forwarded_args forwarded_restarg].include?(n.type)
208
+ n.type == :hash && n.children.any? { |c| Parser.is_ast_node?(c) && c.type == :forwarded_kwrestarg }
209
+ end
210
+ end
211
+
200
212
  # @todo Temporarily here for testing. Move to Solargraph::Parser.
201
213
  # @param node [Parser::AST::Node]
202
214
  # @return [Array<Parser::AST::Node>]
@@ -442,6 +454,11 @@ module Solargraph
442
454
  CONDITIONAL_ALL_BUT_FIRST = %i[if unless].freeze
443
455
  ONLY_ONE_CHILD = [:return].freeze
444
456
  FIRST_TWO_CHILDREN = [:rescue].freeze
457
+ # :ensure's value is always its body's value (first
458
+ # child); the ensure clause itself (second child) never
459
+ # contributes to the method's return value unless it
460
+ # explicitly returns.
461
+ ENSURE = [:ensure].freeze
445
462
  COMPOUND_STATEMENTS = %i[begin kwbegin].freeze
446
463
  SKIPPABLE = %i[def defs class sclass module].freeze
447
464
  FUNCTION_VALUE = [:block].freeze
@@ -488,6 +505,13 @@ module Solargraph
488
505
  result.concat reduce_to_value_nodes([node.children[0]])
489
506
  elsif FIRST_TWO_CHILDREN.include?(node.type)
490
507
  result.concat reduce_to_value_nodes([node.children[0], node.children[1]])
508
+ elsif ENSURE.include?(node.type)
509
+ result.concat reduce_to_value_nodes([node.children[0]])
510
+ ensure_body = node.children[1]
511
+ if include_explicit_returns && ensure_body.is_a?(::Parser::AST::Node)
512
+ # @sg-ignore flow sensitive typing needs to narrow down type with an if is_a? check
513
+ result.concat explicit_return_values_from_compound_statement(ensure_body)
514
+ end
491
515
  elsif FUNCTION_VALUE.include?(node.type)
492
516
  # the block itself is a first class value that could be returned
493
517
  result.push node
@@ -605,14 +629,13 @@ module Solargraph
605
629
  elsif CONDITIONAL_ALL_BUT_FIRST.include?(node.type)
606
630
  # @sg-ignore flow sensitive typing needs to narrow down type with an if is_a? check
607
631
  result.concat reduce_to_value_nodes(node.children[1..])
608
- # @sg-ignore flow sensitive typing needs to narrow down type with an if is_a? check
609
- elsif node.type == :return
632
+ elsif ONLY_ONE_CHILD.include?(node.type) || ENSURE.include?(node.type)
610
633
  # @sg-ignore flow sensitive typing needs to narrow down type with an if is_a? check
611
634
  result.concat reduce_to_value_nodes([node.children[0]])
612
- # @sg-ignore flow sensitive typing needs to narrow down type with an if is_a? check
613
- elsif node.type == :or
614
- # @sg-ignore flow sensitive typing needs to narrow down type with an if is_a? check
615
- result.concat reduce_to_value_nodes(node.children)
635
+ elsif FIRST_TWO_CHILDREN.include?(node.type)
636
+ # a :rescue node's value is its body's value or the
637
+ # value of whichever resbody handled the exception
638
+ result.concat reduce_to_value_nodes([node.children[0], node.children[1]])
616
639
  # @sg-ignore flow sensitive typing needs to narrow down type with an if is_a? check
617
640
  elsif node.type == :block
618
641
  # @sg-ignore flow sensitive typing needs to narrow down type with an if is_a? check
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ module Parser
5
+ module ParserGem
6
+ module NodeProcessors
7
+ module ClassDefinition
8
+ # A node wrapper for a class definition via const assignment.
9
+ # @example
10
+ # MyError = Class.new(StandardError) do
11
+ # def retry_after; 5; end
12
+ # end
13
+ class ClassAssignmentNode
14
+ class << self
15
+ # @example
16
+ # s(:casgn, nil, :Foo,
17
+ # s(:block,
18
+ # s(:send,
19
+ # s(:const, nil, :Class), :new,
20
+ # s(:const, nil, :StandardError)),
21
+ # s(:args),
22
+ # s(:def, :retry_after, s(:args), s(:int, 5))))
23
+ #
24
+ # @param node [Parser::AST::Node]
25
+ # @return [Boolean]
26
+ def match? node
27
+ return false unless node&.type == :casgn
28
+
29
+ class_new_node?(new_node(node))
30
+ end
31
+
32
+ # The `Class.new(...)` send node, whether or not a block is attached.
33
+ #
34
+ # @param node [Parser::AST::Node]
35
+ # @return [Parser::AST::Node, nil]
36
+ def new_node node
37
+ value = node.children[2]
38
+ return nil if value.nil?
39
+ return value unless value.type == :block
40
+
41
+ value.children[0]
42
+ end
43
+
44
+ private
45
+
46
+ # @param send_node [Parser::AST::Node, nil]
47
+ # @return [Boolean]
48
+ def class_new_node? send_node
49
+ return false if send_node.nil?
50
+ return false unless send_node.type == :send
51
+ return false unless send_node.children[1] == :new
52
+
53
+ receiver = send_node.children[0]
54
+ return false if receiver.nil?
55
+ return false unless receiver.type == :const
56
+
57
+ receiver.children[1] == :Class
58
+ end
59
+ end
60
+
61
+ # @param node [Parser::AST::Node]
62
+ def initialize node
63
+ @node = node
64
+ end
65
+
66
+ # @return [String]
67
+ def class_name
68
+ namespace = node.children[0]
69
+ return node.children[1].to_s if namespace.nil?
70
+
71
+ "#{Parser::NodeMethods.unpack_name(namespace)}::#{node.children[1]}"
72
+ end
73
+
74
+ # The superclass, when it is written as a constant. `Class.new(expr)`
75
+ # with a non-constant argument yields nil (superclass stays Object).
76
+ #
77
+ # @return [String, nil]
78
+ def superclass_name
79
+ send_node = self.class.new_node(node)
80
+ return nil if send_node.nil?
81
+
82
+ arg = send_node.children[2]
83
+ return nil if arg.nil?
84
+ return nil unless arg.type == :const
85
+
86
+ Parser::NodeMethods.unpack_name(arg)
87
+ end
88
+
89
+ private
90
+
91
+ # @return [Parser::AST::Node]
92
+ attr_reader :node
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ module Parser
5
+ module ParserGem
6
+ module NodeProcessors
7
+ module ClassDefinition
8
+ autoload :ClassAssignmentNode, 'solargraph/parser/parser_gem/node_processors/class_definition/class_assignment_node'
9
+
10
+ class ClassNode < Parser::NodeProcessor::Base
11
+ # @return [Boolean] continue processing the next processor of the same node.
12
+ def process
13
+ definition_node = class_definition_node
14
+ return true if definition_node.nil?
15
+
16
+ loc = get_node_location(node)
17
+ nspin = Solargraph::Pin::Namespace.new(
18
+ type: :class,
19
+ location: loc,
20
+ closure: region.closure,
21
+ name: definition_node.class_name,
22
+ comments: comments_for(node),
23
+ visibility: :public,
24
+ gates: region.closure.gates.freeze,
25
+ source: :class_definition
26
+ )
27
+ pins.push nspin
28
+
29
+ superclass_name = definition_node.superclass_name
30
+ if superclass_name
31
+ pins.push Pin::Reference::Superclass.new(
32
+ location: loc,
33
+ closure: nspin,
34
+ name: superclass_name,
35
+ source: :class_definition
36
+ )
37
+ end
38
+
39
+ process_children region.update(closure: nspin, visibility: :public)
40
+ false
41
+ end
42
+
43
+ private
44
+
45
+ # @return [ClassDefinition::ClassAssignmentNode, nil]
46
+ def class_definition_node
47
+ @class_definition_node ||= ClassAssignmentNode.new(node) if ClassAssignmentNode.match?(node)
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -12,6 +12,7 @@ module Solargraph
12
12
  closure: region.closure,
13
13
  name: node.children[0].to_s,
14
14
  comments: comments_for(node),
15
+ type_assertion: type_assertion_for(node),
15
16
  assignment: node.children[1],
16
17
  source: :parser
17
18
  )
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ module Parser
5
+ module ParserGem
6
+ module NodeProcessors
7
+ module DataDefinition
8
+ # A node wrapper for a Data definition via const assignment.
9
+ # @example
10
+ # MyData = Data.new(:bar, :baz) do
11
+ # def foo
12
+ # end
13
+ # end
14
+ class DataAssignmentNode < DataDefintionNode
15
+ class << self
16
+ # @example
17
+ # s(:casgn, nil, :Foo,
18
+ # s(:block,
19
+ # s(:send,
20
+ # s(:const, nil, :Data), :define,
21
+ # s(:sym, :bar),
22
+ # s(:sym, :baz)),
23
+ # s(:args),
24
+ # s(:def, :foo,
25
+ # s(:args),
26
+ # s(:send, nil, :bar))))
27
+ # @param node [::Parser::AST::Node]
28
+ def match? node
29
+ return false unless node&.type == :casgn
30
+ return false if node.children[2].nil?
31
+
32
+ data_node = if node.children[2].type == :block
33
+ node.children[2].children[0]
34
+ else
35
+ node.children[2]
36
+ end
37
+
38
+ data_definition_node?(data_node)
39
+ end
40
+ end
41
+
42
+ def class_name
43
+ if node.children[0]
44
+ Parser::NodeMethods.unpack_name(node.children[0]) + "::#{node.children[1]}"
45
+ else
46
+ node.children[1].to_s
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ # @return [Parser::AST::Node]
53
+ def data_node
54
+ if node.children[2].type == :block
55
+ node.children[2].children[0]
56
+ else
57
+ node.children[2]
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ module Parser
5
+ module ParserGem
6
+ module NodeProcessors
7
+ module DataDefinition
8
+ # A node wrapper for a Data definition via inheritance.
9
+ # @example
10
+ # class MyData < Data.new(:bar, :baz)
11
+ # def foo
12
+ # end
13
+ # end
14
+ class DataDefintionNode
15
+ class << self
16
+ # @example
17
+ # s(:class,
18
+ # s(:const, nil, :Foo),
19
+ # s(:send,
20
+ # s(:const, nil, :Data), :define,
21
+ # s(:sym, :bar),
22
+ # s(:sym, :baz)),
23
+ # s(:hash,
24
+ # s(:pair,
25
+ # s(:sym, :keyword_init),
26
+ # s(:true)))),
27
+ # s(:def, :foo,
28
+ # s(:args),
29
+ # s(:send, nil, :bar)))
30
+ #
31
+ # @param node [Parser::AST::Node]
32
+ def match? node
33
+ return false unless node&.type == :class
34
+
35
+ data_definition_node?(node.children[1])
36
+ end
37
+
38
+ private
39
+
40
+ # @param data_node [Parser::AST::Node]
41
+ # @return [Boolean]
42
+ def data_definition_node? data_node
43
+ return false unless data_node.is_a?(::Parser::AST::Node)
44
+ return false unless data_node&.type == :send
45
+ return false unless data_node.children[0]&.type == :const
46
+ return false unless data_node.children[0].children[1] == :Data
47
+ return false unless data_node.children[1] == :define
48
+
49
+ true
50
+ end
51
+ end
52
+
53
+ # @param node [Parser::AST::Node]
54
+ def initialize node
55
+ @node = node
56
+ end
57
+
58
+ # @return [String]
59
+ def class_name
60
+ Parser::NodeMethods.unpack_name(node)
61
+ end
62
+
63
+ # @return [Array<Array(Parser::AST::Node, String)>]
64
+ def attributes
65
+ data_attribute_nodes.map do |data_def_param|
66
+ next unless data_def_param.type == :sym
67
+ [data_def_param, data_def_param.children[0].to_s]
68
+ end.compact
69
+ end
70
+
71
+ # @return [Parser::AST::Node, nil]
72
+ def body_node
73
+ node.children[2]
74
+ end
75
+
76
+ private
77
+
78
+ # @return [Parser::AST::Node]
79
+ attr_reader :node
80
+
81
+ # @return [Parser::AST::Node, nil]
82
+ def data_node
83
+ node.children[1]
84
+ end
85
+
86
+ # @sg-ignore Need to add nil check here
87
+ # @return [Array<Parser::AST::Node>]
88
+ def data_attribute_nodes
89
+ # @sg-ignore Need to add nil check here
90
+ data_node.children[2..]
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end