solargraph 0.56.1 → 0.57.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 (132) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/linting.yml +125 -0
  3. data/.github/workflows/plugins.yml +148 -6
  4. data/.github/workflows/rspec.yml +39 -4
  5. data/.github/workflows/typecheck.yml +5 -2
  6. data/.gitignore +5 -0
  7. data/.overcommit.yml +72 -0
  8. data/.rspec +1 -0
  9. data/.rubocop.yml +66 -0
  10. data/.rubocop_todo.yml +2627 -0
  11. data/.yardopts +1 -0
  12. data/CHANGELOG.md +50 -0
  13. data/README.md +8 -4
  14. data/Rakefile +125 -13
  15. data/lib/solargraph/api_map/cache.rb +3 -2
  16. data/lib/solargraph/api_map/constants.rb +218 -0
  17. data/lib/solargraph/api_map/index.rb +20 -26
  18. data/lib/solargraph/api_map/source_to_yard.rb +10 -4
  19. data/lib/solargraph/api_map/store.rb +126 -18
  20. data/lib/solargraph/api_map.rb +212 -234
  21. data/lib/solargraph/bench.rb +1 -0
  22. data/lib/solargraph/complex_type/type_methods.rb +1 -0
  23. data/lib/solargraph/complex_type/unique_type.rb +7 -7
  24. data/lib/solargraph/complex_type.rb +5 -1
  25. data/lib/solargraph/convention/active_support_concern.rb +111 -0
  26. data/lib/solargraph/convention/base.rb +17 -0
  27. data/lib/solargraph/convention/data_definition/data_assignment_node.rb +61 -0
  28. data/lib/solargraph/convention/data_definition/data_definition_node.rb +91 -0
  29. data/lib/solargraph/convention/data_definition.rb +105 -0
  30. data/lib/solargraph/convention/gemspec.rb +3 -2
  31. data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +2 -1
  32. data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +4 -2
  33. data/lib/solargraph/convention/struct_definition.rb +87 -24
  34. data/lib/solargraph/convention.rb +32 -2
  35. data/lib/solargraph/diagnostics/rubocop.rb +6 -1
  36. data/lib/solargraph/diagnostics/rubocop_helpers.rb +1 -1
  37. data/lib/solargraph/doc_map.rb +40 -12
  38. data/lib/solargraph/environ.rb +9 -2
  39. data/lib/solargraph/gem_pins.rb +17 -11
  40. data/lib/solargraph/language_server/host/dispatch.rb +2 -0
  41. data/lib/solargraph/language_server/host/message_worker.rb +3 -0
  42. data/lib/solargraph/language_server/host.rb +2 -1
  43. data/lib/solargraph/language_server/message/base.rb +2 -1
  44. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +1 -1
  45. data/lib/solargraph/language_server/message/text_document/definition.rb +2 -0
  46. data/lib/solargraph/language_server/message/text_document/formatting.rb +16 -2
  47. data/lib/solargraph/language_server/message/text_document/type_definition.rb +1 -0
  48. data/lib/solargraph/language_server/message/workspace/did_change_workspace_folders.rb +2 -0
  49. data/lib/solargraph/language_server/progress.rb +8 -0
  50. data/lib/solargraph/language_server/request.rb +1 -0
  51. data/lib/solargraph/library.rb +17 -24
  52. data/lib/solargraph/location.rb +2 -0
  53. data/lib/solargraph/logging.rb +11 -2
  54. data/lib/solargraph/page.rb +4 -0
  55. data/lib/solargraph/parser/comment_ripper.rb +8 -1
  56. data/lib/solargraph/parser/flow_sensitive_typing.rb +32 -4
  57. data/lib/solargraph/parser/node_methods.rb +2 -2
  58. data/lib/solargraph/parser/node_processor/base.rb +10 -5
  59. data/lib/solargraph/parser/node_processor.rb +24 -8
  60. data/lib/solargraph/parser/parser_gem/class_methods.rb +1 -1
  61. data/lib/solargraph/parser/parser_gem/flawed_builder.rb +1 -0
  62. data/lib/solargraph/parser/parser_gem/node_chainer.rb +3 -1
  63. data/lib/solargraph/parser/parser_gem/node_methods.rb +4 -2
  64. data/lib/solargraph/parser/parser_gem/node_processors/block_node.rb +3 -2
  65. data/lib/solargraph/parser/parser_gem/node_processors/casgn_node.rb +1 -21
  66. data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +2 -0
  67. data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +7 -1
  68. data/lib/solargraph/parser/parser_gem/node_processors/namespace_node.rb +0 -22
  69. data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +1 -0
  70. data/lib/solargraph/parser/parser_gem/node_processors/orasgn_node.rb +1 -0
  71. data/lib/solargraph/parser/parser_gem/node_processors/resbody_node.rb +1 -0
  72. data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +35 -14
  73. data/lib/solargraph/parser/parser_gem/node_processors/sym_node.rb +1 -0
  74. data/lib/solargraph/parser/parser_gem/node_processors.rb +4 -0
  75. data/lib/solargraph/parser/region.rb +3 -0
  76. data/lib/solargraph/parser/snippet.rb +2 -0
  77. data/lib/solargraph/pin/base.rb +65 -8
  78. data/lib/solargraph/pin/base_variable.rb +1 -2
  79. data/lib/solargraph/pin/callable.rb +9 -0
  80. data/lib/solargraph/pin/closure.rb +2 -0
  81. data/lib/solargraph/pin/common.rb +6 -2
  82. data/lib/solargraph/pin/constant.rb +2 -0
  83. data/lib/solargraph/pin/delegated_method.rb +1 -0
  84. data/lib/solargraph/pin/local_variable.rb +4 -1
  85. data/lib/solargraph/pin/method.rb +12 -7
  86. data/lib/solargraph/pin/method_alias.rb +3 -0
  87. data/lib/solargraph/pin/parameter.rb +18 -8
  88. data/lib/solargraph/pin/proxy_type.rb +1 -0
  89. data/lib/solargraph/pin/reference/override.rb +15 -1
  90. data/lib/solargraph/pin/reference/superclass.rb +5 -0
  91. data/lib/solargraph/pin/reference.rb +26 -0
  92. data/lib/solargraph/pin/search.rb +3 -1
  93. data/lib/solargraph/pin/signature.rb +2 -0
  94. data/lib/solargraph/pin/symbol.rb +5 -0
  95. data/lib/solargraph/pin_cache.rb +64 -4
  96. data/lib/solargraph/position.rb +2 -0
  97. data/lib/solargraph/range.rb +1 -0
  98. data/lib/solargraph/rbs_map/conversions.rb +47 -18
  99. data/lib/solargraph/rbs_map/core_map.rb +3 -0
  100. data/lib/solargraph/rbs_map.rb +15 -2
  101. data/lib/solargraph/shell.rb +3 -0
  102. data/lib/solargraph/source/chain/link.rb +10 -1
  103. data/lib/solargraph/source/chain.rb +9 -2
  104. data/lib/solargraph/source/change.rb +2 -2
  105. data/lib/solargraph/source/cursor.rb +2 -3
  106. data/lib/solargraph/source/source_chainer.rb +1 -1
  107. data/lib/solargraph/source.rb +5 -2
  108. data/lib/solargraph/source_map/clip.rb +1 -1
  109. data/lib/solargraph/source_map/data.rb +4 -0
  110. data/lib/solargraph/source_map/mapper.rb +4 -2
  111. data/lib/solargraph/source_map.rb +21 -14
  112. data/lib/solargraph/type_checker/param_def.rb +2 -0
  113. data/lib/solargraph/type_checker/rules.rb +8 -0
  114. data/lib/solargraph/type_checker.rb +173 -120
  115. data/lib/solargraph/version.rb +1 -1
  116. data/lib/solargraph/workspace/config.rb +1 -3
  117. data/lib/solargraph/workspace/require_paths.rb +98 -0
  118. data/lib/solargraph/workspace.rb +16 -48
  119. data/lib/solargraph/yard_map/helpers.rb +29 -1
  120. data/lib/solargraph/yard_map/mapper/to_constant.rb +5 -5
  121. data/lib/solargraph/yard_map/mapper/to_method.rb +3 -8
  122. data/lib/solargraph/yard_map/mapper/to_namespace.rb +7 -7
  123. data/lib/solargraph/yardoc.rb +16 -3
  124. data/lib/solargraph.rb +15 -0
  125. data/rbs/fills/tuple.rbs +2 -3
  126. data/sig/shims/parser/3.2.0.1/builders/default.rbs +195 -0
  127. data/sig/shims/thor/1.2.0.1/.rbs_meta.yaml +9 -0
  128. data/sig/shims/thor/1.2.0.1/manifest.yaml +7 -0
  129. data/sig/shims/thor/1.2.0.1/thor.rbs +17 -0
  130. data/solargraph.gemspec +14 -4
  131. metadata +126 -9
  132. data/lib/.rubocop.yml +0 -22
