solargraph 0.56.2 → 0.58.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 (147) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/linting.yml +127 -0
  3. data/.github/workflows/plugins.yml +183 -7
  4. data/.github/workflows/rspec.yml +55 -5
  5. data/.github/workflows/typecheck.yml +6 -3
  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 +1279 -0
  11. data/.yardopts +1 -0
  12. data/CHANGELOG.md +69 -0
  13. data/README.md +8 -4
  14. data/Rakefile +125 -13
  15. data/bin/solargraph +8 -5
  16. data/lib/solargraph/api_map/cache.rb +3 -2
  17. data/lib/solargraph/api_map/constants.rb +279 -0
  18. data/lib/solargraph/api_map/index.rb +49 -31
  19. data/lib/solargraph/api_map/source_to_yard.rb +13 -4
  20. data/lib/solargraph/api_map/store.rb +144 -26
  21. data/lib/solargraph/api_map.rb +217 -245
  22. data/lib/solargraph/bench.rb +1 -0
  23. data/lib/solargraph/complex_type/type_methods.rb +6 -0
  24. data/lib/solargraph/complex_type/unique_type.rb +19 -12
  25. data/lib/solargraph/complex_type.rb +24 -3
  26. data/lib/solargraph/convention/active_support_concern.rb +111 -0
  27. data/lib/solargraph/convention/base.rb +17 -0
  28. data/lib/solargraph/convention/data_definition/data_assignment_node.rb +1 -0
  29. data/lib/solargraph/convention/data_definition/data_definition_node.rb +4 -2
  30. data/lib/solargraph/convention/data_definition.rb +2 -1
  31. data/lib/solargraph/convention/gemspec.rb +1 -1
  32. data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +1 -0
  33. data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +3 -1
  34. data/lib/solargraph/convention/struct_definition.rb +36 -13
  35. data/lib/solargraph/convention.rb +31 -2
  36. data/lib/solargraph/diagnostics/rubocop.rb +6 -1
  37. data/lib/solargraph/diagnostics/rubocop_helpers.rb +5 -3
  38. data/lib/solargraph/doc_map.rb +44 -13
  39. data/lib/solargraph/environ.rb +9 -2
  40. data/lib/solargraph/equality.rb +1 -0
  41. data/lib/solargraph/gem_pins.rb +21 -11
  42. data/lib/solargraph/language_server/host/dispatch.rb +2 -0
  43. data/lib/solargraph/language_server/host/message_worker.rb +3 -0
  44. data/lib/solargraph/language_server/host.rb +12 -5
  45. data/lib/solargraph/language_server/message/base.rb +2 -1
  46. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +1 -1
  47. data/lib/solargraph/language_server/message/text_document/definition.rb +2 -0
  48. data/lib/solargraph/language_server/message/text_document/formatting.rb +19 -2
  49. data/lib/solargraph/language_server/message/text_document/type_definition.rb +1 -0
  50. data/lib/solargraph/language_server/message/workspace/did_change_workspace_folders.rb +2 -0
  51. data/lib/solargraph/language_server/progress.rb +8 -0
  52. data/lib/solargraph/language_server/request.rb +4 -1
  53. data/lib/solargraph/library.rb +11 -18
  54. data/lib/solargraph/location.rb +3 -0
  55. data/lib/solargraph/logging.rb +11 -2
  56. data/lib/solargraph/page.rb +3 -0
  57. data/lib/solargraph/parser/comment_ripper.rb +8 -1
  58. data/lib/solargraph/parser/flow_sensitive_typing.rb +33 -5
  59. data/lib/solargraph/parser/node_processor/base.rb +1 -1
  60. data/lib/solargraph/parser/node_processor.rb +6 -2
  61. data/lib/solargraph/parser/parser_gem/class_methods.rb +3 -13
  62. data/lib/solargraph/parser/parser_gem/flawed_builder.rb +1 -0
  63. data/lib/solargraph/parser/parser_gem/node_chainer.rb +3 -1
  64. data/lib/solargraph/parser/parser_gem/node_methods.rb +5 -16
  65. data/lib/solargraph/parser/parser_gem/node_processors/and_node.rb +1 -0
  66. data/lib/solargraph/parser/parser_gem/node_processors/block_node.rb +3 -2
  67. data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +2 -0
  68. data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +3 -0
  69. data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +64 -8
  70. data/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb +12 -3
  71. data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +36 -16
  72. data/lib/solargraph/parser/region.rb +3 -0
  73. data/lib/solargraph/parser/snippet.rb +2 -0
  74. data/lib/solargraph/pin/base.rb +77 -14
  75. data/lib/solargraph/pin/base_variable.rb +6 -5
  76. data/lib/solargraph/pin/block.rb +3 -2
  77. data/lib/solargraph/pin/callable.rb +14 -1
  78. data/lib/solargraph/pin/closure.rb +5 -7
  79. data/lib/solargraph/pin/common.rb +6 -2
  80. data/lib/solargraph/pin/constant.rb +2 -0
  81. data/lib/solargraph/pin/local_variable.rb +1 -2
  82. data/lib/solargraph/pin/method.rb +28 -9
  83. data/lib/solargraph/pin/method_alias.rb +3 -0
  84. data/lib/solargraph/pin/parameter.rb +24 -10
  85. data/lib/solargraph/pin/proxy_type.rb +5 -1
  86. data/lib/solargraph/pin/reference/override.rb +15 -1
  87. data/lib/solargraph/pin/reference/superclass.rb +5 -0
  88. data/lib/solargraph/pin/reference.rb +17 -0
  89. data/lib/solargraph/pin/search.rb +6 -1
  90. data/lib/solargraph/pin/signature.rb +2 -0
  91. data/lib/solargraph/pin/symbol.rb +5 -0
  92. data/lib/solargraph/pin_cache.rb +64 -4
  93. data/lib/solargraph/position.rb +3 -0
  94. data/lib/solargraph/range.rb +5 -0
  95. data/lib/solargraph/rbs_map/conversions.rb +29 -6
  96. data/lib/solargraph/rbs_map/core_fills.rb +18 -0
  97. data/lib/solargraph/rbs_map/core_map.rb +14 -7
  98. data/lib/solargraph/rbs_map.rb +14 -1
  99. data/lib/solargraph/shell.rb +85 -1
  100. data/lib/solargraph/source/chain/call.rb +7 -3
  101. data/lib/solargraph/source/chain/constant.rb +3 -66
  102. data/lib/solargraph/source/chain/if.rb +1 -1
  103. data/lib/solargraph/source/chain/link.rb +11 -2
  104. data/lib/solargraph/source/chain/or.rb +1 -1
  105. data/lib/solargraph/source/chain.rb +11 -2
  106. data/lib/solargraph/source/change.rb +2 -2
  107. data/lib/solargraph/source/cursor.rb +2 -3
  108. data/lib/solargraph/source/source_chainer.rb +1 -1
  109. data/lib/solargraph/source.rb +6 -3
  110. data/lib/solargraph/source_map/clip.rb +18 -26
  111. data/lib/solargraph/source_map/data.rb +4 -0
  112. data/lib/solargraph/source_map/mapper.rb +2 -2
  113. data/lib/solargraph/source_map.rb +28 -16
  114. data/lib/solargraph/type_checker/param_def.rb +2 -0
  115. data/lib/solargraph/type_checker/rules.rb +30 -8
  116. data/lib/solargraph/type_checker.rb +301 -186
  117. data/lib/solargraph/version.rb +1 -1
  118. data/lib/solargraph/workspace/config.rb +21 -5
  119. data/lib/solargraph/workspace/require_paths.rb +97 -0
  120. data/lib/solargraph/workspace.rb +30 -67
  121. data/lib/solargraph/yard_map/mapper/to_method.rb +4 -3
  122. data/lib/solargraph/yard_map/mapper/to_namespace.rb +1 -0
  123. data/lib/solargraph/yard_map/to_method.rb +2 -1
  124. data/lib/solargraph/yardoc.rb +39 -3
  125. data/lib/solargraph.rb +2 -0
  126. data/rbs/fills/bundler/0/bundler.rbs +4271 -0
  127. data/rbs/fills/open3/0/open3.rbs +172 -0
  128. data/rbs/fills/rubygems/0/basic_specification.rbs +326 -0
  129. data/rbs/fills/rubygems/0/errors.rbs +364 -0
  130. data/rbs/fills/rubygems/0/spec_fetcher.rbs +107 -0
  131. data/rbs/fills/rubygems/0/specification.rbs +1753 -0
  132. data/rbs/fills/{tuple.rbs → tuple/tuple.rbs} +2 -3
  133. data/rbs_collection.yaml +4 -4
  134. data/sig/shims/ast/0/node.rbs +5 -0
  135. data/sig/shims/ast/2.4/.rbs_meta.yaml +9 -0
  136. data/sig/shims/ast/2.4/ast.rbs +73 -0
  137. data/sig/shims/parser/3.2.0.1/builders/default.rbs +195 -0
  138. data/sig/shims/parser/3.2.0.1/manifest.yaml +7 -0
  139. data/sig/shims/parser/3.2.0.1/parser.rbs +201 -0
  140. data/sig/shims/parser/3.2.0.1/polyfill.rbs +4 -0
  141. data/sig/shims/thor/1.2.0.1/.rbs_meta.yaml +9 -0
  142. data/sig/shims/thor/1.2.0.1/manifest.yaml +7 -0
  143. data/sig/shims/thor/1.2.0.1/thor.rbs +17 -0
  144. data/solargraph.gemspec +26 -5
  145. metadata +181 -13
  146. data/lib/.rubocop.yml +0 -22
  147. data/lib/solargraph/parser/node_methods.rb +0 -97