@@ -19,8 +19,14 @@ module Solargraph
19
19
  require_rubocop(config['version'])
20
20
  options, paths = ::RuboCop::Options.new.parse(args)
21
21
  options[:stdin] = original
22
- corrections = redirect_stdout do
23
- ::RuboCop::Runner.new(options, ::RuboCop::ConfigStore.new).run(paths)
22
+
23
+ # Ensure only one instance of RuboCop::Runner is running at
24
+ # a time - it uses 'chdir' to read config files with ERB,
25
+ # which can conflict with other chdirs.
26
+ corrections = Solargraph::CHDIR_MUTEX.synchronize do
27
+ redirect_stdout do
28
+ ::RuboCop::Runner.new(options, ::RuboCop::ConfigStore.new).run(paths)
29
+ end
24
30
  end
25
31
  result = options[:stdin]
26
32
 
@@ -34,6 +40,7 @@ module Solargraph
34
40
  private
35
41
 
36
42
  # @param corrections [String]
43
+ # @return [void]
37
44
  def log_corrections(corrections)
38
45
  corrections = corrections&.strip
39
46
  return if corrections&.empty?
@@ -45,6 +52,8 @@ module Solargraph
45
52
  end
46
53
  end
47
54
 
55
+ # @param file_uri [String]
56
+ # @return [Hash{String => undefined}]
48
57
  def config_for(file_uri)
49
58
  conf = host.formatter_config(file_uri)
50
59
  return {} unless conf.is_a?(Hash)
@@ -52,7 +61,9 @@ module Solargraph
52
61
  conf['rubocop'] || {}
53
62
  end
54
63
 
64
+ # @param file_uri [String]
55
65
  # @param config [Hash{String => String}]
66
+ # @return [Array<String>]
56
67
  def cli_args file_uri, config
57
68
  file = UriHelpers.uri_to_file(file_uri)
58
69
  args = [
@@ -71,6 +82,8 @@ module Solargraph
71
82
  end
72
83
 
73
84
  # @param config [Hash{String => String}]
85
+ # @sg-ignore
86
+ # @return [Class<RuboCop::Formatter::BaseFormatter>]
74
87
  def formatter_class(config)
75
88
  if self.class.const_defined?('BlankRubocopFormatter')
76
89
  # @sg-ignore
@@ -83,6 +96,7 @@ module Solargraph
83
96
  end
84
97
 
85
98
  # @param value [Array, String]
99
+ # @return [String]
86
100
  def cop_list(value)
87
101
  value = value.join(',') if value.respond_to?(:join)
88
102
  return nil if value == '' || !value.is_a?(String)
@@ -10,6 +10,7 @@ module Solargraph::LanguageServer::Message::TextDocument
10
10
 
11
11
  private
12
12
 
13
+ # @return [Array<Hash>]
13
14
  def code_location
14
15
  suggestions = host.type_definitions_at(params['textDocument']['uri'], @line, @column)
15
16
  return nil if suggestions.empty?
@@ -9,11 +9,13 @@ module Solargraph::LanguageServer::Message::Workspace
9
9
 
10
10
  private
11
11
 
12
+ # @return [void]
12
13
  def add_folders
13
14
  return unless params['event'] && params['event']['added']
14
15
  host.prepare_folders params['event']['added']
15
16
  end
16
17
 
18
+ # @return [void]
17
19
  def remove_folders
18
20
  return unless params['event'] && params['event']['removed']
19
21
  params['event']['removed'].each do |folder|
@@ -39,6 +39,7 @@ module Solargraph
39
39
 
40
40
  # @param message [String]
41
41
  # @param percentage [Integer]
42
+ # @return [void]
42
43
  def begin message, percentage
43
44
  @kind = 'begin'
44
45
  @message = message
@@ -47,6 +48,7 @@ module Solargraph
47
48
 
48
49
  # @param message [String]
49
50
  # @param percentage [Integer]
51
+ # @return [void]
50
52
  def report message, percentage
51
53
  @kind = 'report'
52
54
  @message = message
@@ -54,6 +56,7 @@ module Solargraph
54
56
  end
55
57
 
56
58
  # @param message [String]
59
+ # @return [void]
57
60
  def finish message
58
61
  @kind = 'end'
59
62
  @message = message
@@ -62,6 +65,7 @@ module Solargraph
62
65
  end
63
66
 
64
67
  # @param host [Solargraph::LanguageServer::Host]
68
+ # @return [void]
65
69
  def send host
66
70
  return unless host.client_supports_progress? && !finished?
67
71
 
@@ -91,6 +95,7 @@ module Solargraph
91
95
  @status = CREATED
92
96
  end
93
97
 
98
+ # @return [Hash]
94
99
  def build
95
100
  {
96
101
  token: uuid,
@@ -101,6 +106,7 @@ module Solargraph
101
106
  }
102
107
  end
103
108
 
109
+ # @return [Hash]
104
110
  def build_value
105
111
  case kind
106
112
  when 'begin'
@@ -115,6 +121,7 @@ module Solargraph
115
121
  end
116
122
 
117
123
  # @param host [Host]
124
+ # @return [void]
118
125
  def keep_alive host
119
126
  mutex.synchronize { @last = Time.now }
120
127
  @keep_alive ||= Thread.new do
@@ -127,6 +134,7 @@ module Solargraph
127
134
  end
128
135
  end
129
136
 
137
+ # @return [Mutex]
130
138
  def mutex
131
139
  @mutex ||= Mutex.new
132
140
  end
@@ -16,6 +16,7 @@ module Solargraph
16
16
  @block.call(result) unless @block.nil?
17
17
  end
18
18
 
19
+ # @return [void]
19
20
  def send_response
20
21
  # noop
21
22
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'pathname'
4
4
  require 'observer'
5
+ require 'open3'
5
6
 
6
7
  module Solargraph
7
8
  # A Library handles coordination between a Workspace and an ApiMap.
@@ -401,8 +402,8 @@ module Solargraph
401
402
  repargs = {}
402
403
  workspace.config.reporters.each do |line|
403
404
  if line == 'all!'
404
- Diagnostics.reporters.each do |reporter|
405
- repargs[reporter] ||= []
405
+ Diagnostics.reporters.each do |reporter_name|
406
+ repargs[Diagnostics.reporter(reporter_name)] ||= []
406
407
  end
407
408
  else
408
409
  args = line.split(':').map(&:strip)
@@ -436,17 +437,6 @@ module Solargraph
436
437
  )