data/.yardopts CHANGED
@@ -1,2 +1,3 @@
1
1
  lib/**/*.rb
2
2
  --plugin yard-solargraph
3
+ --plugin activesupport-concern
data/CHANGELOG.md CHANGED
@@ -1,3 +1,72 @@
1
+ ## 0.58.0 - January 1, 2026
2
+ - Faster constant resolution (#1083)
3
+ - [regression] Handle RBS static method aliases (#1094)
4
+ - More type fills and shims (#1005)
5
+ - Fix resolution in blocks in type checker (#890)
6
+ - Annotation fixes for strong typechecking (#1057)
7
+ - Remove dead code (#1077)
8
+ - Fix flakey spec (#1080)
9
+ - Fix bad sexpr generation in op_asgn (#1089)
10
+ - Opt-in for MFA requirement (#730)
11
+ - [regression] Fix resolution issues with namespaces from YARD (#1097)
12
+ - Improve a pin combination case around selfy types (#1024)
13
+ - Rescue reference errors in hosts (#1105)
14
+ - Relax bundler runtime dependency version constraint to support newer versions (#1125)
15
+ - Remove stale Pathname test (#1135)
16
+ - Enable strong type checking in CI (#928)
17
+ - Stale sg-ignore
18
+ - Use rbs 3.9.5 in tests (#1136)
19
+ - Drop broken 'namespaces' method (#1065)
20
+ - Add ActiveRecord example from RBS (#1074)
21
+ - Keep workspace directories as absolute paths (#1076)
22
+ - Handle bad gem_dir from gemspec object (#1079)
23
+ - Test for absolute require paths (#1137)
24
+ - [regression] Fix resolution of ambiguous argument types (#1098)
25
+ - Remove sg-ignore for String#=~ (#1138)
26
+ - Allow levels to be changed for typechecking rules in .solargraph.yml (#1126)
27
+
28
+ ## 0.57.0 - September 16, 2025
29
+ - Support ActiveSupport::Concern pattern for class methods (#948)
30
+ - More CI checks (#996)
31
+ - Linting / type annotation fixes (#999)
32
+ - Avoid overlapping chdir calls in multiple threads (#1007)
33
+ - Fix kwarg generation in ApiMap::SourceToYard (#1003)
34
+ - Enable Steep typechecking of Solargraph code (#1004)
35
+ - Fix convention requires (#1008)
36
+ - Plugin Util: Add Combination Priority (#1010)
37
+ - [regression] Fix crash while typechecking files with Struct use (#1031)
38
+ - Remove yard reference from gemfile (#1033)
39
+ - Allow newer RBS gem versions, exclude incompatible ones (#995)
40
+ - Look for external requires before cataloging bench (#1021)
41
+ - Remove Library#folding_ranges (#904)
42
+ - Complain in strong type-checking if an @sg-ignore line is not needed (#1011)
43
+ - Document a log level env variable (#894)
44
+ - Fix hole in type checking evaluation (#1009)
45
+ - Improve typechecking error message (#1014)
46
+ - Internal strict type-checking fixes (#1013)
47
+ - Reproduce and fix a ||= (or-asgn) evaluation issue (#1017)
48
+ - Define closure for Pin::Symbol, for completeness (#1027)
49
+ - Fix 'all!' config to reporters (#1018)
50
+ - Fix DocMap.all_rbs_collection_gems_in_memory return type (#1037)
51
+ - Fix RuboCop linting errors in regular expressions (#1038)
52
+ - Resolve class aliases via Constant pins (#1029)
53
+ - Speed-up LSP completion response times (#1035)
54
+ - Revert "Resolve class aliases via Constant pins (#1029)" (#1041)
55
+ - Avoid stack errors when resolving method aliases (#1040)
56
+ - [regression] Refine order of object convention method pins (#1036)
57
+ - Fix crash while generating activesupport pins (#1043)
58
+ - Type annotation improvements (#1016)
59
+ - Resolve class aliases via Constant pins (#1048)
60
+ - Understand "Parser::AST::Node < AST::Node" in RBS (#1060)
61
+ - Factor out require_paths logic to its own class (#1062)
62
+ - Fix type errors found in strong typechecking (#1045)
63
+ - Run plugin specs separately for perf insights (#1046)
64
+ - Run specs from solargraph-rails configured against current code in CI (#892)
65
+ - Fix Convention/Plugin pins not being updated on file change (#1028)
66
+ - Fix solargraph-rails check (#1073)
67
+ - Flow sensitive typing handles x.is_a? without an argument (#1070)
68
+ - Refactor reference pin handling (#1058)
69
+
1
70
  ## 0.56.2 - July 29, 2025
2
71
  - Add support for Ruby Data.define (#970)
3
72
  - Ensure that pin locations are always populated (#965)
data/README.md CHANGED
@@ -53,7 +53,7 @@ Solargraph's behavior can be controlled via optional [configuration](https://sol
53
53
 
54
54
  ### Plugins
55
55
 
56
- Solargraph supports [plugins](https://solargraph.org/guides/plugins) that implements their own Solargraph features, such as diagnostics reporters and conventions to provide LSP features and type-checking, e.g. for frameworks which use metaprogramming and/or DSLs.
56
+ Solargraph supports [plugins](https://solargraph.org/guides/plugins) that implement their own Solargraph features, such as diagnostics reporters and conventions to provide LSP features and type-checking, e.g. for frameworks which use metaprogramming and/or DSLs.
57
57
 
58
58
  For better Rails support, please consider using [solargraph-rails](https://github.com/iftheshoefritz/solargraph-rails/)
59
59
 
@@ -65,7 +65,7 @@ The RSpec framework is supported via [solargraph-rspec](https://github.com/lekem
65
65
 
66
66
  When editing code, a `require` call that references a gem will pull the documentation into the code maps and include the gem's API in code completion and intellisense. Solargraph automatically generates code maps from installed gems, based on the YARD or RBS type information inside the gem. You can also eagerly cache gem documentation with the `solargraph gems` command.
67
67
 
68
- If your project automatically requires bundled gems (e.g., `require 'bundler/require'`), Solargraph will add all of the Gemfile's default dependencies to the map.
68
+ If your project automatically requires bundled gem with the `Bundler.require` statement, Solargraph will add all of the Gemfile's default dependencies to the map.
69
69
 
70
70
  To ensure you have types for gems which contain neither RBS nor YARD
71
71
  information, use
@@ -81,7 +81,7 @@ Once installed, you can also insert your own local overrides and definitions in
81
81
 
82
82
  ### Type Checking
83
83
 
84
- As of version 0.33.0, Solargraph includes a [type checker](https://github.com/castwide/solargraph/issues/192) that uses a combination of YARD tags and code analysis to report missing type definitions. In strict mode, it performs type inference to determine whether the tags match the types it detects from code.
84
+ As of version 0.33.0, Solargraph includes a [type checker](https://github.com/castwide/solargraph/issues/192) that uses a combination of YARD tags and code analysis to report missing type definitions. In strict mode, it performs type inference to determine whether the tags match the types it detects from code. In strong mode it will ask you to clarify your intentions by adding annotations for better validation.
85
85
 
86
86
  ### The Documentation Cache
87
87
 
@@ -101,7 +101,7 @@ Run `bundle install` and optionally use `bundle exec solargraph gems` to generat
101
101
 
102
102
  In order to make sure you're using the correct dependencies, you can start the language server with Bundler. In VS Code, there's a `solargraph.useBundler` option. Other clients will vary, but the command you probably want to run is `bundle exec solargraph socket` or `bundle exec solargraph stdio`.
103
103
 
104
- ### Rubocop Version
104
+ ### RuboCop Version
105
105
 
106
106
  If you have multiple versions of [`rubocop`](https://rubygems.org/gems/rubocop) installed and you would like to choose a version other than the latest to use, this specific version can be configured.
107
107
 
@@ -132,6 +132,10 @@ See [https://solargraph.org/guides](https://solargraph.org/guides) for more tips
132
132
 
133
133
  ### Development
134
134
 
135
+ To see more logging when typechecking or running specs, set the
136
+ `SOLARGRAPH_LOG` environment variable to `debug` or `info`. `warn` is
137
+ the default value.
138
+
135
139
  Code contributions are always appreciated. Feel free to fork the repo and submit pull requests. Check for open issues that could use help. Start new issues to discuss changes that have a major impact on the code or require large time commitments.
136
140
 
137
141
  ### Sponsorship and Donation
data/Rakefile CHANGED
@@ -1,12 +1,7 @@
1
1
  require 'rake'
2
- require 'rspec/core/rake_task'
3
2
  require 'bundler/gem_tasks'
4
-
5
- begin
6
- require 'rspec/core/rake_task'
7
- RSpec::Core::RakeTask.new(:spec)
8
- rescue LoadError
9
- end
3
+ require 'fileutils'
4
+ require 'open3'
10
5
 
11
6
  desc "Open a Pry session preloaded with this library"
12
7
  task :console do
@@ -14,12 +9,129 @@ task :console do
14
9
  end
15
10
 
16
11
  desc "Run the type checker"
17
- task :typecheck do
18
- sh "bundle exec solargraph typecheck --level typed"
12
+ task typecheck: [:typecheck_strong]
13
+
14
+ desc "Run the type checker at typed level - return code issues provable without annotations being correct"
15
+ task :typecheck_typed do
16
+ sh "SOLARGRAPH_ASSERTS=on bundle exec solargraph typecheck --level typed"
17
+ end
18
+
19
+ desc "Run the type checker at strict level - report issues using type annotations"
20
+ task :typecheck_strict do
21
+ sh "SOLARGRAPH_ASSERTS=on bundle exec solargraph typecheck --level strict"
22
+ end
23
+
24
+ desc "Run the type checker at strong level - enforce that type annotations exist"
25
+ task :typecheck_strong do
26
+ sh "SOLARGRAPH_ASSERTS=on bundle exec solargraph typecheck --level strong"
27
+ end
28
+
29
+ desc "Run the type checker at alpha level - run high-false-alarm checks"
30
+ task :typecheck_alpha do
31
+ sh "SOLARGRAPH_ASSERTS=on bundle exec solargraph typecheck --level alpha"
32
+ end
33
+
34
+ desc "Run RSpec tests, starting with the ones that failed last time"
35
+ task spec: %i[spec_failed undercover_no_fail full_spec] do
36
+ undercover
37
+ end
38
+
39
+ desc "Run all RSpec tests"
40
+ task :full_spec do
41
+ warn 'starting spec'
42
+ sh 'TEST_COVERAGE_COMMAND_NAME=full-new bundle exec rspec' # --profile'
43
+ warn 'ending spec'
44
+ # move coverage/full-new to coverage/full on success so that we
45
+ # always have the last successful run's 'coverage info
46
+ FileUtils.rm_rf('coverage/full')
47
+ FileUtils.mv('coverage/full-new', 'coverage/full')
48
+ end
49
+
50
+ # @sg-ignore #undercover return type could not be inferred
51
+ # @return [Process::Status]
52
+ def undercover
53
+ simplecov_collate
54
+ cmd = 'bundle exec undercover ' \
55
+ '--simplecov coverage/combined/coverage.json ' \
56
+ '--exclude-files "Rakefile,spec/*,spec/**/*,lib/solargraph/version.rb" ' \
57
+ '--compare origin/master'
58
+ output, status = Bundler.with_unbundled_env do
59
+ Open3.capture2e(cmd)
60
+ end
61
+ puts output
62
+ $stdout.flush
63
+ status
64
+ rescue StandardError => e
65
+ warn "hit error: #{e.message}"
66
+ warn "Backtrace:\n#{e.backtrace.join("\n")}"
67
+ warn "output: #{output}"
68
+ puts "Flushing"
69
+ $stdout.flush
70
+ raise
71
+ end
72
+
73
+ desc "Check PR coverage"
74
+ task :undercover do
75
+ raise "Undercover failed" unless undercover.success?
76
+ end
77
+
78
+ desc "Branch-focused fast-feedback quality/spec/coverage checks"
79
+ task test: %i[overcommit spec typecheck] do
80
+ # do these in order
81
+ Rake::Task['typecheck_strict'].invoke
82
+ Rake::Task['typecheck_strong'].invoke
83
+ Rake::Task['typecheck_alpha'].invoke
84
+ end
85
+
86
+ desc "Re-run failed specs. Add --fail-fast in your .rspec-local file if desired."
87
+ task :spec_failed do
88
+ # allow user to check out any persistent failures while looking for
89
+ # more in the whole test suite
90
+ sh 'TEST_COVERAGE_COMMAND_NAME=next-failure bundle exec rspec --only-failures || true'
91
+ end
92
+
93
+ desc "Run undercover and show output without failing the task if it fails"
94
+ task :undercover_no_fail do
95
+ undercover
96
+ rescue StandardError
97
+ puts "Undercover failed, but continuing with other tasks."
98
+ end
99
+
100
+ # @return [void]
101
+ def simplecov_collate
102
+ require 'simplecov'
103
+ require 'simplecov-lcov'
104
+ require 'undercover/simplecov_formatter'
105
+
106
+ SimpleCov.collate(Dir["coverage/{next-failure,full,ad-hoc}/.resultset.json"]) do
107
+ cname = 'combined'
108
+ command_name cname
109
+ new_dir = File.join('coverage', cname)
110
+ coverage_dir new_dir
111
+
112
+ formatter \
113
+ SimpleCov::Formatter::MultiFormatter
114
+ .new([
115
+ SimpleCov::Formatter::HTMLFormatter,
116
+ SimpleCov::Formatter::Undercover,
117
+ SimpleCov::Formatter::LcovFormatter
118
+ ])
119
+ SimpleCov::Formatter::LcovFormatter.config.report_with_single_file = true
120
+ end
121
+ puts "Simplecov collated results into coverage/combined/.resultset.json"
122
+ rescue StandardError => e
123
+ puts "Simplecov collate failed: #{e.message}"
124
+ ensure
125
+ $stdout.flush
126
+ end
127
+
128
+ desc 'Add incremental coverage for rapid iteration with undercover'
129
+ task :simplecov_collate do
130
+ simplecov_collate
19
131
  end
20
132
 
21
- desc "Run all tests"
22
- task :test do
23
- Rake::Task["typecheck"].invoke
24
- Rake::Task["spec"].invoke
133
+ desc "Show quality checks on this development branch so far, including any staged files"
134
+ task :overcommit do
135
+ # OVERCOMMIT_DEBUG=1 will show more detail
136
+ sh 'SOLARGRAPH_ASSERTS=on bundle exec overcommit --run --diff origin/master'
25
137
  end
data/bin/solargraph CHANGED
@@ -1,5 +1,8 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'solargraph'
4
-
5
- Solargraph::Shell.start(ARGV)
1
+ #!/usr/bin/env ruby
2
+
3
+ # turn off warning diagnostics from Ruby
4
+ $VERBOSE=nil
5
+
6
+ require 'solargraph'
7
+
8
+ Solargraph::Shell.start(ARGV)
@@ -4,9 +4,9 @@ module Solargraph
4
4
  class ApiMap
5
5
  class Cache
6
6
  def initialize
7
- # @type [Hash{Array => Array<Pin::Method>}]
7
+ # @type [Hash{String => Array<Pin::Method>}]
8
8
  @methods = {}
9
- # @type [Hash{(String, Array<String>) => Array<Pin::Base>}]
9
+ # @type [Hash{String, Array<String> => Array<Pin::Base>}]
10
10
  @constants = {}
11
11
  # @type [Hash{String => String}]
12
12
  @qualified_namespaces = {}
@@ -101,6 +101,7 @@ module Solargraph
101
101
 
102
102
  private
103
103
 
104
+ # @return [Array<Object>]
104
105
  def all_caches
105
106
  [@methods, @constants, @qualified_namespaces, @receiver_definitions, @clips]
106
107
  end
@@ -0,0 +1,279 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ class ApiMap
5
+ # Methods for handling constants.
6
+ #
7
+ class Constants
8
+ # @param store [Store]
9
+ def initialize store
10
+ @store = store
11
+ end
12
+
13
+ # Resolve a name to a fully qualified namespace or constant.
14
+ #
15
+ # `Constants#resolve` finds fully qualified (absolute)
16
+ # namespaces based on relative names and the open gates
17
+ # (namespaces) provided. Names must be runtime-visible (erased)
18
+ # non-literal types, non-duck, non-signature types - e.g.,
19
+ # TrueClass, NilClass, Integer and Hash instead of true, nil,
20
+ # 96, or Hash{String => Symbol}
21
+ #
22
+ # Note: You may want to be using #qualify. Notably, #resolve:
23
+ # - does not handle anything with type parameters
24
+ # - will not gracefully handle nil, self and Boolean
25
+ # - will return a constant name instead of following its assignment
26
+ #
27
+ # @param name [String] Namespace which may relative and not be rooted.
28
+ # @param gates [Array<Array<String>, String>] Namespaces to search while resolving the name
29
+ #
30
+ # @return [String, nil] fully qualified namespace (i.e., is
31
+ # absolute, but will not start with ::)
32
+ def resolve(name, *gates)
33
+ return store.get_path_pins(name[2..]).first&.path if name.start_with?('::')
34
+
35
+ flat = gates.flatten
36
+ flat.push '' if flat.empty?
37
+ if cached_resolve.include? [name, flat]
38
+ cached_result = cached_resolve[[name, flat]]
39
+ # don't recurse
40
+ return nil if cached_result == :in_process
41
+ return cached_result
42
+ end
43
+ resolve_and_cache(name, flat)
44
+ end
45
+
46
+ # Get a fully qualified namespace from a reference pin.
47
+ #
48
+ # @param pin [Pin::Reference]
49
+ # @return [String, nil]
50
+ def dereference pin
51
+ qualify_type(pin.type, *pin.reference_gates)&.tag
52
+ end
53
+
54
+ # Collect a list of all constants defined in the specified gates.
55
+ #
56
+ # @param gates [Array<Array<String>, String>]
57
+ # @return [Array<Solargraph::Pin::Namespace, Solargraph::Pin::Constant>]
58
+ def collect(*gates)
59
+ flat = gates.flatten
60
+ cached_collect[flat] || collect_and_cache(flat)
61
+ end
62
+
63
+ # Determine a fully qualified namespace for a given tag
64
+ # referenced from the specified open gates. This method will
65
+ # search in each gate until it finds a match for the name.
66
+ #
67
+ # @param tag [String, nil] The type to match
68
+ # @param gates [Array<String>]
69
+ # @return [String, nil] fully qualified tag
70
+ def qualify tag, *gates
71
+ type = ComplexType.try_parse(tag)
72
+ qualify_type(type, *gates)&.tag
73
+ end
74
+
75
+ # @param type [ComplexType, nil] The type to match
76
+ # @param gates [Array<String>]
77
+ #
78
+ # @return [ComplexType, nil] A new rooted ComplexType
79
+ def qualify_type type, *gates
80
+ return nil if type.nil?
81
+ return type if type.selfy? || type.literal? || type.tag == 'nil' || type.interface? ||
82
+ type.tag == 'Boolean'
83
+
84
+ gates.push '' unless gates.include?('')
85
+ fqns = resolve(type.rooted_namespace, *gates)
86
+ return unless fqns
87
+ pin = store.get_path_pins(fqns).first
88
+ if pin.is_a?(Pin::Constant)
89
+ const = Solargraph::Parser::NodeMethods.unpack_name(pin.assignment)
90
+ return unless const
91
+ fqns = resolve(const, *pin.gates)
92
+ end
93
+ type.recreate(new_name: fqns, make_rooted: true)
94
+ end
95
+
96
+ # @return [void]
97
+ def clear
98
+ [cached_collect, cached_resolve].each(&:clear)
99
+ end
100
+
101
+ private
102
+
103
+ # @return [Store]
104
+ attr_reader :store
105
+
106
+ # @param name [String]
107
+ # @param gates [Array<String>]
108
+ # @return [String, nil]
109
+ def resolve_and_cache name, gates
110
+ cached_resolve[[name, gates]] = :in_process
111
+ cached_resolve[[name, gates]] = resolve_uncached(name, gates)
112
+ end
113
+
114
+ # @param name [String]
115
+ # @param gates [Array<String>]
116
+ # @return [String, nil]
117
+ def resolve_uncached name, gates
118
+ resolved = nil
119
+ base = gates
120
+ parts = name.split('::')
121
+ first = nil
122
+ parts.each.with_index do |nam, idx|
123
+ resolved, remainder = complex_resolve(nam, base, idx != parts.length - 1)
124
+ first ||= remainder
125
+ if resolved
126
+ base = [resolved]
127
+ else
128
+ return resolve(name, first) unless first.empty?
129
+ end
130
+ end
131
+ resolved
132
+ end
133
+
134
+ # @todo I'm not sure of a better way to express the return value in YARD.
135
+ # It's a tuple where the first element is a nullable string. Something
136
+ # like `Array(String|nil, Array<String>)` would be more accurate.
137
+ #
138
+ # @param name [String]
139
+ # @param gates [Array<String>]
140
+ # @param internal [Boolean] True if the name is not the last in the namespace
141
+ # @return [Array(Object, Array<String>)]
142
+ def complex_resolve name, gates, internal
143
+ resolved = nil
144
+ gates.each.with_index do |gate, idx|
145
+ resolved = simple_resolve(name, gate, internal)
146
+ return [resolved, gates[(idx + 1)..]] if resolved
147
+ store.get_ancestor_references(gate).each do |ref|
148
+ return ref.name.sub(/^::/, '') if ref.name.end_with?("::#{name}") && ref.name.start_with?('::')
149
+
150
+ mixin = resolve(ref.name, ref.reference_gates)
151
+ next unless mixin
152
+
153
+ resolved = simple_resolve(name, mixin, internal)
154
+ return [resolved, gates[(idx + 1)..]] if resolved
155
+ end
156
+ end
157
+ [nil, []]
158
+ end
159
+
160
+ # @param name [String]
161
+ # @param gate [String]
162
+ # @param internal [Boolean] True if the name is not the last in the namespace
163
+ # @return [String, nil]
164
+ def simple_resolve name, gate, internal
165
+ here = "#{gate}::#{name}".sub(/^::/, '').sub(/::$/, '')
166
+ pin = store.get_path_pins(here).first
167
+ if pin.is_a?(Pin::Constant) && internal
168
+ const = Solargraph::Parser::NodeMethods.unpack_name(pin.assignment)
169
+ return unless const
170
+ resolve(const, pin.gates)
171
+ else
172
+ pin&.path
173
+ end
174
+ end
175
+
176
+ # @param gates [Array<String>]
177
+ # @return [Array<Solargraph::Pin::Namespace, Solargraph::Pin::Constant>]
178
+ def collect_and_cache gates
179
+ skip = Set.new
180
+ cached_collect[gates] = gates.flat_map do |gate|
181
+ inner_get_constants(gate, %i[public private], skip)
182
+ end
183
+ end
184
+
185
+ # @return [Hash{Array(String, Array<String>) => String, :in_process, nil}]
186
+ def cached_resolve
187
+ @cached_resolve ||= {}
188
+ end
189
+
190
+ # @return [Hash{Array<String> => Array<Solargraph::Pin::Namespace, Solargraph::Pin::Constant>}]
191
+ def cached_collect
192
+ @cached_collect ||= {}
193
+ end
194
+
195
+ # Determine fully qualified namespace for a given namespace used
196
+ # inside the definition of another tag ("context"). This method
197
+ # will start the search in the specified context until it finds a
198
+ # match for the namespace.
199
+ #
200
+ # @param namespace [String, nil] The namespace to
201
+ # match
202
+ # @param context_namespace [String] The context namespace in which the
203
+ # tag was referenced; start from here to resolve the name
204
+ # @return [String, nil] fully qualified namespace
205
+ def qualify_namespace namespace, context_namespace = ''
206
+ if namespace.start_with?('::')
207
+ inner_qualify(namespace[2..], '', Set.new)
208
+ else
209
+ inner_qualify(namespace, context_namespace, Set.new)
210
+ end
211
+ end
212
+
213
+ # @param name [String] Namespace to fully qualify
214
+ # @param root [String] The context to search
215
+ # @param skip [Set<String>] Contexts already searched
216
+ # @return [String, nil] Fully qualified ("rooted") namespace
217
+ def inner_qualify name, root, skip
218
+ return name if name == ComplexType::GENERIC_TAG_NAME
219
+ return nil if name.nil?
220
+ return nil if skip.include?(root)
221
+ skip.add root
222
+ possibles = []
223
+ if name == ''
224
+ return '' if root == ''
225
+
226
+ inner_qualify(root, '', skip)
227
+ else
228
+ return name if root == '' && store.namespace_exists?(name)
229
+ roots = root.to_s.split('::')
230
+ while roots.length.positive?
231
+ fqns = "#{roots.join('::')}::#{name}"
232
+ return fqns if store.namespace_exists?(fqns)
233
+ incs = store.get_includes(roots.join('::'))
234
+ incs.each do |inc|
235
+ foundinc = inner_qualify(name, inc.type.to_s, skip)
236
+ possibles.push foundinc unless foundinc.nil?
237
+ end
238
+ roots.pop
239
+ end
240
+ if possibles.empty?
241
+ incs = store.get_includes('')
242
+ incs.each do |inc|
243
+ foundinc = inner_qualify(name, inc.type.to_s, skip)
244
+ possibles.push foundinc unless foundinc.nil?
245
+ end
246
+ end
247
+ return name if store.namespace_exists?(name)
248
+ possibles.last
249
+ end
250
+ end
251
+
252
+ # @param fqns [String]
253
+ # @param visibility [Array<Symbol>]
254
+ # @param skip [Set<String>]
255
+ # @return [Array<Solargraph::Pin::Namespace, Solargraph::Pin::Constant>]
256
+ def inner_get_constants fqns, visibility, skip
257
+ return [] if fqns.nil? || skip.include?(fqns)
258
+ skip.add fqns
259
+ result = []
260
+
261
+ store.get_prepends(fqns).each do |pre|
262
+ pre_fqns = resolve(pre.name, pre.closure.gates - skip.to_a)
263
+ result.concat inner_get_constants(pre_fqns, [:public], skip)
264
+ end
265
+ result.concat(store.get_constants(fqns, visibility).sort { |a, b| a.name <=> b.name })
266
+ store.get_includes(fqns).each do |pin|
267
+ inc_fqns = resolve(pin.name, pin.closure.gates - skip.to_a)
268
+ result.concat inner_get_constants(inc_fqns, [:public], skip)
269
+ end
270
+ sc_ref = store.get_superclass(fqns)
271
+ if sc_ref
272
+ fqsc = dereference(sc_ref)
273
+ result.concat inner_get_constants(fqsc, [:public], skip) unless %w[Object BasicObject].include?(fqsc)
274
+ end
275
+ result
276
+ end
277
+ end
278
+ end
279
+ end