437
438
  end
438
439
 
439
- # Get an array of foldable ranges for the specified file.
440
- #
441
- # @deprecated The library should not need to handle folding ranges. The
442
- # source itself has all the information it needs.
443
- #
444
- # @param filename [String]
445
- # @return [Array<Range>]
446
- def folding_ranges filename
447
- read(filename).folding_ranges
448
- end
449
-
450
440
  # Create a library from a directory.
451
441
  #
452
442
  # @param directory [String] The path to be used for the workspace
@@ -510,7 +500,7 @@ module Solargraph
510
500
 
511
501
  private
512
502
 
513
- # @return [Hash{String => Set<String>}]
503
+ # @return [Hash{String => Array<String>}]
514
504
  def source_map_external_require_hash
515
505
  @source_map_external_require_hash ||= {}
516
506
  end
@@ -518,6 +508,7 @@ module Solargraph
518
508
  # @param source_map [SourceMap]
519
509
  # @return [void]
520
510
  def find_external_requires source_map
511
+ # @type [Set<String>]
521
512
  new_set = source_map.requires.map(&:name).to_set
522
513
  # return if new_set == source_map_external_require_hash[source_map.filename]
523
514
  _filenames = nil
@@ -604,16 +595,15 @@ module Solargraph
604
595
  else
605
596
  logger.info "Caching #{spec.name} #{spec.version}"
606
597
  Thread.new do
607
- cache_pid = Process.spawn(workspace.command_path, 'cache', spec.name, spec.version.to_s)
608
598
  report_cache_progress spec.name, pending
609
- Process.wait(cache_pid)
610
- logger.info "Cached #{spec.name} #{spec.version}"
611
- rescue Errno::EINVAL => _e
612
- logger.info "Cached #{spec.name} #{spec.version} with EINVAL"
613
- rescue StandardError => e
614
- cache_errors.add spec
615
- Solargraph.logger.warn "Error caching gemspec #{spec.name} #{spec.version}: [#{e.class}] #{e.message}"
616
- ensure
599
+ _o, e, s = Open3.capture3(workspace.command_path, 'cache', spec.name, spec.version.to_s)
600
+ if s.success?
601
+ logger.info "Cached #{spec.name} #{spec.version}"
602
+ else
603
+ cache_errors.add spec
604
+ logger.warn "Error caching gemspec #{spec.name} #{spec.version}"
605
+ logger.warn e
606
+ end
617
607
  end_cache_progress
618
608
  catalog
619
609
  sync_catalog
@@ -621,6 +611,7 @@ module Solargraph
621
611
  end
622
612
  end
623
613
 
614
+ # @return [Array<Gem::Specification>]
624
615
  def cacheable_specs
625
616
  cacheable = api_map.uncached_yard_gemspecs +
626
617
  api_map.uncached_rbs_collection_gemspecs -
@@ -631,6 +622,7 @@ module Solargraph
631
622
  queued_gemspec_cache
632
623
  end
633
624
 
625
+ # @return [Array<Gem::Specification>]
634
626
  def queued_gemspec_cache
635
627
  @queued_gemspec_cache ||= []
636
628
  end
@@ -672,13 +664,14 @@ module Solargraph
672
664
  @total = nil
673
665
  end
674
666
 
667
+ # @return [void]
675
668
  def sync_catalog
676
669
  return if @sync_count == 0
677
670
 
678
671
  mutex.synchronize do
679
672
  logger.info "Cataloging #{workspace.directory.empty? ? 'generic workspace' : workspace.directory}"
680
- api_map.catalog bench
681
673
  source_map_hash.values.each { |map| find_external_requires(map) }
674
+ api_map.catalog bench
682
675
  logger.info "Catalog complete (#{api_map.source_maps.length} files, #{api_map.pins.length} pins)"
683
676
  logger.info "#{api_map.uncached_yard_gemspecs.length} uncached YARD gemspecs"
684
677
  logger.info "#{api_map.uncached_rbs_collection_gemspecs.length} uncached RBS collection gemspecs"
@@ -25,6 +25,7 @@ module Solargraph
25
25
  [filename, range]
26
26
  end
27
27
 
28
+ # @param other [self]
28
29
  def <=>(other)
29
30
  return nil unless other.is_a?(Location)
30
31
  if filename == other.filename
@@ -60,6 +61,7 @@ module Solargraph
60
61
  end
61
62
 
62
63
  # @param node [Parser::AST::Node, nil]
64
+ # @return [Location, nil]
63
65
  def self.from_node(node)
64
66
  return nil if node.nil? || node.loc.nil?
65
67
  range = Range.from_node(node)
@@ -11,8 +11,17 @@ module Solargraph
11
11
  'info' => Logger::INFO,
12
12
  'debug' => Logger::DEBUG
13
13
  }
14
-
15
- @@logger = Logger.new(STDERR, level: DEFAULT_LOG_LEVEL)
14
+ configured_level = ENV.fetch('SOLARGRAPH_LOG', nil)
15
+ level = if LOG_LEVELS.keys.include?(configured_level)
16
+ LOG_LEVELS.fetch(configured_level)
17
+ else
18
+ if configured_level
19
+ warn "Invalid value for SOLARGRAPH_LOG: #{configured_level.inspect} - " \
20
+ "valid values are #{LOG_LEVELS.keys}"
21
+ end
22
+ DEFAULT_LOG_LEVEL
23
+ end
24
+ @@logger = Logger.new(STDERR, level: level)
16
25
  # @sg-ignore Fix cvar issue
17
26
  @@logger.formatter = proc do |severity, datetime, progname, msg|
18
27
  "[#{severity}] #{msg}\n"
@@ -27,8 +27,10 @@ module Solargraph
27
27
  end
28
28
 
29
29
  # @param text [String]
30
+ # @sg-ignore https://github.com/lsegal/yard/pull/1615
30
31
  # @return [String]
31
32
  def htmlify text
33
+ # @type [String]
32
34
  YARD::Templates::Helpers::Markup::RDocMarkup.new(text).to_html
33
35
  end
34
36
 
@@ -70,8 +72,10 @@ module Solargraph
70
72
  # @param template [String]
71
73
  # @param layout [Boolean]
72
74
  # @param locals [Hash]
75
+ # @sg-ignore
73
76
  # @return [String]
74
77
  def render template, layout: true, locals: {}
78
+ # @type [String]
75
79
  @render_method.call(template, layout: layout, locals: locals)
76
80
  end
77
81
 
@@ -3,6 +3,13 @@ require 'ripper'
3
3
  module Solargraph
4
4
  module Parser
5
5
  class CommentRipper < Ripper::SexpBuilderPP
6
+ # @!override Ripper::SexpBuilder#on_embdoc_beg
7
+ # @return [Array(Symbol, String, Array)]
8
+ # @!override Ripper::SexpBuilder#on_embdoc
9
+ # @return [Array(Symbol, String, Array)]
10
+ # @!override Ripper::SexpBuilder#on_embdoc_end
11
+ # @return [Array(Symbol, String, Array)]
12
+
6
13
  # @param src [String]
7
14
  # @param filename [String]
8
15
  # @param lineno [Integer]
@@ -51,7 +58,7 @@ module Solargraph
51
58
  result
52
59
  end
53
60
 
54
- # @return [Hash{Integer => String}]
61
+ # @return [Hash{Integer => Solargraph::Parser::Snippet}]
55
62
  def parse
56
63
  @comments = {}
57
64
  super
@@ -4,14 +4,20 @@ module Solargraph
4
4
  include Solargraph::Parser::NodeMethods
5
5
 
6
6
  # @param locals [Array<Solargraph::Pin::LocalVariable, Solargraph::Pin::Parameter>]
7
+ # @param enclosing_breakable_pin [Solargraph::Pin::Breakable, nil]
7
8
  def initialize(locals, enclosing_breakable_pin = nil)
8
9
  @locals = locals
9
10
  @enclosing_breakable_pin = enclosing_breakable_pin
10
11
  end
11
12
 
12
13
  # @param and_node [Parser::AST::Node]
14
+ # @param true_ranges [Array<Range>]
15
+ #
16
+ # @return [void]
13
17
  def process_and(and_node, true_ranges = [])
18
+ # @type [Parser::AST::Node]
14
19
  lhs = and_node.children[0]
20
+ # @type [Parser::AST::Node]
15
21
  rhs = and_node.children[1]
16
22
 
17
23
  before_rhs_loc = rhs.location.expression.adjust(begin_pos: -1)
@@ -23,6 +29,8 @@ module Solargraph
23
29
  end
24
30
 
25
31
  # @param if_node [Parser::AST::Node]
32
+ #
33
+ # @return [void]
26
34
  def process_if(if_node)
27
35
  #
28
36
  # See if we can refine a type based on the result of 'if foo.nil?'
@@ -36,7 +44,9 @@ module Solargraph
36
44
  # s(:send, nil, :bar))
37
45
  # [4] pry(main)>
38
46
  conditional_node = if_node.children[0]
47
+ # @type [Parser::AST::Node]
39
48
  then_clause = if_node.children[1]
49
+ # @type [Parser::AST::Node]
40
50
  else_clause = if_node.children[2]
41
51
 
42
52
  true_ranges = []
@@ -72,8 +82,11 @@ module Solargraph
72
82
  # them based on the Closure and Location.
73
83
  #
74
84
  # @param pins [Array<Pin::LocalVariable>]
85
+ # @param name [String]
75
86
  # @param closure [Pin::Closure]
76
87
  # @param location [Location]
88
+ #
89
+ # @return [Array<Pin::LocalVariable>]
77
90
  def self.visible_pins(pins, name, closure, location)
78
91
  logger.debug { "FlowSensitiveTyping#visible_pins(name=#{name}, closure=#{closure}, location=#{location})" }
79
92
  pins_with_name = pins.select { |p| p.name == name }
@@ -107,7 +120,10 @@ module Solargraph
107
120
  private
108
121
 
109
122
  # @param pin [Pin::LocalVariable]
110
- # @param if_node [Parser::AST::Node]
123
+ # @param downcast_type_name [String]
124
+ # @param presence [Range]
125
+ #
126
+ # @return [void]
111
127
  def add_downcast_local(pin, downcast_type_name, presence)
112
128
  # @todo Create pin#update method
113
129
  new_pin = Solargraph::Pin::LocalVariable.new(
@@ -126,6 +142,7 @@ module Solargraph
126
142
 
127
143
  # @param facts_by_pin [Hash{Pin::LocalVariable => Array<Hash{Symbol => String}>}]
128
144
  # @param presences [Array<Range>]
145
+ #
129
146
  # @return [void]
130
147
  def process_facts(facts_by_pin, presences)
131
148
  #
@@ -142,6 +159,9 @@ module Solargraph
142
159
  end
143
160
 
144
161
  # @param conditional_node [Parser::AST::Node]
162
+ # @param true_ranges [Array<Range>]
163
+ #
164
+ # @return [void]
145
165
  def process_conditional(conditional_node, true_ranges)
146
166
  if conditional_node.type == :send
147
167
  process_isa(conditional_node, true_ranges)
@@ -176,12 +196,20 @@ module Solargraph
176
196
  [isa_type_name, variable_name]
177
197
  end
178
198
 
199
+ # @param variable_name [String]
200
+ # @param position [Position]
201
+ #
202
+ # @return [Solargraph::Pin::LocalVariable, nil]
179
203
  def find_local(variable_name, position)
180
204
  pins = locals.select { |pin| pin.name == variable_name && pin.presence.include?(position) }
181
205
  return unless pins.length == 1
182
206
  pins.first
183
207
  end
184
208
 
209
+ # @param isa_node [Parser::AST::Node]
210
+ # @param true_presences [Array<Range>]
211
+ #
212
+ # @return [void]
185
213
  def process_isa(isa_node, true_presences)
186
214
  isa_type_name, variable_name = parse_isa(isa_node)
187
215
  return if variable_name.nil? || variable_name.empty?
@@ -197,10 +225,12 @@ module Solargraph
197
225
  end
198
226
 
199
227
  # @param node [Parser::AST::Node]
228
+ #
229
+ # @return [String, nil]
200
230
  def type_name(node)
201
231
  # e.g.,
202
232
  # s(:const, nil, :Baz)
203
- return unless node.type == :const
233
+ return unless node&.type == :const
204
234
  module_node = node.children[0]
205
235
  class_node = node.children[1]
206
236
 
@@ -212,8 +242,6 @@ module Solargraph
212
242
  "#{module_type_name}::#{class_node}"
213
243
  end
214
244
 
215
- # @todo "return type could not be inferred" should not trigger here
216
- # @sg-ignore
217
245
  # @param clause_node [Parser::AST::Node]
218
246
  def always_breaks?(clause_node)
219
247
  clause_node&.type == :break
@@ -1,6 +1,6 @@
1
1
  module Solargraph
2
2
  module Parser
3
- class NodeMethods
3
+ module NodeMethods
4
4
  module_function
5
5
 
6
6
  # @abstract
@@ -74,7 +74,7 @@ module Solargraph
74
74
 
75
75
  # @abstract
76
76
  # @param node [Parser::AST::Node]
77
- # @return [Hash{Parser::AST::Node => Source::Chain}]
77
+ # @return [Hash{Symbol => Source::Chain}]
78
78
  def convert_hash node
79
79
  raise NotImplementedError
80
80
  end
@@ -13,7 +13,7 @@ module Solargraph
13
13
  # @return [Array<Pin::Base>]
14
14
  attr_reader :pins
15
15
 
16
- # @return [Array<Pin::BaseVariable>]
16
+ # @return [Array<Pin::LocalVariable>]
17
17
  attr_reader :locals
18
18
 
19
19
  # @param node [Parser::AST::Node]
@@ -30,9 +30,12 @@ module Solargraph
30
30
 
31
31
  # Subclasses should override this method to generate new pins.
32
32
  #
33
- # @return [void]
33
+ # @return [Boolean] continue processing the next processor of the same node type.
34
+ # @return [void] In case there is only one processor registered for the node type, it can be void.
34
35
  def process
35
36
  process_children
37
+
38
+ true
36
39
  end
37
40
 
38
41
  private
@@ -64,7 +67,9 @@ module Solargraph
64
67
  # @param position [Solargraph::Position]
65
68
  # @return [Pin::Closure, nil]
66
69
  def named_path_pin position
67
- pins.select{|pin| pin.is_a?(Pin::Closure) && pin.path && !pin.path.empty? && pin.location.range.contain?(position)}.last
70
+ pins.select do |pin|
71
+ pin.is_a?(Pin::Closure) && pin.path && !pin.path.empty? && pin.location.range.contain?(position)
72
+ end.last
68
73
  end
69
74
 
70
75
  # @todo Candidate for deprecation
@@ -72,14 +77,14 @@ module Solargraph
72
77
  # @return [Pin::Closure, nil]
73
78
  def block_pin position
74
79
  # @todo determine if this can return a Pin::Block
75
- pins.select{|pin| pin.is_a?(Pin::Closure) && pin.location.range.contain?(position)}.last
80
+ pins.select { |pin| pin.is_a?(Pin::Closure) && pin.location.range.contain?(position) }.last
76
81
  end
77
82
 
78
83
  # @todo Candidate for deprecation
79
84
  # @param position [Solargraph::Position]
80
85
  # @return [Pin::Closure, nil]
81
86
  def closure_pin position
82
- pins.select{|pin| pin.is_a?(Pin::Closure) && pin.location.range.contain?(position)}.last
87
+ pins.select { |pin| pin.is_a?(Pin::Closure) && pin.location.range.contain?(position) }.last
83
88
  end
84
89
  end
85
90
  end
@@ -9,16 +9,26 @@ module Solargraph
9
9
  autoload :Base, 'solargraph/parser/node_processor/base'
10
10
 
11
11
  class << self
12
- # @type [Hash{Symbol => Class<NodeProcessor::Base>}]
12
+ # @type [Hash{Symbol => Array<Class<NodeProcessor::Base>>}]
13
13
  @@processors ||= {}
14
14
 
15
- # Register a processor for a node type.
15
+ # Register a processor for a node type. You can register multiple processors for the same type.
16
+ # If a node processor returns true, it will skip the next processor of the same node type.
16
17
  #
17
18
  # @param type [Symbol]
18
19
  # @param cls [Class<NodeProcessor::Base>]
19
- # @return [Class<NodeProcessor::Base>]
20
+ # @return [Array<Class<NodeProcessor::Base>>]
20
21
  def register type, cls
21
- @@processors[type] = cls
22
+ @@processors[type] ||= []
23
+ @@processors[type] << cls
24
+ end
25
+
26
+ # @param type [Symbol]
27
+ # @param cls [Class<NodeProcessor::Base>]
28
+ #
29
+ # @return [void]
30
+ def deregister type, cls
31
+ @@processors[type].delete(cls)
22
32
  end
23
33
  end
24
34
 
@@ -36,10 +46,16 @@ module Solargraph
36
46
  )
37
47
  end
38
48
  return [pins, locals] unless Parser.is_ast_node?(node)
39
- klass = @@processors[node.type] || NodeProcessor::Base
40
- processor = klass.new(node, region, pins, locals)
41
- processor.process
42
- [processor.pins, processor.locals]
49
+ node_processor_classes = @@processors[node.type] || [NodeProcessor::Base]
50
+
51
+ node_processor_classes.each do |klass|
52
+ processor = klass.new(node, region, pins, locals)
53
+ process_next = processor.process
54
+
55
+ break unless process_next
56
+ end
57
+
58
+ [pins, locals]
43
59
  end
44
60
  end
45
61
  end
@@ -17,7 +17,7 @@ module Solargraph
17
17
  module ClassMethods
18
18
  # @param code [String]
19
19
  # @param filename [String, nil]
20
- # @return [Array(Parser::AST::Node, Hash{Integer => String})]
20
+ # @return [Array(Parser::AST::Node, Hash{Integer => Solargraph::Parser::Snippet})]
21
21
  def parse_with_comments code, filename = nil
22
22
  node = parse(code, filename)
23
23
  comments = CommentRipper.new(code, filename, 0).parse
@@ -9,6 +9,7 @@ module Solargraph
9
9
  class FlawedBuilder < ::Parser::Builders::Default
10
10
  # @param token [::Parser::AST::Node]
11
11
  # @return [String]
12
+ # @sg-ignore
12
13
  def string_value(token)
13
14
  value(token)
14
15
  end
@@ -7,6 +7,7 @@ module Solargraph
7
7
  #
8
8
  class NodeChainer
9
9
  include NodeMethods
10
+
10
11
  Chain = Source::Chain
11
12
 
12
13
  # @param node [Parser::AST::Node]
@@ -98,7 +99,8 @@ module Solargraph
98
99
  elsif [:gvar, :gvasgn].include?(n.type)
99
100
  result.push Chain::GlobalVariable.new(n.children[0].to_s)
100
101
  elsif n.type == :or_asgn
101
- result.concat generate_links n.children[1]
102
+ new_node = n.updated(n.children[0].type, n.children[0].children + [n.children[1]])
103
+ result.concat generate_links new_node
102
104
  elsif [:class, :module, :def, :defs].include?(n.type)
103
105
  # @todo Undefined or what?
104
106
  result.push Chain::UNDEFINED_CALL
@@ -120,7 +120,7 @@ module Solargraph
120
120
  end
121
121
 
122
122
  # @param node [Parser::AST::Node]
123
- # @return [Hash{Parser::AST::Node => Chain}]
123
+ # @return [Hash{Symbol => Chain}]
124
124
  def convert_hash node
125
125
  return {} unless Parser.is_ast_node?(node)
126
126
  return convert_hash(node.children[0]) if node.type == :kwsplat
@@ -179,6 +179,7 @@ module Solargraph
179
179
  node.children[1..-1].each { |child| result.concat call_nodes_from(child) }
180
180
  elsif node.type == :send
181
181
  result.push node
182
+ result.concat call_nodes_from(node.children.first)
182
183
  node.children[2..-1].each { |child| result.concat call_nodes_from(child) }
183
184
  elsif [:super, :zsuper].include?(node.type)
184
185
  result.push node
@@ -232,6 +233,7 @@ module Solargraph
232
233
  else
233
234
  source.tree_at(position.line, position.column - 1)
234
235
  end
236
+ # @type [AST::Node, nil]
235
237
  prev = nil
236
238
  tree.each do |node|
237
239
  if node.type == :send
@@ -242,7 +244,7 @@ module Solargraph
242
244
  if source.synchronized?
243
245
  return node if source.code[0..offset-1] =~ /\(\s*\z/ && source.code[offset..-1] =~ /^\s*\)/
244
246
  else
245
- return node if source.code[0..offset-1] =~ /\([^\(]*\z/
247
+ return node if source.code[0..offset-1] =~ /\([^(]*\z/
246
248
  end
247
249
  end
248
250
  end
@@ -19,7 +19,7 @@ module Solargraph
19
19
  else
20
20
  region.closure
21
21
  end
22
- pins.push Solargraph::Pin::Block.new(
22
+ block_pin = Solargraph::Pin::Block.new(
23
23
  location: location,
24
24
  closure: parent,
25
25
  node: node,
@@ -28,7 +28,8 @@ module Solargraph
28
28
  scope: region.scope || region.closure.context.scope,
29
29
  source: :parser
30
30
  )
31
- process_children region.update(closure: pins.last)
31
+ pins.push block_pin
32
+ process_children region.update(closure: block_pin)
32
33
  end
33
34
 
34
35
  private