From 99d69adc25a3c5efb81b38d70414452951bc91a4 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 11 Apr 2025 17:46:23 -0400 Subject: [PATCH 001/561] Fix resolution from blocks in type checker --- lib/solargraph/pin/closure.rb | 4 ++ lib/solargraph/source_map.rb | 4 +- lib/solargraph/source_map/clip.rb | 39 +++++++---------- lib/solargraph/type_checker.rb | 57 ++++++++++++++----------- spec/source_map_spec.rb | 2 +- spec/type_checker/levels/strict_spec.rb | 18 ++++++++ 6 files changed, 74 insertions(+), 50 deletions(-) diff --git a/lib/solargraph/pin/closure.rb b/lib/solargraph/pin/closure.rb index 0b8645355..b77a804bb 100644 --- a/lib/solargraph/pin/closure.rb +++ b/lib/solargraph/pin/closure.rb @@ -29,6 +29,10 @@ def binder @binder || context end + # @param api_map [Solargraph::ApiMap] + # @return [void] + def rebind api_map; end + # @return [::Array] def gates # @todo This check might not be necessary. There should always be a diff --git a/lib/solargraph/source_map.rb b/lib/solargraph/source_map.rb index 4f53e0d95..5389670e3 100644 --- a/lib/solargraph/source_map.rb +++ b/lib/solargraph/source_map.rb @@ -113,8 +113,8 @@ def locate_named_path_pin line, character # @param line [Integer] # @param character [Integer] # @return [Pin::Namespace,Pin::Method,Pin::Block] - def locate_block_pin line, character - _locate_pin line, character, Pin::Namespace, Pin::Method, Pin::Block + def locate_closure_pin line, character + _locate_pin line, character, Pin::Closure end # @param other_map [SourceMap] diff --git a/lib/solargraph/source_map/clip.rb b/lib/solargraph/source_map/clip.rb index d408a24bc..d54d74983 100644 --- a/lib/solargraph/source_map/clip.rb +++ b/lib/solargraph/source_map/clip.rb @@ -11,13 +11,14 @@ class Clip def initialize api_map, cursor @api_map = api_map @cursor = cursor - block.rebind(api_map) if block.is_a?(Pin::Block) - end + closure_pin = closure + closure_pin.rebind(api_map) + end # @return [Array] Relevant pins for infering the type of the Cursor's position def define return [] if cursor.comment? || cursor.chain.literal? - result = cursor.chain.define(api_map, block, locals) + result = cursor.chain.define(api_map, closure, locals) result.concat file_global_methods result.concat((source_map.pins + source_map.locals).select{ |p| p.name == cursor.word && p.location.range.contain?(cursor.position) }) if result.empty? result @@ -49,14 +50,14 @@ def signify # @return [ComplexType] def infer - result = cursor.chain.infer(api_map, block, locals) + result = cursor.chain.infer(api_map, closure, locals) if result.tag == 'Class' # HACK: Exception to return BasicObject from Class#new - dfn = cursor.chain.define(api_map, block, locals).first + dfn = cursor.chain.define(api_map, closure, locals).first return ComplexType.try_parse('BasicObject') if dfn && dfn.path == 'Class#new' end return result unless result.tag == 'self' - ComplexType.try_parse(cursor.chain.base.infer(api_map, block, locals).tag) + cursor.chain.base.infer(api_map, closure, locals) end # Get an array of all the locals that are visible from the cursors's @@ -70,22 +71,14 @@ def locals # @return [::Array] def gates - block.gates - end - - def in_block? - return @in_block unless @in_block.nil? - @in_block = begin - tree = cursor.source.tree_at(cursor.position.line, cursor.position.column) - Parser.is_ast_node?(tree[1]) && [:block, :ITER].include?(tree[1].type) - end + closure.gates end # @param phrase [String] # @return [Array] def translate phrase chain = Parser.chain(Parser.parse(phrase)) - chain.define(api_map, block, locals) + chain.define(api_map, closure, locals) end private @@ -107,8 +100,8 @@ def location end # @return [Solargraph::Pin::Closure] - def block - @block ||= source_map.locate_block_pin(cursor.node_position.line, cursor.node_position.character) + def closure + @block ||= source_map.locate_closure_pin(cursor.node_position.line, cursor.node_position.character) end # The context at the current position. @@ -199,20 +192,20 @@ def code_complete result.concat api_map.get_constants(type.namespace, cursor.start_of_constant? ? '' : context_pin.full_context.namespace, *gates) end else - type = cursor.chain.base.infer(api_map, block, locals) - result.concat api_map.get_complex_type_methods(type, block.binder.namespace, cursor.chain.links.length == 1) + type = cursor.chain.base.infer(api_map, closure, locals) + result.concat api_map.get_complex_type_methods(type, closure.binder.namespace, cursor.chain.links.length == 1) if cursor.chain.links.length == 1 if cursor.word.start_with?('@@') return package_completions(api_map.get_class_variable_pins(context_pin.full_context.namespace)) elsif cursor.word.start_with?('@') - return package_completions(api_map.get_instance_variable_pins(block.binder.namespace, block.binder.scope)) + return package_completions(api_map.get_instance_variable_pins(closure.binder.namespace, closure.binder.scope)) elsif cursor.word.start_with?('$') return package_completions(api_map.get_global_variable_pins) end result.concat locals - result.concat file_global_methods unless block.binder.namespace.empty? + result.concat file_global_methods unless closure.binder.namespace.empty? result.concat api_map.get_constants(context_pin.context.namespace, *gates) - result.concat api_map.get_methods(block.binder.namespace, scope: block.binder.scope, visibility: [:public, :private, :protected]) + result.concat api_map.get_methods(closure.binder.namespace, scope: closure.binder.scope, visibility: [:public, :private, :protected]) result.concat api_map.get_methods('Kernel') result.concat api_map.keyword_pins.to_a end diff --git a/lib/solargraph/type_checker.rb b/lib/solargraph/type_checker.rb index e67af786c..3270fa660 100644 --- a/lib/solargraph/type_checker.rb +++ b/lib/solargraph/type_checker.rb @@ -233,10 +233,11 @@ def const_problems Solargraph::Parser::NodeMethods.const_nodes_from(source_map.source.node).each do |const| rng = Solargraph::Range.from_node(const) chain = Solargraph::Parser.chain(const, filename) - block_pin = source_map.locate_block_pin(rng.start.line, rng.start.column) + closure_pin = source_map.locate_closure_pin(rng.start.line, rng.start.column) + closure_pin.rebind(api_map) location = Location.new(filename, rng) locals = source_map.locals_at(location) - pins = chain.define(api_map, block_pin, locals) + pins = chain.define(api_map, closure_pin, locals) if pins.empty? result.push Problem.new(location, "Unresolved constant #{Solargraph::Parser::NodeMethods.unpack_name(const)}") @marked_ranges.push location.range @@ -252,17 +253,25 @@ def call_problems rng = Solargraph::Range.from_node(call) next if @marked_ranges.any? { |d| d.contain?(rng.start) } chain = Solargraph::Parser.chain(call, filename) - block_pin = source_map.locate_block_pin(rng.start.line, rng.start.column) + closure_pin = source_map.locate_closure_pin(rng.start.line, rng.start.column) + namespace_pin = closure_pin + if call.type == :block + # blocks in the AST include the method call as well, so the + # node returned by #call_nodes_from needs to be backed out + # one closure + closure_pin = closure_pin.closure + end + closure_pin.rebind(api_map) location = Location.new(filename, rng) locals = source_map.locals_at(location) - type = chain.infer(api_map, block_pin, locals) + type = chain.infer(api_map, closure_pin, locals) if type.undefined? && !rules.ignore_all_undefined? base = chain missing = chain found = nil closest = ComplexType::UNDEFINED until base.links.first.undefined? - found = base.define(api_map, block_pin, locals).first + found = base.define(api_map, closure_pin, locals).first break if found missing = base base = base.base @@ -276,18 +285,18 @@ def call_problems end end end - result.concat argument_problems_for(chain, api_map, block_pin, locals, location) + result.concat argument_problems_for(chain, api_map, closure_pin, locals, location) end result end # @param chain [Solargraph::Source::Chain] # @param api_map [Solargraph::ApiMap] - # @param block_pin [Solargraph::Pin::Base] + # @param closure_pin [Solargraph::Pin::Closure] # @param locals [Array] # @param location [Solargraph::Location] # @return [Array] - def argument_problems_for chain, api_map, block_pin, locals, location + def argument_problems_for chain, api_map, closure_pin, locals, location result = [] base = chain until base.links.length == 1 && base.undefined? @@ -296,7 +305,7 @@ def argument_problems_for chain, api_map, block_pin, locals, location arguments = last_base_link.arguments - pins = base.define(api_map, block_pin, locals) + pins = base.define(api_map, closure_pin, locals) first_pin = pins.first if first_pin.is_a?(Pin::DelegatedMethod) && !first_pin.resolvable?(api_map) @@ -305,12 +314,12 @@ def argument_problems_for chain, api_map, block_pin, locals, location # @type [Pin::Method] pin = first_pin ap = if base.links.last.is_a?(Solargraph::Source::Chain::ZSuper) - arity_problems_for(pin, fake_args_for(block_pin), location) + arity_problems_for(pin, fake_args_for(closure_pin), location) elsif pin.path == 'Class#new' fqns = if base.links.one? - block_pin.namespace + closure_pin.namespace else - base.base.infer(api_map, block_pin, locals).namespace + base.base.infer(api_map, closure_pin, locals).namespace end init = api_map.get_method_stack(fqns, 'initialize').first init ? arity_problems_for(init, arguments, location) : [] @@ -352,7 +361,7 @@ def argument_problems_for chain, api_map, block_pin, locals, location end if argchain if par.decl != :arg - errors.concat kwarg_problems_for sig, argchain, api_map, block_pin, locals, location, pin, params, idx + errors.concat kwarg_problems_for sig, argchain, api_map, closure_pin, locals, location, pin, params, idx next else last = arguments.last @@ -364,7 +373,7 @@ def argument_problems_for chain, api_map, block_pin, locals, location if ptype.nil? # @todo Some level (strong, I guess) should require the param here else - argtype = argchain.infer(api_map, block_pin, locals) + argtype = argchain.infer(api_map, closure_pin, locals) if argtype.defined? && ptype.defined? && !any_types_match?(api_map, ptype, argtype) errors.push Problem.new(location, "Wrong argument type for #{pin.path}: #{par.name} expected #{ptype}, received #{argtype}") next @@ -392,7 +401,7 @@ def argument_problems_for chain, api_map, block_pin, locals, location # @param sig [Pin::Signature] # @param argchain [Source::Chain] # @param api_map [ApiMap] - # @param block_pin [Pin::Block] + # @param closure_pin [Pin::Closure] # @param locals [Array] # @param location [Location] # @param pin [Pin::Method] @@ -400,13 +409,13 @@ def argument_problems_for chain, api_map, block_pin, locals, location # @param idx [Integer] # # @return [Array] - def kwarg_problems_for sig, argchain, api_map, block_pin, locals, location, pin, params, idx + def kwarg_problems_for sig, argchain, api_map, closure_pin, locals, location, pin, params, idx result = [] kwargs = convert_hash(argchain.node) par = sig.parameters[idx] argchain = kwargs[par.name.to_sym] if par.decl == :kwrestarg || (par.decl == :optarg && idx == pin.parameters.length - 1 && par.asgn_code == '{}') - result.concat kwrestarg_problems_for(api_map, block_pin, locals, location, pin, params, kwargs) + result.concat kwrestarg_problems_for(api_map, closure_pin, locals, location, pin, params, kwargs) else if argchain data = params[par.name] @@ -415,7 +424,7 @@ def kwarg_problems_for sig, argchain, api_map, block_pin, locals, location, pin, else ptype = data[:qualified] unless ptype.undefined? - argtype = argchain.infer(api_map, block_pin, locals) + argtype = argchain.infer(api_map, closure_pin, locals) if argtype.defined? && ptype && !any_types_match?(api_map, ptype, argtype) result.push Problem.new(location, "Wrong argument type for #{pin.path}: #{par.name} expected #{ptype}, received #{argtype}") end @@ -429,19 +438,19 @@ def kwarg_problems_for sig, argchain, api_map, block_pin, locals, location, pin, end # @param api_map [ApiMap] - # @param block_pin [Pin::Block] + # @param closure_pin [Pin::Closure] # @param locals [Array] # @param location [Location] # @param pin [Pin::Method] # @param params [Hash{String => [nil, Hash]}] # @param kwargs [Hash{Symbol => Source::Chain}] # @return [Array] - def kwrestarg_problems_for(api_map, block_pin, locals, location, pin, params, kwargs) + def kwrestarg_problems_for(api_map, closure_pin, locals, location, pin, params, kwargs) result = [] kwargs.each_pair do |pname, argchain| next unless params.key?(pname.to_s) ptype = params[pname.to_s][:qualified] - argtype = argchain.infer(api_map, block_pin, locals) + argtype = argchain.infer(api_map, closure_pin, locals) if argtype.defined? && ptype && !any_types_match?(api_map, ptype, argtype) result.push Problem.new(location, "Wrong argument type for #{pin.path}: #{pname} expected #{ptype}, received #{argtype}") end @@ -500,17 +509,17 @@ def declared_externally? pin return true if pin.assignment.nil? chain = Solargraph::Parser.chain(pin.assignment, filename) rng = Solargraph::Range.from_node(pin.assignment) - block_pin = source_map.locate_block_pin(rng.start.line, rng.start.column) + closure_pin = source_map.locate_closure_pin(rng.start.line, rng.start.column) location = Location.new(filename, Range.from_node(pin.assignment)) locals = source_map.locals_at(location) - type = chain.infer(api_map, block_pin, locals) + type = chain.infer(api_map, closure_pin, locals) if type.undefined? && !rules.ignore_all_undefined? base = chain missing = chain found = nil closest = ComplexType::UNDEFINED until base.links.first.undefined? - found = base.define(api_map, block_pin, locals).first + found = base.define(api_map, closure_pin, locals).first break if found missing = base base = base.base diff --git a/spec/source_map_spec.rb b/spec/source_map_spec.rb index 247453624..1da97a8ba 100644 --- a/spec/source_map_spec.rb +++ b/spec/source_map_spec.rb @@ -70,7 +70,7 @@ class Foo end end )) - pin = map.locate_block_pin(3, 0) + pin = map.locate_closure_pin(3, 0) expect(pin).to be_a(Solargraph::Pin::Block) end diff --git a/spec/type_checker/levels/strict_spec.rb b/spec/type_checker/levels/strict_spec.rb index 0e58190cf..daf1df054 100644 --- a/spec/type_checker/levels/strict_spec.rb +++ b/spec/type_checker/levels/strict_spec.rb @@ -738,5 +738,23 @@ def foo *path, baz; end )) expect(checker.problems.map(&:message)).to eq([]) end + + it "understands enough of define_method not to think the block is in class scope" do + checker = type_checker(%( + class Foo + def initialize + @resolved_method = nil + end + + def bar + end + + define_method('a') do + bar + end + end + )) + expect(checker.problems.map(&:message)).to eq([]) + end end end From 46f611bfab99f4b87f50107e5e0661b9639e9e3e Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 12 Apr 2025 08:56:23 -0400 Subject: [PATCH 002/561] Run specs from plugin projects configured against current code --- .github/workflows/plugins.yml | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index 0f2fe01c1..0bab857a7 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -10,7 +10,7 @@ permissions: contents: read jobs: - test: + typecheck_with_plugins: runs-on: ubuntu-latest steps: @@ -38,3 +38,29 @@ jobs: run: bundle exec solargraph typecheck --level typed - name: Ensure specs still run run: bundle exec rake spec + run_solargraph_rspec_specs: + # check out solargraph-rspec as well as this project, and point the former to use the latter as a local gem + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + # + - name: clone https://github.com/lekemula/solargraph-rspec/ + run: | + git clone https://github.com/lekemula/solargraph-rspec.git + cd solargraph-rspec + echo "gem 'solargraph', path: '../solargraph'" >> Gemfile + bundle install + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.0' + bundler-cache: false + - name: Run specs + run: | + cd solargraph-rspec + bundle exec rake spec + + run_solargraph_rails_specs: + - name: Run + run: | + false From 8eda50b43c39331c6796c947176a39c39aaa204b Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 12 Apr 2025 08:59:16 -0400 Subject: [PATCH 003/561] Fix YAML syntax --- .github/workflows/plugins.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index 0bab857a7..f8ec9c626 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -61,6 +61,7 @@ jobs: bundle exec rake spec run_solargraph_rails_specs: + steps: - name: Run run: | false From dc1162e0e7f29c28180c4d8ae130752321afd9ed Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 12 Apr 2025 09:00:32 -0400 Subject: [PATCH 004/561] Fix GitHub workflow syntax --- .github/workflows/plugins.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index f8ec9c626..b55827112 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -61,6 +61,7 @@ jobs: bundle exec rake spec run_solargraph_rails_specs: + runs-on: ubuntu-latest steps: - name: Run run: | From 7bca091a8bca80637152e334406e91a6250943f0 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 12 Apr 2025 09:03:33 -0400 Subject: [PATCH 005/561] Move bundle install after Ruby install --- .github/workflows/plugins.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index b55827112..f2420d1d4 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -48,13 +48,16 @@ jobs: run: | git clone https://github.com/lekemula/solargraph-rspec.git cd solargraph-rspec - echo "gem 'solargraph', path: '../solargraph'" >> Gemfile - bundle install - name: Set up Ruby uses: ruby/setup-ruby@v1 with: ruby-version: '3.0' bundler-cache: false + - name: Install gems + run: | + cd solargraph-rspec + echo "gem 'solargraph', path: '../solargraph'" >> Gemfile + bundle install - name: Run specs run: | cd solargraph-rspec From 9489269efb4a2304632adcb458281ab6a987042c Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 12 Apr 2025 09:04:51 -0400 Subject: [PATCH 006/561] Debug --- .github/workflows/plugins.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index f2420d1d4..74cab6fd6 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -56,6 +56,7 @@ jobs: - name: Install gems run: | cd solargraph-rspec + ls .. echo "gem 'solargraph', path: '../solargraph'" >> Gemfile bundle install - name: Run specs From 564538201a82f2b3034e3ea43bc3aed4a410709a Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 12 Apr 2025 09:06:58 -0400 Subject: [PATCH 007/561] Fix paths --- .github/workflows/plugins.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index 74cab6fd6..f749a4a79 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -46,6 +46,7 @@ jobs: # - name: clone https://github.com/lekemula/solargraph-rspec/ run: | + cd .. git clone https://github.com/lekemula/solargraph-rspec.git cd solargraph-rspec - name: Set up Ruby @@ -55,13 +56,12 @@ jobs: bundler-cache: false - name: Install gems run: | - cd solargraph-rspec - ls .. + cd ../solargraph-rspec echo "gem 'solargraph', path: '../solargraph'" >> Gemfile bundle install - name: Run specs run: | - cd solargraph-rspec + cd ../solargraph-rspec bundle exec rake spec run_solargraph_rails_specs: From 821024c5254c2f98920d4ffc8df50e1b722a568b Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 13 Apr 2025 12:29:41 -0400 Subject: [PATCH 008/561] Document a log level env variable --- README.md | 4 ++++ lib/solargraph/logging.rb | 10 ++++++++-- lib/solargraph/source/chain.rb | 6 +++++- spec/spec_helper.rb | 2 +- 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 30edafcf0..b1630773c 100755 --- a/README.md +++ b/README.md @@ -122,6 +122,10 @@ See [https://solargraph.org/guides](https://solargraph.org/guides) for more tips ### Development +To see more logging when typechecking or running specs, set the +`SOLARGRAPH_LOG` environment variable to `debug` or `info`. `warn` is +the default value. + 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. ### Sponsorship and Donation diff --git a/lib/solargraph/logging.rb b/lib/solargraph/logging.rb index 4dce90f77..d74a08b0a 100644 --- a/lib/solargraph/logging.rb +++ b/lib/solargraph/logging.rb @@ -11,8 +11,14 @@ module Logging 'info' => Logger::INFO, 'debug' => Logger::DEBUG } - - @@logger = Logger.new(STDERR, level: DEFAULT_LOG_LEVEL) + configured_level = ENV['SOLARGRAPH_LOG'] + level = if LOG_LEVELS.keys.include?(configured_level) + LOG_LEVELS.fetch(configured_level) + else + STDERR.puts("Invalid value for SOLARGRAPH_LOG: #{configured_level.inspect} - valid values are #{LOG_LEVELS.keys}") if configured_level + DEFAULT_LOG_LEVEL + end + @@logger = Logger.new(STDERR, level: level) @@logger.formatter = proc do |severity, datetime, progname, msg| "[#{severity}] #{msg}\n" end diff --git a/lib/solargraph/source/chain.rb b/lib/solargraph/source/chain.rb index 556da1718..a49406865 100644 --- a/lib/solargraph/source/chain.rb +++ b/lib/solargraph/source/chain.rb @@ -9,6 +9,8 @@ class Source # values. # class Chain + include Logging + autoload :Link, 'solargraph/source/chain/link' autoload :Call, 'solargraph/source/chain/call' autoload :QCall, 'solargraph/source/chain/q_call' @@ -108,7 +110,9 @@ def infer_uncached api_map, name_pin, locals end pins = define(api_map, name_pin, locals) type = infer_first_defined(pins, links.last.last_context, api_map, locals) - maybe_nil(type) + out = maybe_nil(type) + logging.logger.debug { "Chain#infer_uncached(links=#{self.links} => #{out}" } + out end # @return [Boolean] diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 5e0385b74..4710c6534 100755 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -7,4 +7,4 @@ end require 'solargraph' # Suppress logger output in specs (if possible) -Solargraph::Logging.logger.reopen(File::NULL) if Solargraph::Logging.logger.respond_to?(:reopen) +Solargraph::Logging.logger.reopen(File::NULL) if Solargraph::Logging.logger.respond_to?(:reopen) && !ENV.key?('SOLARGRAPH_DEBUG_LEVEL') From 772217c444d2aadc55ff9c60ecd0c2265fbdf425 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 13 Apr 2025 12:36:28 -0400 Subject: [PATCH 009/561] Fix logger reference --- lib/solargraph/source/chain.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/solargraph/source/chain.rb b/lib/solargraph/source/chain.rb index a49406865..42fb506ea 100644 --- a/lib/solargraph/source/chain.rb +++ b/lib/solargraph/source/chain.rb @@ -111,7 +111,7 @@ def infer_uncached api_map, name_pin, locals pins = define(api_map, name_pin, locals) type = infer_first_defined(pins, links.last.last_context, api_map, locals) out = maybe_nil(type) - logging.logger.debug { "Chain#infer_uncached(links=#{self.links} => #{out}" } + logger.debug { "Chain#infer_uncached(links=#{self.links} => #{out}" } out end From c15e15726e37070e51bd9e2dfe29010795110269 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 13 Apr 2025 15:19:36 -0400 Subject: [PATCH 010/561] Fix env var name --- spec/spec_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 4710c6534..a0d99cb88 100755 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -7,4 +7,4 @@ end require 'solargraph' # Suppress logger output in specs (if possible) -Solargraph::Logging.logger.reopen(File::NULL) if Solargraph::Logging.logger.respond_to?(:reopen) && !ENV.key?('SOLARGRAPH_DEBUG_LEVEL') +Solargraph::Logging.logger.reopen(File::NULL) if Solargraph::Logging.logger.respond_to?(:reopen) && !ENV.key?('SOLARGRAPH_LOG') From 87c7e4f6b8d55b241742d0430767036f2864f3c5 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 18 Apr 2025 10:06:04 -0400 Subject: [PATCH 011/561] Allow log level to be overridden per file Useful for debug-level logging being turned on selectively at dev time --- lib/solargraph/logging.rb | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/lib/solargraph/logging.rb b/lib/solargraph/logging.rb index 4dce90f77..e3173e78c 100644 --- a/lib/solargraph/logging.rb +++ b/lib/solargraph/logging.rb @@ -16,12 +16,29 @@ module Logging @@logger.formatter = proc do |severity, datetime, progname, msg| "[#{severity}] #{msg}\n" end + @@dev_null_logger = Logger.new('/dev/null') + module_function + # override this in your class to temporarily set a custom + # filtering log level for the class (e.g., suppress any debug + # message by setting it to :info even if it is set elsewhere, or + # show existing debug messages by setting to :debug). @return + # [Symbol] + def log_level + @@logger.level + end + # @return [Logger] def logger - @@logger + @logger ||= if log_level == @@logger.level + @@logger + else + logger = Logger.new(STDERR, log_level) + logger.formatter = @@logger.formatter + logger + end end end end From 1074b60206b0e410cf4a29d96e372df4ebc67f86 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 16 Apr 2025 11:35:55 -0400 Subject: [PATCH 012/561] Populate location information from RBS files (#768) * Populate location information from RBS files The 'rbs' gem maps the location of different definitions to the relevant point in the RGS files themselves - this change provides the ability to jump into the right place in those files to see the type definition via the LSP. * Prefer source location in language server * Resolve merge issue * Fix Path vs String type error --- .../message/text_document/definition.rb | 6 ++-- .../message/text_document/document_symbol.rb | 6 ++-- .../message/text_document/type_definition.rb | 6 ++-- .../message/workspace/workspace_symbol.rb | 4 +-- lib/solargraph/pin/base.rb | 12 ++++++- lib/solargraph/rbs_map/conversions.rb | 32 ++++++++++++++----- 6 files changed, 46 insertions(+), 20 deletions(-) diff --git a/lib/solargraph/language_server/message/text_document/definition.rb b/lib/solargraph/language_server/message/text_document/definition.rb index 99d908652..47bf7a60d 100644 --- a/lib/solargraph/language_server/message/text_document/definition.rb +++ b/lib/solargraph/language_server/message/text_document/definition.rb @@ -13,10 +13,10 @@ def process def code_location suggestions = host.definitions_at(params['textDocument']['uri'], @line, @column) return nil if suggestions.empty? - suggestions.reject { |pin| pin.location.nil? || pin.location.filename.nil? }.map do |pin| + suggestions.reject { |pin| pin.best_location.nil? || pin.best_location.filename.nil? }.map do |pin| { - uri: file_to_uri(pin.location.filename), - range: pin.location.range.to_hash + uri: file_to_uri(pin.best_location.filename), + range: pin.best_location.range.to_hash } end end diff --git a/lib/solargraph/language_server/message/text_document/document_symbol.rb b/lib/solargraph/language_server/message/text_document/document_symbol.rb index 19a64cf93..2490f5c6d 100644 --- a/lib/solargraph/language_server/message/text_document/document_symbol.rb +++ b/lib/solargraph/language_server/message/text_document/document_symbol.rb @@ -6,15 +6,15 @@ class Solargraph::LanguageServer::Message::TextDocument::DocumentSymbol < Solarg def process pins = host.document_symbols params['textDocument']['uri'] info = pins.map do |pin| - next nil unless pin.location&.filename + next nil unless pin.best_location&.filename result = { name: pin.name, containerName: pin.namespace, kind: pin.symbol_kind, location: { - uri: file_to_uri(pin.location.filename), - range: pin.location.range.to_hash + uri: file_to_uri(pin.best_location.filename), + range: pin.best_location.range.to_hash }, deprecated: pin.deprecated? } diff --git a/lib/solargraph/language_server/message/text_document/type_definition.rb b/lib/solargraph/language_server/message/text_document/type_definition.rb index feb5dfdce..8143d7710 100644 --- a/lib/solargraph/language_server/message/text_document/type_definition.rb +++ b/lib/solargraph/language_server/message/text_document/type_definition.rb @@ -13,10 +13,10 @@ def process def code_location suggestions = host.type_definitions_at(params['textDocument']['uri'], @line, @column) return nil if suggestions.empty? - suggestions.reject { |pin| pin.location.nil? || pin.location.filename.nil? }.map do |pin| + suggestions.reject { |pin| pin.best_location.nil? || pin.best_location.filename.nil? }.map do |pin| { - uri: file_to_uri(pin.location.filename), - range: pin.location.range.to_hash + uri: file_to_uri(pin.best_location.filename), + range: pin.best_location.range.to_hash } end end diff --git a/lib/solargraph/language_server/message/workspace/workspace_symbol.rb b/lib/solargraph/language_server/message/workspace/workspace_symbol.rb index ab1c1248f..780e4aa0b 100644 --- a/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +++ b/lib/solargraph/language_server/message/workspace/workspace_symbol.rb @@ -6,14 +6,14 @@ class Solargraph::LanguageServer::Message::Workspace::WorkspaceSymbol < Solargra def process pins = host.query_symbols(params['query']) info = pins.map do |pin| - uri = file_to_uri(pin.location.filename) + uri = file_to_uri(pin.best_location.filename) { name: pin.path, containerName: pin.namespace, kind: pin.symbol_kind, location: { uri: uri, - range: pin.location.range.to_hash + range: pin.best_location.range.to_hash }, deprecated: pin.deprecated? } diff --git a/lib/solargraph/pin/base.rb b/lib/solargraph/pin/base.rb index 455e370f1..13b2e4c15 100644 --- a/lib/solargraph/pin/base.rb +++ b/lib/solargraph/pin/base.rb @@ -15,6 +15,9 @@ class Base # @return [Solargraph::Location] attr_reader :location + # @return [Solargraph::Location] + attr_reader :type_location + # @return [String] attr_reader :name @@ -25,11 +28,13 @@ class Base attr_accessor :source # @param location [Solargraph::Location, nil] + # @param type_location [Solargraph::Location, nil] # @param closure [Solargraph::Pin::Closure, nil] # @param name [String] # @param comments [String] - def initialize location: nil, closure: nil, name: '', comments: '' + def initialize location: nil, type_location: nil, closure: nil, name: '', comments: '' @location = location + @type_location = type_location @closure = closure @name = name @comments = comments @@ -102,6 +107,11 @@ def variable? false end + # @return [Location, nil] + def best_location + location || type_location + end + # Pin equality is determined using the #nearly? method and also # requiring both pins to have the same location. # diff --git a/lib/solargraph/rbs_map/conversions.rb b/lib/solargraph/rbs_map/conversions.rb index 6388572f5..9737c2eba 100644 --- a/lib/solargraph/rbs_map/conversions.rb +++ b/lib/solargraph/rbs_map/conversions.rb @@ -82,7 +82,7 @@ def convert_self_types_to_pins decl, module_pin def convert_self_type_to_pins decl, closure include_pin = Solargraph::Pin::Reference::Include.new( name: decl.name.relative!.to_s, - location: rbs_location_to_location(decl.location), + type_location: location_decl_to_pin_location(decl.location), closure: closure ) pins.push include_pin @@ -144,6 +144,7 @@ def class_decl_to_pin decl name: decl.name.relative!.to_s, closure: Solargraph::Pin::ROOT_PIN, comments: decl.comment&.string, + type_location: location_decl_to_pin_location(decl.location), # @todo some type parameters in core/stdlib have default # values; Solargraph doesn't support that yet as so these # get treated as undefined if not specified @@ -152,6 +153,7 @@ def class_decl_to_pin decl pins.push class_pin if decl.super_class pins.push Solargraph::Pin::Reference::Superclass.new( + type_location: location_decl_to_pin_location(decl.super_class.location), closure: class_pin, name: decl.super_class.name.relative!.to_s ) @@ -166,6 +168,7 @@ def class_decl_to_pin decl def interface_decl_to_pin decl, closure class_pin = Solargraph::Pin::Namespace.new( type: :module, + type_location: location_decl_to_pin_location(decl.location), name: decl.name.relative!.to_s, closure: Solargraph::Pin::ROOT_PIN, comments: decl.comment&.string, @@ -185,6 +188,7 @@ def module_decl_to_pin decl module_pin = Solargraph::Pin::Namespace.new( type: :module, name: decl.name.relative!.to_s, + type_location: location_decl_to_pin_location(decl.location), closure: Solargraph::Pin::ROOT_PIN, comments: decl.comment&.string, generics: decl.type_params.map(&:name).map(&:to_s), @@ -199,10 +203,12 @@ def module_decl_to_pin decl # @param name [String] # @param tag [String] # @param comments [String] + # @param decl [RBS::AST::Declarations::ClassAlias, RBS::AST::Declarations::Constant, RBS::AST::Declarations::ModuleAlias] # @param base [String, nil] Optional conversion of tag to base # # @return [Solargraph::Pin::Constant] - def create_constant(name, tag, comments, base = nil) + def create_constant(name, tag, comments, decl, base = nil) + comments = decl.comment&.string parts = name.split('::') if parts.length > 1 name = parts.last @@ -214,6 +220,7 @@ def create_constant(name, tag, comments, base = nil) constant_pin = Solargraph::Pin::Constant.new( name: name, closure: closure, + type_location: location_decl_to_pin_location(decl.location), comments: comments ) tag = "#{base}<#{tag}>" if base @@ -228,7 +235,7 @@ def class_alias_decl_to_pin decl new_name = decl.new_name.relative!.to_s old_name = decl.old_name.relative!.to_s - pins.push create_constant(new_name, old_name, decl.comment&.string, 'Class') + pins.push create_constant(new_name, old_name, decl.comment&.string, decl, 'Class') end # @param decl [RBS::AST::Declarations::ModuleAlias] @@ -238,14 +245,14 @@ def module_alias_decl_to_pin decl new_name = decl.new_name.relative!.to_s old_name = decl.old_name.relative!.to_s - pins.push create_constant(new_name, old_name, decl.comment&.string, 'Module') + pins.push create_constant(new_name, old_name, decl.comment&.string, decl, 'Module') end # @param decl [RBS::AST::Declarations::Constant] # @return [void] def constant_decl_to_pin decl tag = other_type_to_tag(decl.type) - pins.push create_constant(decl.name.relative!.to_s, tag, decl.comment&.string) + pins.push create_constant(decl.name.relative!.to_s, tag, decl.comment&.string, decl) end # @param decl [RBS::AST::Declarations::Global] @@ -276,6 +283,7 @@ def method_def_to_pin decl, closure pin = Solargraph::Pin::Method.new( name: decl.name.to_s, closure: closure, + type_location: location_decl_to_pin_location(decl.location), comments: decl.comment&.string, scope: :instance, signatures: [], @@ -295,6 +303,7 @@ def method_def_to_pin decl, closure name: decl.name.to_s, closure: closure, comments: decl.comment&.string, + type_location: location_decl_to_pin_location(decl.location), scope: :class, signatures: [], generics: generics @@ -321,13 +330,13 @@ def method_def_to_sigs decl, pin # @param location [RBS::Location, nil] # @return [Solargraph::Location, nil] - def rbs_location_to_location(location) + def location_decl_to_pin_location(location) return nil if location&.name.nil? start_pos = Position.new(location.start_line - 1, location.start_column) end_pos = Position.new(location.end_line - 1, location.end_column) range = Range.new(start_pos, end_pos) - Location.new(location.name, range) + Location.new(location.name.to_s, range) end # @param type [RBS::MethodType,RBS::Types::Block] @@ -379,6 +388,7 @@ def parts_of_function type, pin def attr_reader_to_pin(decl, closure) pin = Solargraph::Pin::Method.new( name: decl.name.to_s, + type_location: location_decl_to_pin_location(decl.location), closure: closure, comments: decl.comment&.string, scope: :instance, @@ -394,6 +404,7 @@ def attr_reader_to_pin(decl, closure) def attr_writer_to_pin(decl, closure) pin = Solargraph::Pin::Method.new( name: "#{decl.name.to_s}=", + type_location: location_decl_to_pin_location(decl.location), closure: closure, comments: decl.comment&.string, scope: :instance, @@ -418,6 +429,7 @@ def ivar_to_pin(decl, closure) pin = Solargraph::Pin::InstanceVariable.new( name: decl.name.to_s, closure: closure, + type_location: location_decl_to_pin_location(decl.location), comments: decl.comment&.string ) pin.docstring.add_tag(YARD::Tags::Tag.new(:type, '', other_type_to_tag(decl.type))) @@ -460,6 +472,7 @@ def include_to_pin decl, closure generic_values = type.all_params.map(&:to_s) pins.push Solargraph::Pin::Reference::Include.new( name: decl.name.relative!.to_s, + type_location: location_decl_to_pin_location(decl.location), generic_values: generic_values, closure: closure ) @@ -471,6 +484,7 @@ def include_to_pin decl, closure def prepend_to_pin decl, closure pins.push Solargraph::Pin::Reference::Prepend.new( name: decl.name.relative!.to_s, + type_location: location_decl_to_pin_location(decl.location), closure: closure ) end @@ -481,6 +495,7 @@ def prepend_to_pin decl, closure def extend_to_pin decl, closure pins.push Solargraph::Pin::Reference::Extend.new( name: decl.name.relative!.to_s, + type_location: location_decl_to_pin_location(decl.location), closure: closure ) end @@ -491,6 +506,7 @@ def extend_to_pin decl, closure def alias_to_pin decl, closure pins.push Solargraph::Pin::MethodAlias.new( name: decl.new_name.to_s, + type_location: location_decl_to_pin_location(decl.location), original: decl.old_name.to_s, closure: closure ) @@ -597,7 +613,7 @@ def add_mixins decl, namespace klass = mixin.is_a?(RBS::AST::Members::Include) ? Pin::Reference::Include : Pin::Reference::Extend pins.push klass.new( name: mixin.name.relative!.to_s, - location: rbs_location_to_location(mixin.location), + location: location_decl_to_pin_location(mixin.location), closure: namespace ) end From 020a7e92a9e9704f7166388c20d42251362eacf7 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 16 Apr 2025 12:30:11 -0400 Subject: [PATCH 013/561] Consolidate parameter handling into Pin::Callable (#844) * Consolidate parameter handling into Pin::Closure * Clarify clobbered variable names * Fix bug in to_rbs, add spec, then fix new bug found after running spec * Catch one more Signature.new to translate from strict typechecking * Introduce Pin::Callable * Introduce Pin::Callable * Introduce Pin::Callable * Introduce Pin::Callable * Introduce Pin::Callable * Introduce Pin::Callable * Introduce Pin::Callable * Use Pin::Callable type in args_node.rb * Select String#each_line overload with mandatory vs optional arg info --- lib/solargraph/complex_type.rb | 13 +- .../parser_gem/node_processors/args_node.rb | 42 ++--- lib/solargraph/pin.rb | 1 + lib/solargraph/pin/block.rb | 22 +-- lib/solargraph/pin/callable.rb | 147 ++++++++++++++++++ lib/solargraph/pin/closure.rb | 9 +- lib/solargraph/pin/method.rb | 40 ++--- lib/solargraph/pin/namespace.rb | 2 +- lib/solargraph/pin/parameter.rb | 4 + lib/solargraph/pin/signature.rb | 136 +--------------- lib/solargraph/rbs_map/conversions.rb | 12 +- lib/solargraph/source/chain/call.rb | 15 +- spec/pin/namespace_spec.rb | 1 + spec/source_map/clip_spec.rb | 37 +++++ 14 files changed, 258 insertions(+), 223 deletions(-) create mode 100644 lib/solargraph/pin/callable.rb diff --git a/lib/solargraph/complex_type.rb b/lib/solargraph/complex_type.rb index cbfcc7afe..7f1e81b09 100644 --- a/lib/solargraph/complex_type.rb +++ b/lib/solargraph/complex_type.rb @@ -221,10 +221,15 @@ class << self # # @param *strings [Array] The type definitions to parse # @return [ComplexType] - # @overload parse(*strings, partial: false) - # @todo Need ability to use a literal true as a type below - # @param partial [Boolean] True if the string is part of a another type - # @return [Array] + # # @overload parse(*strings, partial: false) + # # @todo Need ability to use a literal true as a type below + # # @param partial [Boolean] True if the string is part of a another type + # # @return [Array] + # @sg-ignore + # @todo To be able to select the right signature above, + # Chain::Call needs to know the decl type (:arg, :optarg, + # :kwarg, etc) of the arguments given, instead of just having + # an array of Chains as the arguments. def parse *strings, partial: false # @type [Hash{Array => ComplexType}] @cache ||= {} diff --git a/lib/solargraph/parser/parser_gem/node_processors/args_node.rb b/lib/solargraph/parser/parser_gem/node_processors/args_node.rb index 7f6006111..ce9d77241 100644 --- a/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +++ b/lib/solargraph/parser/parser_gem/node_processors/args_node.rb @@ -6,22 +6,25 @@ module ParserGem module NodeProcessors class ArgsNode < Parser::NodeProcessor::Base def process - if node.type == :forward_args - forward - else - node.children.each do |u| - loc = get_node_location(u) - locals.push Solargraph::Pin::Parameter.new( - location: loc, - closure: region.closure, - comments: comments_for(node), - name: u.children[0].to_s, - assignment: u.children[1], - asgn_code: u.children[1] ? region.code_for(u.children[1]) : nil, - presence: region.closure.location.range, - decl: get_decl(u) - ) - region.closure.parameters.push locals.last + callable = region.closure + if callable.is_a? Pin::Callable + if node.type == :forward_args + forward(callable) + else + node.children.each do |u| + loc = get_node_location(u) + locals.push Solargraph::Pin::Parameter.new( + location: loc, + closure: callable, + comments: comments_for(node), + name: u.children[0].to_s, + assignment: u.children[1], + asgn_code: u.children[1] ? region.code_for(u.children[1]) : nil, + presence: callable.location.range, + decl: get_decl(u) + ) + callable.parameters.push locals.last + end end end process_children @@ -29,16 +32,17 @@ def process private + # @param callable [Pin::Callable] # @return [void] - def forward + def forward(callable) loc = get_node_location(node) locals.push Solargraph::Pin::Parameter.new( location: loc, - closure: region.closure, + closure: callable, presence: region.closure.location.range, decl: get_decl(node) ) - region.closure.parameters.push locals.last + callable.parameters.push locals.last end # @param node [AST::Node] diff --git a/lib/solargraph/pin.rb b/lib/solargraph/pin.rb index 5aaa753b4..fe056e60b 100644 --- a/lib/solargraph/pin.rb +++ b/lib/solargraph/pin.rb @@ -34,6 +34,7 @@ module Pin autoload :Singleton, 'solargraph/pin/singleton' autoload :KeywordParam, 'solargraph/pin/keyword_param' autoload :Search, 'solargraph/pin/search' + autoload :Callable, 'solargraph/pin/callable' ROOT_PIN = Pin::Namespace.new(type: :class, name: '', closure: nil) end diff --git a/lib/solargraph/pin/block.rb b/lib/solargraph/pin/block.rb index 57239144f..d3a1089b5 100644 --- a/lib/solargraph/pin/block.rb +++ b/lib/solargraph/pin/block.rb @@ -2,7 +2,7 @@ module Solargraph module Pin - class Block < Closure + class Block < Callable # @return [Parser::AST::Node] attr_reader :receiver @@ -14,10 +14,9 @@ class Block < Closure # @param context [ComplexType, nil] # @param args [::Array] def initialize receiver: nil, args: [], context: nil, node: nil, **splat - super(**splat) + super(**splat, parameters: args) @receiver = receiver @context = context - @parameters = args @return_type = ComplexType.parse('::Proc') @node = node end @@ -32,16 +31,6 @@ def binder @rebind&.defined? ? @rebind : closure.binder end - # @return [::Array] - def parameters - @parameters ||= [] - end - - # @return [::Array] - def parameter_names - @parameter_names ||= parameters.map(&:name) - end - # @param yield_types [::Array] # @param parameters [::Array] # @@ -57,13 +46,6 @@ def destructure_yield_types(yield_types, parameters) parameters.map { ComplexType::UNDEFINED } end - # @todo the next step with parameters, arguments, destructuring, - # kwargs, etc logic is probably either creating a Parameters - # or Callable pin that encapsulates and shares the logic - # between methods, blocks and signatures. It could live in - # Signature if Method didn't also own potentially different - # set of parameters, generics and return types. - # @param api_map [ApiMap] # @return [::Array] def typify_parameters(api_map) diff --git a/lib/solargraph/pin/callable.rb b/lib/solargraph/pin/callable.rb new file mode 100644 index 000000000..33c05e176 --- /dev/null +++ b/lib/solargraph/pin/callable.rb @@ -0,0 +1,147 @@ +# frozen_string_literal: true + +module Solargraph + module Pin + class Callable < Closure + # @return [self] + attr_reader :block + + attr_reader :parameters + + # @return [ComplexType, nil] + attr_reader :return_type + + # @param block [Signature, nil] + # @param return_type [ComplexType, nil] + # @param parameters [::Array] + def initialize block: nil, return_type: nil, parameters: [], **splat + super(**splat) + @block = block + @return_type = return_type + @parameters = parameters + end + + # @return [::Array] + def parameter_names + @parameter_names ||= parameters.map(&:name) + end + + # @param generics_to_resolve [Enumerable] + # @param arg_types [Array, nil] + # @param return_type_context [ComplexType, nil] + # @param yield_arg_types [Array, nil] + # @param yield_return_type_context [ComplexType, nil] + # @param context [ComplexType, nil] + # @param resolved_generic_values [Hash{String => ComplexType}] + # @return [self] + def resolve_generics_from_context(generics_to_resolve, + arg_types = nil, + return_type_context = nil, + yield_arg_types = nil, + yield_return_type_context = nil, + resolved_generic_values: {}) + callable = super(generics_to_resolve, return_type_context, resolved_generic_values: resolved_generic_values) + callable.parameters = callable.parameters.each_with_index.map do |param, i| + if arg_types.nil? + param.dup + else + param.resolve_generics_from_context(generics_to_resolve, + arg_types[i], + resolved_generic_values: resolved_generic_values) + end + end + callable.block = block.resolve_generics_from_context(generics_to_resolve, + yield_arg_types, + yield_return_type_context, + resolved_generic_values: resolved_generic_values) if callable.block? + callable + end + + # @param generics_to_resolve [Enumerable] + # @param arg_types [Array, nil] + # @param return_type_context [ComplexType, nil] + # @param yield_arg_types [Array, nil] + # @param yield_return_type_context [ComplexType, nil] + # @param context [ComplexType, nil] + # @param resolved_generic_values [Hash{String => ComplexType}] + # @return [self] + def resolve_generics_from_context_until_complete(generics_to_resolve, + arg_types = nil, + return_type_context = nil, + yield_arg_types = nil, + yield_return_type_context = nil, + resolved_generic_values: {}) + # See + # https://github.com/soutaro/steep/tree/master/lib/steep/type_inference + # and + # https://github.com/sorbet/sorbet/blob/master/infer/inference.cc + # for other implementations + + return self if generics_to_resolve.empty? + + last_resolved_generic_values = resolved_generic_values.dup + new_pin = resolve_generics_from_context(generics_to_resolve, + arg_types, + return_type_context, + yield_arg_types, + yield_return_type_context, + resolved_generic_values: resolved_generic_values) + if last_resolved_generic_values == resolved_generic_values + # erase anything unresolved + return new_pin.erase_generics(self.generics) + end + new_pin.resolve_generics_from_context_until_complete(generics_to_resolve, + arg_types, + return_type_context, + yield_arg_types, + yield_return_type_context, + resolved_generic_values: resolved_generic_values) + end + + # @return [Array] + # @yieldparam [ComplexType] + # @yieldreturn [ComplexType] + # @return [self] + def transform_types(&transform) + # @todo 'super' alone should work here I think, but doesn't typecheck at level typed + callable = super(&transform) + callable.block = block.transform_types(&transform) if block? + callable.parameters = parameters.map do |param| + param.transform_types(&transform) + end + callable + end + + # @param arguments [::Array] + # @param signature [Pin::Signature] + # @return [Boolean] + def arity_matches? arguments, with_block + argcount = arguments.length + parcount = mandatory_positional_param_count + parcount -= 1 if !parameters.empty? && parameters.last.block? + return false if block? && !with_block + return false if argcount < parcount && !(argcount == parcount - 1 && parameters.last.restarg?) + true + end + + def mandatory_positional_param_count + parameters.count(&:arg?) + end + + # @return [String] + def to_rbs + rbs_generics + '(' + parameters.map { |param| param.to_rbs }.join(', ') + ') ' + (block.nil? ? '' : '{ ' + block.to_rbs + ' } ') + '-> ' + return_type.to_rbs + end + + def block? + !!@block + end + + protected + + attr_writer :block + + attr_writer :parameters + end + end +end diff --git a/lib/solargraph/pin/closure.rb b/lib/solargraph/pin/closure.rb index 0b8645355..939709de9 100644 --- a/lib/solargraph/pin/closure.rb +++ b/lib/solargraph/pin/closure.rb @@ -42,10 +42,15 @@ def generics end # @return [String] - def generics_as_rbs + def to_rbs + rbs_generics + return_type.to_rbs + end + + # @return [String] + def rbs_generics return '' if generics.empty? - generics.join(', ') + ' ' + '[' + generics.map { |gen| gen.to_s }.join(', ') + '] ' end end end diff --git a/lib/solargraph/pin/method.rb b/lib/solargraph/pin/method.rb index 753e1b3c3..bb8a1ba6f 100644 --- a/lib/solargraph/pin/method.rb +++ b/lib/solargraph/pin/method.rb @@ -4,12 +4,9 @@ module Solargraph module Pin # The base class for method and attribute pins. # - class Method < Closure + class Method < Callable include Solargraph::Parser::NodeMethods - # @return [::Array] - attr_reader :parameters - # @return [::Symbol] :public, :private, or :protected attr_reader :visibility @@ -18,24 +15,20 @@ class Method < Closure # @param visibility [::Symbol] :public, :protected, or :private # @param explicit [Boolean] - # @param parameters [::Array] # @param block [Pin::Signature, nil, ::Symbol] # @param node [Parser::AST::Node, nil] # @param attribute [Boolean] # @param signatures [::Array, nil] # @param anon_splat [Boolean] - # @param return_type [ComplexType, nil] - def initialize visibility: :public, explicit: true, parameters: [], block: :undefined, node: nil, attribute: false, signatures: nil, anon_splat: false, return_type: nil, **splat + def initialize visibility: :public, explicit: true, block: :undefined, node: nil, attribute: false, signatures: nil, anon_splat: false, **splat super(**splat) @visibility = visibility @explicit = explicit - @parameters = parameters @block = block @node = node @attribute = attribute @signatures = signatures @anon_splat = anon_splat - @return_type = return_type end def transform_types(&transform) @@ -44,9 +37,6 @@ def transform_types(&transform) m.signatures = m.signatures.map do |sig| sig.transform_types(&transform) end - m.parameters = m.parameters.map do |param| - param.transform_types(&transform) - end m.block = block&.transform_types(&transform) m.signature_help = nil m.documentation = nil @@ -71,17 +61,16 @@ def with_single_signature(signature) m end + def block? + !block.nil? + end + # @return [Pin::Signature, nil] def block return @block unless @block == :undefined @block = signatures.first.block end - # @return [::Array] - def parameter_names - @parameter_names ||= parameters.map(&:name) - end - def completion_item_kind attribute? ? Solargraph::LanguageServer::CompletionItemKinds::PROPERTY : Solargraph::LanguageServer::CompletionItemKinds::METHOD end @@ -123,9 +112,9 @@ def generate_signature(parameters, return_type) ) end yield_return_type = ComplexType.try_parse(*yieldreturn_tags.flat_map(&:types)) - block = Signature.new(generics, yield_parameters, yield_return_type) + block = Signature.new(generics: generics, parameters: yield_parameters, return_type: yield_return_type) end - Signature.new(generics, parameters, return_type, block) + Signature.new(generics: generics, parameters: parameters, return_type: return_type, block: block) end # @return [::Array] @@ -292,8 +281,8 @@ def overloads # tag's source is likely malformed. @overloads ||= docstring.tags(:overload).select(&:parameters).map do |tag| Pin::Signature.new( - generics, - tag.parameters.map do |src| + generics: generics, + parameters: tag.parameters.map do |src| name, decl = parse_overload_param(src.first) Pin::Parameter.new( location: location, @@ -305,7 +294,7 @@ def overloads return_type: param_type_from_name(tag, src.first) ) end, - ComplexType.try_parse(*tag.docstring.tags(:return).flat_map(&:types)) + return_type: ComplexType.try_parse(*tag.docstring.tags(:return).flat_map(&:types)) ) end @overloads @@ -319,8 +308,6 @@ def anon_splat? attr_writer :block - attr_writer :parameters - attr_writer :signatures attr_writer :signature_help @@ -475,6 +462,7 @@ def infer_from_iv api_map # @param name [String] # @return [::Array(String, ::Symbol)] def parse_overload_param(name) + # @todo this needs to handle mandatory vs not args, kwargs, blocks, etc if name.start_with?('**') [name[2..-1], :kwrestarg] elsif name.start_with?('*') @@ -496,6 +484,10 @@ def concat_example_tags .join("\n") .concat("```\n") end + + protected + + attr_writer :signatures end end end diff --git a/lib/solargraph/pin/namespace.rb b/lib/solargraph/pin/namespace.rb index f84a395cc..d4e66a354 100644 --- a/lib/solargraph/pin/namespace.rb +++ b/lib/solargraph/pin/namespace.rb @@ -41,7 +41,7 @@ def initialize type: :class, visibility: :public, gates: [''], **splat end def to_rbs - "#{@type.to_s} #{generics_as_rbs}#{return_type.to_rbs}" + "#{@type.to_s} #{return_type.all_params.first.to_rbs}#{rbs_generics}".strip end def desc diff --git a/lib/solargraph/pin/parameter.rb b/lib/solargraph/pin/parameter.rb index ce7133899..d76309d0e 100644 --- a/lib/solargraph/pin/parameter.rb +++ b/lib/solargraph/pin/parameter.rb @@ -27,6 +27,10 @@ def kwrestarg? decl == :kwrestarg || (assignment && [:HASH, :hash].include?(assignment.type)) end + def arg? + decl == :arg + end + def restarg? decl == :restarg end diff --git a/lib/solargraph/pin/signature.rb b/lib/solargraph/pin/signature.rb index 725ac8387..da6f6a385 100644 --- a/lib/solargraph/pin/signature.rb +++ b/lib/solargraph/pin/signature.rb @@ -1,147 +1,17 @@ module Solargraph module Pin - class Signature < Base - # @return [::Array] - attr_reader :parameters - - # @return [ComplexType] - attr_reader :return_type - - # @return [self] - attr_reader :block - - # @param generics [Array] - # @param parameters [Array] - # @param return_type [ComplexType] - # @param block [Signature, nil] - def initialize generics, parameters, return_type, block = nil - @generics = generics - @parameters = parameters - @return_type = return_type - @block = block + class Signature < Callable + def initialize **splat + super(**splat) end def generics @generics ||= [].freeze end - # @return [String] - def to_rbs - rbs_generics + '(' + parameters.map { |param| param.to_rbs }.join(', ') + ') ' + (block.nil? ? '' : '{ ' + block.to_rbs + ' } ') + '-> ' + return_type.to_rbs - end - - # @return [String] - def rbs_generics - if generics.empty? - return '' - else - return '[' + generics.map { |gen| gen.to_s }.join(', ') + '] ' - end - end - - # @return [Array] - # @yieldparam [ComplexType] - # @yieldreturn [ComplexType] - # @return [self] - def transform_types(&transform) - # @todo 'super' alone should work here I think, but doesn't typecheck at level typed - signature = super(&transform) - signature.parameters = signature.parameters.map do |param| - param.transform_types(&transform) - end - signature.block = block.transform_types(&transform) if signature.block? - signature - end - - # Probe the concrete type for each of the generic type - # parameters used in this method, and return a new method pin if - # possible. - # - # @param generics_to_resolve [Enumerable] - # @param arg_types [Array, nil] - # @param return_type_context [ComplexType, nil] - # @param yield_arg_types [Array, nil] - # @param yield_return_type_context [ComplexType, nil] - # @param context [ComplexType, nil] - # @param resolved_generic_values [Hash{String => ComplexType}] - # @return [self] - def resolve_generics_from_context(generics_to_resolve, - arg_types = nil, - return_type_context = nil, - yield_arg_types = nil, - yield_return_type_context = nil, - resolved_generic_values: {}) - signature = super(generics_to_resolve, return_type_context, resolved_generic_values: resolved_generic_values) - signature.parameters = signature.parameters.each_with_index.map do |param, i| - if arg_types.nil? - param.dup - else - param.resolve_generics_from_context(generics_to_resolve, - arg_types[i], - resolved_generic_values: resolved_generic_values) - end - end - signature.block = block.resolve_generics_from_context(generics_to_resolve, - yield_arg_types, - yield_return_type_context, - resolved_generic_values: resolved_generic_values) if signature.block? - signature - end - - # @param generics_to_resolve [Enumerable] - # @param arg_types [Array, nil] - # @param return_type_context [ComplexType, nil] - # @param yield_arg_types [Array, nil] - # @param yield_return_type_context [ComplexType, nil] - # @param context [ComplexType, nil] - # @param resolved_generic_values [Hash{String => ComplexType}] - # @return [self] - def resolve_generics_from_context_until_complete(generics_to_resolve, - arg_types = nil, - return_type_context = nil, - yield_arg_types = nil, - yield_return_type_context = nil, - resolved_generic_values: {}) - # See - # https://github.com/soutaro/steep/tree/master/lib/steep/type_inference - # and - # https://github.com/sorbet/sorbet/blob/master/infer/inference.cc - # for other implementations - - return self if generics_to_resolve.empty? - - last_resolved_generic_values = resolved_generic_values.dup - new_pin = resolve_generics_from_context(generics_to_resolve, - arg_types, - return_type_context, - yield_arg_types, - yield_return_type_context, - resolved_generic_values: resolved_generic_values) - if last_resolved_generic_values == resolved_generic_values - # erase anything unresolved - return new_pin.erase_generics(self.generics) - end - new_pin.resolve_generics_from_context_until_complete(generics_to_resolve, - arg_types, - return_type_context, - yield_arg_types, - yield_return_type_context, - resolved_generic_values: resolved_generic_values) - end - def identity @identity ||= "signature#{object_id}" end - - def block? - !!@block - end - - protected - - attr_writer :block - - attr_writer :parameters end end end diff --git a/lib/solargraph/rbs_map/conversions.rb b/lib/solargraph/rbs_map/conversions.rb index 9737c2eba..60e487e5b 100644 --- a/lib/solargraph/rbs_map/conversions.rb +++ b/lib/solargraph/rbs_map/conversions.rb @@ -319,12 +319,12 @@ def method_def_to_pin decl, closure def method_def_to_sigs decl, pin decl.overloads.map do |overload| generics = overload.method_type.type_params.map(&:to_s) - parameters, return_type = parts_of_function(overload.method_type, pin) + signature_parameters, signature_return_type = parts_of_function(overload.method_type, pin) block = if overload.method_type.block - Pin::Signature.new(generics, *parts_of_function(overload.method_type.block, pin)) - end - return_type = ComplexType.try_parse(method_type_to_tag(overload.method_type)) - Pin::Signature.new(generics, parameters, return_type, block) + block_parameters, block_return_type = parts_of_function(overload.method_type.block, pin) + Pin::Signature.new(generics: generics, parameters: block_parameters, return_type: block_return_type) + end + Pin::Signature.new(generics: generics, parameters: signature_parameters, return_type: signature_return_type, block: block) end end @@ -341,7 +341,7 @@ def location_decl_to_pin_location(location) # @param type [RBS::MethodType,RBS::Types::Block] # @param pin [Pin::Method] - # @return [Array, ComplexType>] + # @return [Array(Array, ComplexType)] def parts_of_function type, pin return [[Solargraph::Pin::Parameter.new(decl: :restarg, name: 'arg', closure: pin)], ComplexType.try_parse(method_type_to_tag(type))] if defined?(RBS::Types::UntypedFunction) && type.type.is_a?(RBS::Types::UntypedFunction) diff --git a/lib/solargraph/source/chain/call.rb b/lib/solargraph/source/chain/call.rb index 5a76ab094..dda2a69c8 100644 --- a/lib/solargraph/source/chain/call.rb +++ b/lib/solargraph/source/chain/call.rb @@ -68,7 +68,7 @@ def inferred_pins pins, api_map, context, locals sorted_overloads = overloads.sort { |ol| ol.block? ? -1 : 1 } new_signature_pin = nil sorted_overloads.each do |ol| - next unless arity_matches?(arguments, ol) + next unless ol.arity_matches?(arguments, with_block?) match = true atypes = [] @@ -192,19 +192,6 @@ def extra_return_type docstring, context nil end - # @param arguments [::Array] - # @param signature [Pin::Signature] - # @return [Boolean] - def arity_matches? arguments, signature - parameters = signature.parameters - argcount = arguments.length - parcount = parameters.length - parcount -= 1 if !parameters.empty? && parameters.last.block? - return false if signature.block? && !with_block? - return false if argcount < parcount && !(argcount == parcount - 1 && parameters.last.restarg?) - true - end - # @param api_map [ApiMap] # @param name_pin [Pin::Base] # @return [::Array] diff --git a/spec/pin/namespace_spec.rb b/spec/pin/namespace_spec.rb index 2ff0e1073..a70e6c869 100644 --- a/spec/pin/namespace_spec.rb +++ b/spec/pin/namespace_spec.rb @@ -30,5 +30,6 @@ class Foo it 'uses @param tags as generic type parameters' do pin = Solargraph::Pin::Namespace.new(name: 'Foo', comments: '@generic GenericType') expect(pin.generics).to eq(['GenericType']) + expect(pin.to_rbs).to eq('class Foo[GenericType]') end end diff --git a/spec/source_map/clip_spec.rb b/spec/source_map/clip_spec.rb index 603ecedf1..e21125374 100644 --- a/spec/source_map/clip_spec.rb +++ b/spec/source_map/clip_spec.rb @@ -2266,4 +2266,41 @@ def blah clip = api_map.clip_at('test.rb', [10, 8]) expect(clip.infer.to_s).to eq('nil') end + + xit 'resolves overloads based on kwarg existence' do + source = Solargraph::Source.load_string(%( + class Blah + # @param *strings [Array] The type definitions to parse + # @return [Blah] + # @overload parse(*strings, partial:) + # @param *strings [Array] The type definitions to parse + # @param partial [Boolean] True if the string is part of a another type + # @return [Array] + def self.parse *strings, partial: false; end + + def foo + x = Blah.parse('blah') + x + end + end + ), 'test.rb') + api_map = Solargraph::ApiMap.new.map(source) + clip = api_map.clip_at('test.rb', [12, 10]) + expect(clip.infer.to_s).to eq('Blah') + end + + it 'handles resolving String#each_line overloads' do + source = Solargraph::Source.load_string(%( + def foo + 'abc\ndef'.each_line do |line| + line + end + end + ), 'test.rb') + + api_map = Solargraph::ApiMap.new.map(source) + + clip = api_map.clip_at('test.rb', [3, 10]) + expect(clip.infer.to_s).to eq('String') + end end From c008197d873157a845c7dd6c584eb7568d5f2d6c Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 19 Apr 2025 10:44:55 -0400 Subject: [PATCH 014/561] Adjust local variable presence to start after assignment, not before (#864) * Adjust local variable presence to start after assignment, not before * Add regression test around assignment in return position * Fix assignment visibility code, which relied on bad asgn semantics --- .../parser/parser_gem/node_chainer.rb | 20 +++--- .../parser_gem/node_processors/lvasgn_node.rb | 4 +- spec/parser/node_chainer_spec.rb | 5 +- spec/source_map/clip_spec.rb | 62 +++++++++++++++++-- 4 files changed, 74 insertions(+), 17 deletions(-) diff --git a/lib/solargraph/parser/parser_gem/node_chainer.rb b/lib/solargraph/parser/parser_gem/node_chainer.rb index bb5d7655c..b8d69f7a2 100644 --- a/lib/solargraph/parser/parser_gem/node_chainer.rb +++ b/lib/solargraph/parser/parser_gem/node_chainer.rb @@ -89,15 +89,21 @@ def generate_links n elsif n.type == :const const = unpack_name(n) result.push Chain::Constant.new(const) - elsif [:lvar, :lvasgn].include?(n.type) + elsif [:lvasgn, :ivasgn, :gvasgn, :cvasgn].include?(n.type) + result.concat generate_links(n.children[1]) + elsif n.type == :lvar result.push Chain::Call.new(n.children[0].to_s) - elsif [:ivar, :ivasgn].include?(n.type) - result.push Chain::InstanceVariable.new(n.children[0].to_s) - elsif [:cvar, :cvasgn].include?(n.type) - result.push Chain::ClassVariable.new(n.children[0].to_s) - elsif [:gvar, :gvasgn].include?(n.type) - result.push Chain::GlobalVariable.new(n.children[0].to_s) + elsif n.type == :ivar + result.push Chain::InstanceVariable.new(n.children[0].to_s) + elsif n.type == :cvar + result.push Chain::ClassVariable.new(n.children[0].to_s) + elsif n.type == :gvar + result.push Chain::GlobalVariable.new(n.children[0].to_s) elsif n.type == :or_asgn + # @todo: Need a new Link class here that evaluates the + # existing variable type with the RHS, and generates a + # union type of the LHS alone if never nil, or minus nil + + # RHS if it is nilable. result.concat generate_links n.children[1] elsif [:class, :module, :def, :defs].include?(n.type) # @todo Undefined or what? diff --git a/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb b/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb index efff73645..47e79d66d 100644 --- a/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +++ b/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb @@ -8,8 +8,8 @@ class LvasgnNode < Parser::NodeProcessor::Base include ParserGem::NodeMethods def process - here = get_node_start_position(node) - presence = Range.new(here, region.closure.location.range.ending) + # variable not visible until next statement + presence = Range.new(get_node_end_position(node), region.closure.location.range.ending) loc = get_node_location(node) locals.push Solargraph::Pin::LocalVariable.new( location: loc, diff --git a/spec/parser/node_chainer_spec.rb b/spec/parser/node_chainer_spec.rb index e92431aae..fee801e7c 100644 --- a/spec/parser/node_chainer_spec.rb +++ b/spec/parser/node_chainer_spec.rb @@ -111,10 +111,9 @@ class Foo foo = [1, 2] )) chain = Solargraph::Parser.chain(source.node) - expect(chain.links.map(&:word)).to eq(['foo']) + expect(chain.links.map(&:word)).to eq(['<::Array>']) foo_link = chain.links.first - expect(foo_link.class).to eq(Solargraph::Source::Chain::Call) - expect(foo_link.arguments).to eq([]) + expect(foo_link.class).to eq(Solargraph::Source::Chain::Array) end it 'tracks complex lhs' do diff --git a/spec/source_map/clip_spec.rb b/spec/source_map/clip_spec.rb index e21125374..efadae775 100644 --- a/spec/source_map/clip_spec.rb +++ b/spec/source_map/clip_spec.rb @@ -361,10 +361,11 @@ def bar # @return [String, Array] def foo; end var = foo + var ), 'test.rb') map = Solargraph::ApiMap.new map.map source - clip = map.clip_at('test.rb', Solargraph::Position.new(3, 7)) + clip = map.clip_at('test.rb', Solargraph::Position.new(4, 6)) type = clip.infer expect(type.to_s).to eq('String, Array') end @@ -731,10 +732,11 @@ def self.new end end value = Value.new + value ), 'test.rb') api_map = Solargraph::ApiMap.new api_map.map source - clip = api_map.clip_at('test.rb', [6, 11]) + clip = api_map.clip_at('test.rb', [7, 11]) expect(clip.infer.tag).to eq('Class') end @@ -1252,6 +1254,7 @@ class Foo class Mod def meth arr = [] + arr 1.times do arr end @@ -1261,11 +1264,11 @@ def meth ), 'test.rb') api_map = Solargraph::ApiMap.new api_map.map source - clip = api_map.clip_at('test.rb', [3, 11]) + clip = api_map.clip_at('test.rb', [4, 11]) expect(clip.infer.tag).to eq('Array') - clip = api_map.clip_at('test.rb', [5, 12]) + clip = api_map.clip_at('test.rb', [6, 12]) expect(clip.infer.tag).to eq('Array') - clip = api_map.clip_at('test.rb', [7, 10]) + clip = api_map.clip_at('test.rb', [8, 10]) expect(clip.infer.tag).to eq('Array') end @@ -2267,6 +2270,55 @@ def blah expect(clip.infer.to_s).to eq('nil') end + it 'can infer assignments-in-return-position from complex expressions' do + source = Solargraph::Source.load_string(%( + class A + def foo + blah = ['foo'].map { 456 } + end + + def bar + nah ||= ['foo'].map { 456 } + end + + def foo1 + @blah = ['foo'].map { 456 } + end + + def bar1 + @nah2 ||= ['foo'].map { 456 } + end + + def baz + a = foo + a + b = bar + b + a1 = foo1 + a1 + b2 = bar1 + b2 + end + end + ), 'test.rb') + + api_map = Solargraph::ApiMap.new.map(source) + + clip = api_map.clip_at('test.rb', [20, 10]) + expect(clip.infer.to_s).to eq('Array') + + # @todo pending https://github.com/castwide/solargraph/pull/888 + # clip = api_map.clip_at('test.rb', [22, 10]) + # expect(clip.infer.to_s).to eq('Array') + + clip = api_map.clip_at('test.rb', [24, 10]) + expect(clip.infer.to_s).to eq('Array') + + # @todo pending https://github.com/castwide/solargraph/pull/888 + # clip = api_map.clip_at('test.rb', [26, 10]) + # expect(clip.infer.to_s).to eq('Array') + end + xit 'resolves overloads based on kwarg existence' do source = Solargraph::Source.load_string(%( class Blah From 9aa5e368b456487e7b6ca1830b5fc6e386c4c6b7 Mon Sep 17 00:00:00 2001 From: Fred Snyder Date: Sat, 19 Apr 2025 10:46:11 -0400 Subject: [PATCH 015/561] Resolve params from ref tags (#872) * Resolve params from ref tags * Resolve ref tags with namespaces --- lib/solargraph/pin/method.rb | 24 ++++++++++++++++++ lib/solargraph/pin/parameter.rb | 10 ++------ spec/pin/method_spec.rb | 44 +++++++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+), 8 deletions(-) diff --git a/lib/solargraph/pin/method.rb b/lib/solargraph/pin/method.rb index bb8a1ba6f..e2fed6626 100644 --- a/lib/solargraph/pin/method.rb +++ b/lib/solargraph/pin/method.rb @@ -272,6 +272,7 @@ def probe api_map def try_merge! pin return false unless super @node = pin.node + @resolved_ref_tag = false true end @@ -304,6 +305,29 @@ def anon_splat? @anon_splat end + # @param [ApiMap] + # @return [self] + def resolve_ref_tag api_map + return self if @resolved_ref_tag + + @resolved_ref_tag = true + return self unless docstring.ref_tags.any? + docstring.ref_tags.each do |tag| + ref = if tag.owner.to_s.start_with?(/[#\.]/) + api_map.get_methods(namespace) + .select { |pin| pin.path.end_with?(tag.owner.to_s) } + .first + else + # @todo Resolve relative namespaces + api_map.get_path_pins(tag.owner.to_s).first + end + next unless ref + + docstring.add_tag(*ref.docstring.tags(:param)) + end + self + end + protected attr_writer :block diff --git a/lib/solargraph/pin/parameter.rb b/lib/solargraph/pin/parameter.rb index d76309d0e..878f1deea 100644 --- a/lib/solargraph/pin/parameter.rb +++ b/lib/solargraph/pin/parameter.rb @@ -130,17 +130,11 @@ def try_merge! pin # @return [YARD::Tags::Tag, nil] def param_tag - found = nil params = closure.docstring.tags(:param) params.each do |p| - next unless p.name == name - found = p - break + return p if p.name == name end - if found.nil? and !index.nil? - found = params[index] if params[index] && (params[index].name.nil? || params[index].name.empty?) - end - found + params[index] if index && params[index] && (params[index].name.nil? || params[index].name.empty?) end # @param api_map [ApiMap] diff --git a/spec/pin/method_spec.rb b/spec/pin/method_spec.rb index bce1dfd11..4e367c521 100644 --- a/spec/pin/method_spec.rb +++ b/spec/pin/method_spec.rb @@ -461,6 +461,50 @@ def bar?; end expect(pin.documentation).to include('# Call foo') end + it 'resolves ref tags' do + source = Solargraph::Source.load_string(%( + class Example + # @param param1 [String] + # @param param2 [Integer] + def foo param1, param2 + end + + # @param (see #foo) + def bar param1, param2 + end + end + )) + api_map = Solargraph::ApiMap.new + api_map.map source + pin = api_map.get_path_pins('Example#bar').first + pin.resolve_ref_tag(api_map) + expect(pin.docstring.tags(:param).map(&:name)).to eq(['param1', 'param2']) + expect(pin.docstring.tags(:param).map(&:type)).to eq(['String', 'Integer']) + end + + it 'resolves ref tags with namespaces' do + source = Solargraph::Source.load_string(%( + class Example1 + # @param param1 [String] + # @param param2 [Integer] + def foo param1, param2 + end + end + + class Example2 + # @param (see Example1#foo) + def bar param1, param2 + end + end + )) + api_map = Solargraph::ApiMap.new + api_map.map source + pin = api_map.get_path_pins('Example2#bar').first + pin.resolve_ref_tag(api_map) + expect(pin.docstring.tags(:param).map(&:name)).to eq(['param1', 'param2']) + expect(pin.docstring.tags(:param).map(&:type)).to eq(['String', 'Integer']) + end + context 'as attribute' do it "is a kind of attribute/property" do source = Solargraph::Source.load_string(%( From a94221a25b0fa15b11427ec801c9ee69aa49b8b6 Mon Sep 17 00:00:00 2001 From: Fred Snyder Date: Sat, 19 Apr 2025 12:27:37 -0400 Subject: [PATCH 016/561] Remove Library#folding_ranges --- lib/solargraph/library.rb | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/lib/solargraph/library.rb b/lib/solargraph/library.rb index 17acd5baf..6a02be3fc 100644 --- a/lib/solargraph/library.rb +++ b/lib/solargraph/library.rb @@ -441,17 +441,6 @@ def bench ) end - # Get an array of foldable ranges for the specified file. - # - # @deprecated The library should not need to handle folding ranges. The - # source itself has all the information it needs. - # - # @param filename [String] - # @return [Array] - def folding_ranges filename - read(filename).folding_ranges - end - # Create a library from a directory. # # @param directory [String] The path to be used for the workspace From 67dab545260166f904d643f033b92ff40ab2f8db Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 20 Apr 2025 11:17:45 -0400 Subject: [PATCH 017/561] Fix numeric vs symbol logic --- lib/solargraph/logging.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/solargraph/logging.rb b/lib/solargraph/logging.rb index e3173e78c..9640be39a 100644 --- a/lib/solargraph/logging.rb +++ b/lib/solargraph/logging.rb @@ -27,15 +27,16 @@ module Logging # show existing debug messages by setting to :debug). @return # [Symbol] def log_level - @@logger.level + :warn end # @return [Logger] def logger - @logger ||= if log_level == @@logger.level + @logger ||= if LOG_LEVELS[log_level.to_s] == @@logger.level @@logger else - logger = Logger.new(STDERR, log_level) + new_log_level = LOG_LEVELS[log_level.to_s] + logger = Logger.new(STDERR, level: new_log_level) logger.formatter = @@logger.formatter logger end From eb49ddbf1279a9be25c98c062491c5d78d6bfa1c Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 21 Apr 2025 07:20:07 -0400 Subject: [PATCH 018/561] Avoid marshaling issues --- lib/solargraph/logging.rb | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/solargraph/logging.rb b/lib/solargraph/logging.rb index 9640be39a..7c7531aea 100644 --- a/lib/solargraph/logging.rb +++ b/lib/solargraph/logging.rb @@ -32,14 +32,14 @@ def log_level # @return [Logger] def logger - @logger ||= if LOG_LEVELS[log_level.to_s] == @@logger.level - @@logger - else - new_log_level = LOG_LEVELS[log_level.to_s] - logger = Logger.new(STDERR, level: new_log_level) - logger.formatter = @@logger.formatter - logger - end + if LOG_LEVELS[log_level.to_s] == DEFAULT_LOG_LEVEL + @@logger + else + new_log_level = LOG_LEVELS[log_level.to_s] + logger = Logger.new(STDERR, level: new_log_level) + logger.formatter = @@logger.formatter + logger + end end end end From eb1a2177c2234ed62932f51abdcc72671d43fe2f Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 26 Apr 2025 10:49:06 -0400 Subject: [PATCH 019/561] Fix missed spot in rename --- lib/solargraph/library.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/solargraph/library.rb b/lib/solargraph/library.rb index 17acd5baf..d478d501a 100644 --- a/lib/solargraph/library.rb +++ b/lib/solargraph/library.rb @@ -254,11 +254,11 @@ def references_from filename, line, column, strip: false, only: false referenced&.path == pin.path end if pin.path == 'Class#new' - caller = cursor.chain.base.infer(api_map, clip.send(:block), clip.locals).first + caller = cursor.chain.base.infer(api_map, clip.send(:closure), clip.locals).first if caller.defined? found.select! do |loc| clip = api_map.clip_at(loc.filename, loc.range.start) - other = clip.send(:cursor).chain.base.infer(api_map, clip.send(:block), clip.locals).first + other = clip.send(:cursor).chain.base.infer(api_map, clip.send(:closure), clip.locals).first caller == other end else From d75212ac4985776d22c77c53da7cc5d6389ed0c1 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 29 Apr 2025 09:02:48 -0400 Subject: [PATCH 020/561] Add alias for rubocop-rspec --- lib/solargraph/source_map.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/solargraph/source_map.rb b/lib/solargraph/source_map.rb index 53821401f..522cd0c52 100644 --- a/lib/solargraph/source_map.rb +++ b/lib/solargraph/source_map.rb @@ -118,6 +118,11 @@ def locate_closure_pin line, character _locate_pin line, character, Pin::Closure end + # @deprecated Please use locate_closure_pin instead + alias locate_block_pin locate_closure_pin + + # @todo Candidate for deprecation + # # @param other_map [SourceMap] # @return [Boolean] def try_merge! other_map From beacbc18b8ef337f8313c87279aefc34a102797a Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 29 Apr 2025 10:47:12 -0400 Subject: [PATCH 021/561] Fix merge issue --- lib/solargraph/rbs_map/conversions.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/solargraph/rbs_map/conversions.rb b/lib/solargraph/rbs_map/conversions.rb index 7ee647abd..504a1c84a 100644 --- a/lib/solargraph/rbs_map/conversions.rb +++ b/lib/solargraph/rbs_map/conversions.rb @@ -211,7 +211,6 @@ def module_decl_to_pin decl # # @return [Solargraph::Pin::Constant] def create_constant(name, tag, comments, decl, base = nil) - comments = decl.comment&.string parts = name.split('::') if parts.length > 1 name = parts.last From df1334b7ff02bfaa637ba2fe180c3a8ea966f6da Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 6 May 2025 12:39:50 -0400 Subject: [PATCH 022/561] Enable strict type checking in CI --- .github/workflows/typecheck.yml | 2 +- Rakefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/typecheck.yml b/.github/workflows/typecheck.yml index 5b1b5e151..8f9119592 100644 --- a/.github/workflows/typecheck.yml +++ b/.github/workflows/typecheck.yml @@ -31,4 +31,4 @@ jobs: - name: Install gems run: bundle install - name: Typecheck self - run: bundle exec solargraph typecheck --level typed + run: bundle exec solargraph typecheck --level strict diff --git a/Rakefile b/Rakefile index 33b91bfa4..a7fea9b13 100755 --- a/Rakefile +++ b/Rakefile @@ -15,7 +15,7 @@ end desc "Run the type checker" task :typecheck do - sh "bundle exec solargraph typecheck --level typed" + sh "bundle exec solargraph typecheck --level strict" end desc "Run all tests" From 56e6276209819904033948c27950da2349de13d1 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 9 May 2025 08:41:16 -0400 Subject: [PATCH 023/561] Initial workflow for solargraph-rails --- .github/workflows/plugins.yml | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index f749a4a79..a790b8b98 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -43,7 +43,6 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - # - name: clone https://github.com/lekemula/solargraph-rspec/ run: | cd .. @@ -65,8 +64,27 @@ jobs: bundle exec rake spec run_solargraph_rails_specs: + # check out solargraph-rails as well as this project, and point the former to use the latter as a local gem runs-on: ubuntu-latest steps: - - name: Run + - uses: actions/checkout@v3 + - name: clone https://github.com/iftheshoefritz/solargraph-rails/ + run: | + cd .. + # TODO: Replace with main branch of iftheshoefritz/solargraph-rails once fixes are merged + git -b 2025-05-07 clone https://github.com/apiology/solargraph-rails.git + cd solargraph-rails + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.0' + bundler-cache: false + - name: Install gems run: | - false + cd ../solargraph-rails + echo "gem 'solargraph', path: '../solargraph'" >> Gemfile + bundle install + - name: Run specs + run: | + cd ../solargraph-rails + bundle exec rake spec From dfaf79b56280d1ffa9c5e77c61afd034b10ed598 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 9 May 2025 08:43:26 -0400 Subject: [PATCH 024/561] solargraph-rails workflow fixes --- .github/workflows/plugins.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index a790b8b98..cb9ef4ea6 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -72,7 +72,7 @@ jobs: run: | cd .. # TODO: Replace with main branch of iftheshoefritz/solargraph-rails once fixes are merged - git -b 2025-05-07 clone https://github.com/apiology/solargraph-rails.git + git clone -b 2025-05-07 https://github.com/apiology/solargraph-rails.git cd solargraph-rails - name: Set up Ruby uses: ruby/setup-ruby@v1 From 8c9aa31e85b29608a7990cb4c8c4c5792e084bf5 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 9 May 2025 09:01:43 -0400 Subject: [PATCH 025/561] Force rerun --- .github/workflows/plugins.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index cb9ef4ea6..a1844481a 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -71,7 +71,7 @@ jobs: - name: clone https://github.com/iftheshoefritz/solargraph-rails/ run: | cd .. - # TODO: Replace with main branch of iftheshoefritz/solargraph-rails once fixes are merged + # TODO: Replace with main branch of iftheshoefritz/solargraph-rails once fixes are merged git clone -b 2025-05-07 https://github.com/apiology/solargraph-rails.git cd solargraph-rails - name: Set up Ruby From 4cd81204c9866787f6e5023a53ef91109b3ccabe Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 9 May 2025 09:03:50 -0400 Subject: [PATCH 026/561] solargraph-rails workflow fixes --- .github/workflows/plugins.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index a1844481a..82cd9a3e3 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -79,6 +79,8 @@ jobs: with: ruby-version: '3.0' bundler-cache: false + # specified in Gemfile + bundler: 2.3 - name: Install gems run: | cd ../solargraph-rails From 0a7db9f7023bac03d5022c49b318b499b98c5ae3 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 19 May 2025 07:27:41 -0400 Subject: [PATCH 027/561] Run specs from plugin projects configured against current code --- .github/workflows/plugins.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index 82cd9a3e3..125d63b14 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -19,7 +19,7 @@ jobs: uses: ruby/setup-ruby@v1 with: ruby-version: '3.0' - bundler-cache: false + bundler-cache: true - uses: awalsh128/cache-apt-pkgs-action@latest with: packages: yq @@ -38,6 +38,7 @@ jobs: run: bundle exec solargraph typecheck --level typed - name: Ensure specs still run run: bundle exec rake spec + run_solargraph_rspec_specs: # check out solargraph-rspec as well as this project, and point the former to use the latter as a local gem runs-on: ubuntu-latest @@ -77,7 +78,8 @@ jobs: - name: Set up Ruby uses: ruby/setup-ruby@v1 with: - ruby-version: '3.0' + # solargraph-rails supports Ruby 3.1+ + ruby-version: '3.1' bundler-cache: false # specified in Gemfile bundler: 2.3 From c597140be8fa291dbb3346660092ea05a9399d12 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 19 May 2025 16:41:29 -0400 Subject: [PATCH 028/561] Install RBS collection --- .github/workflows/plugins.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index 125d63b14..116276e46 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -88,6 +88,12 @@ jobs: cd ../solargraph-rails echo "gem 'solargraph', path: '../solargraph'" >> Gemfile bundle install + bundle exec rbs collection init + bundle exec rbs collection install + cd spec/rails7 + bundle install + bundle exec rbs collection init + bundle exec rbs collection install - name: Run specs run: | cd ../solargraph-rails From 14ddaa5e6beb54d90a2e561b560e2274c6333b1a Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 19 May 2025 16:45:03 -0400 Subject: [PATCH 029/561] Install RBS collection --- .github/workflows/plugins.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index 116276e46..615155453 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -86,11 +86,13 @@ jobs: - name: Install gems run: | cd ../solargraph-rails - echo "gem 'solargraph', path: '../solargraph'" >> Gemfile + cd spec/rails7 + echo "gem 'solargraph', path: '../../../solargraph'" >> Gemfile bundle install bundle exec rbs collection init bundle exec rbs collection install - cd spec/rails7 + cd ../../ + echo "gem 'solargraph', path: '../solargraph'" >> Gemfile bundle install bundle exec rbs collection init bundle exec rbs collection install From f4d21d0dbb1cd50fb8574193553eae1e69cd9da6 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 19 May 2025 16:48:12 -0400 Subject: [PATCH 030/561] Don't do redundant rbs collection init --- .github/workflows/plugins.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index 615155453..77b049215 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -89,7 +89,6 @@ jobs: cd spec/rails7 echo "gem 'solargraph', path: '../../../solargraph'" >> Gemfile bundle install - bundle exec rbs collection init bundle exec rbs collection install cd ../../ echo "gem 'solargraph', path: '../solargraph'" >> Gemfile From dfe251375223800c5151c9daece723ea751b92ba Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 19 May 2025 16:52:16 -0400 Subject: [PATCH 031/561] Specify absolute directory --- .github/workflows/plugins.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index 77b049215..a78c7c166 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -87,11 +87,11 @@ jobs: run: | cd ../solargraph-rails cd spec/rails7 - echo "gem 'solargraph', path: '../../../solargraph'" >> Gemfile + echo "gem 'solargraph', path: '/home/runner/work/solargraph/solargraph'" >> Gemfile bundle install bundle exec rbs collection install cd ../../ - echo "gem 'solargraph', path: '../solargraph'" >> Gemfile + echo "gem 'solargraph', path: '/home/runner/work/solargraph/solargraph'" >> Gemfile bundle install bundle exec rbs collection init bundle exec rbs collection install From 2e1b0eb3c430bbb09f5dae4a0d03e14b26c64dbd Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 22 May 2025 07:53:54 -0400 Subject: [PATCH 032/561] Support ActiveSupport::Concern pattern for class methods --- .yardopts | 1 + lib/solargraph/api_map.rb | 20 ++++++++++++++++++++ lib/solargraph/pin/method.rb | 8 +++++++- lib/solargraph/yardoc.rb | 2 +- solargraph.gemspec | 1 + 5 files changed, 30 insertions(+), 2 deletions(-) diff --git a/.yardopts b/.yardopts index b5adca9f9..d5e994511 100644 --- a/.yardopts +++ b/.yardopts @@ -1,2 +1,3 @@ lib/**/*.rb --plugin yard-solargraph +--plugin activesupport-concern diff --git a/lib/solargraph/api_map.rb b/lib/solargraph/api_map.rb index 9443e8529..b74de2b93 100755 --- a/lib/solargraph/api_map.rb +++ b/lib/solargraph/api_map.rb @@ -643,6 +643,8 @@ def inner_get_methods rooted_tag, scope, visibility, deep, skip, no_core = false # namespaces; resolving the generics in the method pins is this # class' responsibility methods = store.get_methods(fqns, scope: scope, visibility: visibility).sort{ |a, b| a.name <=> b.name } + methods = methods.map(&:as_virtual_class_method) if store.get_includes(fqns).include?('ActiveSupport::Concern') && scope == :class + logger.info { "ApiMap#inner_get_methods(rooted_tag=#{rooted_tag.inspect}, scope=#{scope.inspect}, visibility=#{visibility.inspect}, deep=#{deep.inspect}, skip=#{skip.inspect}, fqns=#{fqns}) - added from store: #{methods}" } result.concat methods if deep if scope == :instance @@ -664,6 +666,24 @@ def inner_get_methods rooted_tag, scope, visibility, deep, skip, no_core = false result.concat inner_get_methods(fqsc, scope, visibility, true, skip, no_core) unless fqsc.nil? end else + store.get_includes(fqns).reverse.each do |include_tag| + logger.info { "ApiMap#inner_get_methods(#{fqns}, #{scope}, #{visibility}, #{deep}) - Handling class include include_tag=#{include_tag}" } + rooted_include_tag = qualify(include_tag, rooted_tag) + + # ActiveSupport::Concern is syntactic sugar for a common + # pattern to provide virtual class method - i.e., if Foo + # includes Bar and Bar is a module using this + # pattern, Bar can supply class methods which will also + # appear under Foo. + + # See https://api.rubyonrails.org/classes/ActiveSupport/Concern.html + included_class_pins = inner_get_methods_from_reference(rooted_include_tag, namespace_pin, rooted_type, scope, visibility, deep, skip, true) + # activesupport_concern_pins = included_class_pins.select { |p| p.virtual_class_method? } + # result.concat activesupport_concern_pins + result.concat included_class_pins # TODO remove this line once we have activesupport::concern support + end + + logger.info { "ApiMap#inner_get_methods(#{fqns}, #{scope}, #{visibility}, #{deep}, #{skip}) - looking for get_extends() from #{fqns}" } store.get_extends(fqns).reverse.each do |em| fqem = qualify(em, fqns) result.concat inner_get_methods(fqem, :instance, visibility, deep, skip, true) unless fqem.nil? diff --git a/lib/solargraph/pin/method.rb b/lib/solargraph/pin/method.rb index 5c94647e6..cb0aa3704 100644 --- a/lib/solargraph/pin/method.rb +++ b/lib/solargraph/pin/method.rb @@ -10,6 +10,10 @@ class Method < Callable # @return [::Symbol] :public, :private, or :protected attr_reader :visibility + def virtual_class_method? + @virtual_class_method + end + # @return [Parser::AST::Node] attr_reader :node @@ -20,7 +24,8 @@ class Method < Callable # @param attribute [Boolean] # @param signatures [::Array, nil] # @param anon_splat [Boolean] - def initialize visibility: :public, explicit: true, block: :undefined, node: nil, attribute: false, signatures: nil, anon_splat: false, **splat + def initialize visibility: :public, explicit: true, block: :undefined, node: nil, attribute: false, signatures: nil, anon_splat: false, + virtual_class_method: false, **splat super(**splat) @visibility = visibility @explicit = explicit @@ -29,6 +34,7 @@ def initialize visibility: :public, explicit: true, block: :undefined, node: nil @attribute = attribute @signatures = signatures @anon_splat = anon_splat + @virtual_class_method = virtual_class_method end def == other diff --git a/lib/solargraph/yardoc.rb b/lib/solargraph/yardoc.rb index 4fd9b193f..858c7774a 100644 --- a/lib/solargraph/yardoc.rb +++ b/lib/solargraph/yardoc.rb @@ -17,7 +17,7 @@ def cache(gemspec) Solargraph.logger.info "Caching yardoc for #{gemspec.name} #{gemspec.version}" Dir.chdir gemspec.gem_dir do - `yardoc --db #{path} --no-output --plugin solargraph` + `yardoc --db #{path} --no-output --plugin solargraph --plugin activesupport-concern` end path end diff --git a/solargraph.gemspec b/solargraph.gemspec index 2bfb6dbdf..b19772703 100755 --- a/solargraph.gemspec +++ b/solargraph.gemspec @@ -41,6 +41,7 @@ Gem::Specification.new do |s| s.add_runtime_dependency 'tilt', '~> 2.0' s.add_runtime_dependency 'yard', '~> 0.9', '>= 0.9.24' s.add_runtime_dependency 'yard-solargraph', '~> 0.1' + s.add_runtime_dependency 'yard-activesupport-concern', '~> 0.0' s.add_development_dependency 'pry', '~> 0.15' s.add_development_dependency 'public_suffix', '~> 3.1' From 16b4a0d2700d293b4dc23bd4631f80b13daa9190 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 22 May 2025 14:20:29 -0400 Subject: [PATCH 033/561] Add assertions around method aliases --- lib/solargraph.rb | 19 +++++++++++++++++++ lib/solargraph/api_map.rb | 10 ++++++++-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/lib/solargraph.rb b/lib/solargraph.rb index 352b0eaad..8834fc3fb 100755 --- a/lib/solargraph.rb +++ b/lib/solargraph.rb @@ -52,6 +52,25 @@ class InvalidRubocopVersionError < RuntimeError; end dir = File.dirname(__FILE__) VIEWS_PATH = File.join(dir, 'solargraph', 'views') + # @param type [Symbol] Type of assert. Not used yet, but may be + # used in the future to allow configurable asserts mixes for + # different situations. + def self.asserts_on?(type) + if ENV['SOLARGRAPH_ASSERTS'].nil? || ENV['SOLARGRAPH_ASSERTS'].empty? + false + elsif ENV['SOLARGRAPH_ASSERTS'] == 'on' + true + else + logger.warn "Unrecognized SOLARGRAPH_ASSERTS value: #{ENV['SOLARGRAPH_ASSERTS']}" + false + end + end + + def self.assert_or_log(type, msg = nil, &block) + raise (msg || block.call) if asserts_on?(type) && ![:combine_with_visibility].include?(type) + logger.info msg, &block + end + # A convenience method for Solargraph::Logging.logger. # # @return [Logger] diff --git a/lib/solargraph/api_map.rb b/lib/solargraph/api_map.rb index 9443e8529..b0225305e 100755 --- a/lib/solargraph/api_map.rb +++ b/lib/solargraph/api_map.rb @@ -808,7 +808,10 @@ def prefer_non_nil_variables pins def resolve_method_aliases pins, visibility = [:public, :private, :protected] pins.map do |pin| resolved = resolve_method_alias(pin) - next pin if resolved.respond_to?(:visibility) && !visibility.include?(resolved.visibility) + if resolved.respond_to?(:visibility) && !visibility.include?(resolved.visibility) + Solargraph.assert_or_log(:alias_visibility) { "Rejecting alias - visibility of target is #{resolved.visibility}, looking for visibility #{visibility}" } + next pin + end resolved end.compact end @@ -821,7 +824,10 @@ def resolve_method_alias pin @method_alias_stack.push pin.path origin = get_method_stack(pin.full_context.tag, pin.original, scope: pin.scope).first @method_alias_stack.pop - return nil if origin.nil? + if origin.nil? + Solargraph.assert_or_log(:alias_target_missing) { "Rejecting alias - target is missing = #{pin.inspect}" } + return nil + end args = { location: pin.location, closure: pin.closure, From 16ad2c19354717b99533d36906f40afade13516f Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 1 Jun 2025 10:50:06 -0400 Subject: [PATCH 034/561] Add ::ClassMethods support --- lib/solargraph/api_map.rb | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/lib/solargraph/api_map.rb b/lib/solargraph/api_map.rb index b74de2b93..656c02858 100755 --- a/lib/solargraph/api_map.rb +++ b/lib/solargraph/api_map.rb @@ -667,20 +667,26 @@ def inner_get_methods rooted_tag, scope, visibility, deep, skip, no_core = false end else store.get_includes(fqns).reverse.each do |include_tag| - logger.info { "ApiMap#inner_get_methods(#{fqns}, #{scope}, #{visibility}, #{deep}) - Handling class include include_tag=#{include_tag}" } - rooted_include_tag = qualify(include_tag, rooted_tag) - + logger.debug { "ApiMap#inner_get_methods(#{fqns}, #{scope}, #{visibility}, #{deep}) - Handling class include include_tag=#{include_tag}" } + module_extends = store.get_extends(include_tag) # ActiveSupport::Concern is syntactic sugar for a common - # pattern to provide virtual class method - i.e., if Foo - # includes Bar and Bar is a module using this - # pattern, Bar can supply class methods which will also - # appear under Foo. + # pattern to include class methods while mixing-in a Module # See https://api.rubyonrails.org/classes/ActiveSupport/Concern.html - included_class_pins = inner_get_methods_from_reference(rooted_include_tag, namespace_pin, rooted_type, scope, visibility, deep, skip, true) - # activesupport_concern_pins = included_class_pins.select { |p| p.virtual_class_method? } - # result.concat activesupport_concern_pins - result.concat included_class_pins # TODO remove this line once we have activesupport::concern support + logger.debug { "ApiMap#inner_get_methods(#{fqns}, #{scope}, #{visibility}, #{deep}) - Handling class include include_tag=#{include_tag}" } + if module_extends.include? 'ActiveSupport::Concern' + rooted_include_tag = qualify(include_tag, rooted_tag) + unless rooted_include_tag.nil? + # yard-activesupport-concern pulls methods inside + # 'class_methods' blocks into main class visible from YARD + included_class_pins = inner_get_methods_from_reference(rooted_include_tag, namespace_pin, rooted_type, :class, visibility, deep, skip, true) + result.concat included_class_pins + + # another pattern is to put class methods inside a submodule + included_classmethods_pins = inner_get_methods_from_reference(rooted_include_tag + "::ClassMethods", namespace_pin, rooted_type, :instance, visibility, deep, skip, true) + result.concat included_classmethods_pins + end + end end logger.info { "ApiMap#inner_get_methods(#{fqns}, #{scope}, #{visibility}, #{deep}, #{skip}) - looking for get_extends() from #{fqns}" } From 02c7c4ebb491af1fe8b0ca49deca17473fbcb1eb Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 12 Jun 2025 14:50:43 -0400 Subject: [PATCH 035/561] Ensure that pin locations are always populated --- .github/workflows/typecheck.yml | 2 +- lib/solargraph.rb | 19 +++++++++++++ lib/solargraph/pin/base.rb | 7 +++++ lib/solargraph/rbs_map/conversions.rb | 7 +++-- lib/solargraph/yard_map/helpers.rb | 28 ++++++++++++++++++- lib/solargraph/yard_map/mapper/to_constant.rb | 5 +--- lib/solargraph/yard_map/mapper/to_method.rb | 7 +---- .../yard_map/mapper/to_namespace.rb | 9 ++---- 8 files changed, 64 insertions(+), 20 deletions(-) diff --git a/.github/workflows/typecheck.yml b/.github/workflows/typecheck.yml index 5b1b5e151..9c827049f 100644 --- a/.github/workflows/typecheck.yml +++ b/.github/workflows/typecheck.yml @@ -31,4 +31,4 @@ jobs: - name: Install gems run: bundle install - name: Typecheck self - run: bundle exec solargraph typecheck --level typed + run: SOLARGRAPH_ASSERTS=on bundle exec solargraph typecheck --level typed diff --git a/lib/solargraph.rb b/lib/solargraph.rb index 352b0eaad..8834fc3fb 100755 --- a/lib/solargraph.rb +++ b/lib/solargraph.rb @@ -52,6 +52,25 @@ class InvalidRubocopVersionError < RuntimeError; end dir = File.dirname(__FILE__) VIEWS_PATH = File.join(dir, 'solargraph', 'views') + # @param type [Symbol] Type of assert. Not used yet, but may be + # used in the future to allow configurable asserts mixes for + # different situations. + def self.asserts_on?(type) + if ENV['SOLARGRAPH_ASSERTS'].nil? || ENV['SOLARGRAPH_ASSERTS'].empty? + false + elsif ENV['SOLARGRAPH_ASSERTS'] == 'on' + true + else + logger.warn "Unrecognized SOLARGRAPH_ASSERTS value: #{ENV['SOLARGRAPH_ASSERTS']}" + false + end + end + + def self.assert_or_log(type, msg = nil, &block) + raise (msg || block.call) if asserts_on?(type) && ![:combine_with_visibility].include?(type) + logger.info msg, &block + end + # A convenience method for Solargraph::Logging.logger. # # @return [Logger] diff --git a/lib/solargraph/pin/base.rb b/lib/solargraph/pin/base.rb index 1e79397a7..90571510d 100644 --- a/lib/solargraph/pin/base.rb +++ b/lib/solargraph/pin/base.rb @@ -44,6 +44,13 @@ def initialize location: nil, type_location: nil, closure: nil, source: nil, nam @source = source @comments = comments @source = source + assert_location_provided + end + + def assert_location_provided + return unless best_location.nil? && [:yardoc, :source, :rbs].include?(source) + + Solargraph.assert_or_log(:best_location, "Neither location nor type_location provided - #{path} #{source} #{self.class}") end # @return [String] diff --git a/lib/solargraph/rbs_map/conversions.rb b/lib/solargraph/rbs_map/conversions.rb index 0aca728f1..cd63dc77e 100644 --- a/lib/solargraph/rbs_map/conversions.rb +++ b/lib/solargraph/rbs_map/conversions.rb @@ -267,6 +267,7 @@ def global_decl_to_pin decl name: name, closure: closure, comments: decl.comment&.string, + type_location: location_decl_to_pin_location(decl.location) ) rooted_tag = ComplexType.parse(other_type_to_tag(decl.type)).force_rooted.rooted_tags pin.docstring.add_tag(YARD::Tags::Tag.new(:type, '', rooted_tag)) @@ -452,7 +453,8 @@ def cvar_to_pin(decl, closure) pin = Solargraph::Pin::ClassVariable.new( name: name, closure: closure, - comments: decl.comment&.string + comments: decl.comment&.string, + type_location: location_decl_to_pin_location(decl.location) ) rooted_tag = ComplexType.parse(other_type_to_tag(decl.type)).force_rooted.rooted_tags pin.docstring.add_tag(YARD::Tags::Tag.new(:type, '', rooted_tag)) @@ -467,7 +469,8 @@ def civar_to_pin(decl, closure) pin = Solargraph::Pin::InstanceVariable.new( name: name, closure: closure, - comments: decl.comment&.string + comments: decl.comment&.string, + type_location: location_decl_to_pin_location(decl.location) ) rooted_tag = ComplexType.parse(other_type_to_tag(decl.type)).force_rooted.rooted_tags pin.docstring.add_tag(YARD::Tags::Tag.new(:type, '', rooted_tag)) diff --git a/lib/solargraph/yard_map/helpers.rb b/lib/solargraph/yard_map/helpers.rb index 71b047df1..7058af9c3 100644 --- a/lib/solargraph/yard_map/helpers.rb +++ b/lib/solargraph/yard_map/helpers.rb @@ -7,10 +7,36 @@ module Helpers # @param spec [Gem::Specification, nil] # @return [Solargraph::Location, nil] def object_location code_object, spec - return nil if spec.nil? || code_object.nil? || code_object.file.nil? || code_object.line.nil? + if spec.nil? || code_object.nil? || code_object.file.nil? || code_object.line.nil? + if code_object.namespace.is_a?(YARD::CodeObjects::NamespaceObject) + # If the code object is a namespace, use the namespace's location + return object_location(code_object.namespace, spec) + end + return Solargraph::Location.new(__FILE__, Solargraph::Range.from_to(__LINE__ - 1, 0, __LINE__ - 1, 0)) + end file = File.join(spec.full_gem_path, code_object.file) Solargraph::Location.new(file, Solargraph::Range.from_to(code_object.line - 1, 0, code_object.line - 1, 0)) end + + # @param spec [Gem::Specification, nil] + def create_closure_namespace_for(code_object, spec) + code_object_for_location = code_object + # code_object.namespace is sometimes a YARD proxy object pointing to a method path ("Object#new") + code_object_for_location = code_object.namespace if code_object.namespace.is_a?(YARD::CodeObjects::NamespaceObject) + namespace_location = object_location(code_object_for_location, spec) + ns_name = code_object.namespace.to_s + if ns_name.empty? + Solargraph::Pin::ROOT_PIN + else + Solargraph::Pin::Namespace.new( + name: ns_name, + closure: Pin::ROOT_PIN, + gates: [code_object.namespace.to_s], + source: :yardoc, + location: namespace_location + ) + end + end end end end diff --git a/lib/solargraph/yard_map/mapper/to_constant.rb b/lib/solargraph/yard_map/mapper/to_constant.rb index 46c585264..6cbefc455 100644 --- a/lib/solargraph/yard_map/mapper/to_constant.rb +++ b/lib/solargraph/yard_map/mapper/to_constant.rb @@ -8,10 +8,7 @@ module ToConstant # @param code_object [YARD::CodeObjects::Base] def self.make code_object, closure = nil, spec = nil - closure ||= Solargraph::Pin::Namespace.new( - name: code_object.namespace.to_s, - gates: [code_object.namespace.to_s] - ) + closure ||= create_closure_namespace_for(code_object, spec) Pin::Constant.new( location: object_location(code_object, spec), closure: closure, diff --git a/lib/solargraph/yard_map/mapper/to_method.rb b/lib/solargraph/yard_map/mapper/to_method.rb index 58c040573..21435ee6a 100644 --- a/lib/solargraph/yard_map/mapper/to_method.rb +++ b/lib/solargraph/yard_map/mapper/to_method.rb @@ -14,12 +14,7 @@ module ToMethod # @param spec [Gem::Specification, nil] # @return [Solargraph::Pin::Method] def self.make code_object, name = nil, scope = nil, visibility = nil, closure = nil, spec = nil - closure ||= Solargraph::Pin::Namespace.new( - name: code_object.namespace.to_s, - gates: [code_object.namespace.to_s], - type: code_object.namespace.is_a?(YARD::CodeObjects::ClassObject) ? :class : :module, - source: :yardoc, - ) + closure ||= create_closure_namespace_for(code_object, spec) location = object_location(code_object, spec) name ||= code_object.name.to_s return_type = ComplexType::SELF if name == 'new' diff --git a/lib/solargraph/yard_map/mapper/to_namespace.rb b/lib/solargraph/yard_map/mapper/to_namespace.rb index 62425d0f1..9539242e3 100644 --- a/lib/solargraph/yard_map/mapper/to_namespace.rb +++ b/lib/solargraph/yard_map/mapper/to_namespace.rb @@ -8,13 +8,10 @@ module ToNamespace # @param code_object [YARD::CodeObjects::NamespaceObject] def self.make code_object, spec, closure = nil - closure ||= Solargraph::Pin::Namespace.new( - name: code_object.namespace.to_s, - closure: Pin::ROOT_PIN, - gates: [code_object.namespace.to_s] - ) + closure ||= create_closure_namespace_for(code_object, spec) + location = object_location(code_object, spec) Pin::Namespace.new( - location: object_location(code_object, spec), + location: location, name: code_object.name.to_s, comments: code_object.docstring ? code_object.docstring.all.to_s : '', type: code_object.is_a?(YARD::CodeObjects::ClassObject) ? :class : :module, From 76f5249daeda88d995a2a19b8fd935d02244679f Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 13 Jun 2025 07:11:25 -0400 Subject: [PATCH 036/561] Add RuboCop workflow --- .github/workflows/rubocop.yml | 39 +++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 .github/workflows/rubocop.yml diff --git a/.github/workflows/rubocop.yml b/.github/workflows/rubocop.yml new file mode 100644 index 000000000..760e6890c --- /dev/null +++ b/.github/workflows/rubocop.yml @@ -0,0 +1,39 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. +# This workflow will download a prebuilt Ruby version, install dependencies and run tests with rspec. +# For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby + +name: RuboCop + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +permissions: + contents: read + +jobs: + test: + + runs-on: ubuntu-latest + strategy: + matrix: + ruby-version: '3.0' + + steps: + - uses: actions/checkout@v3 + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby-version }} + bundler-cache: false + + - name: Install gems + run: bundle install + + - name: Run RuboCop + run: bundle exec rubocop From 1849b925c2317fc1af52bd506f7a1b6b845a8b12 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 13 Jun 2025 08:25:30 -0400 Subject: [PATCH 037/561] Add RuboCop workflow --- .github/workflows/rubocop.yml | 3 +- .rubocop.yml | 36 + .rubocop_todo.yml | 1727 +++++++++++++++++++++++++++++++++ lib/.rubocop.yml | 22 - solargraph.gemspec | 4 +- 5 files changed, 1768 insertions(+), 24 deletions(-) create mode 100644 .rubocop.yml create mode 100644 .rubocop_todo.yml delete mode 100644 lib/.rubocop.yml diff --git a/.github/workflows/rubocop.yml b/.github/workflows/rubocop.yml index 760e6890c..5d55e4a20 100644 --- a/.github/workflows/rubocop.yml +++ b/.github/workflows/rubocop.yml @@ -36,4 +36,5 @@ jobs: run: bundle install - name: Run RuboCop - run: bundle exec rubocop + # Use -c flag so we ignore config files used within rubocop-related fixtures + run: bundle exec rubocop -c .rubocop.yml diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 000000000..198447e83 --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,36 @@ +--- +inherit_from: .rubocop_todo.yml + +# The behavior of RuboCop can be controlled via the .rubocop.yml +# configuration file. It makes it possible to enable/disable +# certain cops (checks) and to alter their behavior if they accept +# any parameters. The file can be placed either in your home +# directory or in some project directory. +# +# RuboCop will start looking for the configuration file in the directory +# where the inspected file is and continue its way up to the root directory. +# +# See https://docs.rubocop.org/rubocop/configuration +AllCops: + NewCops: enable + Exclude: + - "spec/fixtures/rubocop-validation-error/.*" + - "spec/fixtures/rubocop-validation-error/*" + - "spec/fixtures/rubocop-unused-variable-error/.*" + - "spec/fixtures/rubocop-unused-variable-error/*" + - "spec/fixtures/rubocop-subfolder-configuration/.*" + - "spec/fixtures/rubocop-subfolder-configuration/*" + TargetRubyVersion: 3.0 + +Style/MethodDefParentheses: + EnforcedStyle: require_no_parentheses + +Layout/EmptyLineAfterGuardClause: + Enabled: false + +Metrics/MethodLength: + Max: 25 + +plugins: + - rubocop-rspec + - rubocop-rake diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml new file mode 100644 index 000000000..2f0e023a3 --- /dev/null +++ b/.rubocop_todo.yml @@ -0,0 +1,1727 @@ +# This configuration was generated by +# `rubocop --auto-gen-config` +# on 2025-06-13 12:23:43 UTC using RuboCop version 1.76.1. +# The point is for the user to remove these configuration records +# one by one as the offenses are removed from the code base. +# Note that changes in the inspected code, or installation of new +# versions of RuboCop, may require this file to be generated again. + +# Offense count: 20 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: Include. +# Include: **/*.gemspec +Gemspec/AddRuntimeDependency: + Exclude: + - 'solargraph.gemspec' + +# Offense count: 2 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: Severity, Include. +# Include: **/*.gemspec +Gemspec/DeprecatedAttributeAssignment: + Exclude: + - 'solargraph.gemspec' + - 'spec/fixtures/rdoc-lib/rdoc-lib.gemspec' + +# Offense count: 7 +# Configuration parameters: EnforcedStyle, AllowedGems, Include. +# SupportedStyles: Gemfile, gems.rb, gemspec +# Include: **/*.gemspec, **/Gemfile, **/gems.rb +Gemspec/DevelopmentDependencies: + Exclude: + - 'solargraph.gemspec' + +# Offense count: 3 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: Severity, Include. +# Include: **/*.gemspec +Gemspec/RequireMFA: + Exclude: + - 'solargraph.gemspec' + - 'spec/fixtures/rdoc-lib/rdoc-lib.gemspec' + - 'spec/fixtures/rubocop-custom-version/specifications/rubocop-0.0.0.gemspec' + +# Offense count: 3 +# Configuration parameters: Severity, Include. +# Include: **/*.gemspec +Gemspec/RequiredRubyVersion: + Exclude: + - 'spec/fixtures/rdoc-lib/rdoc-lib.gemspec' + - 'spec/fixtures/rubocop-custom-version/specifications/rubocop-0.0.0.gemspec' + - 'spec/fixtures/vendored/vendor/do_not_use.gemspec' + +# Offense count: 5 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle, IndentationWidth. +# SupportedStyles: with_first_argument, with_fixed_indentation +Layout/ArgumentAlignment: + Exclude: + - 'lib/solargraph/pin/callable.rb' + - 'spec/source/source_chainer_spec.rb' + +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyleAlignWith. +# SupportedStylesAlignWith: either, start_of_block, start_of_line +Layout/BlockAlignment: + Exclude: + - 'spec/source_map/mapper_spec.rb' + +# Offense count: 8 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: AllowForAlignment. +Layout/CommentIndentation: + Exclude: + - 'lib/solargraph/api_map.rb' + - 'lib/solargraph/language_server/host.rb' + - 'lib/solargraph/parser/parser_gem/node_methods.rb' + - 'lib/solargraph/source_map/mapper.rb' + +# Offense count: 22 +# This cop supports safe autocorrection (--autocorrect). +Layout/ElseAlignment: + Enabled: false + +# Offense count: 2 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EmptyLineBetweenMethodDefs, EmptyLineBetweenClassDefs, EmptyLineBetweenModuleDefs, DefLikeMacros, AllowAdjacentOneLineDefs, NumberOfEmptyLines. +Layout/EmptyLineBetweenDefs: + Exclude: + - 'lib/solargraph/language_server/message/initialize.rb' + - 'lib/solargraph/pin/delegated_method.rb' + +# Offense count: 8 +# This cop supports safe autocorrection (--autocorrect). +Layout/EmptyLines: + Exclude: + - 'lib/solargraph/bench.rb' + - 'lib/solargraph/complex_type/unique_type.rb' + - 'lib/solargraph/convention.rb' + - 'lib/solargraph/language_server/message/extended/check_gem_version.rb' + - 'lib/solargraph/language_server/message/initialize.rb' + - 'lib/solargraph/pin/delegated_method.rb' + - 'spec/pin/parameter_spec.rb' + - 'spec/pin/symbol_spec.rb' + +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle. +# SupportedStyles: empty_lines, empty_lines_except_namespace, empty_lines_special, no_empty_lines +Layout/EmptyLinesAroundModuleBody: + Exclude: + - 'lib/solargraph/api_map/source_to_yard.rb' + +# Offense count: 22 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyleAlignWith, Severity. +# SupportedStylesAlignWith: keyword, variable, start_of_line +Layout/EndAlignment: + Enabled: false + +# Offense count: 4 +# Configuration parameters: EnforcedStyle. +# SupportedStyles: native, lf, crlf +Layout/EndOfLine: + Exclude: + - 'Gemfile' + - 'Rakefile' + - 'lib/solargraph/source/encoding_fixes.rb' + - 'solargraph.gemspec' + +# Offense count: 3 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: AllowForAlignment, AllowBeforeTrailingComments, ForceEqualSignAlignment. +Layout/ExtraSpacing: + Exclude: + - 'lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb' + - 'lib/solargraph/rbs_map/conversions.rb' + - 'lib/solargraph/type_checker.rb' + +# Offense count: 2 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle, IndentationWidth. +# SupportedStyles: consistent, consistent_relative_to_receiver, special_for_inner_method_call, special_for_inner_method_call_in_parentheses +Layout/FirstArgumentIndentation: + Exclude: + - 'lib/solargraph/parser/parser_gem/node_processors/args_node.rb' + - 'spec/source/source_chainer_spec.rb' + +# Offense count: 18 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle, IndentationWidth. +# SupportedStyles: special_inside_parentheses, consistent, align_brackets +Layout/FirstArrayElementIndentation: + Exclude: + - 'lib/solargraph/source.rb' + - 'spec/diagnostics/update_errors_spec.rb' + - 'spec/source/cursor_spec.rb' + - 'spec/source/source_chainer_spec.rb' + - 'spec/source_spec.rb' + +# Offense count: 63 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle, IndentationWidth. +# SupportedStyles: special_inside_parentheses, consistent, align_braces +Layout/FirstHashElementIndentation: + Exclude: + - 'lib/solargraph/language_server/message/extended/check_gem_version.rb' + - 'lib/solargraph/language_server/message/extended/document_gems.rb' + - 'lib/solargraph/language_server/message/text_document/completion.rb' + - 'lib/solargraph/language_server/message/text_document/rename.rb' + - 'lib/solargraph/language_server/message/text_document/signature_help.rb' + - 'spec/language_server/host_spec.rb' + - 'spec/language_server/message/completion_item/resolve_spec.rb' + - 'spec/language_server/message/initialize_spec.rb' + - 'spec/language_server/message/text_document/definition_spec.rb' + - 'spec/language_server/message/text_document/formatting_spec.rb' + - 'spec/language_server/message/text_document/hover_spec.rb' + - 'spec/language_server/message/text_document/rename_spec.rb' + - 'spec/language_server/message/text_document/type_definition_spec.rb' + - 'spec/language_server/message/workspace/did_change_watched_files_spec.rb' + +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: AllowMultipleStyles, EnforcedHashRocketStyle, EnforcedColonStyle, EnforcedLastArgumentHashStyle. +# SupportedHashRocketStyles: key, separator, table +# SupportedColonStyles: key, separator, table +# SupportedLastArgumentHashStyles: always_inspect, always_ignore, ignore_implicit, ignore_explicit +Layout/HashAlignment: + Exclude: + - 'lib/solargraph/workspace/config.rb' + +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +Layout/HeredocIndentation: + Exclude: + - 'spec/yard_map/mapper/to_method_spec.rb' + +# Offense count: 24 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: Width, AllowedPatterns. +Layout/IndentationWidth: + Enabled: false + +# Offense count: 5 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: AllowDoxygenCommentStyle, AllowGemfileRubyComment, AllowRBSInlineAnnotation, AllowSteepAnnotation. +Layout/LeadingCommentSpace: + Exclude: + - 'lib/solargraph/complex_type.rb' + - 'lib/solargraph/rbs_map/conversions.rb' + - 'lib/solargraph/source/chain/call.rb' + - 'lib/solargraph/source_map/clip.rb' + +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle. +# SupportedStyles: space, no_space +Layout/LineContinuationSpacing: + Exclude: + - 'lib/solargraph/diagnostics/rubocop_helpers.rb' + +# Offense count: 3 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle. +# SupportedStyles: symmetrical, new_line, same_line +Layout/MultilineMethodCallBraceLayout: + Exclude: + - 'lib/solargraph/parser/parser_gem/node_processors/send_node.rb' + - 'spec/source/source_chainer_spec.rb' + +# Offense count: 12 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle, IndentationWidth. +# SupportedStyles: aligned, indented, indented_relative_to_receiver +Layout/MultilineMethodCallIndentation: + Exclude: + - 'lib/solargraph/api_map.rb' + - 'lib/solargraph/diagnostics/type_check.rb' + - 'lib/solargraph/language_server/message/completion_item/resolve.rb' + - 'lib/solargraph/language_server/message/text_document/hover.rb' + - 'lib/solargraph/library.rb' + - 'lib/solargraph/pin/search.rb' + - 'lib/solargraph/type_checker.rb' + +# Offense count: 3 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle, IndentationWidth. +# SupportedStyles: aligned, indented +Layout/MultilineOperationIndentation: + Exclude: + - 'lib/solargraph/language_server/host/dispatch.rb' + - 'lib/solargraph/source.rb' + +# Offense count: 6 +# This cop supports safe autocorrection (--autocorrect). +Layout/SpaceAfterComma: + Exclude: + - 'spec/source/cursor_spec.rb' + +# Offense count: 27 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: AllowForAlignment, EnforcedStyleForExponentOperator, EnforcedStyleForRationalLiterals. +# SupportedStylesForExponentOperator: space, no_space +# SupportedStylesForRationalLiterals: space, no_space +Layout/SpaceAroundOperators: + Exclude: + - 'lib/solargraph/library.rb' + - 'lib/solargraph/parser/parser_gem/node_methods.rb' + - 'lib/solargraph/source.rb' + - 'lib/solargraph/source/change.rb' + - 'lib/solargraph/source/cursor.rb' + - 'lib/solargraph/source/source_chainer.rb' + - 'lib/solargraph/source_map/clip.rb' + - 'lib/solargraph/workspace/config.rb' + - 'spec/library_spec.rb' + - 'spec/yard_map/mapper_spec.rb' + +# Offense count: 112 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces. +# SupportedStyles: space, no_space +# SupportedStylesForEmptyBraces: space, no_space +Layout/SpaceBeforeBlockBraces: + Enabled: false + +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +Layout/SpaceBeforeComma: + Exclude: + - 'spec/source/cursor_spec.rb' + +# Offense count: 189 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces, SpaceBeforeBlockParameters. +# SupportedStyles: space, no_space +# SupportedStylesForEmptyBraces: space, no_space +Layout/SpaceInsideBlockBraces: + Enabled: false + +# Offense count: 26 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces. +# SupportedStyles: space, no_space, compact +# SupportedStylesForEmptyBraces: space, no_space +Layout/SpaceInsideHashLiteralBraces: + Exclude: + - 'lib/solargraph/language_server/message/extended/document.rb' + - 'lib/solargraph/language_server/message/extended/search.rb' + - 'lib/solargraph/language_server/message/initialize.rb' + - 'lib/solargraph/workspace/config.rb' + - 'spec/complex_type_spec.rb' + - 'spec/language_server/host/message_worker_spec.rb' + - 'spec/language_server/message/extended/check_gem_version_spec.rb' + +# Offense count: 2 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle. +# SupportedStyles: space, compact, no_space +Layout/SpaceInsideParens: + Exclude: + - 'lib/solargraph/pin/namespace.rb' + - 'lib/solargraph/source_map.rb' + +# Offense count: 4 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: AllowInHeredoc. +Layout/TrailingWhitespace: + Exclude: + - 'lib/solargraph/language_server/message/client/register_capability.rb' + - 'spec/api_map/config_spec.rb' + - 'spec/convention_spec.rb' + - 'spec/source_map/clip_spec.rb' + +# Offense count: 2 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: AllowedMethods, AllowedPatterns. +Lint/AmbiguousBlockAssociation: + Exclude: + - 'lib/solargraph/api_map.rb' + - 'lib/solargraph/language_server/host.rb' + +# Offense count: 15 +# This cop supports safe autocorrection (--autocorrect). +Lint/AmbiguousOperator: + Exclude: + - 'lib/solargraph/api_map.rb' + - 'lib/solargraph/complex_type.rb' + - 'lib/solargraph/language_server/message/workspace/did_change_watched_files.rb' + - 'lib/solargraph/parser/parser_gem/class_methods.rb' + - 'lib/solargraph/pin/constant.rb' + - 'lib/solargraph/pin/method.rb' + +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +Lint/AmbiguousOperatorPrecedence: + Exclude: + - 'lib/solargraph/source.rb' + +# Offense count: 1 +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: AllowSafeAssignment. +Lint/AssignmentInCondition: + Exclude: + - 'lib/solargraph/library.rb' + +# Offense count: 3 +Lint/BinaryOperatorWithIdenticalOperands: + Exclude: + - 'lib/solargraph/api_map/source_to_yard.rb' + +# Offense count: 5 +# This cop supports unsafe autocorrection (--autocorrect-all). +Lint/BooleanSymbol: + Exclude: + - 'lib/solargraph/convention/struct_definition/struct_definition_node.rb' + - 'lib/solargraph/parser/parser_gem/node_methods.rb' + - 'lib/solargraph/source/chain/literal.rb' + +# Offense count: 1 +# Configuration parameters: AllowedMethods. +# AllowedMethods: enums +Lint/ConstantDefinitionInBlock: + Exclude: + - 'spec/complex_type_spec.rb' + +# Offense count: 6 +# Configuration parameters: IgnoreLiteralBranches, IgnoreConstantBranches, IgnoreDuplicateElseBranch. +Lint/DuplicateBranch: + Exclude: + - 'lib/solargraph/complex_type/type_methods.rb' + - 'lib/solargraph/parser/parser_gem/node_chainer.rb' + - 'lib/solargraph/rbs_map/conversions.rb' + +# Offense count: 5 +Lint/DuplicateMethods: + Exclude: + - 'lib/solargraph/complex_type.rb' + - 'lib/solargraph/location.rb' + - 'lib/solargraph/pin/method.rb' + - 'lib/solargraph/source/chain/link.rb' + +# Offense count: 1 +# Configuration parameters: AllowComments, AllowEmptyLambdas. +Lint/EmptyBlock: + Exclude: + - 'spec/convention_spec.rb' + +# Offense count: 5 +# Configuration parameters: AllowComments. +Lint/EmptyClass: + Exclude: + - 'spec/fixtures/workspace-with-gemfile/lib/other.rb' + - 'spec/fixtures/workspace/lib/other.rb' + - 'spec/fixtures/workspace/lib/something.rb' + - 'spec/fixtures/workspace_folders/folder1/app.rb' + - 'spec/fixtures/workspace_folders/folder2/app.rb' + +# Offense count: 2 +# Configuration parameters: AllowComments. +Lint/EmptyFile: + Exclude: + - 'spec/fixtures/rubocop-subfolder-configuration/folder1/folder2/test.rb' + - 'spec/fixtures/vendored/vendor/do_not_use.gemspec' + +# Offense count: 6 +# This cop supports unsafe autocorrection (--autocorrect-all). +Lint/InterpolationCheck: + Exclude: + - 'spec/complex_type_spec.rb' + - 'spec/parser/node_methods_spec.rb' + - 'spec/source/chain_spec.rb' + - 'spec/source/cursor_spec.rb' + +# Offense count: 5 +# Configuration parameters: AllowedParentClasses. +Lint/MissingSuper: + Exclude: + - 'lib/solargraph/source/chain/call.rb' + - 'lib/solargraph/source/chain/constant.rb' + - 'lib/solargraph/source/chain/if.rb' + - 'lib/solargraph/source/chain/literal.rb' + - 'lib/solargraph/source/chain/or.rb' + +# Offense count: 4 +# This cop supports safe autocorrection (--autocorrect). +Lint/ParenthesesAsGroupedExpression: + Exclude: + - 'lib/solargraph/parser/parser_gem/node_chainer.rb' + - 'spec/complex_type_spec.rb' + - 'spec/language_server/host_spec.rb' + - 'spec/source_map/clip_spec.rb' + +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +Lint/RedundantRequireStatement: + Exclude: + - 'spec/language_server/protocol_spec.rb' + +# Offense count: 2 +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: AllowedMethods. +# AllowedMethods: instance_of?, kind_of?, is_a?, eql?, respond_to?, equal? +Lint/RedundantSafeNavigation: + Exclude: + - 'lib/solargraph/api_map/source_to_yard.rb' + - 'lib/solargraph/rbs_map.rb' + +# Offense count: 6 +# This cop supports safe autocorrection (--autocorrect). +Lint/RedundantStringCoercion: + Exclude: + - 'lib/solargraph/parser/parser_gem/node_chainer.rb' + - 'lib/solargraph/pin/conversions.rb' + - 'lib/solargraph/pin/method.rb' + - 'lib/solargraph/pin/namespace.rb' + - 'lib/solargraph/rbs_map/conversions.rb' + +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +Lint/RedundantWithIndex: + Exclude: + - 'lib/solargraph/language_server/message/completion_item/resolve.rb' + +# Offense count: 1 +# Configuration parameters: AllowComments, AllowNil. +Lint/SuppressedException: + Exclude: + - 'Rakefile' + +# Offense count: 1 +# Configuration parameters: AllowKeywordBlockArguments. +Lint/UnderscorePrefixedVariableName: + Exclude: + - 'lib/solargraph/library.rb' + +# Offense count: 2 +# Configuration parameters: Methods. +Lint/UnexpectedBlockArity: + Exclude: + - 'lib/solargraph/language_server/message/completion_item/resolve.rb' + - 'lib/solargraph/type_checker.rb' + +# Offense count: 4 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: AutoCorrect, IgnoreEmptyBlocks, AllowUnusedKeywordArguments. +Lint/UnusedBlockArgument: + Exclude: + - 'lib/solargraph/language_server/message/workspace/did_change_workspace_folders.rb' + - 'lib/solargraph/logging.rb' + - 'spec/language_server/transport/data_reader_spec.rb' + +# Offense count: 35 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: AutoCorrect, AllowUnusedKeywordArguments, IgnoreEmptyMethods, IgnoreNotImplementedMethods, NotImplementedExceptions. +# NotImplementedExceptions: NotImplementedError +Lint/UnusedMethodArgument: + Enabled: false + +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: AutoCorrect, ContextCreatingMethods, MethodCreatingMethods. +Lint/UselessAccessModifier: + Exclude: + - 'lib/solargraph/api_map.rb' + +# Offense count: 32 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: AutoCorrect. +Lint/UselessAssignment: + Enabled: false + +# Offense count: 1 +Lint/UselessConstantScoping: + Exclude: + - 'lib/solargraph/rbs_map/conversions.rb' + +# Offense count: 1 +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: AutoCorrect. +Lint/UselessMethodDefinition: + Exclude: + - 'lib/solargraph/pin/signature.rb' + +# Offense count: 209 +# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes. +Metrics/AbcSize: + Max: 200 + +# Offense count: 11 +# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns, inherit_mode. +# AllowedMethods: refine +Metrics/BlockLength: + Max: 54 + +# Offense count: 13 +# Configuration parameters: CountBlocks, CountModifierForms. +Metrics/BlockNesting: + Max: 6 + +# Offense count: 27 +# Configuration parameters: CountComments, CountAsOne. +Metrics/ClassLength: + Max: 541 + +# Offense count: 97 +# Configuration parameters: AllowedMethods, AllowedPatterns. +Metrics/CyclomaticComplexity: + Max: 42 + +# Offense count: 47 +# Configuration parameters: CountComments, Max, CountAsOne, AllowedMethods, AllowedPatterns. +Metrics/MethodLength: + Enabled: false + +# Offense count: 4 +# Configuration parameters: CountComments, CountAsOne. +Metrics/ModuleLength: + Max: 458 + +# Offense count: 13 +# Configuration parameters: CountKeywordArgs. +Metrics/ParameterLists: + Max: 9 + MaxOptionalParameters: 5 + +# Offense count: 82 +# Configuration parameters: AllowedMethods, AllowedPatterns. +Metrics/PerceivedComplexity: + Max: 47 + +# Offense count: 5 +Naming/AccessorMethodName: + Exclude: + - 'lib/solargraph/api_map.rb' + - 'lib/solargraph/api_map/store.rb' + - 'lib/solargraph/language_server/message/base.rb' + +# Offense count: 1 +# Configuration parameters: AsciiConstants. +Naming/AsciiIdentifiers: + Exclude: + - 'spec/fixtures/unicode.rb' + +# Offense count: 1 +# Configuration parameters: ForbiddenDelimiters. +# ForbiddenDelimiters: (?i-mx:(^|\s)(EO[A-Z]{1}|END)(\s|$)) +Naming/HeredocDelimiterNaming: + Exclude: + - 'spec/yard_map/mapper/to_method_spec.rb' + +# Offense count: 5 +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: EnforcedStyleForLeadingUnderscores. +# SupportedStylesForLeadingUnderscores: disallowed, required, optional +Naming/MemoizedInstanceVariableName: + Exclude: + - 'lib/solargraph/complex_type/type_methods.rb' + - 'lib/solargraph/convention/gemfile.rb' + - 'lib/solargraph/convention/gemspec.rb' + - 'lib/solargraph/convention/rakefile.rb' + - 'lib/solargraph/workspace.rb' + +# Offense count: 16 +# Configuration parameters: MinNameLength, AllowNamesEndingInNumbers, AllowedNames, ForbiddenNames. +# AllowedNames: as, at, by, cc, db, id, if, in, io, ip, of, on, os, pp, to +Naming/MethodParameterName: + Exclude: + - 'lib/solargraph/parser/parser_gem/node_chainer.rb' + - 'lib/solargraph/pin/base.rb' + - 'lib/solargraph/range.rb' + - 'lib/solargraph/source.rb' + - 'lib/solargraph/yard_map/mapper/to_method.rb' + - 'lib/solargraph/yard_map/to_method.rb' + +# Offense count: 17 +# Configuration parameters: Mode, AllowedMethods. +# AllowedMethods: call +Naming/PredicateMethod: + Exclude: + - 'lib/solargraph/api_map/store.rb' + - 'lib/solargraph/doc_map.rb' + - 'lib/solargraph/language_server/progress.rb' + - 'lib/solargraph/library.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/send_node.rb' + - 'lib/solargraph/pin/base.rb' + - 'lib/solargraph/pin/base_variable.rb' + - 'lib/solargraph/pin/local_variable.rb' + - 'lib/solargraph/pin/method.rb' + - 'lib/solargraph/pin/parameter.rb' + - 'lib/solargraph/source_map.rb' + - 'lib/solargraph/workspace.rb' + +# Offense count: 4 +# Configuration parameters: NamePrefix, ForbiddenPrefixes, AllowedMethods, MethodDefinitionMacros, UseSorbetSigs. +# NamePrefix: is_, has_, have_, does_ +# ForbiddenPrefixes: is_, has_, have_, does_ +# AllowedMethods: is_a? +# MethodDefinitionMacros: define_method, define_singleton_method +Naming/PredicatePrefix: + Exclude: + - 'spec/**/*' + - 'lib/solargraph/api_map.rb' + - 'lib/solargraph/language_server/host.rb' + - 'lib/solargraph/parser/parser_gem/class_methods.rb' + - 'lib/solargraph/workspace.rb' + +# Offense count: 1 +# Configuration parameters: EnforcedStyle, AllowedIdentifiers, AllowedPatterns, ForbiddenIdentifiers, ForbiddenPatterns. +# SupportedStyles: snake_case, camelCase +Naming/VariableName: + Exclude: + - 'spec/fixtures/unicode.rb' + +# Offense count: 3 +RSpec/Be: + Exclude: + - 'spec/rbs_map/stdlib_map_spec.rb' + - 'spec/rbs_map_spec.rb' + - 'spec/source/source_chainer_spec.rb' + +# Offense count: 5 +# This cop supports unsafe autocorrection (--autocorrect-all). +RSpec/BeEq: + Exclude: + - 'spec/complex_type_spec.rb' + - 'spec/pin/base_spec.rb' + - 'spec/pin/method_spec.rb' + +# Offense count: 7 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle. +# SupportedStyles: be, be_nil +RSpec/BeNil: + Exclude: + - 'spec/api_map/source_to_yard_spec.rb' + - 'spec/language_server/host_spec.rb' + +# Offense count: 5 +RSpec/BeforeAfterAll: + Exclude: + - '**/spec/spec_helper.rb' + - '**/spec/rails_helper.rb' + - '**/spec/support/**/*.rb' + - 'spec/api_map_spec.rb' + - 'spec/doc_map_spec.rb' + - 'spec/language_server/host/dispatch_spec.rb' + - 'spec/language_server/protocol_spec.rb' + +# Offense count: 14 +# Configuration parameters: Prefixes, AllowedPatterns. +# Prefixes: when, with, without +RSpec/ContextWording: + Exclude: + - 'spec/complex_type_spec.rb' + - 'spec/library_spec.rb' + - 'spec/pin/method_spec.rb' + - 'spec/pin/parameter_spec.rb' + - 'spec/pin/symbol_spec.rb' + - 'spec/type_checker/levels/normal_spec.rb' + - 'spec/type_checker/levels/strict_spec.rb' + - 'spec/type_checker/levels/strong_spec.rb' + - 'spec/type_checker/levels/typed_spec.rb' + +# Offense count: 1 +# Configuration parameters: IgnoredMetadata. +RSpec/DescribeClass: + Exclude: + - '**/spec/features/**/*' + - '**/spec/requests/**/*' + - '**/spec/routing/**/*' + - '**/spec/system/**/*' + - '**/spec/views/**/*' + - 'spec/source_map/node_processor_spec.rb' + +# Offense count: 493 +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: SkipBlocks, EnforcedStyle, OnlyStaticConstants. +# SupportedStyles: described_class, explicit +RSpec/DescribedClass: + Enabled: false + +# Offense count: 1 +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: AutoCorrect. +RSpec/EmptyExampleGroup: + Exclude: + - 'spec/convention_spec.rb' + +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +RSpec/EmptyLineAfterFinalLet: + Exclude: + - 'spec/workspace/config_spec.rb' + +# Offense count: 914 +# Configuration parameters: CountAsOne. +RSpec/ExampleLength: + Max: 59 + +# Offense count: 4 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: CustomTransform, IgnoredWords, DisallowedExamples. +# DisallowedExamples: works +RSpec/ExampleWording: + Exclude: + - 'spec/pin/base_spec.rb' + - 'spec/pin/method_spec.rb' + +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +RSpec/ExcessiveDocstringSpacing: + Exclude: + - 'spec/source/chain/call_spec.rb' + +# Offense count: 5 +# This cop supports safe autocorrection (--autocorrect). +RSpec/ExpectActual: + Exclude: + - '**/spec/routing/**/*' + - 'spec/rbs_map/stdlib_map_spec.rb' + - 'spec/source_map/mapper_spec.rb' + +# Offense count: 13 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle. +# SupportedStyles: implicit, each, example +RSpec/HookArgument: + Exclude: + - 'spec/api_map/config_spec.rb' + - 'spec/diagnostics/require_not_found_spec.rb' + - 'spec/language_server/host/dispatch_spec.rb' + - 'spec/language_server/host_spec.rb' + - 'spec/language_server/message/extended/check_gem_version_spec.rb' + - 'spec/language_server/protocol_spec.rb' + - 'spec/workspace/config_spec.rb' + - 'spec/workspace_spec.rb' + +# Offense count: 246 +# Configuration parameters: AssignmentOnly. +RSpec/InstanceVariable: + Exclude: + - 'spec/api_map/config_spec.rb' + - 'spec/api_map_spec.rb' + - 'spec/diagnostics/require_not_found_spec.rb' + - 'spec/language_server/host/dispatch_spec.rb' + - 'spec/language_server/host_spec.rb' + - 'spec/language_server/protocol_spec.rb' + +# Offense count: 1 +RSpec/LeakyConstantDeclaration: + Exclude: + - 'spec/complex_type_spec.rb' + +# Offense count: 3 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: AutoCorrect. +RSpec/LetBeforeExamples: + Exclude: + - 'spec/complex_type_spec.rb' + +# Offense count: 3 +# Configuration parameters: . +# SupportedStyles: have_received, receive +RSpec/MessageSpies: + EnforcedStyle: receive + +# Offense count: 2 +RSpec/MissingExampleGroupArgument: + Exclude: + - 'spec/diagnostics/rubocop_helpers_spec.rb' + +# Offense count: 466 +RSpec/MultipleExpectations: + Max: 14 + +# Offense count: 8 +# Configuration parameters: AllowedPatterns. +# AllowedPatterns: ^expect_, ^assert_ +RSpec/NoExpectationExample: + Exclude: + - 'spec/language_server/protocol_spec.rb' + - 'spec/parser/node_methods_spec.rb' + - 'spec/pin/block_spec.rb' + - 'spec/pin/method_spec.rb' + - 'spec/source/chain/call_spec.rb' + - 'spec/type_checker/checks_spec.rb' + - 'spec/type_checker/levels/typed_spec.rb' + +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle. +# SupportedStyles: not_to, to_not +RSpec/NotToNot: + Exclude: + - 'spec/rbs_map/core_map_spec.rb' + +# Offense count: 33 +RSpec/PendingWithoutReason: + Exclude: + - 'spec/api_map_spec.rb' + - 'spec/complex_type_spec.rb' + - 'spec/parser/node_chainer_spec.rb' + - 'spec/parser/node_methods_spec.rb' + - 'spec/pin/method_spec.rb' + - 'spec/rbs_map/core_map_spec.rb' + - 'spec/source/chain/call_spec.rb' + - 'spec/source/chain_spec.rb' + - 'spec/source_map/clip_spec.rb' + - 'spec/type_checker/levels/strict_spec.rb' + - 'spec/yard_map/mapper/to_method_spec.rb' + +# Offense count: 3 +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: Strict, EnforcedStyle, AllowedExplicitMatchers. +# SupportedStyles: inflected, explicit +RSpec/PredicateMatcher: + Exclude: + - 'spec/language_server/message/workspace/did_change_configuration_spec.rb' + - 'spec/source_spec.rb' + +# Offense count: 3 +# This cop supports unsafe autocorrection (--autocorrect-all). +RSpec/ReceiveMessages: + Exclude: + - 'spec/language_server/host_spec.rb' + +# Offense count: 1 +RSpec/RemoveConst: + Exclude: + - 'spec/diagnostics/rubocop_helpers_spec.rb' + +# Offense count: 46 +RSpec/RepeatedDescription: + Exclude: + - 'spec/api_map_spec.rb' + - 'spec/language_server/protocol_spec.rb' + - 'spec/parser/node_methods_spec.rb' + - 'spec/source/chain/call_spec.rb' + - 'spec/source/source_chainer_spec.rb' + - 'spec/source_map/clip_spec.rb' + - 'spec/source_map/mapper_spec.rb' + - 'spec/type_checker/levels/normal_spec.rb' + - 'spec/type_checker/levels/strict_spec.rb' + +# Offense count: 28 +RSpec/RepeatedExample: + Exclude: + - 'spec/api_map_spec.rb' + - 'spec/parser/node_methods_spec.rb' + - 'spec/source/cursor_spec.rb' + - 'spec/source_map/clip_spec.rb' + - 'spec/type_checker/levels/strict_spec.rb' + +# Offense count: 2 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: AutoCorrect. +RSpec/ScatteredLet: + Exclude: + - 'spec/complex_type_spec.rb' + +# Offense count: 87 +# Configuration parameters: Include, CustomTransform, IgnoreMethods, IgnoreMetadata. +# Include: **/*_spec.rb +RSpec/SpecFilePathFormat: + Enabled: false + +# Offense count: 1 +RSpec/StubbedMock: + Exclude: + - 'spec/language_server/host/message_worker_spec.rb' + +# Offense count: 24 +# Configuration parameters: IgnoreNameless, IgnoreSymbolicNames. +RSpec/VerifiedDoubles: + Exclude: + - 'spec/complex_type_spec.rb' + - 'spec/language_server/host/diagnoser_spec.rb' + - 'spec/language_server/host/message_worker_spec.rb' + - 'spec/language_server/host_spec.rb' + - 'spec/language_server/message/completion_item/resolve_spec.rb' + - 'spec/language_server/message/extended/check_gem_version_spec.rb' + - 'spec/language_server/message/text_document/formatting_spec.rb' + - 'spec/language_server/message/workspace/did_change_watched_files_spec.rb' + - 'spec/language_server/protocol_spec.rb' + - 'spec/source/chain/class_variable_spec.rb' + - 'spec/source/cursor_spec.rb' + - 'spec/source/source_chainer_spec.rb' + - 'spec/workspace_spec.rb' + +# Offense count: 1 +Security/MarshalLoad: + Exclude: + - 'lib/solargraph/cache.rb' + +# Offense count: 12 +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: EnforcedStyle, AllowModifiersOnSymbols, AllowModifiersOnAttrs, AllowModifiersOnAliasMethod. +# SupportedStyles: inline, group +Style/AccessModifierDeclarations: + Exclude: + - 'lib/solargraph/api_map.rb' + - 'lib/solargraph/complex_type.rb' + - 'lib/solargraph/complex_type/unique_type.rb' + - 'lib/solargraph/location.rb' + - 'lib/solargraph/position.rb' + - 'lib/solargraph/range.rb' + - 'lib/solargraph/source/chain.rb' + - 'lib/solargraph/source/chain/call.rb' + - 'lib/solargraph/source/chain/hash.rb' + - 'lib/solargraph/source/chain/if.rb' + - 'lib/solargraph/source/chain/link.rb' + - 'lib/solargraph/source/chain/literal.rb' + +# Offense count: 9 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle. +# SupportedStyles: separated, grouped +Style/AccessorGrouping: + Exclude: + - 'lib/solargraph/parser/flow_sensitive_typing.rb' + - 'lib/solargraph/pin/callable.rb' + - 'lib/solargraph/pin/method.rb' + +# Offense count: 39 +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: EnforcedStyle. +# SupportedStyles: always, conditionals +Style/AndOr: + Exclude: + - 'lib/solargraph/api_map/source_to_yard.rb' + - 'lib/solargraph/complex_type/unique_type.rb' + - 'lib/solargraph/language_server/message/base.rb' + - 'lib/solargraph/page.rb' + - 'lib/solargraph/parser/parser_gem/node_methods.rb' + - 'lib/solargraph/pin/method.rb' + - 'lib/solargraph/pin/parameter.rb' + - 'lib/solargraph/position.rb' + - 'lib/solargraph/source/change.rb' + - 'lib/solargraph/source/cursor.rb' + - 'lib/solargraph/source/source_chainer.rb' + - 'lib/solargraph/source/updater.rb' + - 'lib/solargraph/workspace.rb' + - 'lib/solargraph/yard_map/mapper.rb' + +# Offense count: 4 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: AllowOnlyRestArgument, UseAnonymousForwarding, RedundantRestArgumentNames, RedundantKeywordRestArgumentNames, RedundantBlockArgumentNames. +# RedundantRestArgumentNames: args, arguments +# RedundantKeywordRestArgumentNames: kwargs, options, opts +# RedundantBlockArgumentNames: blk, block, proc +Style/ArgumentsForwarding: + Exclude: + - 'lib/solargraph/api_map.rb' + - 'lib/solargraph/complex_type.rb' + +# Offense count: 61 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle, ProceduralMethods, FunctionalMethods, AllowedMethods, AllowedPatterns, AllowBracesOnProceduralOneLiners, BracesRequiredMethods. +# SupportedStyles: line_count_based, semantic, braces_for_chaining, always_braces +# ProceduralMethods: benchmark, bm, bmbm, create, each_with_object, measure, new, realtime, tap, with_object +# FunctionalMethods: let, let!, subject, watch +# AllowedMethods: lambda, proc, it +Style/BlockDelimiters: + Enabled: false + +# Offense count: 7 +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: MinBranchesCount. +Style/CaseLikeIf: + Exclude: + - 'lib/solargraph/language_server/message/workspace/did_change_watched_files.rb' + - 'lib/solargraph/parser/parser_gem/node_methods.rb' + - 'lib/solargraph/pin/parameter.rb' + - 'lib/solargraph/rbs_map/conversions.rb' + - 'lib/solargraph/source/source_chainer.rb' + - 'lib/solargraph/yard_map/mapper.rb' + +# Offense count: 11 +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: EnforcedStyle, EnforcedStyleForClasses, EnforcedStyleForModules. +# SupportedStyles: nested, compact +# SupportedStylesForClasses: ~, nested, compact +# SupportedStylesForModules: ~, nested, compact +Style/ClassAndModuleChildren: + Exclude: + - 'lib/solargraph/language_server/message/text_document/definition.rb' + - 'lib/solargraph/language_server/message/text_document/document_highlight.rb' + - 'lib/solargraph/language_server/message/text_document/document_symbol.rb' + - 'lib/solargraph/language_server/message/text_document/prepare_rename.rb' + - 'lib/solargraph/language_server/message/text_document/references.rb' + - 'lib/solargraph/language_server/message/text_document/rename.rb' + - 'lib/solargraph/language_server/message/text_document/type_definition.rb' + - 'lib/solargraph/language_server/message/workspace/did_change_configuration.rb' + - 'lib/solargraph/language_server/message/workspace/did_change_watched_files.rb' + - 'lib/solargraph/language_server/message/workspace/did_change_workspace_folders.rb' + - 'lib/solargraph/language_server/message/workspace/workspace_symbol.rb' + +# Offense count: 2 +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: AllowedMethods, AllowedPatterns. +# AllowedMethods: ==, equal?, eql? +Style/ClassEqualityComparison: + Exclude: + - 'lib/solargraph/pin/base.rb' + +# Offense count: 13 +Style/ClassVars: + Exclude: + - 'lib/solargraph/api_map.rb' + - 'lib/solargraph/convention.rb' + - 'lib/solargraph/logging.rb' + - 'lib/solargraph/parser/node_processor.rb' + - 'lib/solargraph/rbs_map.rb' + - 'lib/solargraph/source/chain.rb' + +# Offense count: 2 +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: AllowedReceivers. +Style/CollectionCompact: + Exclude: + - 'lib/solargraph/pin/constant.rb' + +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +Style/ColonMethodCall: + Exclude: + - 'spec/type_checker_spec.rb' + +# Offense count: 1 +# This cop supports unsafe autocorrection (--autocorrect-all). +Style/CombinableLoops: + Exclude: + - 'lib/solargraph/pin/parameter.rb' + +# Offense count: 1 +# This cop supports unsafe autocorrection (--autocorrect-all). +Style/ConcatArrayLiterals: + Exclude: + - 'lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb' + +# Offense count: 4 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle, SingleLineConditionsOnly, IncludeTernaryExpressions. +# SupportedStyles: assign_to_condition, assign_inside_condition +Style/ConditionalAssignment: + Exclude: + - 'lib/solargraph/api_map/source_to_yard.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/defs_node.rb' + - 'lib/solargraph/source/chain/call.rb' + +# Offense count: 140 +# Configuration parameters: AllowedConstants. +Style/Documentation: + Enabled: false + +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +Style/EmptyLambdaParameter: + Exclude: + - 'spec/rbs_map/core_map_spec.rb' + +# Offense count: 4 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: AutoCorrect, EnforcedStyle. +# SupportedStyles: compact, expanded +Style/EmptyMethod: + Exclude: + - 'lib/solargraph/language_server/message/client/register_capability.rb' + - 'spec/fixtures/formattable.rb' + - 'spec/fixtures/rdoc-lib/lib/example.rb' + - 'spec/fixtures/workspace-with-gemfile/lib/thing.rb' + +# Offense count: 5 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle. +# SupportedStyles: trailing_conditional, ternary +Style/EmptyStringInsideInterpolation: + Exclude: + - 'lib/solargraph/library.rb' + - 'lib/solargraph/pin/documenting.rb' + - 'lib/solargraph/shell.rb' + +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +Style/ExpandPathArguments: + Exclude: + - 'solargraph.gemspec' + +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: AllowedVars. +Style/FetchEnvVar: + Exclude: + - 'spec/api_map/config_spec.rb' + +# Offense count: 2 +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: EnforcedStyle. +# SupportedStyles: left_coerce, right_coerce, single_coerce, fdiv +Style/FloatDivision: + Exclude: + - 'lib/solargraph/library.rb' + - 'lib/solargraph/pin/search.rb' + +# Offense count: 126 +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: EnforcedStyle. +# SupportedStyles: always, always_true, never +Style/FrozenStringLiteralComment: + Enabled: false + +# Offense count: 8 +# This cop supports unsafe autocorrection (--autocorrect-all). +Style/GlobalStdStream: + Exclude: + - 'lib/solargraph/logging.rb' + - 'lib/solargraph/shell.rb' + - 'spec/logging_spec.rb' + +# Offense count: 11 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: MinBodyLength, AllowConsecutiveConditionals. +Style/GuardClause: + Exclude: + - 'lib/solargraph/api_map.rb' + - 'lib/solargraph/library.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/send_node.rb' + - 'lib/solargraph/range.rb' + - 'lib/solargraph/rbs_map/conversions.rb' + - 'lib/solargraph/source.rb' + - 'lib/solargraph/workspace.rb' + +# Offense count: 2 +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: AllowedReceivers. +# AllowedReceivers: Thread.current +Style/HashEachMethods: + Exclude: + - 'lib/solargraph/library.rb' + - 'lib/solargraph/source.rb' + +# Offense count: 25 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle, EnforcedShorthandSyntax, UseHashRocketsWithSymbolValues, PreferHashRocketsForNonAlnumEndingSymbols. +# SupportedStyles: ruby19, hash_rockets, no_mixed_keys, ruby19_no_mixed_keys +# SupportedShorthandSyntax: always, never, either, consistent, either_consistent +Style/HashSyntax: + Exclude: + - 'spec/source/chain/class_variable_spec.rb' + - 'spec/source/cursor_spec.rb' + - 'spec/source/source_chainer_spec.rb' + +# Offense count: 6 +# This cop supports unsafe autocorrection (--autocorrect-all). +Style/IdenticalConditionalBranches: + Exclude: + - 'lib/solargraph/library.rb' + - 'lib/solargraph/type_checker.rb' + +# Offense count: 6 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: AllowIfModifier. +Style/IfInsideElse: + Exclude: + - 'lib/solargraph/language_server/transport/data_reader.rb' + - 'lib/solargraph/parser/parser_gem/node_chainer.rb' + - 'lib/solargraph/parser/parser_gem/node_methods.rb' + - 'lib/solargraph/source_map/clip.rb' + - 'lib/solargraph/type_checker.rb' + +# Offense count: 40 +# This cop supports safe autocorrection (--autocorrect). +Style/IfUnlessModifier: + Enabled: false + +# Offense count: 2 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle. +# SupportedStyles: call, braces +Style/LambdaCall: + Exclude: + - 'lib/solargraph/library.rb' + +# Offense count: 3 +# This cop supports unsafe autocorrection (--autocorrect-all). +Style/MapIntoArray: + Exclude: + - 'lib/solargraph/diagnostics/update_errors.rb' + - 'lib/solargraph/parser/parser_gem/node_chainer.rb' + - 'lib/solargraph/type_checker/param_def.rb' + +# Offense count: 1 +# This cop supports unsafe autocorrection (--autocorrect-all). +Style/MapToHash: + Exclude: + - 'lib/solargraph/bench.rb' + +# Offense count: 2 +# This cop supports unsafe autocorrection (--autocorrect-all). +Style/MapToSet: + Exclude: + - 'lib/solargraph/library.rb' + - 'spec/source_map/clip_spec.rb' + +# Offense count: 147 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle. +# SupportedStyles: require_parentheses, require_no_parentheses, require_no_parentheses_except_multiline +Style/MethodDefParentheses: + Enabled: false + +# Offense count: 1 +Style/MultilineBlockChain: + Exclude: + - 'lib/solargraph/pin/search.rb' + +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +Style/MultilineIfModifier: + Exclude: + - 'lib/solargraph/pin/callable.rb' + +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +Style/MultilineTernaryOperator: + Exclude: + - 'lib/solargraph/language_server/host.rb' + +# Offense count: 19 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: AllowMethodComparison, ComparisonsThreshold. +Style/MultipleComparison: + Exclude: + - 'lib/solargraph/complex_type.rb' + - 'lib/solargraph/complex_type/type_methods.rb' + - 'lib/solargraph/parser/parser_gem/node_methods.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/send_node.rb' + - 'lib/solargraph/pin/method.rb' + - 'lib/solargraph/pin/parameter.rb' + - 'lib/solargraph/source.rb' + - 'lib/solargraph/type_checker.rb' + +# Offense count: 15 +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: EnforcedStyle. +# SupportedStyles: literals, strict +Style/MutableConstant: + Exclude: + - 'lib/solargraph/diagnostics/rubocop.rb' + - 'lib/solargraph/logging.rb' + - 'lib/solargraph/parser/parser_gem/node_methods.rb' + - 'lib/solargraph/rbs_map/conversions.rb' + - 'lib/solargraph/rbs_map/core_fills.rb' + - 'spec/complex_type_spec.rb' + +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle. +# SupportedStyles: both, prefix, postfix +Style/NegatedIf: + Exclude: + - 'lib/solargraph/language_server/host/diagnoser.rb' + +# Offense count: 6 +# This cop supports safe autocorrection (--autocorrect). +Style/NegatedIfElseCondition: + Exclude: + - 'lib/solargraph/diagnostics/rubocop.rb' + - 'lib/solargraph/language_server/message/extended/document_gems.rb' + - 'lib/solargraph/parser/parser_gem/node_methods.rb' + - 'lib/solargraph/shell.rb' + - 'lib/solargraph/type_checker.rb' + +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: AllowedMethods. +# AllowedMethods: be, be_a, be_an, be_between, be_falsey, be_kind_of, be_instance_of, be_truthy, be_within, eq, eql, end_with, include, match, raise_error, respond_to, start_with +Style/NestedParenthesizedCalls: + Exclude: + - 'lib/solargraph/type_checker.rb' + +# Offense count: 2 +# This cop supports safe autocorrection (--autocorrect). +Style/NestedTernaryOperator: + Exclude: + - 'lib/solargraph/pin/conversions.rb' + - 'lib/solargraph/pin/method.rb' + +# Offense count: 4 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle, MinBodyLength, AllowConsecutiveConditionals. +# SupportedStyles: skip_modifier_ifs, always +Style/Next: + Exclude: + - 'lib/solargraph/parser/parser_gem/node_processors/send_node.rb' + - 'lib/solargraph/source_map/clip.rb' + - 'lib/solargraph/type_checker/checks.rb' + +# Offense count: 11 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: Strict, AllowedNumbers, AllowedPatterns. +Style/NumericLiterals: + MinDigits: 6 + +# Offense count: 28 +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: EnforcedStyle, AllowedMethods, AllowedPatterns. +# SupportedStyles: predicate, comparison +Style/NumericPredicate: + Exclude: + - 'spec/**/*' + - 'lib/solargraph/api_map.rb' + - 'lib/solargraph/api_map/index.rb' + - 'lib/solargraph/api_map/store.rb' + - 'lib/solargraph/complex_type.rb' + - 'lib/solargraph/complex_type/type_methods.rb' + - 'lib/solargraph/complex_type/unique_type.rb' + - 'lib/solargraph/language_server/message/extended/check_gem_version.rb' + - 'lib/solargraph/library.rb' + - 'lib/solargraph/parser/comment_ripper.rb' + - 'lib/solargraph/pin/delegated_method.rb' + - 'lib/solargraph/shell.rb' + - 'lib/solargraph/source/change.rb' + - 'lib/solargraph/source/source_chainer.rb' + - 'lib/solargraph/workspace.rb' + +# Offense count: 1 +Style/OpenStructUse: + Exclude: + - 'lib/solargraph/page.rb' + +# Offense count: 8 +# Configuration parameters: AllowedMethods. +# AllowedMethods: respond_to_missing? +Style/OptionalBooleanParameter: + Exclude: + - 'lib/solargraph/api_map.rb' + - 'lib/solargraph/language_server/message/text_document/completion.rb' + - 'lib/solargraph/source/chain.rb' + - 'lib/solargraph/source/chain/hash.rb' + - 'lib/solargraph/source/chain/z_super.rb' + - 'lib/solargraph/source/change.rb' + - 'lib/solargraph/source/updater.rb' + +# Offense count: 3 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: AllowSafeAssignment, AllowInMultilineConditions. +Style/ParenthesesAroundCondition: + Exclude: + - 'lib/solargraph/parser/parser_gem/node_processors/send_node.rb' + - 'lib/solargraph/type_checker.rb' + +# Offense count: 1 +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: EnforcedStyle. +# SupportedStyles: short, verbose +Style/PreferredHashMethods: + Exclude: + - 'lib/solargraph/language_server/message.rb' + +# Offense count: 1 +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: Methods. +Style/RedundantArgument: + Exclude: + - 'lib/solargraph/source_map/mapper.rb' + +# Offense count: 3 +# This cop supports safe autocorrection (--autocorrect). +Style/RedundantAssignment: + Exclude: + - 'lib/solargraph/language_server/host/dispatch.rb' + - 'lib/solargraph/repro.rb' + - 'lib/solargraph/workspace/config.rb' + +# Offense count: 7 +# This cop supports safe autocorrection (--autocorrect). +Style/RedundantBegin: + Exclude: + - 'lib/solargraph/language_server/transport/data_reader.rb' + - 'lib/solargraph/shell.rb' + - 'lib/solargraph/source/cursor.rb' + - 'lib/solargraph/source/encoding_fixes.rb' + - 'lib/solargraph/type_checker.rb' + - 'lib/solargraph/workspace.rb' + +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +Style/RedundantException: + Exclude: + - 'spec/language_server/host_spec.rb' + +# Offense count: 2 +# This cop supports safe autocorrection (--autocorrect). +Style/RedundantFreeze: + Exclude: + - 'lib/solargraph/complex_type.rb' + - 'lib/solargraph/source_map/mapper.rb' + +# Offense count: 3 +# This cop supports unsafe autocorrection (--autocorrect-all). +Style/RedundantInterpolation: + Exclude: + - 'lib/solargraph/api_map/store.rb' + - 'lib/solargraph/parser/parser_gem/node_chainer.rb' + - 'lib/solargraph/source_map/mapper.rb' + +# Offense count: 5 +# This cop supports safe autocorrection (--autocorrect). +Style/RedundantParentheses: + Exclude: + - 'lib/solargraph/language_server/message/initialize.rb' + - 'lib/solargraph/parser/parser_gem/node_chainer.rb' + - 'lib/solargraph/pin/method.rb' + - 'lib/solargraph/source.rb' + - 'lib/solargraph/type_checker.rb' + +# Offense count: 5 +# This cop supports safe autocorrection (--autocorrect). +Style/RedundantRegexpArgument: + Exclude: + - 'lib/solargraph/api_map/index.rb' + - 'lib/solargraph/workspace/config.rb' + - 'spec/diagnostics/rubocop_helpers_spec.rb' + - 'spec/language_server/host_spec.rb' + +# Offense count: 18 +# This cop supports safe autocorrection (--autocorrect). +Style/RedundantRegexpEscape: + Exclude: + - 'lib/solargraph/complex_type.rb' + - 'lib/solargraph/diagnostics/rubocop.rb' + - 'lib/solargraph/language_server/uri_helpers.rb' + - 'lib/solargraph/shell.rb' + - 'lib/solargraph/source_map/clip.rb' + - 'lib/solargraph/source_map/mapper.rb' + +# Offense count: 6 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: AllowMultipleReturnValues. +Style/RedundantReturn: + Exclude: + - 'lib/solargraph/api_map.rb' + - 'lib/solargraph/complex_type/type_methods.rb' + - 'lib/solargraph/parser/parser_gem/node_methods.rb' + - 'lib/solargraph/source/chain/z_super.rb' + +# Offense count: 9 +# This cop supports safe autocorrection (--autocorrect). +Style/RedundantSelf: + Exclude: + - 'lib/solargraph/api_map.rb' + - 'lib/solargraph/equality.rb' + - 'lib/solargraph/location.rb' + - 'lib/solargraph/pin/base.rb' + - 'lib/solargraph/pin/callable.rb' + - 'lib/solargraph/source/chain.rb' + - 'lib/solargraph/source/chain/link.rb' + +# Offense count: 3 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle, AllowInnerSlashes. +# SupportedStyles: slashes, percent_r, mixed +Style/RegexpLiteral: + Exclude: + - 'lib/solargraph/language_server/uri_helpers.rb' + - 'lib/solargraph/workspace/config.rb' + +# Offense count: 13 +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: ConvertCodeThatCanStartToReturnNil, AllowedMethods, MaxChainLength. +# AllowedMethods: present?, blank?, presence, try, try! +Style/SafeNavigation: + Exclude: + - 'lib/solargraph/api_map/index.rb' + - 'lib/solargraph/language_server/message/completion_item/resolve.rb' + - 'lib/solargraph/language_server/request.rb' + - 'lib/solargraph/language_server/transport/data_reader.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/send_node.rb' + - 'lib/solargraph/pin/base.rb' + - 'lib/solargraph/pin/conversions.rb' + - 'lib/solargraph/pin/method.rb' + - 'lib/solargraph/range.rb' + - 'lib/solargraph/type_checker.rb' + +# Offense count: 40 +# This cop supports unsafe autocorrection (--autocorrect-all). +Style/SlicingWithRange: + Enabled: false + +# Offense count: 8 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: AllowModifier. +Style/SoleNestedConditional: + Exclude: + - 'lib/solargraph/api_map.rb' + - 'lib/solargraph/complex_type/unique_type.rb' + - 'lib/solargraph/parser/flow_sensitive_typing.rb' + - 'lib/solargraph/pin/parameter.rb' + - 'lib/solargraph/source.rb' + - 'lib/solargraph/source/source_chainer.rb' + - 'lib/solargraph/type_checker.rb' + +# Offense count: 5 +# This cop supports safe autocorrection (--autocorrect). +Style/StderrPuts: + Exclude: + - 'lib/solargraph/shell.rb' + +# Offense count: 10 +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: Mode. +Style/StringConcatenation: + Exclude: + - 'lib/solargraph/api_map.rb' + - 'lib/solargraph/api_map/index.rb' + - 'lib/solargraph/pin/base.rb' + - 'lib/solargraph/pin/callable.rb' + - 'lib/solargraph/pin/closure.rb' + - 'lib/solargraph/pin/local_variable.rb' + - 'lib/solargraph/pin/method.rb' + - 'lib/solargraph/pin/namespace.rb' + - 'solargraph.gemspec' + +# Offense count: 605 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline. +# SupportedStyles: single_quotes, double_quotes +Style/StringLiterals: + Enabled: false + +# Offense count: 3 +# This cop supports safe autocorrection (--autocorrect). +Style/SuperArguments: + Exclude: + - 'lib/solargraph/pin/callable.rb' + - 'lib/solargraph/pin/method.rb' + - 'lib/solargraph/pin/signature.rb' + +# Offense count: 44 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle, MinSize. +# SupportedStyles: percent, brackets +Style/SymbolArray: + Exclude: + - 'lib/solargraph/api_map.rb' + - 'lib/solargraph/parser/parser_gem/node_chainer.rb' + - 'lib/solargraph/parser/parser_gem/node_methods.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/block_node.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/send_node.rb' + - 'lib/solargraph/pin/method.rb' + - 'lib/solargraph/pin/parameter.rb' + - 'lib/solargraph/shell.rb' + - 'lib/solargraph/source.rb' + - 'lib/solargraph/source/chain/literal.rb' + - 'lib/solargraph/source_map/clip.rb' + - 'lib/solargraph/source_map/mapper.rb' + - 'lib/solargraph/type_checker.rb' + - 'spec/parser/node_methods_spec.rb' + - 'spec/source_map/mapper_spec.rb' + +# Offense count: 6 +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: AllowMethodsWithArguments, AllowedMethods, AllowedPatterns, AllowComments. +# AllowedMethods: define_method +Style/SymbolProc: + Exclude: + - 'lib/solargraph/gem_pins.rb' + - 'lib/solargraph/language_server/message/text_document/hover.rb' + - 'lib/solargraph/language_server/message/text_document/signature_help.rb' + - 'lib/solargraph/parser/flow_sensitive_typing.rb' + - 'lib/solargraph/pin/callable.rb' + - 'lib/solargraph/pin/closure.rb' + +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle, AllowSafeAssignment. +# SupportedStyles: require_parentheses, require_no_parentheses, require_parentheses_when_complex +Style/TernaryParentheses: + Exclude: + - 'lib/solargraph/source_map/mapper.rb' + +# Offense count: 9 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyleForMultiline. +# SupportedStylesForMultiline: comma, consistent_comma, no_comma +Style/TrailingCommaInArguments: + Exclude: + - 'lib/solargraph/api_map.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/until_node.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/while_node.rb' + - 'lib/solargraph/rbs_map/conversions.rb' + - 'lib/solargraph/yard_map/mapper/to_method.rb' + +# Offense count: 2 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyleForMultiline. +# SupportedStylesForMultiline: comma, consistent_comma, diff_comma, no_comma +Style/TrailingCommaInArrayLiteral: + Exclude: + - 'lib/solargraph/language_server/message/text_document/formatting.rb' + - 'spec/complex_type_spec.rb' + +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyleForMultiline. +# SupportedStylesForMultiline: comma, consistent_comma, diff_comma, no_comma +Style/TrailingCommaInHashLiteral: + Exclude: + - 'lib/solargraph/api_map.rb' + +# Offense count: 2 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: ExactNameMatch, AllowPredicates, AllowDSLWriters, IgnoreClassMethods, AllowedMethods. +# AllowedMethods: to_ary, to_a, to_c, to_enum, to_h, to_hash, to_i, to_int, to_io, to_open, to_path, to_proc, to_r, to_regexp, to_str, to_s, to_sym +Style/TrivialAccessors: + Exclude: + - 'lib/solargraph/language_server/message/extended/check_gem_version.rb' + - 'lib/solargraph/pin/keyword.rb' + +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +Style/WhileUntilModifier: + Exclude: + - 'lib/solargraph/complex_type.rb' + +# Offense count: 27 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle, MinSize, WordRegex. +# SupportedStyles: percent, brackets +Style/WordArray: + Exclude: + - 'lib/solargraph/complex_type.rb' + - 'lib/solargraph/complex_type/unique_type.rb' + - 'lib/solargraph/diagnostics/type_check.rb' + - 'lib/solargraph/language_server/message/text_document/formatting.rb' + - 'spec/complex_type_spec.rb' + - 'spec/parser/node_chainer_spec.rb' + - 'spec/pin/method_spec.rb' + - 'spec/source/cursor_spec.rb' + - 'spec/source/source_chainer_spec.rb' + - 'spec/source_map/clip_spec.rb' + - 'spec/source_map/mapper_spec.rb' + - 'spec/source_map_spec.rb' + - 'spec/source_spec.rb' + +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +Style/YAMLFileRead: + Exclude: + - 'lib/solargraph/workspace/config.rb' + +# Offense count: 4 +# This cop supports unsafe autocorrection (--autocorrect-all). +Style/ZeroLengthPredicate: + Exclude: + - 'lib/solargraph/api_map.rb' + - 'lib/solargraph/api_map/index.rb' + - 'lib/solargraph/language_server/host.rb' + - 'spec/language_server/protocol_spec.rb' + +# Offense count: 173 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns, SplitStrings. +# URISchemes: http, https +Layout/LineLength: + Max: 244 diff --git a/lib/.rubocop.yml b/lib/.rubocop.yml deleted file mode 100644 index 16d2f865a..000000000 --- a/lib/.rubocop.yml +++ /dev/null @@ -1,22 +0,0 @@ -AllCops: - NewCops: enable -Style/MethodDefParentheses: - Enabled: false -Layout/EmptyLineAfterGuardClause: - Enabled: false -Layout/SpaceAroundMethodCallOperator: - Enabled: true -Lint/RaiseException: - Enabled: true -Lint/StructNewOverride: - Enabled: true -Metrics/MethodLength: - Max: 25 -Style/ExponentialNotation: - Enabled: true -Style/HashEachMethods: - Enabled: true -Style/HashTransformKeys: - Enabled: true -Style/HashTransformValues: - Enabled: true diff --git a/solargraph.gemspec b/solargraph.gemspec index 2bfb6dbdf..934ba5cbc 100755 --- a/solargraph.gemspec +++ b/solargraph.gemspec @@ -36,7 +36,9 @@ Gem::Specification.new do |s| s.add_runtime_dependency 'parser', '~> 3.0' s.add_runtime_dependency 'rbs', '~> 3.3' s.add_runtime_dependency 'reverse_markdown', '~> 3.0' - s.add_runtime_dependency 'rubocop', '~> 1.38' + s.add_runtime_dependency 'rubocop', '~> 1.76' + s.add_runtime_dependency 'rubocop-rake', '~> 0.7' + s.add_runtime_dependency 'rubocop-rspec', '~> 3.6' s.add_runtime_dependency 'thor', '~> 1.0' s.add_runtime_dependency 'tilt', '~> 2.0' s.add_runtime_dependency 'yard', '~> 0.9', '>= 0.9.24' From 0776c427fa0f5205737ea8da48d79a2d7657e697 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 13 Jun 2025 08:27:47 -0400 Subject: [PATCH 038/561] Add RuboCop workflow --- .github/workflows/rubocop.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/rubocop.yml b/.github/workflows/rubocop.yml index 5d55e4a20..2f77613ad 100644 --- a/.github/workflows/rubocop.yml +++ b/.github/workflows/rubocop.yml @@ -20,16 +20,13 @@ jobs: test: runs-on: ubuntu-latest - strategy: - matrix: - ruby-version: '3.0' steps: - uses: actions/checkout@v3 - name: Set up Ruby uses: ruby/setup-ruby@v1 with: - ruby-version: ${{ matrix.ruby-version }} + ruby-version: 3.4 bundler-cache: false - name: Install gems From 699c9c5a824fd3ccb16c13925071f29931bc242b Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 13 Jun 2025 08:28:16 -0400 Subject: [PATCH 039/561] Add RuboCop workflow --- .github/workflows/rubocop.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/rubocop.yml b/.github/workflows/rubocop.yml index 2f77613ad..9fcbafa23 100644 --- a/.github/workflows/rubocop.yml +++ b/.github/workflows/rubocop.yml @@ -17,8 +17,7 @@ permissions: contents: read jobs: - test: - + rubocop: runs-on: ubuntu-latest steps: From 1c4d161fa38478c84e48a67643424e56453987ce Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 13 Jun 2025 08:31:36 -0400 Subject: [PATCH 040/561] Add RuboCop workflow --- .rubocop.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.rubocop.yml b/.rubocop.yml index 198447e83..1fcf3dea2 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -20,6 +20,9 @@ AllCops: - "spec/fixtures/rubocop-unused-variable-error/*" - "spec/fixtures/rubocop-subfolder-configuration/.*" - "spec/fixtures/rubocop-subfolder-configuration/*" + - "spec/fixtures/invalid_byte.rb" + - "spec/fixtures/invalid_node_comment.rb" + - "spec/fixtures/invalid_utf8.rb" TargetRubyVersion: 3.0 Style/MethodDefParentheses: From 728552c86606ffacf02db488c97f901a5fa19c68 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 13 Jun 2025 08:53:50 -0400 Subject: [PATCH 041/561] Add RuboCop workflow --- .rubocop.yml | 6 ------ .rubocop_todo.yml | 9 +++++---- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 1fcf3dea2..576c17e9e 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -14,12 +14,6 @@ inherit_from: .rubocop_todo.yml AllCops: NewCops: enable Exclude: - - "spec/fixtures/rubocop-validation-error/.*" - - "spec/fixtures/rubocop-validation-error/*" - - "spec/fixtures/rubocop-unused-variable-error/.*" - - "spec/fixtures/rubocop-unused-variable-error/*" - - "spec/fixtures/rubocop-subfolder-configuration/.*" - - "spec/fixtures/rubocop-subfolder-configuration/*" - "spec/fixtures/invalid_byte.rb" - "spec/fixtures/invalid_node_comment.rb" - "spec/fixtures/invalid_utf8.rb" diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 2f0e023a3..d0a679cf4 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,6 +1,6 @@ # This configuration was generated by # `rubocop --auto-gen-config` -# on 2025-06-13 12:23:43 UTC using RuboCop version 1.76.1. +# on 2025-06-13 12:53:14 UTC using RuboCop version 1.76.1. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new @@ -405,10 +405,11 @@ Lint/EmptyBlock: Exclude: - 'spec/convention_spec.rb' -# Offense count: 5 +# Offense count: 6 # Configuration parameters: AllowComments. Lint/EmptyClass: Exclude: + - 'spec/fixtures/rubocop-validation-error/app.rb' - 'spec/fixtures/workspace-with-gemfile/lib/other.rb' - 'spec/fixtures/workspace/lib/other.rb' - 'spec/fixtures/workspace/lib/something.rb' @@ -523,7 +524,7 @@ Lint/UselessAccessModifier: Exclude: - 'lib/solargraph/api_map.rb' -# Offense count: 32 +# Offense count: 33 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AutoCorrect. Lint/UselessAssignment: @@ -1162,7 +1163,7 @@ Style/FloatDivision: - 'lib/solargraph/library.rb' - 'lib/solargraph/pin/search.rb' -# Offense count: 126 +# Offense count: 127 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: EnforcedStyle. # SupportedStyles: always, always_true, never From f3e41a0e212ee36d79329ac8c57cb3cd6e0e8b8f Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 13 Jun 2025 09:41:02 -0400 Subject: [PATCH 042/561] Use undercover tool to report on PR test coverage --- .github/workflows/rspec.yml | 19 +++++++++++++++++-- Rakefile | 6 ++++++ solargraph.gemspec | 4 +++- spec/spec_helper.rb | 10 +++++++++- 4 files changed, 35 insertions(+), 4 deletions(-) diff --git a/.github/workflows/rspec.yml b/.github/workflows/rspec.yml index e2c94cc1b..830e61cd8 100644 --- a/.github/workflows/rspec.yml +++ b/.github/workflows/rspec.yml @@ -17,8 +17,7 @@ permissions: contents: read jobs: - test: - + rspec: runs-on: ubuntu-latest strategy: matrix: @@ -35,3 +34,19 @@ jobs: run: bundle install - name: Run tests run: bundle exec rspec + undercover: + name: 'Undercover' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.4' + bundler-cache: false + - name: Install gems + run: bundle install + - name: Run tests + run: bundle exec rspec + - name: Check PR coverage + run: bundle exec rake undercover diff --git a/Rakefile b/Rakefile index 33b91bfa4..2c92fd843 100755 --- a/Rakefile +++ b/Rakefile @@ -18,8 +18,14 @@ task :typecheck do sh "bundle exec solargraph typecheck --level typed" end +desc "Check PR coverage" +task :undercover do + sh 'bundle exec undercover --exclude-files "Rakefile,spec/**/*" --compare origin/master' +end + desc "Run all tests" task :test do Rake::Task["typecheck"].invoke Rake::Task["spec"].invoke + Rake::Task["undercover"].invoke end diff --git a/solargraph.gemspec b/solargraph.gemspec index 2bfb6dbdf..8715a789d 100755 --- a/solargraph.gemspec +++ b/solargraph.gemspec @@ -46,7 +46,9 @@ Gem::Specification.new do |s| s.add_development_dependency 'public_suffix', '~> 3.1' s.add_development_dependency 'rake', '~> 13.2' s.add_development_dependency 'rspec', '~> 3.5' - s.add_development_dependency 'simplecov', '~> 0.14' + s.add_development_dependency 'simplecov', '~> 0.21' + s.add_development_dependency 'simplecov-lcov', '~> 0.8' + s.add_development_dependency 'undercover', '~> 0.6' s.add_development_dependency 'webmock', '~> 3.6' # work around missing yard dependency needed as of Ruby 3.5 s.add_development_dependency 'irb', '~> 1.15' diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 5e0385b74..cdd81df82 100755 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -2,8 +2,16 @@ require 'webmock/rspec' WebMock.disable_net_connect!(allow_localhost: true) unless ENV['SIMPLECOV_DISABLED'] + # set up lcov reporting for undercover require 'simplecov' - SimpleCov.start + require 'simplecov-lcov' + SimpleCov::Formatter::LcovFormatter.config.report_with_single_file = true + SimpleCov.formatter = SimpleCov::Formatter::LcovFormatter + SimpleCov.start do + add_filter(%r{^/spec/}) + add_filter('/Rakefile') + enable_coverage(:branch) + end end require 'solargraph' # Suppress logger output in specs (if possible) From a1bb9687cd946148e7d4c7586d765804d991e35e Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 13 Jun 2025 10:13:32 -0400 Subject: [PATCH 043/561] Use undercover tool to report on PR test coverage --- .github/workflows/rspec.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/rspec.yml b/.github/workflows/rspec.yml index 830e61cd8..d9cacbaee 100644 --- a/.github/workflows/rspec.yml +++ b/.github/workflows/rspec.yml @@ -39,6 +39,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 + with: + # fetch all history for all branches and tags so we can + # compare against origin/master + fetch-depth: 0 - name: Set up Ruby uses: ruby/setup-ruby@v1 with: From 3faecc91f226d5c916141cc3bf844b3ee2b643ab Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 13 Jun 2025 10:22:05 -0400 Subject: [PATCH 044/561] Use undercover tool to report on PR test coverage --- Rakefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rakefile b/Rakefile index 2c92fd843..dfdce8fc7 100755 --- a/Rakefile +++ b/Rakefile @@ -20,7 +20,7 @@ end desc "Check PR coverage" task :undercover do - sh 'bundle exec undercover --exclude-files "Rakefile,spec/**/*" --compare origin/master' + sh 'bundle exec undercover --exclude-files "Rakefile,spec/*,spec/**/*" --compare origin/master' end desc "Run all tests" From 380e8677d485cef9158dbaaafc878489e75ea4ed Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 23 Jun 2025 20:16:04 -0400 Subject: [PATCH 045/561] Populate locations in additional, uh, spots --- lib/solargraph/gem_pins.rb | 1 + lib/solargraph/pin/method.rb | 6 ++++-- lib/solargraph/rbs_map/conversions.rb | 22 ++++++++++++++-------- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/lib/solargraph/gem_pins.rb b/lib/solargraph/gem_pins.rb index 1713bbace..8edee9a0b 100644 --- a/lib/solargraph/gem_pins.rb +++ b/lib/solargraph/gem_pins.rb @@ -42,6 +42,7 @@ def self.combine(yard_pins, rbs_map) generics: rbs.generics, node: yard.node, signatures: yard.signatures, + type_location: rbs.type_location, return_type: best_return_type(rbs.return_type, yard.return_type), source: :gem_pins ) diff --git a/lib/solargraph/pin/method.rb b/lib/solargraph/pin/method.rb index 2eb488b86..5e223c4e6 100644 --- a/lib/solargraph/pin/method.rb +++ b/lib/solargraph/pin/method.rb @@ -121,9 +121,11 @@ def generate_signature(parameters, return_type) ) end yield_return_type = ComplexType.try_parse(*yieldreturn_tags.flat_map(&:types)) - block = Signature.new(generics: generics, parameters: yield_parameters, return_type: yield_return_type, source: source) + block = Signature.new(generics: generics, parameters: yield_parameters, return_type: yield_return_type, source: source, + location: location, type_location: type_location) end - Signature.new(generics: generics, parameters: parameters, return_type: return_type, block: block, source: source) + Signature.new(generics: generics, parameters: parameters, return_type: return_type, block: block, source: source, + location: location, type_location: type_location) end # @return [::Array] diff --git a/lib/solargraph/rbs_map/conversions.rb b/lib/solargraph/rbs_map/conversions.rb index 2f1974df0..5684a5308 100644 --- a/lib/solargraph/rbs_map/conversions.rb +++ b/lib/solargraph/rbs_map/conversions.rb @@ -329,13 +329,16 @@ def method_def_to_pin decl, closure # @return [void] def method_def_to_sigs decl, pin decl.overloads.map do |overload| + type_location = location_decl_to_pin_location(overload.method_type.location) generics = overload.method_type.type_params.map(&:name).map(&:to_s) signature_parameters, signature_return_type = parts_of_function(overload.method_type, pin) block = if overload.method_type.block block_parameters, block_return_type = parts_of_function(overload.method_type.block, pin) - Pin::Signature.new(generics: generics, parameters: block_parameters, return_type: block_return_type, source: :rbs) + Pin::Signature.new(generics: generics, parameters: block_parameters, return_type: block_return_type, source: :rbs, + type_location: type_location) end - Pin::Signature.new(generics: generics, parameters: signature_parameters, return_type: signature_return_type, block: block, source: :rbs) + Pin::Signature.new(generics: generics, parameters: signature_parameters, return_type: signature_return_type, block: block, source: :rbs, + type_location: type_location) end end @@ -354,44 +357,47 @@ def location_decl_to_pin_location(location) # @param pin [Pin::Method] # @return [Array(Array, ComplexType)] def parts_of_function type, pin - return [[Solargraph::Pin::Parameter.new(decl: :restarg, name: 'arg', closure: pin, source: :rbs)], ComplexType.try_parse(method_type_to_tag(type)).force_rooted] if defined?(RBS::Types::UntypedFunction) && type.type.is_a?(RBS::Types::UntypedFunction) + type_location = pin.type_location + return [[Solargraph::Pin::Parameter.new(decl: :restarg, name: 'arg', closure: pin, source: :rbs, type_location: type_location)], ComplexType.try_parse(method_type_to_tag(type)).force_rooted] if defined?(RBS::Types::UntypedFunction) && type.type.is_a?(RBS::Types::UntypedFunction) parameters = [] arg_num = -1 type.type.required_positionals.each do |param| name = param.name ? param.name.to_s : "arg_#{arg_num += 1}" - parameters.push Solargraph::Pin::Parameter.new(decl: :arg, name: name, closure: pin, return_type: ComplexType.try_parse(other_type_to_tag(param.type)).force_rooted, source: :rbs) + parameters.push Solargraph::Pin::Parameter.new(decl: :arg, name: name, closure: pin, return_type: ComplexType.try_parse(other_type_to_tag(param.type)).force_rooted, source: :rbs, type_location: type_location) end type.type.optional_positionals.each do |param| name = param.name ? param.name.to_s : "arg_#{arg_num += 1}" parameters.push Solargraph::Pin::Parameter.new(decl: :optarg, name: name, closure: pin, return_type: ComplexType.try_parse(other_type_to_tag(param.type)).force_rooted, + type_location: type_location, source: :rbs) end if type.type.rest_positionals name = type.type.rest_positionals.name ? type.type.rest_positionals.name.to_s : "arg_#{arg_num += 1}" - parameters.push Solargraph::Pin::Parameter.new(decl: :restarg, name: name, closure: pin, source: :rbs) + parameters.push Solargraph::Pin::Parameter.new(decl: :restarg, name: name, closure: pin, source: :rbs, type_location: type_location) end type.type.trailing_positionals.each do |param| name = param.name ? param.name.to_s : "arg_#{arg_num += 1}" - parameters.push Solargraph::Pin::Parameter.new(decl: :arg, name: name, closure: pin, source: :rbs) + parameters.push Solargraph::Pin::Parameter.new(decl: :arg, name: name, closure: pin, source: :rbs, type_location: type_location) end type.type.required_keywords.each do |orig, param| name = orig ? orig.to_s : "arg_#{arg_num += 1}" parameters.push Solargraph::Pin::Parameter.new(decl: :kwarg, name: name, closure: pin, return_type: ComplexType.try_parse(other_type_to_tag(param.type)).force_rooted, - source: :rbs) + source: :rbs, type_location: type_location) end type.type.optional_keywords.each do |orig, param| name = orig ? orig.to_s : "arg_#{arg_num += 1}" parameters.push Solargraph::Pin::Parameter.new(decl: :kwoptarg, name: name, closure: pin, return_type: ComplexType.try_parse(other_type_to_tag(param.type)).force_rooted, + type_location: type_location, source: :rbs) end if type.type.rest_keywords name = type.type.rest_keywords.name ? type.type.rest_keywords.name.to_s : "arg_#{arg_num += 1}" parameters.push Solargraph::Pin::Parameter.new(decl: :kwrestarg, name: type.type.rest_keywords.name.to_s, closure: pin, - source: :rbs) + source: :rbs, type_location: type_location) end rooted_tag = method_type_to_tag(type) From 3bfad8b00f410e72e1e75ebd61d2f965ec44b0e6 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 24 Jun 2025 07:37:47 -0400 Subject: [PATCH 046/561] Fix merge issue --- lib/solargraph/rbs_map/conversions.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/solargraph/rbs_map/conversions.rb b/lib/solargraph/rbs_map/conversions.rb index 1ac7343ba..cd1bf320a 100644 --- a/lib/solargraph/rbs_map/conversions.rb +++ b/lib/solargraph/rbs_map/conversions.rb @@ -511,9 +511,10 @@ def attr_writer_to_pin(decl, closure, context) final_scope = decl.kind == :instance ? :instance : :class name = "#{decl.name.to_s}=" visibility = calculate_method_visibility(decl, context, closure, final_scope, name) + type_location = location_decl_to_pin_location(decl.location) pin = Solargraph::Pin::Method.new( name: name, - type_location: location_decl_to_pin_location(decl.location), + type_location: type_location, closure: closure, parameters: [], comments: decl.comment&.string, @@ -527,7 +528,8 @@ def attr_writer_to_pin(decl, closure, context) name: 'value', return_type: ComplexType.try_parse(other_type_to_tag(decl.type)).force_rooted, source: :rbs, - closure: pin + closure: pin, + type_location: type_location ) rooted_tag = ComplexType.parse(other_type_to_tag(decl.type)).force_rooted.rooted_tags pin.docstring.add_tag(YARD::Tags::Tag.new(:return, '', rooted_tag)) From 6adb558096c4cd2b54091140bbaedfe06b623719 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 26 Jun 2025 07:33:41 -0400 Subject: [PATCH 047/561] Differentiate cache based on YARD plugin used --- lib/solargraph/yardoc.rb | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/solargraph/yardoc.rb b/lib/solargraph/yardoc.rb index 858c7774a..287eb9809 100644 --- a/lib/solargraph/yardoc.rb +++ b/lib/solargraph/yardoc.rb @@ -1,5 +1,8 @@ # frozen_string_literal: true +require 'yard' +require 'yard-activesupport-concern' + module Solargraph # Methods for caching and loading YARD documentation for gems. # @@ -35,7 +38,10 @@ def cached?(gemspec) # @param gemspec [Gem::Specification] # @return [String] def path_for(gemspec) - File.join(Solargraph::Cache.base_dir, "yard-#{YARD::VERSION}", "#{gemspec.name}-#{gemspec.version}.yardoc") + File.join(Solargraph::Cache.base_dir, + "yard-#{YARD::VERSION}", + "yard-activesupport-concern-#{YARD::ActiveSupport::Concern::VERSION}", + "#{gemspec.name}-#{gemspec.version}.yardoc") end # Load a gem's yardoc and return its code objects. From 5505e3769bd6d765756ab1a2d674baeeb5f59778 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 26 Jun 2025 09:25:37 -0400 Subject: [PATCH 048/561] Fix issue with rooting a namespace --- lib/solargraph/api_map.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/solargraph/api_map.rb b/lib/solargraph/api_map.rb index 22c00f039..8d3402e34 100755 --- a/lib/solargraph/api_map.rb +++ b/lib/solargraph/api_map.rb @@ -685,15 +685,15 @@ def inner_get_methods rooted_tag, scope, visibility, deep, skip, no_core = false end else store.get_includes(fqns).reverse.each do |include_tag| + rooted_include_tag = qualify(include_tag, rooted_tag) logger.debug { "ApiMap#inner_get_methods(#{fqns}, #{scope}, #{visibility}, #{deep}) - Handling class include include_tag=#{include_tag}" } - module_extends = store.get_extends(include_tag) + module_extends = store.get_extends(rooted_include_tag) # ActiveSupport::Concern is syntactic sugar for a common # pattern to include class methods while mixing-in a Module # See https://api.rubyonrails.org/classes/ActiveSupport/Concern.html logger.debug { "ApiMap#inner_get_methods(#{fqns}, #{scope}, #{visibility}, #{deep}) - Handling class include include_tag=#{include_tag}" } if module_extends.include? 'ActiveSupport::Concern' - rooted_include_tag = qualify(include_tag, rooted_tag) unless rooted_include_tag.nil? # yard-activesupport-concern pulls methods inside # 'class_methods' blocks into main class visible from YARD @@ -701,7 +701,8 @@ def inner_get_methods rooted_tag, scope, visibility, deep, skip, no_core = false result.concat included_class_pins # another pattern is to put class methods inside a submodule - included_classmethods_pins = inner_get_methods_from_reference(rooted_include_tag + "::ClassMethods", namespace_pin, rooted_type, :instance, visibility, deep, skip, true) + classmethods_include_tag = rooted_include_tag + "::ClassMethods" + included_classmethods_pins = inner_get_methods_from_reference(classmethods_include_tag, namespace_pin, rooted_type, :instance, visibility, deep, skip, true) result.concat included_classmethods_pins end end From 18af12975eca1d3f88429fef566c7b625f1fc845 Mon Sep 17 00:00:00 2001 From: Fred Snyder Date: Sun, 23 Mar 2025 15:32:32 -0400 Subject: [PATCH 049/561] First iteration of rbs command --- lib/solargraph/doc_map.rb | 1 + lib/solargraph/shell.rb | 63 +++++++++++++++++++++++++++++++++++++++ solargraph.gemspec | 1 + 3 files changed, 65 insertions(+) diff --git a/lib/solargraph/doc_map.rb b/lib/solargraph/doc_map.rb index bb93b52e7..dab23a294 100644 --- a/lib/solargraph/doc_map.rb +++ b/lib/solargraph/doc_map.rb @@ -147,6 +147,7 @@ def resolve_path_to_gemspecs path gemspec = Gem::Specification.find_by_path(path) if gemspec.nil? gem_name_guess = path.split('/').first + return nil if gem_name_guess.to_s.empty? begin # this can happen when the gem is included via a local path in # a Gemfile; Gem doesn't try to index the paths in that case. diff --git a/lib/solargraph/shell.rb b/lib/solargraph/shell.rb index d08e575c1..59eca778a 100755 --- a/lib/solargraph/shell.rb +++ b/lib/solargraph/shell.rb @@ -3,10 +3,12 @@ require 'benchmark' require 'thor' require 'yard' +require 'sord' module Solargraph class Shell < Thor include Solargraph::ServerMethods + include ApiMap::SourceToYard # Tell Thor to ensure the process exits with status 1 if any error happens. def self.exit_on_failure? @@ -236,6 +238,67 @@ def list puts "#{workspace.filenames.length} files total." end + desc 'cache', 'Cache a gem', hide: true + # @return [void] + # @param gem [String] + # @param version [String, nil] + def cache gem, version = nil + spec = Gem::Specification.find_by_name(gem, version) + pins = GemPins.build(spec) + Cache.save('gems', "#{spec.name}-#{spec.version}.ser", pins) + end + + desc 'gems', 'Cache documentation for installed gems' + option :rebuild, type: :boolean, desc: 'Rebuild existing documentation', default: false + # @return [void] + def gems *names + if names.empty? + Gem::Specification.to_a.each do |spec| + next unless options.rebuild || !Yardoc.cached?(spec) + + puts "Processing gem: #{spec.name} #{spec.version}" + pins = GemPins.build(spec) + Cache.save('gems', "#{spec.name}-#{spec.version}.ser", pins) + end + else + names.each do |name| + spec = Gem::Specification.find_by_name(name) + if spec + next unless options.rebuild || !Yardoc.cached?(spec) + + puts "Processing gem: #{spec.name} #{spec.version}" + pins = GemPins.build(spec) + Cache.save('gems', "#{spec.name}-#{spec.version}.ser", pins) + else + warn "Gem '#{name}' not found" + end + end + end + end + + desc 'rbs', 'Generate RBS definitions' + option :inference, type: :boolean, desc: 'Enhance definitions with type inference', default: true + def rbs + api_map = Solargraph::ApiMap.load('.') + pins = api_map.source_maps.flat_map(&:pins) + store = Solargraph::ApiMap::Store.new(pins) + if options[:inference] + store.method_pins.each do |pin| + if pin.return_type.undefined? + type = pin.typify(api_map) + type = pin.probe(api_map) if type.undefined? + pin.docstring.add_tag YARD::Tags::Tag.new('return', nil, type.items.map(&:to_s)) + pin.instance_variable_set(:@return_type, type) + end + end + end + rake_yard(store) + YARD::Registry.save(false, '.yardoc') + YARD::Registry.load('.yardoc') + FileUtils.mkdir_p('sig') + `sord gen sig/solargraph.rbs --rbs --no-regenerate` + end + private # @param pin [Solargraph::Pin::Base] diff --git a/solargraph.gemspec b/solargraph.gemspec index 5008b6247..cdaf967b0 100755 --- a/solargraph.gemspec +++ b/solargraph.gemspec @@ -38,6 +38,7 @@ Gem::Specification.new do |s| s.add_runtime_dependency 'rbs', '~> 3.3' s.add_runtime_dependency 'reverse_markdown', '~> 3.0' s.add_runtime_dependency 'rubocop', '~> 1.38' + s.add_runtime_dependency 'sord', '~> 7.0' s.add_runtime_dependency 'thor', '~> 1.0' s.add_runtime_dependency 'tilt', '~> 2.0' s.add_runtime_dependency 'yard', '~> 0.9', '>= 0.9.24' From 8881e203c5748666da7d1d19b6f4399119dbce82 Mon Sep 17 00:00:00 2001 From: Fred Snyder Date: Sat, 28 Jun 2025 09:15:24 -0400 Subject: [PATCH 050/561] Handle nil refs in rake_yard --- lib/solargraph/api_map/source_to_yard.rb | 8 +++++--- lib/solargraph/shell.rb | 12 ++++++++---- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/lib/solargraph/api_map/source_to_yard.rb b/lib/solargraph/api_map/source_to_yard.rb index 5e7784f01..f92a7a351 100644 --- a/lib/solargraph/api_map/source_to_yard.rb +++ b/lib/solargraph/api_map/source_to_yard.rb @@ -45,12 +45,14 @@ def rake_yard store code_object_map[pin.path].docstring = pin.docstring store.get_includes(pin.path).each do |ref| include_object = code_object_at(pin.path, YARD::CodeObjects::ClassObject) - include_object.instance_mixins.push code_object_map[ref] unless include_object.nil? or include_object.nil? + code_object = code_object_map[ref] + include_object.instance_mixins.push code_object_map[ref] if include_object && code_object end store.get_extends(pin.path).each do |ref| extend_object = code_object_at(pin.path, YARD::CodeObjects::ClassObject) - extend_object.instance_mixins.push code_object_map[ref] unless extend_object.nil? or extend_object.nil? - extend_object.class_mixins.push code_object_map[ref] unless extend_object.nil? or extend_object.nil? + code_object = code_object_map[ref] + extend_object.instance_mixins.push code_object_map[ref] if extend_object && code_object + extend_object.class_mixins.push code_object_map[ref] if extend_object && code_object end end store.method_pins.each do |pin| diff --git a/lib/solargraph/shell.rb b/lib/solargraph/shell.rb index 59eca778a..57eed438f 100755 --- a/lib/solargraph/shell.rb +++ b/lib/solargraph/shell.rb @@ -4,6 +4,7 @@ require 'thor' require 'yard' require 'sord' +require 'tmpdir' module Solargraph class Shell < Thor @@ -293,10 +294,13 @@ def rbs end end rake_yard(store) - YARD::Registry.save(false, '.yardoc') - YARD::Registry.load('.yardoc') - FileUtils.mkdir_p('sig') - `sord gen sig/solargraph.rbs --rbs --no-regenerate` + Dir.mktmpdir do |tmpdir| + yardoc = File.join(tmpdir, '.yardoc') + YARD::Registry.save(false, yardoc) + YARD::Registry.load(yardoc) + FileUtils.mkdir_p('sig') + `sord gen sig/sig.rbs --rbs --no-regenerate` + end end private From 145436abf29feef2d51118e525c16a1e2150de85 Mon Sep 17 00:00:00 2001 From: Fred Snyder Date: Sat, 28 Jun 2025 09:31:31 -0400 Subject: [PATCH 051/561] Configurable filename --- lib/solargraph/shell.rb | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/solargraph/shell.rb b/lib/solargraph/shell.rb index 57eed438f..8d0ca78b7 100755 --- a/lib/solargraph/shell.rb +++ b/lib/solargraph/shell.rb @@ -278,6 +278,7 @@ def gems *names end desc 'rbs', 'Generate RBS definitions' + option :filename, type: :string, alias: :f, desc: 'Generated file name', default: 'sig.rbs' option :inference, type: :boolean, desc: 'Enhance definitions with type inference', default: true def rbs api_map = Solargraph::ApiMap.load('.') @@ -294,12 +295,15 @@ def rbs end end rake_yard(store) + target = File.absolute_path(File.join('sig', options[:filename])) Dir.mktmpdir do |tmpdir| - yardoc = File.join(tmpdir, '.yardoc') - YARD::Registry.save(false, yardoc) - YARD::Registry.load(yardoc) - FileUtils.mkdir_p('sig') - `sord gen sig/sig.rbs --rbs --no-regenerate` + Dir.chdir tmpdir do + yardoc = File.join(tmpdir, '.yardoc') + YARD::Registry.save(false, yardoc) + YARD::Registry.load(yardoc) + FileUtils.mkdir_p('sig') + `sord #{target} --rbs --no-regenerate` + end end end From 328eb4354e9d8568be59e46df903e5506162fce1 Mon Sep 17 00:00:00 2001 From: Fred Snyder Date: Sat, 28 Jun 2025 09:40:57 -0400 Subject: [PATCH 052/561] False positive in typecheck --- lib/solargraph/rbs_map.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/solargraph/rbs_map.rb b/lib/solargraph/rbs_map.rb index b0b4b470d..35494a592 100644 --- a/lib/solargraph/rbs_map.rb +++ b/lib/solargraph/rbs_map.rb @@ -86,6 +86,8 @@ def conversions # @return [Boolean] true if adding the library succeeded def add_library loader, library, version @resolved = if loader.has_library?(library: library, version: version) + # @todo Typecheck thinks path keyword param is required + # @sg-ignore loader.add library: library, version: version Solargraph.logger.info "#{short_name} successfully loaded library #{library}" true From 7f70c763ccd5f7e22c1907e1bf11ca3cc2e5d6f9 Mon Sep 17 00:00:00 2001 From: Fred Snyder Date: Sat, 28 Jun 2025 10:18:34 -0400 Subject: [PATCH 053/561] Fix directories --- lib/solargraph/shell.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/solargraph/shell.rb b/lib/solargraph/shell.rb index 8d0ca78b7..521c427f5 100755 --- a/lib/solargraph/shell.rb +++ b/lib/solargraph/shell.rb @@ -295,13 +295,14 @@ def rbs end end rake_yard(store) - target = File.absolute_path(File.join('sig', options[:filename])) + work_dir = Dir.pwd Dir.mktmpdir do |tmpdir| Dir.chdir tmpdir do yardoc = File.join(tmpdir, '.yardoc') YARD::Registry.save(false, yardoc) YARD::Registry.load(yardoc) - FileUtils.mkdir_p('sig') + target = File.join(work_dir, 'sig', options[:filename]) + FileUtils.mkdir_p(File.join(work_dir, 'sig')) `sord #{target} --rbs --no-regenerate` end end From 78031cb6c4b30898d8a4218f452ea33d1c5446b1 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 28 Jun 2025 11:37:55 -0400 Subject: [PATCH 054/561] Fix merge --- lib/solargraph/pin/base.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/solargraph/pin/base.rb b/lib/solargraph/pin/base.rb index de19922d1..c2d4c0a97 100644 --- a/lib/solargraph/pin/base.rb +++ b/lib/solargraph/pin/base.rb @@ -309,7 +309,6 @@ def choose_pin_attr(other, attr) end # arbitrary way of choosing a pin [val1, val2].compact.min_by { _1.best_location.to_s } ->>>>>>> master end def assert_source_provided From 3923f5095155d5e73aefbe336bf127efa1517126 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 28 Jun 2025 11:46:53 -0400 Subject: [PATCH 055/561] Fix merge issue --- lib/solargraph/source_map/clip.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/solargraph/source_map/clip.rb b/lib/solargraph/source_map/clip.rb index 4cbec7cfd..e404cc31d 100644 --- a/lib/solargraph/source_map/clip.rb +++ b/lib/solargraph/source_map/clip.rb @@ -12,7 +12,7 @@ def initialize api_map, cursor @api_map = api_map @cursor = cursor closure_pin = closure - closure_pin.rebind(api_map) if closure_pin.is_a?(Pin::Block) && !Solargraph::Range.from_node(block_pin.receiver).contain?(cursor.range.start) + closure_pin.rebind(api_map) if closure_pin.is_a?(Pin::Block) && !Solargraph::Range.from_node(closure_pin.receiver).contain?(cursor.range.start) end # @return [Array] Relevant pins for infering the type of the Cursor's position From d46fcadfed22b834d751facae8b922a3b69f4589 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 28 Jun 2025 12:09:41 -0400 Subject: [PATCH 056/561] Bump version for differentiation of this dev branch in plugins --- lib/solargraph/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/solargraph/version.rb b/lib/solargraph/version.rb index 0f307b521..e34401fc8 100755 --- a/lib/solargraph/version.rb +++ b/lib/solargraph/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module Solargraph - VERSION = '0.55.4' + VERSION = '0.56.0.pre1' end From a1e8162c76737e63689c495c09efca57273eebc3 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 28 Jun 2025 13:23:46 -0400 Subject: [PATCH 057/561] Move to main branch of solargraph-rails --- .github/workflows/plugins.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index a78c7c166..eab358c39 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -1,3 +1,4 @@ +--- name: Plugin Backwards Compatibility Tests on: @@ -69,17 +70,16 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - name: clone https://github.com/iftheshoefritz/solargraph-rails/ + - name: clone solargraph-rails run: | cd .. - # TODO: Replace with main branch of iftheshoefritz/solargraph-rails once fixes are merged - git clone -b 2025-05-07 https://github.com/apiology/solargraph-rails.git + git clone https://github.com/apiology/solargraph-rails.git cd solargraph-rails - name: Set up Ruby uses: ruby/setup-ruby@v1 with: - # solargraph-rails supports Ruby 3.1+ - ruby-version: '3.1' + # solargraph-rails supports Ruby 3.0+ + ruby-version: '3.0' bundler-cache: false # specified in Gemfile bundler: 2.3 From 61e0b0fc877e5a20acc0c87863cb0ead6d3af3fd Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 28 Jun 2025 16:02:09 -0400 Subject: [PATCH 058/561] Updates against latest solargraph-rails --- .github/workflows/plugins.yml | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index eab358c39..b8badc168 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -1,6 +1,10 @@ --- name: Plugin Backwards Compatibility Tests +# To debug locally: +# npm install -g act +# cd /Users/broz/src/solargraph/ && act pull_request -j run_solargraph_rails_specs # e.g + on: push: branches: [ master ] @@ -73,29 +77,39 @@ jobs: - name: clone solargraph-rails run: | cd .. - git clone https://github.com/apiology/solargraph-rails.git + git clone https://github.com/iftheshoefritz/solargraph-rails.git cd solargraph-rails - name: Set up Ruby uses: ruby/setup-ruby@v1 with: # solargraph-rails supports Ruby 3.0+ - ruby-version: '3.0' + ruby-version: '3.1' bundler-cache: false # specified in Gemfile bundler: 2.3 - name: Install gems run: | + set -x + BUNDLE_PATH="${GITHUB_WORKSPACE:?}/vendor/bundle" + export BUNDLE_PATH cd ../solargraph-rails + echo "gem 'solargraph', path: '${GITHUB_WORKSPACE:?}'" >> Gemfile + bundle install cd spec/rails7 - echo "gem 'solargraph', path: '/home/runner/work/solargraph/solargraph'" >> Gemfile bundle install - bundle exec rbs collection install + bundle exec --gemfile ../../Gemfile rbs --version + bundle exec --gemfile ../../Gemfile rbs collection install cd ../../ - echo "gem 'solargraph', path: '/home/runner/work/solargraph/solargraph'" >> Gemfile - bundle install bundle exec rbs collection init bundle exec rbs collection install + env: + MATRIX_SOLARGRAPH_VERSION: '>=0.56.0.pre1' - name: Run specs run: | + BUNDLE_PATH="${GITHUB_WORKSPACE:?}/vendor/bundle" + export BUNDLE_PATH cd ../solargraph-rails bundle exec rake spec + env: + MATRIX_SOLARGRAPH_VERSION: '>=0.56.0.pre1' + MATRIX_RAILS_VERSION: 7.0 From c752f6227324d9d9383e983aa0cc47b9e3d55d21 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 28 Jun 2025 17:01:54 -0400 Subject: [PATCH 059/561] Disable solargraph-rspec specs for now --- .github/workflows/plugins.yml | 48 +++++++++++++++++------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index b8badc168..7d8c1569c 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -44,30 +44,30 @@ jobs: - name: Ensure specs still run run: bundle exec rake spec - run_solargraph_rspec_specs: - # check out solargraph-rspec as well as this project, and point the former to use the latter as a local gem - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: clone https://github.com/lekemula/solargraph-rspec/ - run: | - cd .. - git clone https://github.com/lekemula/solargraph-rspec.git - cd solargraph-rspec - - name: Set up Ruby - uses: ruby/setup-ruby@v1 - with: - ruby-version: '3.0' - bundler-cache: false - - name: Install gems - run: | - cd ../solargraph-rspec - echo "gem 'solargraph', path: '../solargraph'" >> Gemfile - bundle install - - name: Run specs - run: | - cd ../solargraph-rspec - bundle exec rake spec + # run_solargraph_rspec_specs: + # # check out solargraph-rspec as well as this project, and point the former to use the latter as a local gem + # runs-on: ubuntu-latest + # steps: + # - uses: actions/checkout@v3 + # - name: clone https://github.com/lekemula/solargraph-rspec/ + # run: | + # cd .. + # git clone https://github.com/lekemula/solargraph-rspec.git + # cd solargraph-rspec + # - name: Set up Ruby + # uses: ruby/setup-ruby@v1 + # with: + # ruby-version: '3.0' + # bundler-cache: false + # - name: Install gems + # run: | + # cd ../solargraph-rspec + # echo "gem 'solargraph', path: '../solargraph'" >> Gemfile + # bundle install + # - name: Run specs + # run: | + # cd ../solargraph-rspec + # bundle exec rake spec run_solargraph_rails_specs: # check out solargraph-rails as well as this project, and point the former to use the latter as a local gem From 437538ecd5aaf197660952362b14a598b3dd18ed Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 28 Jun 2025 17:08:31 -0400 Subject: [PATCH 060/561] Bump version with new code from master --- lib/solargraph/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/solargraph/version.rb b/lib/solargraph/version.rb index e34401fc8..1e3bfe267 100755 --- a/lib/solargraph/version.rb +++ b/lib/solargraph/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module Solargraph - VERSION = '0.56.0.pre1' + VERSION = '0.56.0.pre2' end From e975c146a36d17d6fff644e9b1b106f72e4acb6a Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 28 Jun 2025 17:36:27 -0400 Subject: [PATCH 061/561] Force build --- .github/workflows/plugins.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index 7d8c1569c..2f782bd99 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -3,7 +3,7 @@ name: Plugin Backwards Compatibility Tests # To debug locally: # npm install -g act -# cd /Users/broz/src/solargraph/ && act pull_request -j run_solargraph_rails_specs # e.g +# cd /Users/broz/src/solargraph/ && act pull_request -j run_solargraph_rails_specs # e.g. on: push: From d65bef342ee1ca15fdc7c7fabed493eac175222a Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 28 Jun 2025 20:17:31 -0400 Subject: [PATCH 062/561] Force build --- .github/workflows/plugins.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index 2f782bd99..ebba89ad9 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -7,9 +7,9 @@ name: Plugin Backwards Compatibility Tests on: push: - branches: [ master ] + branches: [master] pull_request: - branches: [ master ] + branches: [master] permissions: contents: read From 4eb32d844966d550ad19b4458724384458daf724 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 28 Jun 2025 20:22:15 -0400 Subject: [PATCH 063/561] Force build --- .github/workflows/plugins.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index ebba89ad9..8c87ccaf7 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -44,6 +44,7 @@ jobs: - name: Ensure specs still run run: bundle exec rake spec + # # run_solargraph_rspec_specs: # # check out solargraph-rspec as well as this project, and point the former to use the latter as a local gem # runs-on: ubuntu-latest @@ -85,8 +86,8 @@ jobs: # solargraph-rails supports Ruby 3.0+ ruby-version: '3.1' bundler-cache: false - # specified in Gemfile - bundler: 2.3 + # specified in Gemfile.lock + bundler: 2.6.9 - name: Install gems run: | set -x From 55555539113e9ba106e465d97cd241a91734beb0 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 29 Jun 2025 12:16:01 -0400 Subject: [PATCH 064/561] Include Rakefile, Gemfile, and gemspec files in config by default We package conventions for these, but unless the user remembers to configure them, they don't take advantage of Solargraph --- lib/solargraph/convention/gemspec.rb | 3 ++- lib/solargraph/workspace/config.rb | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/solargraph/convention/gemspec.rb b/lib/solargraph/convention/gemspec.rb index 01175a0b1..946171e54 100644 --- a/lib/solargraph/convention/gemspec.rb +++ b/lib/solargraph/convention/gemspec.rb @@ -12,7 +12,8 @@ def local source_map 'Gem::Specification.new', %( @yieldparam [self] - ) + ), + source: :gemspec ) ] ) diff --git a/lib/solargraph/workspace/config.rb b/lib/solargraph/workspace/config.rb index 1514ff617..ce45e5b11 100644 --- a/lib/solargraph/workspace/config.rb +++ b/lib/solargraph/workspace/config.rb @@ -151,7 +151,7 @@ def read_config config_path = '' # @return [Hash{String => Array, Hash, Integer}] def default_config { - 'include' => ['**/*.rb'], + 'include' => ['Rakefile', 'Gemfile', '*.gemspec', '**/*.rb'], 'exclude' => ['spec/**/*', 'test/**/*', 'vendor/**/*', '.bundle/**/*'], 'require' => [], 'domains' => [], From 5525902b8dde3a23f23937faa606a5bdc5834310 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 29 Jun 2025 15:06:49 -0400 Subject: [PATCH 065/561] Debug --- .github/workflows/plugins.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index 8c87ccaf7..fae2694e8 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -44,7 +44,6 @@ jobs: - name: Ensure specs still run run: bundle exec rake spec - # # run_solargraph_rspec_specs: # # check out solargraph-rspec as well as this project, and point the former to use the latter as a local gem # runs-on: ubuntu-latest @@ -110,6 +109,7 @@ jobs: BUNDLE_PATH="${GITHUB_WORKSPACE:?}/vendor/bundle" export BUNDLE_PATH cd ../solargraph-rails + bundle exec solargraph --version bundle exec rake spec env: MATRIX_SOLARGRAPH_VERSION: '>=0.56.0.pre1' From 9996feec100fa3916153e6b21dd0955f84ba6085 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 29 Jun 2025 15:33:28 -0400 Subject: [PATCH 066/561] Test without one rbs collection --- .github/workflows/plugins.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index fae2694e8..882c69a09 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -100,8 +100,8 @@ jobs: bundle exec --gemfile ../../Gemfile rbs --version bundle exec --gemfile ../../Gemfile rbs collection install cd ../../ - bundle exec rbs collection init - bundle exec rbs collection install + # bundle exec rbs collection init + # bundle exec rbs collection install env: MATRIX_SOLARGRAPH_VERSION: '>=0.56.0.pre1' - name: Run specs From 58d69f728e662dbe07d56306105315a6f5a74b77 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 29 Jun 2025 16:13:38 -0400 Subject: [PATCH 067/561] Match spec Ruby versions between projects for consistency --- .github/workflows/plugins.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index 882c69a09..efdb14ea1 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -83,7 +83,7 @@ jobs: uses: ruby/setup-ruby@v1 with: # solargraph-rails supports Ruby 3.0+ - ruby-version: '3.1' + ruby-version: '3.0' bundler-cache: false # specified in Gemfile.lock bundler: 2.6.9 From 1349dd6777dc2ae3c6ab3823c6178b69c89073fa Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 29 Jun 2025 16:15:55 -0400 Subject: [PATCH 068/561] Match spec Ruby versions between projects for consistency --- .github/workflows/plugins.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index efdb14ea1..e217e4cd6 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -85,8 +85,6 @@ jobs: # solargraph-rails supports Ruby 3.0+ ruby-version: '3.0' bundler-cache: false - # specified in Gemfile.lock - bundler: 2.6.9 - name: Install gems run: | set -x From 0e59a87b7a770c0c88a18ddd958f3d2123508b37 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 29 Jun 2025 16:18:25 -0400 Subject: [PATCH 069/561] Specify updated bundler --- .github/workflows/plugins.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index e217e4cd6..a154223f0 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -85,6 +85,7 @@ jobs: # solargraph-rails supports Ruby 3.0+ ruby-version: '3.0' bundler-cache: false + bundler: latest - name: Install gems run: | set -x From 73843221ec558681e0f17efc56ec256ede81a620 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 29 Jun 2025 16:32:38 -0400 Subject: [PATCH 070/561] Set Rails version, debug --- .github/workflows/plugins.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index a154223f0..e6c3c153b 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -103,12 +103,16 @@ jobs: # bundle exec rbs collection install env: MATRIX_SOLARGRAPH_VERSION: '>=0.56.0.pre1' + MATRIX_RAILS_VERSION: 7.0 - name: Run specs run: | BUNDLE_PATH="${GITHUB_WORKSPACE:?}/vendor/bundle" export BUNDLE_PATH cd ../solargraph-rails bundle exec solargraph --version + bundle info solargraph + bundle info rbs + bundle info yard bundle exec rake spec env: MATRIX_SOLARGRAPH_VERSION: '>=0.56.0.pre1' From 9744d16e7c9c6b4537e15afbf5e128b5e63ba417 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 29 Jun 2025 16:43:29 -0400 Subject: [PATCH 071/561] Set env variables in initial bundle install --- .github/workflows/plugins.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index e6c3c153b..20842c28d 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -86,6 +86,9 @@ jobs: ruby-version: '3.0' bundler-cache: false bundler: latest + env: + MATRIX_SOLARGRAPH_VERSION: '>=0.56.0.pre1' + MATRIX_RAILS_VERSION: 7.0 - name: Install gems run: | set -x From 2ee2bf4f917f18b52b8dca343dfd36273b07c79e Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 29 Jun 2025 16:46:52 -0400 Subject: [PATCH 072/561] Quote numbers --- .github/workflows/plugins.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index 20842c28d..5061f42b1 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -88,7 +88,7 @@ jobs: bundler: latest env: MATRIX_SOLARGRAPH_VERSION: '>=0.56.0.pre1' - MATRIX_RAILS_VERSION: 7.0 + MATRIX_RAILS_VERSION: "7.0" - name: Install gems run: | set -x @@ -106,7 +106,7 @@ jobs: # bundle exec rbs collection install env: MATRIX_SOLARGRAPH_VERSION: '>=0.56.0.pre1' - MATRIX_RAILS_VERSION: 7.0 + MATRIX_RAILS_VERSION: "7.0" - name: Run specs run: | BUNDLE_PATH="${GITHUB_WORKSPACE:?}/vendor/bundle" @@ -119,4 +119,4 @@ jobs: bundle exec rake spec env: MATRIX_SOLARGRAPH_VERSION: '>=0.56.0.pre1' - MATRIX_RAILS_VERSION: 7.0 + MATRIX_RAILS_VERSION: "7.0" From 0edbbbb8d8e6c2f0d02c1ef98fb5f6567d0217c6 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 29 Jun 2025 16:58:51 -0400 Subject: [PATCH 073/561] Try bumping RBS --- .github/workflows/plugins.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index 5061f42b1..95254aaed 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -99,6 +99,7 @@ jobs: bundle install cd spec/rails7 bundle install + bundle update rbs bundle exec --gemfile ../../Gemfile rbs --version bundle exec --gemfile ../../Gemfile rbs collection install cd ../../ From 88594061b74408e5782fe75ddeb454347612c357 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 29 Jun 2025 17:01:44 -0400 Subject: [PATCH 074/561] Try bumping RBS --- .github/workflows/plugins.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index 95254aaed..1d1fff950 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -97,9 +97,9 @@ jobs: cd ../solargraph-rails echo "gem 'solargraph', path: '${GITHUB_WORKSPACE:?}'" >> Gemfile bundle install + bundle update rbs cd spec/rails7 bundle install - bundle update rbs bundle exec --gemfile ../../Gemfile rbs --version bundle exec --gemfile ../../Gemfile rbs collection install cd ../../ From 050178d969870e7cfb9731ddd255aa2244d8c6f1 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 29 Jun 2025 17:06:54 -0400 Subject: [PATCH 075/561] debug --- .github/workflows/plugins.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index 1d1fff950..715883c2b 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -97,7 +97,7 @@ jobs: cd ../solargraph-rails echo "gem 'solargraph', path: '${GITHUB_WORKSPACE:?}'" >> Gemfile bundle install - bundle update rbs + bundle add rbs --version='>=3.9.4' cd spec/rails7 bundle install bundle exec --gemfile ../../Gemfile rbs --version From 79e90c592ee47dd088320fb571a5a41a932e9fc8 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 29 Jun 2025 17:10:06 -0400 Subject: [PATCH 076/561] Debug --- .github/workflows/plugins.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index 715883c2b..5061f42b1 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -97,7 +97,6 @@ jobs: cd ../solargraph-rails echo "gem 'solargraph', path: '${GITHUB_WORKSPACE:?}'" >> Gemfile bundle install - bundle add rbs --version='>=3.9.4' cd spec/rails7 bundle install bundle exec --gemfile ../../Gemfile rbs --version From 7ebc99d8b45babe9593b90ef5be91a4303b79f8d Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 29 Jun 2025 17:16:30 -0400 Subject: [PATCH 077/561] Debug --- lib/solargraph/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/solargraph/version.rb b/lib/solargraph/version.rb index 1e3bfe267..7beeb4276 100755 --- a/lib/solargraph/version.rb +++ b/lib/solargraph/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module Solargraph - VERSION = '0.56.0.pre2' + VERSION = '0.56.0.pre3' end From d65078f9755beb6eb7be25d0f78a58fce9049823 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 29 Jun 2025 17:30:01 -0400 Subject: [PATCH 078/561] Debug --- .github/workflows/plugins.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index 5061f42b1..8bc53e10c 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -96,8 +96,9 @@ jobs: export BUNDLE_PATH cd ../solargraph-rails echo "gem 'solargraph', path: '${GITHUB_WORKSPACE:?}'" >> Gemfile - bundle install - cd spec/rails7 + RAILS_DIR="${GITHUB_WORKSPACE:?}/spec/rails7 + export RAILS_DIR + cd ${RAILS_DIR} bundle install bundle exec --gemfile ../../Gemfile rbs --version bundle exec --gemfile ../../Gemfile rbs collection install From 40b0e2427ac2e6b90921dd6d97583c67fb829b03 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 29 Jun 2025 17:34:05 -0400 Subject: [PATCH 079/561] Debug --- .github/workflows/plugins.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index 8bc53e10c..d965e82aa 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -96,6 +96,7 @@ jobs: export BUNDLE_PATH cd ../solargraph-rails echo "gem 'solargraph', path: '${GITHUB_WORKSPACE:?}'" >> Gemfile + bundle update solargraph RAILS_DIR="${GITHUB_WORKSPACE:?}/spec/rails7 export RAILS_DIR cd ${RAILS_DIR} @@ -108,6 +109,7 @@ jobs: env: MATRIX_SOLARGRAPH_VERSION: '>=0.56.0.pre1' MATRIX_RAILS_VERSION: "7.0" + MATRIX_RAILS_MAJOR_VERSION: '7' - name: Run specs run: | BUNDLE_PATH="${GITHUB_WORKSPACE:?}/vendor/bundle" From 6f4728402aef020d6fcfd1bcd899d294782f812a Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 29 Jun 2025 17:35:21 -0400 Subject: [PATCH 080/561] Debug --- .github/workflows/plugins.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index d965e82aa..0d36ea931 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -96,7 +96,7 @@ jobs: export BUNDLE_PATH cd ../solargraph-rails echo "gem 'solargraph', path: '${GITHUB_WORKSPACE:?}'" >> Gemfile - bundle update solargraph + bundle install RAILS_DIR="${GITHUB_WORKSPACE:?}/spec/rails7 export RAILS_DIR cd ${RAILS_DIR} From cdb8a8b453143819713073bbc539e66a655dd5bf Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 29 Jun 2025 17:38:36 -0400 Subject: [PATCH 081/561] Debug --- .github/workflows/plugins.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index 0d36ea931..d59fa66a2 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -97,7 +97,7 @@ jobs: cd ../solargraph-rails echo "gem 'solargraph', path: '${GITHUB_WORKSPACE:?}'" >> Gemfile bundle install - RAILS_DIR="${GITHUB_WORKSPACE:?}/spec/rails7 + RAILS_DIR="${GITHUB_WORKSPACE:?}/spec/rails7" export RAILS_DIR cd ${RAILS_DIR} bundle install From ee87f7635f61c29feb69c19813bf463ac5389ecb Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 29 Jun 2025 17:43:37 -0400 Subject: [PATCH 082/561] Debug --- .github/workflows/plugins.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index d59fa66a2..4ced9e330 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -97,7 +97,7 @@ jobs: cd ../solargraph-rails echo "gem 'solargraph', path: '${GITHUB_WORKSPACE:?}'" >> Gemfile bundle install - RAILS_DIR="${GITHUB_WORKSPACE:?}/spec/rails7" + RAILS_DIR="$(pwd)/spec/rails7" export RAILS_DIR cd ${RAILS_DIR} bundle install From 5eb5fedd83692d5167fd46468c0ad95adab001cd Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 5 Jul 2025 16:46:01 -0400 Subject: [PATCH 083/561] Allow newer RBS gem versions This allow users to upgrade to recent Tapioca versions. Tapioca now requires newish versions of the spoom gem, which depends on 4.x pre-releases of the rbs gem. --- solargraph.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/solargraph.gemspec b/solargraph.gemspec index 5008b6247..29f45e7ef 100755 --- a/solargraph.gemspec +++ b/solargraph.gemspec @@ -35,7 +35,7 @@ Gem::Specification.new do |s| s.add_runtime_dependency 'ostruct', '~> 0.6' s.add_runtime_dependency 'parser', '~> 3.0' s.add_runtime_dependency 'prism', '~> 1.4' - s.add_runtime_dependency 'rbs', '~> 3.3' + s.add_runtime_dependency 'rbs', ['>= 3.3', '<= 4.0.0.dev.4'] s.add_runtime_dependency 'reverse_markdown', '~> 3.0' s.add_runtime_dependency 'rubocop', '~> 1.38' s.add_runtime_dependency 'thor', '~> 1.0' From ae8409068aa02c3ec420734314cfaa13bd9ca2f6 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 5 Jul 2025 16:53:13 -0400 Subject: [PATCH 084/561] Add RBS version to test matrix --- .github/workflows/rspec.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/rspec.yml b/.github/workflows/rspec.yml index e2c94cc1b..a8f2d8ba9 100644 --- a/.github/workflows/rspec.yml +++ b/.github/workflows/rspec.yml @@ -23,7 +23,7 @@ jobs: strategy: matrix: ruby-version: ['3.0', '3.1', '3.2', '3.3', '3.4', 'head'] - + rbs-version: ['3.3.0', '3.9.4', '4.0.0.dev.4'] steps: - uses: actions/checkout@v3 - name: Set up Ruby @@ -31,6 +31,8 @@ jobs: with: ruby-version: ${{ matrix.ruby-version }} bundler-cache: false + - name: Set rbs version + run: echo "gem 'rbs', '${{ matrix.rbs-version }}'" >> .Gemfile - name: Install gems run: bundle install - name: Run tests From 35c7f4c80a52f579a24d1f5e3c6c64bd4afbef33 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 5 Jul 2025 16:55:47 -0400 Subject: [PATCH 085/561] Add exclude rule --- .github/workflows/rspec.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/rspec.yml b/.github/workflows/rspec.yml index a8f2d8ba9..7483dd498 100644 --- a/.github/workflows/rspec.yml +++ b/.github/workflows/rspec.yml @@ -24,6 +24,12 @@ jobs: matrix: ruby-version: ['3.0', '3.1', '3.2', '3.3', '3.4', 'head'] rbs-version: ['3.3.0', '3.9.4', '4.0.0.dev.4'] + # Ruby 3.0 doesn't work with RBS 3.9.4 or 4.0.0.dev.4 + exclude: + - ruby-version: '3.0' + rbs-version: '3.9.4' + - ruby-version: '3.0' + rbs-version: '4.0.0.dev.4' steps: - uses: actions/checkout@v3 - name: Set up Ruby From f012274c02ed2043f19feb3bfd0a17197c03265b Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 5 Jul 2025 16:59:40 -0400 Subject: [PATCH 086/561] Move to 3.6.1 --- .github/workflows/rspec.yml | 2 +- solargraph.gemspec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/rspec.yml b/.github/workflows/rspec.yml index 7483dd498..b45513b79 100644 --- a/.github/workflows/rspec.yml +++ b/.github/workflows/rspec.yml @@ -23,7 +23,7 @@ jobs: strategy: matrix: ruby-version: ['3.0', '3.1', '3.2', '3.3', '3.4', 'head'] - rbs-version: ['3.3.0', '3.9.4', '4.0.0.dev.4'] + rbs-version: ['3.6.1', '3.9.4', '4.0.0.dev.4'] # Ruby 3.0 doesn't work with RBS 3.9.4 or 4.0.0.dev.4 exclude: - ruby-version: '3.0' diff --git a/solargraph.gemspec b/solargraph.gemspec index 29f45e7ef..eb958c929 100755 --- a/solargraph.gemspec +++ b/solargraph.gemspec @@ -35,7 +35,7 @@ Gem::Specification.new do |s| s.add_runtime_dependency 'ostruct', '~> 0.6' s.add_runtime_dependency 'parser', '~> 3.0' s.add_runtime_dependency 'prism', '~> 1.4' - s.add_runtime_dependency 'rbs', ['>= 3.3', '<= 4.0.0.dev.4'] + s.add_runtime_dependency 'rbs', ['>= 3.6.1', '<= 4.0.0.dev.4'] s.add_runtime_dependency 'reverse_markdown', '~> 3.0' s.add_runtime_dependency 'rubocop', '~> 1.38' s.add_runtime_dependency 'thor', '~> 1.0' From 3eaa610bac0de99f1b1be1cb20ba1e27b2b39275 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 9 Jul 2025 14:25:09 -0400 Subject: [PATCH 087/561] Add linting workflow, watching for RuboCop + Solargraph regressions --- .github/workflows/linting.yml | 75 +++++++++++++++++++++++++++++++++++ .overcommit.yml | 51 ++++++++++++++++++++++++ solargraph.gemspec | 2 + 3 files changed, 128 insertions(+) create mode 100644 .github/workflows/linting.yml create mode 100644 .overcommit.yml diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml new file mode 100644 index 000000000..a616a5460 --- /dev/null +++ b/.github/workflows/linting.yml @@ -0,0 +1,75 @@ +--- +# To debug locally: +# npm install -g act +# act pull_release -j overcommit +# + +name: Linting + +on: + workflow_dispatch: {} + pull_request: + branches: [main] + push: + branches: + - 'main' + tags: + - 'v*' + +permissions: + pull-requests: write + +jobs: + overcommit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + # Number of commits to fetch. 0 indicates all history for all branches and tags. + with: + fetch-depth: 0 + + - uses: ruby/setup-ruby@v1 + with: + ruby-version: 3.4 + bundler: latest + bundler-cache: true + cache-version: ${{ matrix.solargraph-version }}-${{ matrix.versions.rails-major }}.${{ matrix.versions.rails-minor }}-2025-06-06 + + - name: Restore cache of gem annotations + id: dot-cache-restore + uses: actions/cache/restore@v4 + with: + key: | + 2025-06-26-09-${{ runner.os }}-dot-cache-${{ matrix.solargraph-version }}-${{ matrix.versions.rails-major }}--${{ matrix.versions.rails-minor }}-${{ hashFiles('spec/**/Gemfile.lock') }}-${{ hashFiles('Gemfile.lock') }} + restore-keys: | + 2025-06-26-09-${{ runner.os }}-dot-cache-${{ matrix.solargraph-version }}-${{ matrix.versions.rails-major }}-${{ matrix.versions.rails-minor }}-${{ hashFiles('spec/**/Gemfile.lock') }}- + 2025-06-26-09-${{ runner.os }}-dot-cache-${{ matrix.solargraph-version }}-${{ matrix.versions.rails-major }}-${{ matrix.versions.rails-minor }}- + 2025-06-26-09-${{ runner.os }}-dot-cache-${{ matrix.solargraph-version }}-${{ matrix.versions.rails-major }}- + 2025-06-26-09-${{ runner.os }}-dot-cache-${{ matrix.solargraph-version }}- + path: | + /home/runner/.cache/solargraph + + - name: Overcommit + run: | + bundle exec overcommit --sign + bundle exec overcommit --run --diff origin/main + rubocop: + name: rubocop + runs-on: ubuntu-latest + env: + BUNDLE_ONLY: rubocop + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: ruby/setup-ruby@1a615958ad9d422dd932dc1d5823942ee002799f # v1.227.0 + with: + ruby-version: '3.3' + bundler-cache: true + - uses: reviewdog/action-rubocop@fcb74ba274da10b18d038d0bcddaae3518739634 # v2.21.2 + with: + reporter: github-pr-check + skip_install: true + use_bundler: true + rubocop_extensions: 'rubocop-performance:gemfile rubocop-rspec:gemfile rubocop-rake:gemfile rubocop-yard:gemfile' + fail_level: info + rubocop_version: Gemfile + level: info diff --git a/.overcommit.yml b/.overcommit.yml new file mode 100644 index 000000000..215770aed --- /dev/null +++ b/.overcommit.yml @@ -0,0 +1,51 @@ +--- +# Use this file to configure the Overcommit hooks you wish to use. This will +# extend the default configuration defined in: +# https://github.com/sds/overcommit/blob/master/config/default.yml +# +# At the topmost level of this YAML file is a key representing type of hook +# being run (e.g. pre-commit, commit-msg, etc.). Within each type you can +# customize each hook, such as whether to only run it on certain files (via +# `include`), whether to only display output if it fails (via `quiet`), etc. +# +# For a complete list of hooks, see: +# https://github.com/sds/overcommit/tree/master/lib/overcommit/hook +# +# For a complete list of options that you can use to customize hooks, see: +# https://github.com/sds/overcommit#configuration +# +# Uncomment the following lines to make the configuration take effect. + +PreCommit: + RuboCop: + enabled: true + on_warn: fail # Treat all warnings as failures + + Solargraph: + enabled: true + exclude: + - 'spec/**/*' + - lib/solargraph/rails/annotations/**/* + - vendor/**/* + - ".bundle/**/*" + + # creates false positives in CI + AuthorName: + enabled: false + + # creates false positives in CI + AuthorEmail: + enabled: false + +# +# TrailingWhitespace: +# enabled: true +# exclude: +# - '**/db/structure.sql' # Ignore trailing whitespace in generated files +# +#PostCheckout: +# ALL: # Special hook name that customizes all hooks of this type +# quiet: true # Change all post-checkout hooks to only display output on failure +# +# IndexTags: +# enabled: true # Generate a tags file with `ctags` each time HEAD changes diff --git a/solargraph.gemspec b/solargraph.gemspec index 8a3c7a20c..94ebbe46c 100755 --- a/solargraph.gemspec +++ b/solargraph.gemspec @@ -40,6 +40,7 @@ Gem::Specification.new do |s| s.add_runtime_dependency 'rubocop', '~> 1.76' s.add_runtime_dependency 'rubocop-rake', '~> 0.7' s.add_runtime_dependency 'rubocop-rspec', '~> 3.6' + s.add_runtime_dependency 'rubocop-yard', '~> 1.0' s.add_runtime_dependency 'thor', '~> 1.0' s.add_runtime_dependency 'tilt', '~> 2.0' s.add_runtime_dependency 'yard', '~> 0.9', '>= 0.9.24' @@ -52,6 +53,7 @@ Gem::Specification.new do |s| s.add_development_dependency 'simplecov', '~> 0.21' s.add_development_dependency 'simplecov-lcov', '~> 0.8' s.add_development_dependency 'undercover', '~> 0.6' + s.add_development_dependency 'overcommit', '~> 0.68.0' s.add_development_dependency 'webmock', '~> 3.6' # work around missing yard dependency needed as of Ruby 3.5 s.add_development_dependency 'irb', '~> 1.15' From 534f2020391f311b815ea6510db5781fa6c7a4f6 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 9 Jul 2025 14:29:47 -0400 Subject: [PATCH 088/561] Add rubocop-yard --- .rubocop.yml | 1 + .rubocop_todo.yml | 405 ++++++++++++++++++++++++++++++++-------------- 2 files changed, 286 insertions(+), 120 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 576c17e9e..a769ad37e 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -31,3 +31,4 @@ Metrics/MethodLength: plugins: - rubocop-rspec - rubocop-rake + - rubocop-yard diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index d0a679cf4..a4307b7e5 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,12 +1,12 @@ # This configuration was generated by # `rubocop --auto-gen-config` -# on 2025-06-13 12:53:14 UTC using RuboCop version 1.76.1. +# on 2025-07-09 18:29:07 UTC using RuboCop version 1.78.0. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. -# Offense count: 20 +# Offense count: 22 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: Include. # Include: **/*.gemspec @@ -23,7 +23,7 @@ Gemspec/DeprecatedAttributeAssignment: - 'solargraph.gemspec' - 'spec/fixtures/rdoc-lib/rdoc-lib.gemspec' -# Offense count: 7 +# Offense count: 10 # Configuration parameters: EnforcedStyle, AllowedGems, Include. # SupportedStyles: Gemfile, gems.rb, gemspec # Include: **/*.gemspec, **/Gemfile, **/gems.rb @@ -31,6 +31,14 @@ Gemspec/DevelopmentDependencies: Exclude: - 'solargraph.gemspec' +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: TreatCommentsAsGroupSeparators, ConsiderPunctuation, Include. +# Include: **/*.gemspec +Gemspec/OrderedDependencies: + Exclude: + - 'solargraph.gemspec' + # Offense count: 3 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: Severity, Include. @@ -67,6 +75,12 @@ Layout/BlockAlignment: Exclude: - 'spec/source_map/mapper_spec.rb' +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +Layout/ClosingHeredocIndentation: + Exclude: + - 'spec/rbs_map/conversions_spec.rb' + # Offense count: 8 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowForAlignment. @@ -77,31 +91,44 @@ Layout/CommentIndentation: - 'lib/solargraph/parser/parser_gem/node_methods.rb' - 'lib/solargraph/source_map/mapper.rb' -# Offense count: 22 +# Offense count: 23 # This cop supports safe autocorrection (--autocorrect). Layout/ElseAlignment: Enabled: false -# Offense count: 2 +# Offense count: 3 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EmptyLineBetweenMethodDefs, EmptyLineBetweenClassDefs, EmptyLineBetweenModuleDefs, DefLikeMacros, AllowAdjacentOneLineDefs, NumberOfEmptyLines. Layout/EmptyLineBetweenDefs: Exclude: + - 'lib/solargraph/doc_map.rb' - 'lib/solargraph/language_server/message/initialize.rb' - 'lib/solargraph/pin/delegated_method.rb' -# Offense count: 8 +# Offense count: 12 # This cop supports safe autocorrection (--autocorrect). Layout/EmptyLines: Exclude: - 'lib/solargraph/bench.rb' - 'lib/solargraph/complex_type/unique_type.rb' - 'lib/solargraph/convention.rb' + - 'lib/solargraph/doc_map.rb' - 'lib/solargraph/language_server/message/extended/check_gem_version.rb' - 'lib/solargraph/language_server/message/initialize.rb' - 'lib/solargraph/pin/delegated_method.rb' - - 'spec/pin/parameter_spec.rb' + - 'lib/solargraph/rbs_map/conversions.rb' + - 'spec/complex_type_spec.rb' + - 'spec/pin/local_variable_spec.rb' - 'spec/pin/symbol_spec.rb' + - 'spec/type_checker/levels/strict_spec.rb' + +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle. +# SupportedStyles: empty_lines, empty_lines_except_namespace, empty_lines_special, no_empty_lines, beginning_only, ending_only +Layout/EmptyLinesAroundClassBody: + Exclude: + - 'lib/solargraph/rbs_map/core_map.rb' # Offense count: 1 # This cop supports safe autocorrection (--autocorrect). @@ -128,14 +155,17 @@ Layout/EndOfLine: - 'lib/solargraph/source/encoding_fixes.rb' - 'solargraph.gemspec' -# Offense count: 3 +# Offense count: 7 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowForAlignment, AllowBeforeTrailingComments, ForceEqualSignAlignment. Layout/ExtraSpacing: Exclude: - 'lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb' + - 'lib/solargraph/pin/closure.rb' + - 'lib/solargraph/pin/local_variable.rb' - 'lib/solargraph/rbs_map/conversions.rb' - 'lib/solargraph/type_checker.rb' + - 'spec/spec_helper.rb' # Offense count: 2 # This cop supports safe autocorrection (--autocorrect). @@ -158,7 +188,7 @@ Layout/FirstArrayElementIndentation: - 'spec/source/source_chainer_spec.rb' - 'spec/source_spec.rb' -# Offense count: 63 +# Offense count: 65 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, IndentationWidth. # SupportedStyles: special_inside_parentheses, consistent, align_braces @@ -169,6 +199,7 @@ Layout/FirstHashElementIndentation: - 'lib/solargraph/language_server/message/text_document/completion.rb' - 'lib/solargraph/language_server/message/text_document/rename.rb' - 'lib/solargraph/language_server/message/text_document/signature_help.rb' + - 'lib/solargraph/pin/base_variable.rb' - 'spec/language_server/host_spec.rb' - 'spec/language_server/message/completion_item/resolve_spec.rb' - 'spec/language_server/message/initialize_spec.rb' @@ -189,10 +220,11 @@ Layout/HashAlignment: Exclude: - 'lib/solargraph/workspace/config.rb' -# Offense count: 1 +# Offense count: 2 # This cop supports safe autocorrection (--autocorrect). Layout/HeredocIndentation: Exclude: + - 'spec/rbs_map/conversions_spec.rb' - 'spec/yard_map/mapper/to_method_spec.rb' # Offense count: 24 @@ -242,12 +274,13 @@ Layout/MultilineMethodCallIndentation: - 'lib/solargraph/pin/search.rb' - 'lib/solargraph/type_checker.rb' -# Offense count: 3 +# Offense count: 6 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, IndentationWidth. # SupportedStyles: aligned, indented Layout/MultilineOperationIndentation: Exclude: + - 'lib/solargraph/api_map.rb' - 'lib/solargraph/language_server/host/dispatch.rb' - 'lib/solargraph/source.rb' @@ -257,7 +290,26 @@ Layout/SpaceAfterComma: Exclude: - 'spec/source/cursor_spec.rb' -# Offense count: 27 +# Offense count: 6 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle. +# SupportedStyles: space, no_space +Layout/SpaceAroundEqualsInParameterDefault: + Exclude: + - 'lib/solargraph/pin/base.rb' + - 'lib/solargraph/pin/base_variable.rb' + - 'lib/solargraph/pin/callable.rb' + - 'lib/solargraph/pin/closure.rb' + - 'lib/solargraph/pin/local_variable.rb' + - 'lib/solargraph/pin/parameter.rb' + +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +Layout/SpaceAroundKeyword: + Exclude: + - 'spec/rbs_map/conversions_spec.rb' + +# Offense count: 28 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowForAlignment, EnforcedStyleForExponentOperator, EnforcedStyleForRationalLiterals. # SupportedStylesForExponentOperator: space, no_space @@ -266,6 +318,7 @@ Layout/SpaceAroundOperators: Exclude: - 'lib/solargraph/library.rb' - 'lib/solargraph/parser/parser_gem/node_methods.rb' + - 'lib/solargraph/pin/local_variable.rb' - 'lib/solargraph/source.rb' - 'lib/solargraph/source/change.rb' - 'lib/solargraph/source/cursor.rb' @@ -275,7 +328,7 @@ Layout/SpaceAroundOperators: - 'spec/library_spec.rb' - 'spec/yard_map/mapper_spec.rb' -# Offense count: 112 +# Offense count: 108 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces. # SupportedStyles: space, no_space @@ -289,7 +342,7 @@ Layout/SpaceBeforeComma: Exclude: - 'spec/source/cursor_spec.rb' -# Offense count: 189 +# Offense count: 183 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces, SpaceBeforeBlockParameters. # SupportedStyles: space, no_space @@ -297,18 +350,16 @@ Layout/SpaceBeforeComma: Layout/SpaceInsideBlockBraces: Enabled: false -# Offense count: 26 +# Offense count: 10 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces. # SupportedStyles: space, no_space, compact # SupportedStylesForEmptyBraces: space, no_space Layout/SpaceInsideHashLiteralBraces: Exclude: - - 'lib/solargraph/language_server/message/extended/document.rb' - 'lib/solargraph/language_server/message/extended/search.rb' - 'lib/solargraph/language_server/message/initialize.rb' - 'lib/solargraph/workspace/config.rb' - - 'spec/complex_type_spec.rb' - 'spec/language_server/host/message_worker_spec.rb' - 'spec/language_server/message/extended/check_gem_version_spec.rb' @@ -321,7 +372,7 @@ Layout/SpaceInsideParens: - 'lib/solargraph/pin/namespace.rb' - 'lib/solargraph/source_map.rb' -# Offense count: 4 +# Offense count: 3 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowInHeredoc. Layout/TrailingWhitespace: @@ -329,7 +380,6 @@ Layout/TrailingWhitespace: - 'lib/solargraph/language_server/message/client/register_capability.rb' - 'spec/api_map/config_spec.rb' - 'spec/convention_spec.rb' - - 'spec/source_map/clip_spec.rb' # Offense count: 2 # This cop supports safe autocorrection (--autocorrect). @@ -350,11 +400,26 @@ Lint/AmbiguousOperator: - 'lib/solargraph/pin/constant.rb' - 'lib/solargraph/pin/method.rb' -# Offense count: 1 +# Offense count: 5 # This cop supports safe autocorrection (--autocorrect). Lint/AmbiguousOperatorPrecedence: Exclude: + - 'lib/solargraph/pin/method.rb' + - 'lib/solargraph/source.rb' + +# Offense count: 18 +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: RequireParenthesesForMethodChains. +Lint/AmbiguousRange: + Exclude: + - 'lib/solargraph/library.rb' + - 'lib/solargraph/parser/parser_gem/node_methods.rb' - 'lib/solargraph/source.rb' + - 'lib/solargraph/source/change.rb' + - 'lib/solargraph/source/cursor.rb' + - 'lib/solargraph/source/source_chainer.rb' + - 'lib/solargraph/source_map/clip.rb' + - 'spec/library_spec.rb' # Offense count: 1 # This cop supports unsafe autocorrection (--autocorrect-all). @@ -383,20 +448,25 @@ Lint/ConstantDefinitionInBlock: Exclude: - 'spec/complex_type_spec.rb' -# Offense count: 6 +# Offense count: 8 # Configuration parameters: IgnoreLiteralBranches, IgnoreConstantBranches, IgnoreDuplicateElseBranch. Lint/DuplicateBranch: Exclude: - 'lib/solargraph/complex_type/type_methods.rb' - 'lib/solargraph/parser/parser_gem/node_chainer.rb' + - 'lib/solargraph/pin/base.rb' - 'lib/solargraph/rbs_map/conversions.rb' -# Offense count: 5 +# Offense count: 9 Lint/DuplicateMethods: Exclude: - 'lib/solargraph/complex_type.rb' - 'lib/solargraph/location.rb' - - 'lib/solargraph/pin/method.rb' + - 'lib/solargraph/pin/base.rb' + - 'lib/solargraph/pin/common.rb' + - 'lib/solargraph/pin/signature.rb' + - 'lib/solargraph/rbs_map.rb' + - 'lib/solargraph/rbs_map/core_map.rb' - 'lib/solargraph/source/chain/link.rb' # Offense count: 1 @@ -446,8 +516,8 @@ Lint/MissingSuper: # This cop supports safe autocorrection (--autocorrect). Lint/ParenthesesAsGroupedExpression: Exclude: + - 'lib/solargraph.rb' - 'lib/solargraph/parser/parser_gem/node_chainer.rb' - - 'spec/complex_type_spec.rb' - 'spec/language_server/host_spec.rb' - 'spec/source_map/clip_spec.rb' @@ -488,6 +558,14 @@ Lint/SuppressedException: Exclude: - 'Rakefile' +# Offense count: 5 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle. +# SupportedStyles: strict, consistent +Lint/SymbolConversion: + Exclude: + - 'lib/solargraph/pin/base.rb' + # Offense count: 1 # Configuration parameters: AllowKeywordBlockArguments. Lint/UnderscorePrefixedVariableName: @@ -501,6 +579,11 @@ Lint/UnexpectedBlockArity: - 'lib/solargraph/language_server/message/completion_item/resolve.rb' - 'lib/solargraph/type_checker.rb' +# Offense count: 1 +Lint/UnmodifiedReduceAccumulator: + Exclude: + - 'lib/solargraph/pin/method.rb' + # Offense count: 4 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AutoCorrect, IgnoreEmptyBlocks, AllowUnusedKeywordArguments. @@ -510,7 +593,7 @@ Lint/UnusedBlockArgument: - 'lib/solargraph/logging.rb' - 'spec/language_server/transport/data_reader_spec.rb' -# Offense count: 35 +# Offense count: 38 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AutoCorrect, AllowUnusedKeywordArguments, IgnoreEmptyMethods, IgnoreNotImplementedMethods, NotImplementedExceptions. # NotImplementedExceptions: NotImplementedError @@ -524,13 +607,13 @@ Lint/UselessAccessModifier: Exclude: - 'lib/solargraph/api_map.rb' -# Offense count: 33 +# Offense count: 40 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AutoCorrect. Lint/UselessAssignment: Enabled: false -# Offense count: 1 +# Offense count: 2 Lint/UselessConstantScoping: Exclude: - 'lib/solargraph/rbs_map/conversions.rb' @@ -542,12 +625,12 @@ Lint/UselessMethodDefinition: Exclude: - 'lib/solargraph/pin/signature.rb' -# Offense count: 209 +# Offense count: 227 # Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes. Metrics/AbcSize: Max: 200 -# Offense count: 11 +# Offense count: 12 # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns, inherit_mode. # AllowedMethods: refine Metrics/BlockLength: @@ -558,17 +641,17 @@ Metrics/BlockLength: Metrics/BlockNesting: Max: 6 -# Offense count: 27 +# Offense count: 30 # Configuration parameters: CountComments, CountAsOne. Metrics/ClassLength: - Max: 541 + Max: 580 -# Offense count: 97 +# Offense count: 111 # Configuration parameters: AllowedMethods, AllowedPatterns. Metrics/CyclomaticComplexity: Max: 42 -# Offense count: 47 +# Offense count: 50 # Configuration parameters: CountComments, Max, CountAsOne, AllowedMethods, AllowedPatterns. Metrics/MethodLength: Enabled: false @@ -576,15 +659,15 @@ Metrics/MethodLength: # Offense count: 4 # Configuration parameters: CountComments, CountAsOne. Metrics/ModuleLength: - Max: 458 + Max: 168 -# Offense count: 13 +# Offense count: 14 # Configuration parameters: CountKeywordArgs. Metrics/ParameterLists: Max: 9 MaxOptionalParameters: 5 -# Offense count: 82 +# Offense count: 88 # Configuration parameters: AllowedMethods, AllowedPatterns. Metrics/PerceivedComplexity: Max: 47 @@ -609,7 +692,7 @@ Naming/HeredocDelimiterNaming: Exclude: - 'spec/yard_map/mapper/to_method_spec.rb' -# Offense count: 5 +# Offense count: 9 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: EnforcedStyleForLeadingUnderscores. # SupportedStylesForLeadingUnderscores: disallowed, required, optional @@ -619,6 +702,8 @@ Naming/MemoizedInstanceVariableName: - 'lib/solargraph/convention/gemfile.rb' - 'lib/solargraph/convention/gemspec.rb' - 'lib/solargraph/convention/rakefile.rb' + - 'lib/solargraph/doc_map.rb' + - 'lib/solargraph/rbs_map.rb' - 'lib/solargraph/workspace.rb' # Offense count: 16 @@ -633,9 +718,10 @@ Naming/MethodParameterName: - 'lib/solargraph/yard_map/mapper/to_method.rb' - 'lib/solargraph/yard_map/to_method.rb' -# Offense count: 17 -# Configuration parameters: Mode, AllowedMethods. +# Offense count: 11 +# Configuration parameters: Mode, AllowedMethods, AllowedPatterns, AllowBangMethods, WaywardPredicates. # AllowedMethods: call +# WaywardPredicates: nonzero? Naming/PredicateMethod: Exclude: - 'lib/solargraph/api_map/store.rb' @@ -644,14 +730,10 @@ Naming/PredicateMethod: - 'lib/solargraph/library.rb' - 'lib/solargraph/parser/parser_gem/node_processors/send_node.rb' - 'lib/solargraph/pin/base.rb' - - 'lib/solargraph/pin/base_variable.rb' - 'lib/solargraph/pin/local_variable.rb' - - 'lib/solargraph/pin/method.rb' - - 'lib/solargraph/pin/parameter.rb' - - 'lib/solargraph/source_map.rb' - 'lib/solargraph/workspace.rb' -# Offense count: 4 +# Offense count: 6 # Configuration parameters: NamePrefix, ForbiddenPrefixes, AllowedMethods, MethodDefinitionMacros, UseSorbetSigs. # NamePrefix: is_, has_, have_, does_ # ForbiddenPrefixes: is_, has_, have_, does_ @@ -663,6 +745,7 @@ Naming/PredicatePrefix: - 'lib/solargraph/api_map.rb' - 'lib/solargraph/language_server/host.rb' - 'lib/solargraph/parser/parser_gem/class_methods.rb' + - 'lib/solargraph/pin_cache.rb' - 'lib/solargraph/workspace.rb' # Offense count: 1 @@ -679,12 +762,11 @@ RSpec/Be: - 'spec/rbs_map_spec.rb' - 'spec/source/source_chainer_spec.rb' -# Offense count: 5 +# Offense count: 3 # This cop supports unsafe autocorrection (--autocorrect-all). RSpec/BeEq: Exclude: - 'spec/complex_type_spec.rb' - - 'spec/pin/base_spec.rb' - 'spec/pin/method_spec.rb' # Offense count: 7 @@ -707,7 +789,7 @@ RSpec/BeforeAfterAll: - 'spec/language_server/host/dispatch_spec.rb' - 'spec/language_server/protocol_spec.rb' -# Offense count: 14 +# Offense count: 25 # Configuration parameters: Prefixes, AllowedPatterns. # Prefixes: when, with, without RSpec/ContextWording: @@ -722,7 +804,7 @@ RSpec/ContextWording: - 'spec/type_checker/levels/strong_spec.rb' - 'spec/type_checker/levels/typed_spec.rb' -# Offense count: 1 +# Offense count: 2 # Configuration parameters: IgnoredMetadata. RSpec/DescribeClass: Exclude: @@ -731,9 +813,10 @@ RSpec/DescribeClass: - '**/spec/routing/**/*' - '**/spec/system/**/*' - '**/spec/views/**/*' + - 'spec/complex_type_spec.rb' - 'spec/source_map/node_processor_spec.rb' -# Offense count: 493 +# Offense count: 407 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: SkipBlocks, EnforcedStyle, OnlyStaticConstants. # SupportedStyles: described_class, explicit @@ -753,7 +836,7 @@ RSpec/EmptyLineAfterFinalLet: Exclude: - 'spec/workspace/config_spec.rb' -# Offense count: 914 +# Offense count: 928 # Configuration parameters: CountAsOne. RSpec/ExampleLength: Max: 59 @@ -767,10 +850,11 @@ RSpec/ExampleWording: - 'spec/pin/base_spec.rb' - 'spec/pin/method_spec.rb' -# Offense count: 1 +# Offense count: 2 # This cop supports safe autocorrection (--autocorrect). RSpec/ExcessiveDocstringSpacing: Exclude: + - 'spec/rbs_map/conversions_spec.rb' - 'spec/source/chain/call_spec.rb' # Offense count: 5 @@ -796,7 +880,14 @@ RSpec/HookArgument: - 'spec/workspace/config_spec.rb' - 'spec/workspace_spec.rb' -# Offense count: 246 +# Offense count: 2 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: . +# SupportedStyles: is_expected, should +RSpec/ImplicitExpect: + EnforcedStyle: should + +# Offense count: 247 # Configuration parameters: AssignmentOnly. RSpec/InstanceVariable: Exclude: @@ -807,6 +898,12 @@ RSpec/InstanceVariable: - 'spec/language_server/host_spec.rb' - 'spec/language_server/protocol_spec.rb' +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +RSpec/LeadingSubject: + Exclude: + - 'spec/rbs_map/conversions_spec.rb' + # Offense count: 1 RSpec/LeakyConstantDeclaration: Exclude: @@ -830,10 +927,15 @@ RSpec/MissingExampleGroupArgument: Exclude: - 'spec/diagnostics/rubocop_helpers_spec.rb' -# Offense count: 466 +# Offense count: 471 RSpec/MultipleExpectations: Max: 14 +# Offense count: 1 +# Configuration parameters: AllowedGroups. +RSpec/NestedGroups: + Max: 4 + # Offense count: 8 # Configuration parameters: AllowedPatterns. # AllowedPatterns: ^expect_, ^assert_ @@ -847,22 +949,23 @@ RSpec/NoExpectationExample: - 'spec/type_checker/checks_spec.rb' - 'spec/type_checker/levels/typed_spec.rb' -# Offense count: 1 +# Offense count: 2 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: not_to, to_not RSpec/NotToNot: Exclude: + - 'spec/api_map_spec.rb' - 'spec/rbs_map/core_map_spec.rb' -# Offense count: 33 +# Offense count: 28 RSpec/PendingWithoutReason: Exclude: - 'spec/api_map_spec.rb' - 'spec/complex_type_spec.rb' - 'spec/parser/node_chainer_spec.rb' - 'spec/parser/node_methods_spec.rb' - - 'spec/pin/method_spec.rb' + - 'spec/pin/local_variable_spec.rb' - 'spec/rbs_map/core_map_spec.rb' - 'spec/source/chain/call_spec.rb' - 'spec/source/chain_spec.rb' @@ -890,7 +993,7 @@ RSpec/RemoveConst: Exclude: - 'spec/diagnostics/rubocop_helpers_spec.rb' -# Offense count: 46 +# Offense count: 48 RSpec/RepeatedDescription: Exclude: - 'spec/api_map_spec.rb' @@ -903,7 +1006,7 @@ RSpec/RepeatedDescription: - 'spec/type_checker/levels/normal_spec.rb' - 'spec/type_checker/levels/strict_spec.rb' -# Offense count: 28 +# Offense count: 30 RSpec/RepeatedExample: Exclude: - 'spec/api_map_spec.rb' @@ -919,7 +1022,7 @@ RSpec/ScatteredLet: Exclude: - 'spec/complex_type_spec.rb' -# Offense count: 87 +# Offense count: 88 # Configuration parameters: Include, CustomTransform, IgnoreMethods, IgnoreMetadata. # Include: **/*_spec.rb RSpec/SpecFilePathFormat: @@ -930,7 +1033,7 @@ RSpec/StubbedMock: Exclude: - 'spec/language_server/host/message_worker_spec.rb' -# Offense count: 24 +# Offense count: 23 # Configuration parameters: IgnoreNameless, IgnoreSymbolicNames. RSpec/VerifiedDoubles: Exclude: @@ -951,9 +1054,9 @@ RSpec/VerifiedDoubles: # Offense count: 1 Security/MarshalLoad: Exclude: - - 'lib/solargraph/cache.rb' + - 'lib/solargraph/pin_cache.rb' -# Offense count: 12 +# Offense count: 13 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: EnforcedStyle, AllowModifiersOnSymbols, AllowModifiersOnAttrs, AllowModifiersOnAliasMethod. # SupportedStyles: inline, group @@ -963,6 +1066,7 @@ Style/AccessModifierDeclarations: - 'lib/solargraph/complex_type.rb' - 'lib/solargraph/complex_type/unique_type.rb' - 'lib/solargraph/location.rb' + - 'lib/solargraph/pin/base.rb' - 'lib/solargraph/position.rb' - 'lib/solargraph/range.rb' - 'lib/solargraph/source/chain.rb' @@ -972,17 +1076,19 @@ Style/AccessModifierDeclarations: - 'lib/solargraph/source/chain/link.rb' - 'lib/solargraph/source/chain/literal.rb' -# Offense count: 9 +# Offense count: 13 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: separated, grouped Style/AccessorGrouping: Exclude: + - 'lib/solargraph/doc_map.rb' - 'lib/solargraph/parser/flow_sensitive_typing.rb' - - 'lib/solargraph/pin/callable.rb' + - 'lib/solargraph/pin/base.rb' - 'lib/solargraph/pin/method.rb' + - 'lib/solargraph/rbs_map.rb' -# Offense count: 39 +# Offense count: 38 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: EnforcedStyle. # SupportedStyles: always, conditionals @@ -1014,7 +1120,7 @@ Style/ArgumentsForwarding: - 'lib/solargraph/api_map.rb' - 'lib/solargraph/complex_type.rb' -# Offense count: 61 +# Offense count: 52 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, ProceduralMethods, FunctionalMethods, AllowedMethods, AllowedPatterns, AllowBracesOnProceduralOneLiners, BracesRequiredMethods. # SupportedStyles: line_count_based, semantic, braces_for_chaining, always_braces @@ -1056,12 +1162,13 @@ Style/ClassAndModuleChildren: - 'lib/solargraph/language_server/message/workspace/did_change_workspace_folders.rb' - 'lib/solargraph/language_server/message/workspace/workspace_symbol.rb' -# Offense count: 2 +# Offense count: 5 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: AllowedMethods, AllowedPatterns. # AllowedMethods: ==, equal?, eql? Style/ClassEqualityComparison: Exclude: + - 'lib/solargraph/gem_pins.rb' - 'lib/solargraph/pin/base.rb' # Offense count: 13 @@ -1120,13 +1227,14 @@ Style/EmptyLambdaParameter: Exclude: - 'spec/rbs_map/core_map_spec.rb' -# Offense count: 4 +# Offense count: 5 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AutoCorrect, EnforcedStyle. # SupportedStyles: compact, expanded Style/EmptyMethod: Exclude: - 'lib/solargraph/language_server/message/client/register_capability.rb' + - 'lib/solargraph/pin/base.rb' - 'spec/fixtures/formattable.rb' - 'spec/fixtures/rdoc-lib/lib/example.rb' - 'spec/fixtures/workspace-with-gemfile/lib/thing.rb' @@ -1147,12 +1255,13 @@ Style/ExpandPathArguments: Exclude: - 'solargraph.gemspec' -# Offense count: 1 +# Offense count: 2 # This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AllowedVars. +# Configuration parameters: AllowedVars, DefaultToNil. Style/FetchEnvVar: Exclude: - 'spec/api_map/config_spec.rb' + - 'spec/spec_helper.rb' # Offense count: 2 # This cop supports unsafe autocorrection (--autocorrect-all). @@ -1163,22 +1272,23 @@ Style/FloatDivision: - 'lib/solargraph/library.rb' - 'lib/solargraph/pin/search.rb' -# Offense count: 127 +# Offense count: 128 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: EnforcedStyle. # SupportedStyles: always, always_true, never Style/FrozenStringLiteralComment: Enabled: false -# Offense count: 8 +# Offense count: 11 # This cop supports unsafe autocorrection (--autocorrect-all). Style/GlobalStdStream: Exclude: - 'lib/solargraph/logging.rb' + - 'lib/solargraph/pin/base.rb' - 'lib/solargraph/shell.rb' - 'spec/logging_spec.rb' -# Offense count: 11 +# Offense count: 12 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: MinBodyLength, AllowConsecutiveConditionals. Style/GuardClause: @@ -1186,11 +1296,19 @@ Style/GuardClause: - 'lib/solargraph/api_map.rb' - 'lib/solargraph/library.rb' - 'lib/solargraph/parser/parser_gem/node_processors/send_node.rb' + - 'lib/solargraph/pin_cache.rb' - 'lib/solargraph/range.rb' - 'lib/solargraph/rbs_map/conversions.rb' - 'lib/solargraph/source.rb' - 'lib/solargraph/workspace.rb' +# Offense count: 2 +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: AllowSplatArgument. +Style/HashConversion: + Exclude: + - 'lib/solargraph/doc_map.rb' + # Offense count: 2 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: AllowedReceivers. @@ -1218,18 +1336,19 @@ Style/IdenticalConditionalBranches: - 'lib/solargraph/library.rb' - 'lib/solargraph/type_checker.rb' -# Offense count: 6 +# Offense count: 7 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowIfModifier. Style/IfInsideElse: Exclude: + - 'lib/solargraph/complex_type/type_methods.rb' - 'lib/solargraph/language_server/transport/data_reader.rb' - 'lib/solargraph/parser/parser_gem/node_chainer.rb' - 'lib/solargraph/parser/parser_gem/node_methods.rb' - 'lib/solargraph/source_map/clip.rb' - 'lib/solargraph/type_checker.rb' -# Offense count: 40 +# Offense count: 46 # This cop supports safe autocorrection (--autocorrect). Style/IfUnlessModifier: Enabled: false @@ -1263,7 +1382,7 @@ Style/MapToSet: - 'lib/solargraph/library.rb' - 'spec/source_map/clip_spec.rb' -# Offense count: 147 +# Offense count: 198 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: require_parentheses, require_no_parentheses, require_no_parentheses_except_multiline @@ -1287,7 +1406,7 @@ Style/MultilineTernaryOperator: Exclude: - 'lib/solargraph/language_server/host.rb' -# Offense count: 19 +# Offense count: 20 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowMethodComparison, ComparisonsThreshold. Style/MultipleComparison: @@ -1296,12 +1415,13 @@ Style/MultipleComparison: - 'lib/solargraph/complex_type/type_methods.rb' - 'lib/solargraph/parser/parser_gem/node_methods.rb' - 'lib/solargraph/parser/parser_gem/node_processors/send_node.rb' + - 'lib/solargraph/pin/base.rb' - 'lib/solargraph/pin/method.rb' - 'lib/solargraph/pin/parameter.rb' - 'lib/solargraph/source.rb' - 'lib/solargraph/type_checker.rb' -# Offense count: 15 +# Offense count: 17 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: EnforcedStyle. # SupportedStyles: literals, strict @@ -1312,6 +1432,7 @@ Style/MutableConstant: - 'lib/solargraph/parser/parser_gem/node_methods.rb' - 'lib/solargraph/rbs_map/conversions.rb' - 'lib/solargraph/rbs_map/core_fills.rb' + - 'lib/solargraph/yard_map/mapper/to_method.rb' - 'spec/complex_type_spec.rb' # Offense count: 1 @@ -1347,13 +1468,14 @@ Style/NestedTernaryOperator: - 'lib/solargraph/pin/conversions.rb' - 'lib/solargraph/pin/method.rb' -# Offense count: 4 +# Offense count: 5 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, MinBodyLength, AllowConsecutiveConditionals. # SupportedStyles: skip_modifier_ifs, always Style/Next: Exclude: - 'lib/solargraph/parser/parser_gem/node_processors/send_node.rb' + - 'lib/solargraph/pin/signature.rb' - 'lib/solargraph/source_map/clip.rb' - 'lib/solargraph/type_checker/checks.rb' @@ -1363,27 +1485,12 @@ Style/Next: Style/NumericLiterals: MinDigits: 6 -# Offense count: 28 +# Offense count: 31 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: EnforcedStyle, AllowedMethods, AllowedPatterns. # SupportedStyles: predicate, comparison Style/NumericPredicate: - Exclude: - - 'spec/**/*' - - 'lib/solargraph/api_map.rb' - - 'lib/solargraph/api_map/index.rb' - - 'lib/solargraph/api_map/store.rb' - - 'lib/solargraph/complex_type.rb' - - 'lib/solargraph/complex_type/type_methods.rb' - - 'lib/solargraph/complex_type/unique_type.rb' - - 'lib/solargraph/language_server/message/extended/check_gem_version.rb' - - 'lib/solargraph/library.rb' - - 'lib/solargraph/parser/comment_ripper.rb' - - 'lib/solargraph/pin/delegated_method.rb' - - 'lib/solargraph/shell.rb' - - 'lib/solargraph/source/change.rb' - - 'lib/solargraph/source/source_chainer.rb' - - 'lib/solargraph/workspace.rb' + Enabled: false # Offense count: 1 Style/OpenStructUse: @@ -1434,7 +1541,7 @@ Style/RedundantAssignment: - 'lib/solargraph/repro.rb' - 'lib/solargraph/workspace/config.rb' -# Offense count: 7 +# Offense count: 8 # This cop supports safe autocorrection (--autocorrect). Style/RedundantBegin: Exclude: @@ -1458,6 +1565,13 @@ Style/RedundantFreeze: - 'lib/solargraph/complex_type.rb' - 'lib/solargraph/source_map/mapper.rb' +# Offense count: 1 +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: AutoCorrect, AllowComments. +Style/RedundantInitialize: + Exclude: + - 'lib/solargraph/rbs_map/core_map.rb' + # Offense count: 3 # This cop supports unsafe autocorrection (--autocorrect-all). Style/RedundantInterpolation: @@ -1466,13 +1580,14 @@ Style/RedundantInterpolation: - 'lib/solargraph/parser/parser_gem/node_chainer.rb' - 'lib/solargraph/source_map/mapper.rb' -# Offense count: 5 +# Offense count: 7 # This cop supports safe autocorrection (--autocorrect). Style/RedundantParentheses: Exclude: - 'lib/solargraph/language_server/message/initialize.rb' - 'lib/solargraph/parser/parser_gem/node_chainer.rb' - 'lib/solargraph/pin/method.rb' + - 'lib/solargraph/pin/parameter.rb' - 'lib/solargraph/source.rb' - 'lib/solargraph/type_checker.rb' @@ -1496,17 +1611,18 @@ Style/RedundantRegexpEscape: - 'lib/solargraph/source_map/clip.rb' - 'lib/solargraph/source_map/mapper.rb' -# Offense count: 6 +# Offense count: 9 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowMultipleReturnValues. Style/RedundantReturn: Exclude: - 'lib/solargraph/api_map.rb' - 'lib/solargraph/complex_type/type_methods.rb' + - 'lib/solargraph/doc_map.rb' - 'lib/solargraph/parser/parser_gem/node_methods.rb' - 'lib/solargraph/source/chain/z_super.rb' -# Offense count: 9 +# Offense count: 19 # This cop supports safe autocorrection (--autocorrect). Style/RedundantSelf: Exclude: @@ -1515,6 +1631,7 @@ Style/RedundantSelf: - 'lib/solargraph/location.rb' - 'lib/solargraph/pin/base.rb' - 'lib/solargraph/pin/callable.rb' + - 'lib/solargraph/pin/signature.rb' - 'lib/solargraph/source/chain.rb' - 'lib/solargraph/source/chain/link.rb' @@ -1527,13 +1644,22 @@ Style/RegexpLiteral: - 'lib/solargraph/language_server/uri_helpers.rb' - 'lib/solargraph/workspace/config.rb' -# Offense count: 13 +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle. +# SupportedStyles: implicit, explicit +Style/RescueStandardError: + Exclude: + - 'lib/solargraph/pin/base.rb' + +# Offense count: 17 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: ConvertCodeThatCanStartToReturnNil, AllowedMethods, MaxChainLength. # AllowedMethods: present?, blank?, presence, try, try! Style/SafeNavigation: Exclude: - 'lib/solargraph/api_map/index.rb' + - 'lib/solargraph/doc_map.rb' - 'lib/solargraph/language_server/message/completion_item/resolve.rb' - 'lib/solargraph/language_server/request.rb' - 'lib/solargraph/language_server/transport/data_reader.rb' @@ -1541,20 +1667,26 @@ Style/SafeNavigation: - 'lib/solargraph/pin/base.rb' - 'lib/solargraph/pin/conversions.rb' - 'lib/solargraph/pin/method.rb' + - 'lib/solargraph/pin_cache.rb' - 'lib/solargraph/range.rb' - 'lib/solargraph/type_checker.rb' -# Offense count: 40 +# Offense count: 1 +# Configuration parameters: Max. +Style/SafeNavigationChainLength: + Exclude: + - 'lib/solargraph/doc_map.rb' + +# Offense count: 39 # This cop supports unsafe autocorrection (--autocorrect-all). Style/SlicingWithRange: Enabled: false -# Offense count: 8 +# Offense count: 6 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowModifier. Style/SoleNestedConditional: Exclude: - - 'lib/solargraph/api_map.rb' - 'lib/solargraph/complex_type/unique_type.rb' - 'lib/solargraph/parser/flow_sensitive_typing.rb' - 'lib/solargraph/pin/parameter.rb' @@ -1562,10 +1694,11 @@ Style/SoleNestedConditional: - 'lib/solargraph/source/source_chainer.rb' - 'lib/solargraph/type_checker.rb' -# Offense count: 5 +# Offense count: 8 # This cop supports safe autocorrection (--autocorrect). Style/StderrPuts: Exclude: + - 'lib/solargraph/pin/base.rb' - 'lib/solargraph/shell.rb' # Offense count: 10 @@ -1583,17 +1716,18 @@ Style/StringConcatenation: - 'lib/solargraph/pin/namespace.rb' - 'solargraph.gemspec' -# Offense count: 605 +# Offense count: 617 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline. # SupportedStyles: single_quotes, double_quotes Style/StringLiterals: Enabled: false -# Offense count: 3 +# Offense count: 4 # This cop supports safe autocorrection (--autocorrect). Style/SuperArguments: Exclude: + - 'lib/solargraph/pin/base_variable.rb' - 'lib/solargraph/pin/callable.rb' - 'lib/solargraph/pin/method.rb' - 'lib/solargraph/pin/signature.rb' @@ -1620,7 +1754,7 @@ Style/SymbolArray: - 'spec/parser/node_methods_spec.rb' - 'spec/source_map/mapper_spec.rb' -# Offense count: 6 +# Offense count: 7 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: AllowMethodsWithArguments, AllowedMethods, AllowedPatterns, AllowComments. # AllowedMethods: define_method @@ -1630,6 +1764,7 @@ Style/SymbolProc: - 'lib/solargraph/language_server/message/text_document/hover.rb' - 'lib/solargraph/language_server/message/text_document/signature_help.rb' - 'lib/solargraph/parser/flow_sensitive_typing.rb' + - 'lib/solargraph/pin/base.rb' - 'lib/solargraph/pin/callable.rb' - 'lib/solargraph/pin/closure.rb' @@ -1641,17 +1776,25 @@ Style/TernaryParentheses: Exclude: - 'lib/solargraph/source_map/mapper.rb' -# Offense count: 9 +# Offense count: 19 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyleForMultiline. # SupportedStylesForMultiline: comma, consistent_comma, no_comma Style/TrailingCommaInArguments: Exclude: - 'lib/solargraph/api_map.rb' + - 'lib/solargraph/parser/node_processor.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/block_node.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/def_node.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/defs_node.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb' - 'lib/solargraph/parser/parser_gem/node_processors/until_node.rb' - 'lib/solargraph/parser/parser_gem/node_processors/while_node.rb' + - 'lib/solargraph/pin/method.rb' - 'lib/solargraph/rbs_map/conversions.rb' + - 'lib/solargraph/yard_map/mapper/to_constant.rb' - 'lib/solargraph/yard_map/mapper/to_method.rb' + - 'lib/solargraph/yard_map/mapper/to_namespace.rb' # Offense count: 2 # This cop supports safe autocorrection (--autocorrect). @@ -1660,15 +1803,21 @@ Style/TrailingCommaInArguments: Style/TrailingCommaInArrayLiteral: Exclude: - 'lib/solargraph/language_server/message/text_document/formatting.rb' - - 'spec/complex_type_spec.rb' + - 'lib/solargraph/rbs_map/core_fills.rb' -# Offense count: 1 +# Offense count: 7 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyleForMultiline. # SupportedStylesForMultiline: comma, consistent_comma, diff_comma, no_comma Style/TrailingCommaInHashLiteral: Exclude: - - 'lib/solargraph/api_map.rb' + - 'lib/solargraph/pin/base.rb' + - 'lib/solargraph/pin/base_variable.rb' + - 'lib/solargraph/pin/callable.rb' + - 'lib/solargraph/pin/closure.rb' + - 'lib/solargraph/pin/local_variable.rb' + - 'lib/solargraph/pin/parameter.rb' + - 'lib/solargraph/rbs_map/conversions.rb' # Offense count: 2 # This cop supports safe autocorrection (--autocorrect). @@ -1685,7 +1834,7 @@ Style/WhileUntilModifier: Exclude: - 'lib/solargraph/complex_type.rb' -# Offense count: 27 +# Offense count: 23 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, MinSize, WordRegex. # SupportedStyles: percent, brackets @@ -1695,7 +1844,6 @@ Style/WordArray: - 'lib/solargraph/complex_type/unique_type.rb' - 'lib/solargraph/diagnostics/type_check.rb' - 'lib/solargraph/language_server/message/text_document/formatting.rb' - - 'spec/complex_type_spec.rb' - 'spec/parser/node_chainer_spec.rb' - 'spec/pin/method_spec.rb' - 'spec/source/cursor_spec.rb' @@ -1711,18 +1859,35 @@ Style/YAMLFileRead: Exclude: - 'lib/solargraph/workspace/config.rb' -# Offense count: 4 +# Offense count: 7 # This cop supports unsafe autocorrection (--autocorrect-all). Style/ZeroLengthPredicate: Exclude: - 'lib/solargraph/api_map.rb' - 'lib/solargraph/api_map/index.rb' - 'lib/solargraph/language_server/host.rb' + - 'lib/solargraph/pin/method.rb' + - 'lib/solargraph/source/chain/array.rb' - 'spec/language_server/protocol_spec.rb' -# Offense count: 173 +# Offense count: 1 # This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns, SplitStrings. -# URISchemes: http, https -Layout/LineLength: - Max: 244 +# Configuration parameters: EnforcedStyle. +# SupportedStyles: long, short +YARD/CollectionType: + Exclude: + - 'lib/solargraph/range.rb' + +# Offense count: 65 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStylePrototypeName. +# SupportedStylesPrototypeName: before, after +YARD/MismatchName: + Enabled: false + +# Offense count: 4 +YARD/TagTypeSyntax: + Exclude: + - 'lib/solargraph/language_server/host.rb' + - 'lib/solargraph/parser/comment_ripper.rb' + - 'lib/solargraph/type_checker.rb' From 0b628959170fe076b249cb57b90bfeaf4a6f3c4a Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 9 Jul 2025 14:31:48 -0400 Subject: [PATCH 089/561] Make undercover capitalization consistent --- .github/workflows/rspec.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/rspec.yml b/.github/workflows/rspec.yml index d9cacbaee..43d7c1f65 100644 --- a/.github/workflows/rspec.yml +++ b/.github/workflows/rspec.yml @@ -35,7 +35,6 @@ jobs: - name: Run tests run: bundle exec rspec undercover: - name: 'Undercover' runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 From f74754132fc1733010f810361dfb13ad57695eb3 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 9 Jul 2025 14:34:52 -0400 Subject: [PATCH 090/561] Fix rubocop todo file --- .rubocop_todo.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index a4307b7e5..54dca7c30 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,6 +1,6 @@ # This configuration was generated by # `rubocop --auto-gen-config` -# on 2025-07-09 18:29:07 UTC using RuboCop version 1.78.0. +# on 2025-07-09 18:34:33 UTC using RuboCop version 1.78.0. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new @@ -1891,3 +1891,10 @@ YARD/TagTypeSyntax: - 'lib/solargraph/language_server/host.rb' - 'lib/solargraph/parser/comment_ripper.rb' - 'lib/solargraph/type_checker.rb' + +# Offense count: 191 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, IgnoreCopDirectives, AllowedPatterns, SplitStrings. +# URISchemes: http, https +Layout/LineLength: + Max: 257 From b53e735d068830071dddc158e1d54eeb8f203238 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 9 Jul 2025 14:53:41 -0400 Subject: [PATCH 091/561] Fix merge error --- lib/solargraph/source/chain.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/solargraph/source/chain.rb b/lib/solargraph/source/chain.rb index b664bccd3..a426fc36c 100644 --- a/lib/solargraph/source/chain.rb +++ b/lib/solargraph/source/chain.rb @@ -163,7 +163,7 @@ def infer_uncached api_map, name_pin, locals logger.debug { "Chain#infer_uncached(links=#{links.map(&:desc)}, locals=#{locals.map(&:desc)}) => undefined - no pins" } return ComplexType::UNDEFINED end - type = infer_first_defined(pins, links.last.last_context, api_map, locals) + type = infer_from_definitions(pins, links.last.last_context, api_map, locals) out = maybe_nil(type) logger.debug { "Chain#infer_uncached(links=#{self.links.map(&:desc)}, locals=#{locals.map(&:desc)}, name_pin=#{name_pin}, name_pin.closure=#{name_pin.closure.inspect}, name_pin.binder=#{name_pin.binder}) => #{out.rooted_tags.inspect}" } out From 809030d75a0b1dcfa08ed301b6140293f173298e Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 9 Jul 2025 15:05:59 -0400 Subject: [PATCH 092/561] Add rubocop binstub --- bin/rubocop | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100755 bin/rubocop diff --git a/bin/rubocop b/bin/rubocop new file mode 100755 index 000000000..369a05bed --- /dev/null +++ b/bin/rubocop @@ -0,0 +1,27 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'rubocop' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +bundle_binstub = File.expand_path("bundle", __dir__) + +if File.file?(bundle_binstub) + if File.read(bundle_binstub, 300).include?("This file was generated by Bundler") + load(bundle_binstub) + else + abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. +Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") + end +end + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("rubocop", "rubocop") From 5bb5d418d0aa04c4716ad6b679220d90508523b4 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 9 Jul 2025 15:33:52 -0400 Subject: [PATCH 093/561] Add some RBS shims to help handle strict-level typechecking issues --- .../parser/parser_gem/node_methods.rb | 14 ---------- rbs_collection.yaml | 6 ++-- sig/shims/ast/node.rbs | 5 ++++ sig/shims/open3/0/open3.rbs | 28 +++++++++++++++++++ sig/shims/rubygems/basic_specification.rbs | 3 ++ sig/shims/rubygems/dependency.rbs | 5 ++++ sig/shims/rubygems/errors.rbs | 17 +++++++++++ sig/shims/rubygems/spec_fetcher.rbs | 9 ++++++ sig/shims/rubygems/specification.rbs | 7 +++++ 9 files changed, 77 insertions(+), 17 deletions(-) create mode 100644 sig/shims/ast/node.rbs create mode 100644 sig/shims/open3/0/open3.rbs create mode 100644 sig/shims/rubygems/basic_specification.rbs create mode 100644 sig/shims/rubygems/dependency.rbs create mode 100644 sig/shims/rubygems/errors.rbs create mode 100644 sig/shims/rubygems/spec_fetcher.rbs create mode 100644 sig/shims/rubygems/specification.rbs diff --git a/lib/solargraph/parser/parser_gem/node_methods.rb b/lib/solargraph/parser/parser_gem/node_methods.rb index b716b352d..cd9ce7728 100644 --- a/lib/solargraph/parser/parser_gem/node_methods.rb +++ b/lib/solargraph/parser/parser_gem/node_methods.rb @@ -3,20 +3,6 @@ require 'parser' require 'ast' -# Teach AST::Node#children about its generic type -# -# @todo contribute back to https://github.com/ruby/gem_rbs_collection/blob/main/gems/ast/2.4/ast.rbs -# -# @!parse -# module ::AST -# class Node -# # New children -# -# # @return [Array] -# attr_reader :children -# end -# end - # https://github.com/whitequark/parser module Solargraph module Parser diff --git a/rbs_collection.yaml b/rbs_collection.yaml index 66e30ecfe..551a475d6 100644 --- a/rbs_collection.yaml +++ b/rbs_collection.yaml @@ -6,9 +6,9 @@ sources: revision: main repo_dir: gems -# You can specify local directories as sources also. -# - type: local -# path: path/to/your/local/repository + # You can specify local directories as sources also. + - type: local + path: sig/shims # A directory to install the downloaded RBSs path: .gem_rbs_collection diff --git a/sig/shims/ast/node.rbs b/sig/shims/ast/node.rbs new file mode 100644 index 000000000..fab1a4de0 --- /dev/null +++ b/sig/shims/ast/node.rbs @@ -0,0 +1,5 @@ +module ::AST + class Node + def children: () -> [self, Integer, String, Symbol, nil] + end +end diff --git a/sig/shims/open3/0/open3.rbs b/sig/shims/open3/0/open3.rbs new file mode 100644 index 000000000..d1397e549 --- /dev/null +++ b/sig/shims/open3/0/open3.rbs @@ -0,0 +1,28 @@ +module Open3 + def self.capture2: (?Hash[String, String] env, *String cmds) -> [String, Process::Status] + + def self.capture2e: (?Hash[String, String] env, *String cmds) -> [String, Process::Status] + + def self.capture3: (?Hash[String, String] env, *String cmds) -> [String, String, Process::Status] + + def self.pipeline: (?Hash[String, String] env, *String cmds) -> Array[Process::Status] + + def self.pipeline_r: (?Hash[String, String] env, *String cmds) -> [IO, Process::Waiter] + + def self.pipeline_rw: (?Hash[String, String] env, *String cmds) -> [IO, IO, Process::Waiter] + + def self.pipeline_start: (?Hash[String, String] env, *String cmds) -> Array[Process::Waiter] + + def self.pipeline_w: (?Hash[String, String] env, *String cmds) -> [IO, Process::Waiter] + + def self.popen2: (?Hash[String, String] env, *String exe_path_or_cmd_with_args) -> [IO, IO, Process::Waiter] + | [U] (?Hash[String, String] env, *String exe_path_or_cmd_with_args) { (IO stdin, IO stdout, Process::Waiter wait_thread) -> U } -> U + + def self.popen2e: (?Hash[String, String] env, *String exe_path_or_cmd_with_args) -> [IO, IO, Process::Waiter] + | [U] (?Hash[String, String] env, *String exe_path_or_cmd_with_args) { (IO stdin, IO stdout_and_stderr, Process::Waiter wait_thread) -> U } -> U + + def self.popen3: (?Hash[String, String] env, *String exe_path_or_cmd_with_args) -> [IO, IO, IO, Process::Waiter] + | [U] (?Hash[String, String] env, *String exe_path_or_cmd_with_args) { (IO stdin, IO stdout, IO stderr, Process::Waiter wait_thread) -> U } -> U + + VERSION: ::String +end diff --git a/sig/shims/rubygems/basic_specification.rbs b/sig/shims/rubygems/basic_specification.rbs new file mode 100644 index 000000000..f254b1b36 --- /dev/null +++ b/sig/shims/rubygems/basic_specification.rbs @@ -0,0 +1,3 @@ +class Gem::BasicSpecification + def name: () -> String +end diff --git a/sig/shims/rubygems/dependency.rbs b/sig/shims/rubygems/dependency.rbs new file mode 100644 index 000000000..13f4549ca --- /dev/null +++ b/sig/shims/rubygems/dependency.rbs @@ -0,0 +1,5 @@ +class Gem::Dependency + # Version of the gem + # + def version: () -> untyped +end diff --git a/sig/shims/rubygems/errors.rbs b/sig/shims/rubygems/errors.rbs new file mode 100644 index 000000000..0f107d78c --- /dev/null +++ b/sig/shims/rubygems/errors.rbs @@ -0,0 +1,17 @@ +module Gem + class LoadError < ::LoadError + attr_accessor name: String + + attr_accessor requirement: untyped + end + + class MissingSpecError < Gem::LoadError + def initialize: (untyped name, untyped requirement, ?untyped? extra_message) -> void + + def message: () -> untyped + end + + class MissingSpecVersionError < MissingSpecError + def initialize: (untyped name, untyped requirement, untyped specs) -> void + end +end diff --git a/sig/shims/rubygems/spec_fetcher.rbs b/sig/shims/rubygems/spec_fetcher.rbs new file mode 100644 index 000000000..7a0297a98 --- /dev/null +++ b/sig/shims/rubygems/spec_fetcher.rbs @@ -0,0 +1,9 @@ +class Gem::SpecFetcher + include Gem::UserInteraction + + include Gem::Text + + def search_for_dependency: (untyped dependency, ?bool matching_platform) -> [::Array[[Gem::Dependency, untyped]], ::Array[untyped]] + + def spec_for_dependency: (untyped dependency, ?bool matching_platform) -> ::Array[untyped] +end diff --git a/sig/shims/rubygems/specification.rbs b/sig/shims/rubygems/specification.rbs new file mode 100644 index 000000000..3fb3c0386 --- /dev/null +++ b/sig/shims/rubygems/specification.rbs @@ -0,0 +1,7 @@ +class Gem::Specification < Gem::BasicSpecification + def self.find_by_name: (untyped name, *untyped requirements) -> instance + + def self.find_by_full_name: (untyped full_name) -> instance + + def self.find_by_path: (untyped path) -> instance +end From 780e516cb4bf3d4af4f9a693d3462397e240cea4 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 9 Jul 2025 15:51:21 -0400 Subject: [PATCH 094/561] RuboCop fixes --- lib/solargraph/logging.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/solargraph/logging.rb b/lib/solargraph/logging.rb index 140c7c2c3..311f313cb 100644 --- a/lib/solargraph/logging.rb +++ b/lib/solargraph/logging.rb @@ -11,11 +11,11 @@ module Logging 'info' => Logger::INFO, 'debug' => Logger::DEBUG } - configured_level = ENV['SOLARGRAPH_LOG'] + configured_level = ENV.fetch('SOLARGRAPH_LOG', nil) level = if LOG_LEVELS.keys.include?(configured_level) LOG_LEVELS.fetch(configured_level) else - STDERR.puts("Invalid value for SOLARGRAPH_LOG: #{configured_level.inspect} - valid values are #{LOG_LEVELS.keys}") if configured_level + warn "Invalid value for SOLARGRAPH_LOG: #{configured_level.inspect} - valid values are #{LOG_LEVELS.keys}" if configured_level DEFAULT_LOG_LEVEL end @@logger = Logger.new(STDERR, level: level) From 950a3918e8452860eec2fd96631f0f6eae684a72 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 9 Jul 2025 15:52:49 -0400 Subject: [PATCH 095/561] Add @sg-ignore --- lib/solargraph/logging.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/solargraph/logging.rb b/lib/solargraph/logging.rb index 311f313cb..b07ae2faf 100644 --- a/lib/solargraph/logging.rb +++ b/lib/solargraph/logging.rb @@ -18,6 +18,7 @@ module Logging warn "Invalid value for SOLARGRAPH_LOG: #{configured_level.inspect} - valid values are #{LOG_LEVELS.keys}" if configured_level DEFAULT_LOG_LEVEL end + # @sg-ignore Fix cvar issue @@logger = Logger.new(STDERR, level: level) # @sg-ignore Fix cvar issue @@logger.formatter = proc do |severity, datetime, progname, msg| From 65e3c0b4309271c6a962dff123f1d77632f93e36 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 9 Jul 2025 16:00:55 -0400 Subject: [PATCH 096/561] Suppress some known problematic aliases --- lib/solargraph.rb | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/lib/solargraph.rb b/lib/solargraph.rb index 2b975a66c..4a674877c 100755 --- a/lib/solargraph.rb +++ b/lib/solargraph.rb @@ -54,19 +54,28 @@ class InvalidRubocopVersionError < RuntimeError; end VIEWS_PATH = File.join(dir, 'solargraph', 'views') # @param type [Symbol] Type of assert. - def self.asserts_on?(type) - if ENV['SOLARGRAPH_ASSERTS'].nil? || ENV['SOLARGRAPH_ASSERTS'].empty? - false - elsif ENV['SOLARGRAPH_ASSERTS'] == 'on' - true - else - logger.warn "Unrecognized SOLARGRAPH_ASSERTS value: #{ENV['SOLARGRAPH_ASSERTS']}" - false - end + def self.asserts_on? + @asserts_on ||= if ENV['SOLARGRAPH_ASSERTS'].nil? || ENV['SOLARGRAPH_ASSERTS'].empty? + false + elsif ENV['SOLARGRAPH_ASSERTS'] == 'on' + true + else + logger.warn "Unrecognized SOLARGRAPH_ASSERTS value: #{ENV['SOLARGRAPH_ASSERTS']}" + false + end end def self.assert_or_log(type, msg = nil, &block) - raise (msg || block.call) if asserts_on?(type) && ![:combine_with_visibility].include?(type) + if asserts_on? + msg ||= block.call + + # not ready for prime time + return if [:combine_with_visibility].include?(type) + # conditional aliases to handle compatibility corner cases + return if type == :alias_target_missing && msg.include?('highline/compatibility.rb') + return if type == :alias_target_missing && msg.include?('lib/json/add/date.rb') + raise msg + end logger.info msg, &block end From 336305f1d4b5eb88524dc3a4a85f445ab22ad03f Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 9 Jul 2025 16:03:01 -0400 Subject: [PATCH 097/561] Drop unintended change --- lib/solargraph.rb | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/lib/solargraph.rb b/lib/solargraph.rb index 4a674877c..8295945cd 100755 --- a/lib/solargraph.rb +++ b/lib/solargraph.rb @@ -53,16 +53,15 @@ class InvalidRubocopVersionError < RuntimeError; end dir = File.dirname(__FILE__) VIEWS_PATH = File.join(dir, 'solargraph', 'views') - # @param type [Symbol] Type of assert. def self.asserts_on? - @asserts_on ||= if ENV['SOLARGRAPH_ASSERTS'].nil? || ENV['SOLARGRAPH_ASSERTS'].empty? - false - elsif ENV['SOLARGRAPH_ASSERTS'] == 'on' - true - else - logger.warn "Unrecognized SOLARGRAPH_ASSERTS value: #{ENV['SOLARGRAPH_ASSERTS']}" - false - end + if ENV['SOLARGRAPH_ASSERTS'].nil? || ENV['SOLARGRAPH_ASSERTS'].empty? + false + elsif ENV['SOLARGRAPH_ASSERTS'] == 'on' + true + else + logger.warn "Unrecognized SOLARGRAPH_ASSERTS value: #{ENV['SOLARGRAPH_ASSERTS']}" + false + end end def self.assert_or_log(type, msg = nil, &block) From c7079d50c3aad7b69a83496a2a25f3da1a7e6e8c Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 9 Jul 2025 16:06:03 -0400 Subject: [PATCH 098/561] Update spec --- spec/pin/local_variable_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/pin/local_variable_spec.rb b/spec/pin/local_variable_spec.rb index 88075efb9..369a58bc4 100644 --- a/spec/pin/local_variable_spec.rb +++ b/spec/pin/local_variable_spec.rb @@ -46,7 +46,7 @@ class Bar # set env variable 'FOO' to 'true' in block with_env_var('SOLARGRAPH_ASSERTS', 'on') do - expect(Solargraph.asserts_on?(:combine_with_closure_name)).to be true + expect(Solargraph.asserts_on?).to be true expect { pin1.combine_with(pin2) }.to raise_error(RuntimeError, /Inconsistent :closure name/) end end From 516236d18244834fa7d5704612f1eff163169a3a Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 9 Jul 2025 16:09:54 -0400 Subject: [PATCH 099/561] Fix RuboCop issues --- lib/solargraph/api_map.rb | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/lib/solargraph/api_map.rb b/lib/solargraph/api_map.rb index 7b6197968..6cdb04730 100755 --- a/lib/solargraph/api_map.rb +++ b/lib/solargraph/api_map.rb @@ -730,19 +730,18 @@ def inner_get_methods rooted_tag, scope, visibility, deep, skip, no_core = false # See https://api.rubyonrails.org/classes/ActiveSupport/Concern.html logger.debug { "ApiMap#inner_get_methods(#{fqns}, #{scope}, #{visibility}, #{deep}) - Handling class include include_tag=#{include_tag}" } - if module_extends.include? 'ActiveSupport::Concern' - unless rooted_include_tag.nil? - # yard-activesupport-concern pulls methods inside - # 'class_methods' blocks into main class visible from YARD - included_class_pins = inner_get_methods_from_reference(rooted_include_tag, namespace_pin, rooted_type, :class, visibility, deep, skip, true) - result.concat included_class_pins - - # another pattern is to put class methods inside a submodule - classmethods_include_tag = rooted_include_tag + "::ClassMethods" - included_classmethods_pins = inner_get_methods_from_reference(classmethods_include_tag, namespace_pin, rooted_type, :instance, visibility, deep, skip, true) - result.concat included_classmethods_pins - end - end + next unless module_extends.include? 'ActiveSupport::Concern' + next if rooted_include_tag.nil? + + # yard-activesupport-concern pulls methods inside + # 'class_methods' blocks into main class visible from YARD + included_class_pins = inner_get_methods_from_reference(rooted_include_tag, namespace_pin, rooted_type, :class, visibility, deep, skip, true) + result.concat included_class_pins + + # another pattern is to put class methods inside a submodule + classmethods_include_tag = rooted_include_tag + "::ClassMethods" + included_classmethods_pins = inner_get_methods_from_reference(classmethods_include_tag, namespace_pin, rooted_type, :instance, visibility, deep, skip, true) + result.concat included_classmethods_pins end logger.info { "ApiMap#inner_get_methods(#{fqns}, #{scope}, #{visibility}, #{deep}, #{skip}) - looking for get_extends() from #{fqns}" } From b99aba0d363169ec3b3967593d4d3be0911f527d Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 9 Jul 2025 16:12:46 -0400 Subject: [PATCH 100/561] Rubocop fixes --- lib/solargraph/pin/base.rb | 2 +- lib/solargraph/rbs_map/conversions.rb | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/solargraph/pin/base.rb b/lib/solargraph/pin/base.rb index c2d4c0a97..67fa323ba 100644 --- a/lib/solargraph/pin/base.rb +++ b/lib/solargraph/pin/base.rb @@ -55,7 +55,7 @@ def initialize location: nil, type_location: nil, closure: nil, source: nil, nam end def assert_location_provided - return unless best_location.nil? && [:yardoc, :source, :rbs].include?(source) + return unless best_location.nil? && %i[yardoc source rbs].include?(source) Solargraph.assert_or_log(:best_location, "Neither location nor type_location provided - #{path} #{source} #{self.class}") end diff --git a/lib/solargraph/rbs_map/conversions.rb b/lib/solargraph/rbs_map/conversions.rb index d641d4e70..17dad63aa 100644 --- a/lib/solargraph/rbs_map/conversions.rb +++ b/lib/solargraph/rbs_map/conversions.rb @@ -440,7 +440,12 @@ def location_decl_to_pin_location(location) # @return [Array(Array, ComplexType)] def parts_of_function type, pin type_location = pin.type_location - return [[Solargraph::Pin::Parameter.new(decl: :restarg, name: 'arg', closure: pin, source: :rbs, type_location: type_location)], ComplexType.try_parse(method_type_to_tag(type)).force_rooted] if defined?(RBS::Types::UntypedFunction) && type.type.is_a?(RBS::Types::UntypedFunction) + if defined?(RBS::Types::UntypedFunction) && type.type.is_a?(RBS::Types::UntypedFunction) + return [ + [Solargraph::Pin::Parameter.new(decl: :restarg, name: 'arg', closure: pin, source: :rbs, type_location: type_location)], + ComplexType.try_parse(method_type_to_tag(type)).force_rooted + ] + end parameters = [] arg_num = -1 From 5e4c5256af3302a92a7fdf9b7ee01fd617f72ba7 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 9 Jul 2025 16:13:39 -0400 Subject: [PATCH 101/561] Fix merge issues --- lib/solargraph/shell.rb | 50 +++++------------------------------------ 1 file changed, 6 insertions(+), 44 deletions(-) diff --git a/lib/solargraph/shell.rb b/lib/solargraph/shell.rb index cba4e7507..a7801c6f4 100755 --- a/lib/solargraph/shell.rb +++ b/lib/solargraph/shell.rb @@ -242,44 +242,6 @@ def list puts "#{workspace.filenames.length} files total." end - desc 'cache', 'Cache a gem', hide: true - # @return [void] - # @param gem [String] - # @param version [String, nil] - def cache gem, version = nil - spec = Gem::Specification.find_by_name(gem, version) - pins = GemPins.build(spec) - Cache.save('gems', "#{spec.name}-#{spec.version}.ser", pins) - end - - desc 'gems', 'Cache documentation for installed gems' - option :rebuild, type: :boolean, desc: 'Rebuild existing documentation', default: false - # @return [void] - def gems *names - if names.empty? - Gem::Specification.to_a.each do |spec| - next unless options.rebuild || !Yardoc.cached?(spec) - - puts "Processing gem: #{spec.name} #{spec.version}" - pins = GemPins.build(spec) - Cache.save('gems', "#{spec.name}-#{spec.version}.ser", pins) - end - else - names.each do |name| - spec = Gem::Specification.find_by_name(name) - if spec - next unless options.rebuild || !Yardoc.cached?(spec) - - puts "Processing gem: #{spec.name} #{spec.version}" - pins = GemPins.build(spec) - Cache.save('gems', "#{spec.name}-#{spec.version}.ser", pins) - else - warn "Gem '#{name}' not found" - end - end - end - end - desc 'rbs', 'Generate RBS definitions' option :filename, type: :string, alias: :f, desc: 'Generated file name', default: 'sig.rbs' option :inference, type: :boolean, desc: 'Enhance definitions with type inference', default: true @@ -289,12 +251,12 @@ def rbs store = Solargraph::ApiMap::Store.new(pins) if options[:inference] store.method_pins.each do |pin| - if pin.return_type.undefined? - type = pin.typify(api_map) - type = pin.probe(api_map) if type.undefined? - pin.docstring.add_tag YARD::Tags::Tag.new('return', nil, type.items.map(&:to_s)) - pin.instance_variable_set(:@return_type, type) - end + next unless pin.return_type.undefined? + + type = pin.typify(api_map) + type = pin.probe(api_map) if type.undefined? + pin.docstring.add_tag YARD::Tags::Tag.new('return', nil, type.items.map(&:to_s)) + pin.instance_variable_set(:@return_type, type) end end rake_yard(store) From 79f0aaea6d933e844169ef1216d46dcb022f458e Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 9 Jul 2025 16:14:23 -0400 Subject: [PATCH 102/561] Fix RuboCop issue --- lib/solargraph/source_map/clip.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/solargraph/source_map/clip.rb b/lib/solargraph/source_map/clip.rb index e404cc31d..ed82fd649 100644 --- a/lib/solargraph/source_map/clip.rb +++ b/lib/solargraph/source_map/clip.rb @@ -101,7 +101,7 @@ def location # @return [Solargraph::Pin::Closure] def closure - @block ||= source_map.locate_closure_pin(cursor.node_position.line, cursor.node_position.character) + @closure ||= source_map.locate_closure_pin(cursor.node_position.line, cursor.node_position.character) end # The context at the current position. From 51eacbedf476201d8600f79cbe6bef263e384e1a Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 9 Jul 2025 16:46:25 -0400 Subject: [PATCH 103/561] Fix some workflow issues --- .github/workflows/linting.yml | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index a616a5460..aa29bb3dd 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -9,7 +9,7 @@ name: Linting on: workflow_dispatch: {} pull_request: - branches: [main] + branches: [ master ] push: branches: - 'main' @@ -33,19 +33,17 @@ jobs: ruby-version: 3.4 bundler: latest bundler-cache: true - cache-version: ${{ matrix.solargraph-version }}-${{ matrix.versions.rails-major }}.${{ matrix.versions.rails-minor }}-2025-06-06 + cache-version: 2025-06-06 - name: Restore cache of gem annotations id: dot-cache-restore uses: actions/cache/restore@v4 with: key: | - 2025-06-26-09-${{ runner.os }}-dot-cache-${{ matrix.solargraph-version }}-${{ matrix.versions.rails-major }}--${{ matrix.versions.rails-minor }}-${{ hashFiles('spec/**/Gemfile.lock') }}-${{ hashFiles('Gemfile.lock') }} + 2025-06-26-09-${{ runner.os }}-dot-cache-${{ hashFiles('spec/**/Gemfile.lock') }}-${{ hashFiles('Gemfile.lock') }} restore-keys: | - 2025-06-26-09-${{ runner.os }}-dot-cache-${{ matrix.solargraph-version }}-${{ matrix.versions.rails-major }}-${{ matrix.versions.rails-minor }}-${{ hashFiles('spec/**/Gemfile.lock') }}- - 2025-06-26-09-${{ runner.os }}-dot-cache-${{ matrix.solargraph-version }}-${{ matrix.versions.rails-major }}-${{ matrix.versions.rails-minor }}- - 2025-06-26-09-${{ runner.os }}-dot-cache-${{ matrix.solargraph-version }}-${{ matrix.versions.rails-major }}- - 2025-06-26-09-${{ runner.os }}-dot-cache-${{ matrix.solargraph-version }}- + 2025-06-26-09-${{ runner.os }}-dot-cache-${{ hashFiles('spec/**/Gemfile.lock') }}- + 2025-06-26-09-${{ runner.os }}-dot-cache- path: | /home/runner/.cache/solargraph From a2e92dc049dba3afaf5e4962a6820f7e4fb0d71c Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 9 Jul 2025 16:49:41 -0400 Subject: [PATCH 104/561] Fix some workflow issues --- .github/workflows/linting.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index aa29bb3dd..4e6d8980b 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -67,7 +67,7 @@ jobs: reporter: github-pr-check skip_install: true use_bundler: true - rubocop_extensions: 'rubocop-performance:gemfile rubocop-rspec:gemfile rubocop-rake:gemfile rubocop-yard:gemfile' + rubocop_extensions: 'rubocop-rspec:gemfile rubocop-rake:gemfile rubocop-yard:gemfile' fail_level: info rubocop_version: Gemfile level: info From 6ac08b7b935bf670d4c3ee9218074e417d472a1f Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 9 Jul 2025 16:58:54 -0400 Subject: [PATCH 105/561] Fix some workflow issues --- .github/workflows/linting.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index 4e6d8980b..c5a10b8c1 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -40,9 +40,9 @@ jobs: uses: actions/cache/restore@v4 with: key: | - 2025-06-26-09-${{ runner.os }}-dot-cache-${{ hashFiles('spec/**/Gemfile.lock') }}-${{ hashFiles('Gemfile.lock') }} + 2025-06-26-09-${{ runner.os }}-dot-cache-${{ hashFiles('Gemfile.lock') }} restore-keys: | - 2025-06-26-09-${{ runner.os }}-dot-cache-${{ hashFiles('spec/**/Gemfile.lock') }}- + 2025-06-26-09-${{ runner.os }}-dot-cache 2025-06-26-09-${{ runner.os }}-dot-cache- path: | /home/runner/.cache/solargraph @@ -68,6 +68,7 @@ jobs: skip_install: true use_bundler: true rubocop_extensions: 'rubocop-rspec:gemfile rubocop-rake:gemfile rubocop-yard:gemfile' + rubocop_flags: '-c .rubocop.yml' fail_level: info rubocop_version: Gemfile level: info From a9963b0f39d2ea31f8be9f426437bd86519217f8 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 9 Jul 2025 17:27:00 -0400 Subject: [PATCH 106/561] Fix some workflow issues --- .github/workflows/linting.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index c5a10b8c1..b33268df1 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -50,7 +50,7 @@ jobs: - name: Overcommit run: | bundle exec overcommit --sign - bundle exec overcommit --run --diff origin/main + bundle exec overcommit --run --diff origin/master rubocop: name: rubocop runs-on: ubuntu-latest From 9b17f752bd569e79f3eef98e720f9c9b981ec3f5 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 9 Jul 2025 18:05:58 -0400 Subject: [PATCH 107/561] Workflow fixes --- .github/workflows/linting.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index b33268df1..026e8c126 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -54,8 +54,6 @@ jobs: rubocop: name: rubocop runs-on: ubuntu-latest - env: - BUNDLE_ONLY: rubocop steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - uses: ruby/setup-ruby@1a615958ad9d422dd932dc1d5823942ee002799f # v1.227.0 From 325d835c37ac4b0788838d4c72b77e946bffada9 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 9 Jul 2025 18:06:09 -0400 Subject: [PATCH 108/561] Workflow fixes --- .github/workflows/rubocop.yml | 2 +- solargraph.gemspec | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/rubocop.yml b/.github/workflows/rubocop.yml index 9fcbafa23..e11526a06 100644 --- a/.github/workflows/rubocop.yml +++ b/.github/workflows/rubocop.yml @@ -17,7 +17,7 @@ permissions: contents: read jobs: - rubocop: + full: runs-on: ubuntu-latest steps: diff --git a/solargraph.gemspec b/solargraph.gemspec index 94ebbe46c..137a14cdb 100755 --- a/solargraph.gemspec +++ b/solargraph.gemspec @@ -38,9 +38,6 @@ Gem::Specification.new do |s| s.add_runtime_dependency 'rbs', '~> 3.3' s.add_runtime_dependency 'reverse_markdown', '~> 3.0' s.add_runtime_dependency 'rubocop', '~> 1.76' - s.add_runtime_dependency 'rubocop-rake', '~> 0.7' - s.add_runtime_dependency 'rubocop-rspec', '~> 3.6' - s.add_runtime_dependency 'rubocop-yard', '~> 1.0' s.add_runtime_dependency 'thor', '~> 1.0' s.add_runtime_dependency 'tilt', '~> 2.0' s.add_runtime_dependency 'yard', '~> 0.9', '>= 0.9.24' @@ -50,6 +47,9 @@ Gem::Specification.new do |s| s.add_development_dependency 'public_suffix', '~> 3.1' s.add_development_dependency 'rake', '~> 13.2' s.add_development_dependency 'rspec', '~> 3.5' + s.add_development_dependency 'rubocop-rake', '~> 0.7' + s.add_development_dependency 'rubocop-rspec', '~> 3.6' + s.add_development_dependency 'rubocop-yard', '~> 1.0' s.add_development_dependency 'simplecov', '~> 0.21' s.add_development_dependency 'simplecov-lcov', '~> 0.8' s.add_development_dependency 'undercover', '~> 0.6' From c473c8075e08b030b9451a8ddf60d07a370dbd1c Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 10 Jul 2025 15:54:43 -0400 Subject: [PATCH 109/561] Ignore vendor directory in RuboCop --- .rubocop.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.rubocop.yml b/.rubocop.yml index a769ad37e..b95236b84 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -17,6 +17,7 @@ AllCops: - "spec/fixtures/invalid_byte.rb" - "spec/fixtures/invalid_node_comment.rb" - "spec/fixtures/invalid_utf8.rb" + - "vendor/**/*.rb" TargetRubyVersion: 3.0 Style/MethodDefParentheses: From 5afc7a31c04f4d93704dc237db3d6cd5cc18726c Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 10 Jul 2025 16:03:31 -0400 Subject: [PATCH 110/561] Drop unneeded code --- lib/solargraph/logging.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/solargraph/logging.rb b/lib/solargraph/logging.rb index 8ff297077..a0dbff3bb 100644 --- a/lib/solargraph/logging.rb +++ b/lib/solargraph/logging.rb @@ -17,8 +17,6 @@ module Logging @@logger.formatter = proc do |severity, datetime, progname, msg| "[#{severity}] #{msg}\n" end - @@dev_null_logger = Logger.new('/dev/null') - module_function From eb63ffe631aa6d9155bcc0c98dfca510eadf978e Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 10 Jul 2025 16:03:31 -0400 Subject: [PATCH 111/561] Drop unneeded code --- lib/solargraph/logging.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/solargraph/logging.rb b/lib/solargraph/logging.rb index 99db66cbe..265232e5f 100644 --- a/lib/solargraph/logging.rb +++ b/lib/solargraph/logging.rb @@ -24,8 +24,6 @@ module Logging @@logger.formatter = proc do |severity, datetime, progname, msg| "[#{severity}] #{msg}\n" end - @@dev_null_logger = Logger.new('/dev/null') - module_function From d69537fecc1b6506ffc3b2ccf6a114ac8161fc65 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 10 Jul 2025 15:54:43 -0400 Subject: [PATCH 112/561] Ignore vendor directory in RuboCop --- .rubocop.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.rubocop.yml b/.rubocop.yml index a769ad37e..b95236b84 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -17,6 +17,7 @@ AllCops: - "spec/fixtures/invalid_byte.rb" - "spec/fixtures/invalid_node_comment.rb" - "spec/fixtures/invalid_utf8.rb" + - "vendor/**/*.rb" TargetRubyVersion: 3.0 Style/MethodDefParentheses: From 404e686fb331835dfd212d4407f62d77a1578269 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 10 Jul 2025 16:21:18 -0400 Subject: [PATCH 113/561] Fix return tag --- lib/solargraph/logging.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/solargraph/logging.rb b/lib/solargraph/logging.rb index a0dbff3bb..35473f6df 100644 --- a/lib/solargraph/logging.rb +++ b/lib/solargraph/logging.rb @@ -23,8 +23,9 @@ module Logging # override this in your class to temporarily set a custom # filtering log level for the class (e.g., suppress any debug # message by setting it to :info even if it is set elsewhere, or - # show existing debug messages by setting to :debug). @return - # [Symbol] + # show existing debug messages by setting to :debug). + # + # @return [Symbol] def log_level :warn end From f6853ce21329bfcac11b6fa43f032f2654500753 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 10 Jul 2025 16:53:39 -0400 Subject: [PATCH 114/561] Bump version for solargraph-rails testing --- lib/solargraph/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/solargraph/version.rb b/lib/solargraph/version.rb index 0a39692fc..7473b20b9 100755 --- a/lib/solargraph/version.rb +++ b/lib/solargraph/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module Solargraph - VERSION = '0.56.0' + VERSION = '0.57.alpha' end From 4bccb96b449e7de62ff3901433f10dc9bc98e0c0 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 11 Jul 2025 11:21:45 -0400 Subject: [PATCH 115/561] Fix missing require --- lib/solargraph/pin_cache.rb | 1 + spec/shell_spec.rb | 43 +++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 spec/shell_spec.rb diff --git a/lib/solargraph/pin_cache.rb b/lib/solargraph/pin_cache.rb index a3f81baac..170a6d17a 100644 --- a/lib/solargraph/pin_cache.rb +++ b/lib/solargraph/pin_cache.rb @@ -1,3 +1,4 @@ +require 'yard-activesupport-concern' require 'fileutils' require 'rbs' diff --git a/spec/shell_spec.rb b/spec/shell_spec.rb new file mode 100644 index 000000000..6455e1216 --- /dev/null +++ b/spec/shell_spec.rb @@ -0,0 +1,43 @@ +require 'tmpdir' +require 'open3' + +describe Solargraph::Shell do + before do + @temp_dir = Dir.mktmpdir + File.open(File.join(@temp_dir, 'Gemfile'), 'w') do |file| + file.puts "source 'https://rubygems.org'" + file.puts "gem 'solargraph', path: #{File.expand_path('../..', __FILE__)}" + end + output, status = Open3.capture2e("bundle install", chdir: @temp_dir) + expect(status.success?).to eq(true), ->{ "Bundle install failed: output=#{output}" } + end + + def bundle_exec(*cmd) + # run the command in the temporary directory with bundle exec + output, status = Open3.capture2e("bundle exec #{cmd.join(' ')}", chdir: @temp_dir) + expect(status.success?).to eq(true), "Command failed: #{output}" + output + end + + after do + # remove the temporary directory after the tests + FileUtils.remove_entry(@temp_dir) if Dir.exist?(@temp_dir) + end + + describe "--version" do + it "returns a version when run" do + output = bundle_exec("solargraph", "--version") + + expect(output).to_not be_empty + expect(output).to eq (Solargraph::VERSION + "\n") + end + end + + describe "uncache" do + it "uncaches without erroring out" do + output = bundle_exec("solargraph", "uncache", "solargraph") + + expect(output).to include('Clearing pin cache in') + end + end +end From d2440eff301b916b343c7441d6ed80450ca04597 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 11 Jul 2025 17:20:46 -0400 Subject: [PATCH 116/561] Add annotations --- lib/solargraph.rb | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/solargraph.rb b/lib/solargraph.rb index 2b975a66c..1ef0a7ee6 100755 --- a/lib/solargraph.rb +++ b/lib/solargraph.rb @@ -65,6 +65,10 @@ def self.asserts_on?(type) end end + # @param type [Symbol] The type of assertion to perform. + # @param msg [String, nil] An optional message to log + # @param block [Proc] A block that returns a message to log + # @return [void] def self.assert_or_log(type, msg = nil, &block) raise (msg || block.call) if asserts_on?(type) && ![:combine_with_visibility].include?(type) logger.info msg, &block @@ -79,6 +83,11 @@ def self.logger # A helper method that runs Bundler.with_unbundled_env or falls back to # Bundler.with_clean_env for earlier versions of Bundler. + # + # @generic T + # @yieldreturn [generic] + # @sg-ignore dynamic call, but both functions behave the same + # @return [generic] def self.with_clean_env &block meth = if Bundler.respond_to?(:with_original_env) :with_original_env From 9a2db0c0c053b72fa4bf7da239b9d1dc20b08242 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 11 Jul 2025 17:22:07 -0400 Subject: [PATCH 117/561] Fix type issues --- lib/solargraph.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/solargraph.rb b/lib/solargraph.rb index 8295945cd..f5348ded6 100755 --- a/lib/solargraph.rb +++ b/lib/solargraph.rb @@ -66,8 +66,11 @@ def self.asserts_on? def self.assert_or_log(type, msg = nil, &block) if asserts_on? + # @type [String, nil] msg ||= block.call + raise "No message given for #{type.inspect}" if msg.nil? + # not ready for prime time return if [:combine_with_visibility].include?(type) # conditional aliases to handle compatibility corner cases From d52d49fc93a7e1878eb054e830d030db736a2eae Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 11 Jul 2025 17:28:33 -0400 Subject: [PATCH 118/561] Typechecking fixes --- lib/solargraph/api_map.rb | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/solargraph/api_map.rb b/lib/solargraph/api_map.rb index 6cdb04730..f9bd84552 100755 --- a/lib/solargraph/api_map.rb +++ b/lib/solargraph/api_map.rb @@ -36,11 +36,13 @@ def initialize pins: [] # just caches), please also change `equality_fields` below. # + # @param other [Object] def eql?(other) self.class == other.class && equality_fields == other.equality_fields end + # @param other [Object] def ==(other) self.eql?(other) end @@ -113,6 +115,7 @@ def catalog bench [self.class, @source_map_hash, implicit, @doc_map, @unresolved_requires] end + # @return [DocMap] def doc_map @doc_map ||= DocMap.new([], []) end @@ -186,10 +189,16 @@ def self.load directory api_map end + # @param out [IO, nil] + # @return [void] def cache_all!(out) @doc_map.cache_all!(out) end + # @param gemspec [Gem::Specification] + # @param rebuild [Boolean] + # @param out [IO, nil] + # @return [void] def cache_gem(gemspec, rebuild: false, out: nil) @doc_map.cache(gemspec, rebuild: rebuild, out: out) end @@ -514,6 +523,8 @@ def get_complex_type_methods complex_type, context = '', internal = false # @param rooted_tag [String] Parameterized namespace, fully qualified # @param name [String] Method name to look up # @param scope [Symbol] :instance or :class + # @param visibility [Array] :public, :protected, and/or :private + # @param preserve_generics [Boolean] # @return [Array] def get_method_stack rooted_tag, name, scope: :instance, visibility: [:private, :protected, :public], preserve_generics: false rooted_type = ComplexType.parse(rooted_tag) @@ -837,7 +848,7 @@ def qualify_lower namespace, context qualify namespace, context.split('::')[0..-2].join('::') end - # @param fq_tag [String] + # @param fq_sub_tag [String] # @return [String, nil] def qualify_superclass fq_sub_tag fq_sub_type = ComplexType.try_parse(fq_sub_tag) From 4da922fe1fcc0ccab06b975c77f7fbaeecb712e5 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 11 Jul 2025 17:37:41 -0400 Subject: [PATCH 119/561] Various linting fixes --- lib/solargraph/doc_map.rb | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/lib/solargraph/doc_map.rb b/lib/solargraph/doc_map.rb index d51fc3022..45a669678 100644 --- a/lib/solargraph/doc_map.rb +++ b/lib/solargraph/doc_map.rb @@ -2,6 +2,7 @@ require 'pathname' require 'benchmark' +require 'open3' module Solargraph # A collection of pins generated from required gems. @@ -32,8 +33,10 @@ def uncached_gemspecs # @return [Array] attr_reader :uncached_rbs_collection_gemspecs + # @return [String, nil] attr_reader :rbs_collection_path + # @return [String, nil] attr_reader :rbs_collection_config_path # @return [Workspace, nil] @@ -56,6 +59,8 @@ def initialize(requires, preferences, workspace = nil) pins.concat @environ.pins end + # @param out [IO] + # @return [void] def cache_all!(out) # if we log at debug level: if logger.info? @@ -73,12 +78,18 @@ def cache_all!(out) @uncached_yard_gemspecs = [] end + # @param gemspec [Gem::Specification] + # @param out [IO] + # @return [void] def cache_yard_pins(gemspec, out) pins = GemPins.build_yard_pins(gemspec) PinCache.serialize_yard_gem(gemspec, pins) logger.info { "Cached #{pins.length} YARD pins for gem #{gemspec.name}:#{gemspec.version}" } unless pins.empty? end + # @param gemspec [Gem::Specification] + # @param out [IO] + # @return [void] def cache_rbs_collection_pins(gemspec, out) rbs_map = RbsMap.from_gemspec(gemspec, rbs_collection_path, rbs_collection_config_path) pins = rbs_map.pins @@ -90,6 +101,9 @@ def cache_rbs_collection_pins(gemspec, out) end # @param gemspec [Gem::Specification] + # @param rebuild [Boolean] whether to rebuild the pins even if they are cached + # @param out [IO, nil] output stream for logging + # @return [void] def cache(gemspec, rebuild: false, out: nil) build_yard = uncached_yard_gemspecs.include?(gemspec) || rebuild build_rbs_collection = uncached_rbs_collection_gemspecs.include?(gemspec) || rebuild @@ -113,26 +127,33 @@ def unresolved_requires @unresolved_requires ||= required_gems_map.select { |_, gemspecs| gemspecs.nil? }.keys end + # @return [Hash{Array(String, String) => Array}] Indexed by gemspec name and version def self.all_yard_gems_in_memory @yard_gems_in_memory ||= {} end + # @return [Hash{String => Array}] stored by RBS collection path def self.all_rbs_collection_gems_in_memory @rbs_collection_gems_in_memory ||= {} end + # @return [Hash{Array(String, String) => Array}] Indexed by gemspec name and version def yard_pins_in_memory self.class.all_yard_gems_in_memory end + # @return [Hash{Array(String, String) => Array}] Indexed by gemspec name and version def rbs_collection_pins_in_memory self.class.all_rbs_collection_gems_in_memory[rbs_collection_path] ||= {} end + # @return [Hash{Array(String, String) => Array}] Indexed by gemspec name and version def self.all_combined_pins_in_memory @combined_pins_in_memory ||= {} end + # @todo this should also include an index by the hash of the RBS collection + # @return [Hash{Array(String, String) => Array}] Indexed by gemspec name and version def combined_pins_in_memory self.class.all_combined_pins_in_memory end @@ -150,7 +171,9 @@ def load_serialized_gem_pins @uncached_yard_gemspecs = [] @uncached_rbs_collection_gemspecs = [] with_gemspecs, without_gemspecs = required_gems_map.partition { |_, v| v } + # @type [Array] paths = Hash[without_gemspecs].keys + # @type [Array] gemspecs = Hash[with_gemspecs].values.flatten.compact + dependencies.to_a paths.each do |path| @@ -259,6 +282,8 @@ def deserialize_stdlib_rbs_map path end end + # @param gemspec [Gem::Specification] + # @param rbs_version_cache_key [String] # @return [Array, nil] def deserialize_rbs_collection_cache gemspec, rbs_version_cache_key return if rbs_collection_pins_in_memory.key?([gemspec, rbs_version_cache_key]) @@ -274,22 +299,13 @@ def deserialize_rbs_collection_cache gemspec, rbs_version_cache_key end end - # @param gemspec [Gem::Specification] - # @return [Boolean] - def try_gem_in_memory gemspec - gempins = DocMap.gems_in_memory[gemspec] - return false unless gempins - Solargraph.logger.debug "Found #{gemspec.name} #{gemspec.version} in memory" - @pins.concat gempins - true - end - # @param path [String] # @return [::Array, nil] def resolve_path_to_gemspecs path return nil if path.empty? return gemspecs_required_from_bundler if path == 'bundler/require' + # @type [Gem::Specification, nil] gemspec = Gem::Specification.find_by_path(path) if gemspec.nil? gem_name_guess = path.split('/').first @@ -355,6 +371,7 @@ def inspect self.class.inspect end + # @return [Array] def gemspecs_required_from_bundler # @todo Handle projects with custom Bundler/Gemfile setups return unless workspace.gemfile? @@ -377,6 +394,7 @@ def gemspecs_required_from_bundler end end + # @return [Array] def gemspecs_required_from_external_bundle logger.info 'Fetching gemspecs required from external bundle' return [] unless workspace&.directory From 6014c699bb767cb1cee68e1474e5e15f04a5b5cd Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 11 Jul 2025 17:48:29 -0400 Subject: [PATCH 120/561] Linting for Library class --- lib/solargraph/library.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/solargraph/library.rb b/lib/solargraph/library.rb index 8eadeed62..75b0e3e2b 100644 --- a/lib/solargraph/library.rb +++ b/lib/solargraph/library.rb @@ -621,6 +621,7 @@ def cache_next_gemspec end end + # @return [Array] def cacheable_specs cacheable = api_map.uncached_yard_gemspecs + api_map.uncached_rbs_collection_gemspecs - @@ -631,6 +632,7 @@ def cacheable_specs queued_gemspec_cache end + # @return [Array] def queued_gemspec_cache @queued_gemspec_cache ||= [] end @@ -672,6 +674,7 @@ def end_cache_progress @total = nil end + # @return [void] def sync_catalog return if @sync_count == 0 From 56c327e488d15a29c558967b090f55dd6ab332ae Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 11 Jul 2025 17:52:57 -0400 Subject: [PATCH 121/561] Linting --- lib/solargraph/pin/base.rb | 8 ++++++++ lib/solargraph/rbs_map/conversions.rb | 17 +++++++++++++++-- lib/solargraph/yard_map/helpers.rb | 2 ++ lib/solargraph/yard_map/mapper/to_constant.rb | 3 +++ lib/solargraph/yard_map/mapper/to_namespace.rb | 3 +++ 5 files changed, 31 insertions(+), 2 deletions(-) diff --git a/lib/solargraph/pin/base.rb b/lib/solargraph/pin/base.rb index 67fa323ba..cdd6a5ace 100644 --- a/lib/solargraph/pin/base.rb +++ b/lib/solargraph/pin/base.rb @@ -54,6 +54,7 @@ def initialize location: nil, type_location: nil, closure: nil, source: nil, nam assert_location_provided end + # @return [void] def assert_location_provided return unless best_location.nil? && %i[yardoc source rbs].include?(source) @@ -297,6 +298,9 @@ def choose_pin_attr_with_same_name(other, attr) choose_pin_attr(other, attr) end + # @param other [self] + # @param attr [::Symbol] + # @return [undefined] def choose_pin_attr(other, attr) # @type [Pin::Base, nil] val1 = send(attr) @@ -311,6 +315,7 @@ def choose_pin_attr(other, attr) [val1, val2].compact.min_by { _1.best_location.to_s } end + # @return [void] def assert_source_provided Solargraph.assert_or_log(:source, "source not provided - #{@path} #{@source} #{self.class}") if source.nil? end @@ -555,6 +560,7 @@ def inner_desc "name=#{name.inspect} return_type=#{type_desc}, context=#{context.rooted_tags}, closure=#{closure_info}, binder=#{binder_info}" end + # @return [String] def desc "[#{inner_desc}]" end @@ -564,6 +570,7 @@ def inspect "#<#{self.class} `#{self.inner_desc}`#{all_location_text} via #{source.inspect}>" end + # @return [String] def all_location_text if location.nil? && type_location.nil? '' @@ -576,6 +583,7 @@ def all_location_text end end + # @return [void] def reset_generated! end diff --git a/lib/solargraph/rbs_map/conversions.rb b/lib/solargraph/rbs_map/conversions.rb index 17dad63aa..8802de51a 100644 --- a/lib/solargraph/rbs_map/conversions.rb +++ b/lib/solargraph/rbs_map/conversions.rb @@ -22,12 +22,14 @@ def initialize visibility = :public end end + # @param loader [RBS::EnvironmentLoader] def initialize(loader:) @loader = loader @pins = [] load_environment_to_pins(loader) end + # @return [RBS::EnvironmentLoader] attr_reader :loader # @return [Array] @@ -102,14 +104,14 @@ def convert_self_type_to_pins decl, closure # @param closure [Pin::Namespace] # @return [void] def convert_members_to_pins decl, closure - context = Context.new + context = Conversions::Context.new decl.members.each { |m| context = convert_member_to_pin(m, closure, context) } end # @param member [RBS::AST::Members::Base,RBS::AST::Declarations::Base] # @param closure [Pin::Namespace] # @param context [Context] - # @return [void] + # @return [Context] def convert_member_to_pin member, closure, context case member when RBS::AST::Members::MethodDefinition @@ -313,6 +315,7 @@ def global_decl_to_pin decl # related overrides # @todo externalize remaining overrides into yaml file, then # allow that to be extended via .solargraph.yml + # @type [Hash{Array(String, Symbol, String) => Symbol} VISIBILITY_OVERRIDE = { ["Rails::Engine", :instance, "run_tasks_blocks"] => :protected, # Should have been marked as both instance and class method in module -e.g., 'module_function' @@ -337,6 +340,13 @@ def global_decl_to_pin decl ["Rainbow::Presenter", :instance, "wrap_with_sgr"] => :private, } + # @param decl [RBS::AST::Members::MethodDefinition, RBS::AST::Members::AttrReader, RBS::AST::Members::AttrAccessor] + # @param closure [Pin::Namespace] + # @param context [Context] + # @param scope [Symbol] :instance or :class + # @param name [String] The name of the method + # @sg-ignore + # @return [Symbol] def calculate_method_visibility(decl, context, closure, scope, name) override_key = [closure.path, scope, name] visibility = VISIBILITY_OVERRIDE[override_key] @@ -494,6 +504,7 @@ def parts_of_function type, pin # @param decl [RBS::AST::Members::AttrReader,RBS::AST::Members::AttrAccessor] # @param closure [Pin::Namespace] + # @param context [Context] # @return [void] def attr_reader_to_pin(decl, closure, context) name = decl.name.to_s @@ -517,6 +528,7 @@ def attr_reader_to_pin(decl, closure, context) # @param decl [RBS::AST::Members::AttrWriter, RBS::AST::Members::AttrAccessor] # @param closure [Pin::Namespace] + # @param context [Context] # @return [void] def attr_writer_to_pin(decl, closure, context) final_scope = decl.kind == :instance ? :instance : :class @@ -549,6 +561,7 @@ def attr_writer_to_pin(decl, closure, context) # @param decl [RBS::AST::Members::AttrAccessor] # @param closure [Pin::Namespace] + # @param context [Context] # @return [void] def attr_accessor_to_pin(decl, closure, context) attr_reader_to_pin(decl, closure, context) diff --git a/lib/solargraph/yard_map/helpers.rb b/lib/solargraph/yard_map/helpers.rb index 7058af9c3..96bc454b5 100644 --- a/lib/solargraph/yard_map/helpers.rb +++ b/lib/solargraph/yard_map/helpers.rb @@ -18,7 +18,9 @@ def object_location code_object, spec Solargraph::Location.new(file, Solargraph::Range.from_to(code_object.line - 1, 0, code_object.line - 1, 0)) end + # @param code_object [YARD::CodeObjects::Base] # @param spec [Gem::Specification, nil] + # @return [Solargraph::Pin::Namespace] def create_closure_namespace_for(code_object, spec) code_object_for_location = code_object # code_object.namespace is sometimes a YARD proxy object pointing to a method path ("Object#new") diff --git a/lib/solargraph/yard_map/mapper/to_constant.rb b/lib/solargraph/yard_map/mapper/to_constant.rb index 0c2ac6dd2..415c9bf29 100644 --- a/lib/solargraph/yard_map/mapper/to_constant.rb +++ b/lib/solargraph/yard_map/mapper/to_constant.rb @@ -7,6 +7,9 @@ module ToConstant extend YardMap::Helpers # @param code_object [YARD::CodeObjects::Base] + # @param closure [Pin::Closure, nil] + # @param spec [Gem::Specification, nil] + # @return [Pin::Constant] def self.make code_object, closure = nil, spec = nil closure ||= create_closure_namespace_for(code_object, spec) diff --git a/lib/solargraph/yard_map/mapper/to_namespace.rb b/lib/solargraph/yard_map/mapper/to_namespace.rb index 8dfb818a6..054ba3306 100644 --- a/lib/solargraph/yard_map/mapper/to_namespace.rb +++ b/lib/solargraph/yard_map/mapper/to_namespace.rb @@ -7,6 +7,9 @@ module ToNamespace extend YardMap::Helpers # @param code_object [YARD::CodeObjects::NamespaceObject] + # @param spec [Gem::Specification, nil] + # @param closure [Pin::Closure, nil] + # @return [Pin::Namespace] def self.make code_object, spec, closure = nil closure ||= create_closure_namespace_for(code_object, spec) location = object_location(code_object, spec) From f121cc2528bdaccf94d4e40de8e71b8c80bb9b72 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 11 Jul 2025 18:00:29 -0400 Subject: [PATCH 122/561] Linting --- lib/solargraph/pin/closure.rb | 2 ++ lib/solargraph/source_map.rb | 2 ++ 2 files changed, 4 insertions(+) diff --git a/lib/solargraph/pin/closure.rb b/lib/solargraph/pin/closure.rb index 9456cc622..2d87bad07 100644 --- a/lib/solargraph/pin/closure.rb +++ b/lib/solargraph/pin/closure.rb @@ -8,6 +8,7 @@ class Closure < Base # @param scope [::Symbol] :class or :instance # @param generics [::Array, nil] + # @param generic_defaults [Hash{String => ComplexType}] def initialize scope: :class, generics: nil, generic_defaults: {}, **splat super(**splat) @scope = scope @@ -15,6 +16,7 @@ def initialize scope: :class, generics: nil, generic_defaults: {}, **splat @generic_defaults = generic_defaults end + # @return [Hash{String => ComplexType}] def generic_defaults @generic_defaults ||= {} end diff --git a/lib/solargraph/source_map.rb b/lib/solargraph/source_map.rb index ae885d929..c16827863 100644 --- a/lib/solargraph/source_map.rb +++ b/lib/solargraph/source_map.rb @@ -161,10 +161,12 @@ def map source private + # @return [Hash{Class => Array}] def pin_class_hash @pin_class_hash ||= pins.to_set.classify(&:class).transform_values(&:to_a) end + # @return [Data] def data @data ||= Data.new(source) end From 32a2f342066b29fcbf568ce82d6546c7f77bc1e5 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 11 Jul 2025 18:04:21 -0400 Subject: [PATCH 123/561] Linting --- lib/solargraph/pin_cache.rb | 62 +++++++++++++++++++++++++++++++++++-- spec/shell_spec.rb | 21 +++++++------ 2 files changed, 70 insertions(+), 13 deletions(-) diff --git a/lib/solargraph/pin_cache.rb b/lib/solargraph/pin_cache.rb index 170a6d17a..2a0ec4639 100644 --- a/lib/solargraph/pin_cache.rb +++ b/lib/solargraph/pin_cache.rb @@ -27,6 +27,8 @@ def work_dir File.join(base_dir, "ruby-#{RUBY_VERSION}", "rbs-#{RBS::VERSION}", "solargraph-#{Solargraph::VERSION}") end + # @param gemspec [Gem::Specification] + # @return [String] def yardoc_path gemspec File.join(base_dir, "yard-#{YARD::VERSION}", @@ -34,94 +36,147 @@ def yardoc_path gemspec "#{gemspec.name}-#{gemspec.version}.yardoc") end + # @return [String] def stdlib_path File.join(work_dir, 'stdlib') end + # @param require [String] + # @return [String] def stdlib_require_path require File.join(stdlib_path, "#{require}.ser") end + # @param require [String] + # @return [Array] def deserialize_stdlib_require require load(stdlib_require_path(require)) end + # @param require [String] + # @param pins [Array] + # @return [void] def serialize_stdlib_require require, pins save(stdlib_require_path(require), pins) end + # @return [String] def core_path File.join(work_dir, 'core.ser') end + # @return [Array] def deserialize_core load(core_path) end + # @param pins [Array] + # @return [void] def serialize_core pins save(core_path, pins) end + # @param gemspec [Gem::Specification] + # @return [String] def yard_gem_path gemspec File.join(work_dir, 'yard', "#{gemspec.name}-#{gemspec.version}.ser") end + # @param gemspec [Gem::Specification] + # @return [Array] def deserialize_yard_gem(gemspec) load(yard_gem_path(gemspec)) end + # @param gemspec [Gem::Specification] + # @param pins [Array] + # @return [void] def serialize_yard_gem(gemspec, pins) save(yard_gem_path(gemspec), pins) end + # @param gemspec [Gem::Specification] + # @return [Boolean] def has_yard?(gemspec) exist?(yard_gem_path(gemspec)) end + # @param gemspec [Gem::Specification] + # @param hash [String, nil] + # @return [String] def rbs_collection_path(gemspec, hash) File.join(work_dir, 'rbs', "#{gemspec.name}-#{gemspec.version}-#{hash || 0}.ser") end + # @param gemspec [Gem::Specification] + # @return [String] def rbs_collection_path_prefix(gemspec) File.join(work_dir, 'rbs', "#{gemspec.name}-#{gemspec.version}-") end + # @param gemspec [Gem::Specification] + # @param hash [String, nil] + # @return [Array] def deserialize_rbs_collection_gem(gemspec, hash) load(rbs_collection_path(gemspec, hash)) end + # @param gemspec [Gem::Specification] + # @param hash [String, nil] + # @param pins [Array]n + # @return [void] def serialize_rbs_collection_gem(gemspec, hash, pins) save(rbs_collection_path(gemspec, hash), pins) end + # @param gemspec [Gem::Specification] + # @param hash [String, nil] + # @return [String] def combined_path(gemspec, hash) File.join(work_dir, 'combined', "#{gemspec.name}-#{gemspec.version}-#{hash || 0}.ser") end + # @param gemspec [Gem::Specification] + # @return [String] def combined_path_prefix(gemspec) File.join(work_dir, 'combined', "#{gemspec.name}-#{gemspec.version}-") end + # @param gemspec [Gem::Specification] + # @param hash [String, nil] + # @param pins [Array] + # @return [void] def serialize_combined_gem(gemspec, hash, pins) save(combined_path(gemspec, hash), pins) end + # @param gemspec [Gem::Specification] + # @param hash [String, nil] + # @return [Array] def deserialize_combined_gem gemspec, hash load(combined_path(gemspec, hash)) end + # @param gemspec [Gem::Specification] + # @param hash [String, nil] + # @return [Boolean] def has_rbs_collection?(gemspec, hash) exist?(rbs_collection_path(gemspec, hash)) end + # @return [void] def uncache_core uncache(core_path) end + # @return [void] def uncache_stdlib uncache(stdlib_path) end + # @param gemspec [Gem::Specification] + # @param out [IO, nil] + # @return [void] def uncache_gem(gemspec, out: nil) uncache(yardoc_path(gemspec), out: out) uncache_by_prefix(rbs_collection_path_prefix(gemspec), out: out) @@ -147,11 +202,12 @@ def load file nil end + # @param path [String] def exist? *path - File.file? join(*path) + File.file? File.join(*path) end - # @param path [Array] + # @param file [String] # @param pins [Array] # @return [void] def save file, pins @@ -162,8 +218,8 @@ def save file, pins logger.debug { "Cache#save: Saved #{pins.length} pins to #{file}" } end - # @return [void] # @param path_segments [Array] + # @return [void] def uncache *path_segments, out: nil path = File.join(*path_segments) if File.exist?(path) diff --git a/spec/shell_spec.rb b/spec/shell_spec.rb index 6455e1216..91f84b4c7 100644 --- a/spec/shell_spec.rb +++ b/spec/shell_spec.rb @@ -2,34 +2,35 @@ require 'open3' describe Solargraph::Shell do + let(:temp_dir) { Dir.mktmpdir } + before do - @temp_dir = Dir.mktmpdir - File.open(File.join(@temp_dir, 'Gemfile'), 'w') do |file| + File.open(File.join(temp_dir, 'Gemfile'), 'w') do |file| file.puts "source 'https://rubygems.org'" - file.puts "gem 'solargraph', path: #{File.expand_path('../..', __FILE__)}" + file.puts "gem 'solargraph', path: #{File.expand_path('..', __dir__)}" end - output, status = Open3.capture2e("bundle install", chdir: @temp_dir) - expect(status.success?).to eq(true), ->{ "Bundle install failed: output=#{output}" } + output, status = Open3.capture2e("bundle install", chdir: temp_dir) + raise "Failure installing bundle: #{output}" unless status.success? end def bundle_exec(*cmd) # run the command in the temporary directory with bundle exec - output, status = Open3.capture2e("bundle exec #{cmd.join(' ')}", chdir: @temp_dir) - expect(status.success?).to eq(true), "Command failed: #{output}" + output, status = Open3.capture2e("bundle exec #{cmd.join(' ')}", chdir: temp_dir) + expect(status.success?).to be(true), "Command failed: #{output}" output end after do # remove the temporary directory after the tests - FileUtils.remove_entry(@temp_dir) if Dir.exist?(@temp_dir) + FileUtils.rm_rf(temp_dir) end describe "--version" do it "returns a version when run" do output = bundle_exec("solargraph", "--version") - expect(output).to_not be_empty - expect(output).to eq (Solargraph::VERSION + "\n") + expect(output).not_to be_empty + expect(output).to eq("#{Solargraph::VERSION}\n") end end From 86e68a2feb79aa114bf06951551756ebefe801d2 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 11 Jul 2025 18:13:25 -0400 Subject: [PATCH 124/561] Fix merge issue --- lib/solargraph/yardoc.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/solargraph/yardoc.rb b/lib/solargraph/yardoc.rb index 763b20489..3d1b5b4d9 100644 --- a/lib/solargraph/yardoc.rb +++ b/lib/solargraph/yardoc.rb @@ -35,6 +35,7 @@ def cached?(gemspec) # True if another process is currently building the yardoc cache. # + # @param gemspec [Gem::Specification] def processing?(gemspec) yardoc = File.join(PinCache.yardoc_path(gemspec), 'processing') File.exist?(yardoc) From 26c0ea438731f6f45a01e6a1d7b9c4899ce791ee Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 11 Jul 2025 18:16:41 -0400 Subject: [PATCH 125/561] Linting --- lib/solargraph/source/chain.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/solargraph/source/chain.rb b/lib/solargraph/source/chain.rb index 9ab76bfe8..065c3bf10 100644 --- a/lib/solargraph/source/chain.rb +++ b/lib/solargraph/source/chain.rb @@ -76,7 +76,9 @@ def base # Determine potential Pins returned by this chain of words # - # @param api_map [ApiMap] @param name_pin [Pin::Base] A pin + # @param api_map [ApiMap] + # + # @param name_pin [Pin::Base] A pin # representing the place in which expression is evaluated (e.g., # a Method pin, or a Module or Class pin if not run within a # method - both in terms of the closure around the chain, as well @@ -193,6 +195,7 @@ def nullable? include Logging + # @return [String] def desc links.map(&:desc).to_s end From b10791202d100fc612c2cb7cba72f272e5c7056a Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 11 Jul 2025 18:17:36 -0400 Subject: [PATCH 126/561] Linting fixes --- lib/solargraph/rbs_map.rb | 17 +++++++++++++++-- lib/solargraph/shell.rb | 1 + 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/lib/solargraph/rbs_map.rb b/lib/solargraph/rbs_map.rb index c2673c71b..2f36ce991 100644 --- a/lib/solargraph/rbs_map.rb +++ b/lib/solargraph/rbs_map.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true +require 'digest' require 'pathname' require 'rbs' @@ -22,7 +23,8 @@ class RbsMap attr_reader :rbs_collection_config_path # @param library [String] - # @param version [String, nil] + # @param version [String, nil + # @param rbs_collection_config_path [String, Pathname, nil] # @param rbs_collection_paths [Array] def initialize library, version = nil, rbs_collection_config_path: nil, rbs_collection_paths: [] if rbs_collection_config_path.nil? && !rbs_collection_paths.empty? @@ -35,11 +37,13 @@ def initialize library, version = nil, rbs_collection_config_path: nil, rbs_coll add_library loader, library, version end + # @return [RBS::EnvironmentLoader] def loader @loader ||= RBS::EnvironmentLoader.new(core_root: nil, repository: repository) end - # @return string representing the version of the RBS info fetched + # @sg-ignore + # @return [String] representing the version of the RBS info fetched # for the given library. Must change when the RBS info is # updated upstream for the same library and version. May change # if the config for where information comes form changes. @@ -68,6 +72,10 @@ def cache_key end end + # @param gemspec [Gem::Specification] + # @param rbs_collection_path [String, Pathname, nil] + # @param rbs_collection_config_path [String, Pathname, nil] + # @return [RbsMap] def self.from_gemspec gemspec, rbs_collection_path, rbs_collection_config_path rbs_map = RbsMap.new(gemspec.name, gemspec.version, rbs_collection_paths: [rbs_collection_path].compact, @@ -80,6 +88,7 @@ def self.from_gemspec gemspec, rbs_collection_path, rbs_collection_config_path rbs_collection_config_path: rbs_collection_config_path) end + # @return [Array] def pins @pins ||= resolved? ? conversions.pins : [] end @@ -103,6 +112,7 @@ def resolved? @resolved end + # @return [RBS::Repository] def repository @repository ||= RBS::Repository.new(no_stdlib: false).tap do |repo| @rbs_collection_paths.each do |dir| @@ -120,16 +130,19 @@ def self.load library private + # @return [RBS::EnvironmentLoader] def loader @loader ||= RBS::EnvironmentLoader.new(core_root: nil, repository: repository) end + # @return [Conversions] def conversions @conversions ||= Conversions.new(loader: loader) end # @param loader [RBS::EnvironmentLoader] # @param library [String] + # @param version [String, nil] # @return [Boolean] true if adding the library succeeded def add_library loader, library, version @resolved = if loader.has_library?(library: library, version: version) diff --git a/lib/solargraph/shell.rb b/lib/solargraph/shell.rb index 8f02f6ec9..afea86a92 100755 --- a/lib/solargraph/shell.rb +++ b/lib/solargraph/shell.rb @@ -258,6 +258,7 @@ def pin_description pin end # @param gemspec [Gem::Specification] + # @param api_map [ApiMap] # @return [void] def do_cache gemspec, api_map # @todo if the rebuild: option is passed as a positional arg, From e7a6c0a8ec5e637d36bae0933ae22053f42909d6 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 11 Jul 2025 18:18:07 -0400 Subject: [PATCH 127/561] Fix merge issue --- lib/solargraph/shell.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/solargraph/shell.rb b/lib/solargraph/shell.rb index b91084b24..b98dd9fc3 100755 --- a/lib/solargraph/shell.rb +++ b/lib/solargraph/shell.rb @@ -245,6 +245,7 @@ def list desc 'rbs', 'Generate RBS definitions' option :filename, type: :string, alias: :f, desc: 'Generated file name', default: 'sig.rbs' option :inference, type: :boolean, desc: 'Enhance definitions with type inference', default: true + # @return [void] def rbs api_map = Solargraph::ApiMap.load('.') pins = api_map.source_maps.flat_map(&:pins) From 876bc4227650aba472e39f010a4fcff4124baf60 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 12 Jul 2025 10:04:07 -0400 Subject: [PATCH 128/561] Linting fixes --- lib/solargraph/api_map/index.rb | 3 +++ lib/solargraph/api_map/store.rb | 10 +++++++--- lib/solargraph/diagnostics/rubocop_helpers.rb | 1 + .../message/text_document/formatting.rb | 8 ++++++++ lib/solargraph/page.rb | 4 ++++ lib/solargraph/parser/flow_sensitive_typing.rb | 1 + lib/solargraph/source/chain/link.rb | 1 + lib/solargraph/workspace.rb | 1 + 8 files changed, 26 insertions(+), 3 deletions(-) diff --git a/lib/solargraph/api_map/index.rb b/lib/solargraph/api_map/index.rb index 810600534..990fc7169 100644 --- a/lib/solargraph/api_map/index.rb +++ b/lib/solargraph/api_map/index.rb @@ -15,6 +15,9 @@ def pins @pins ||= [] end + # @return [Set] + attr_reader :namespaces + # @return [Hash{String => Array}] def namespace_hash @namespace_hash ||= Hash.new { |h, k| h[k] = [] } diff --git a/lib/solargraph/api_map/store.rb b/lib/solargraph/api_map/store.rb index 47f92194c..d41a2a0ae 100644 --- a/lib/solargraph/api_map/store.rb +++ b/lib/solargraph/api_map/store.rb @@ -8,6 +8,7 @@ class ApiMap class Store # @param pinsets [Array>] def initialize *pinsets + @pinsets = pinsets catalog pinsets end @@ -200,10 +201,13 @@ def fqns_pins fqns private + # @return [Index] def index @indexes.last end + # @param pinsets [Array>] + # @return [Boolean] def catalog pinsets @pinsets = pinsets @indexes = [] @@ -235,17 +239,17 @@ def superclass_references index.superclass_references end - # @return [Hash{String => Array}] + # @return [Hash{String => Array}] def include_references index.include_references end - # @return [Hash{String => Array}] + # @return [Hash{String => Array}] def prepend_references index.prepend_references end - # @return [Hash{String => Array}] + # @return [Hash{String => Array}] def extend_references index.extend_references end diff --git a/lib/solargraph/diagnostics/rubocop_helpers.rb b/lib/solargraph/diagnostics/rubocop_helpers.rb index 8e2e13359..4eb2c711d 100644 --- a/lib/solargraph/diagnostics/rubocop_helpers.rb +++ b/lib/solargraph/diagnostics/rubocop_helpers.rb @@ -15,6 +15,7 @@ module RubocopHelpers # @return [void] def require_rubocop(version = nil) begin + # @type [String] gem_path = Gem::Specification.find_by_name('rubocop', version).full_gem_path gem_lib_path = File.join(gem_path, 'lib') $LOAD_PATH.unshift(gem_lib_path) unless $LOAD_PATH.include?(gem_lib_path) diff --git a/lib/solargraph/language_server/message/text_document/formatting.rb b/lib/solargraph/language_server/message/text_document/formatting.rb index 15117e0f4..036375ccd 100644 --- a/lib/solargraph/language_server/message/text_document/formatting.rb +++ b/lib/solargraph/language_server/message/text_document/formatting.rb @@ -34,6 +34,7 @@ def process private # @param corrections [String] + # @return [void] def log_corrections(corrections) corrections = corrections&.strip return if corrections&.empty? @@ -45,6 +46,8 @@ def log_corrections(corrections) end end + # @param file_uri [String] + # @return [Hash{String => undefined}] def config_for(file_uri) conf = host.formatter_config(file_uri) return {} unless conf.is_a?(Hash) @@ -52,7 +55,9 @@ def config_for(file_uri) conf['rubocop'] || {} end + # @param file_uri [String] # @param config [Hash{String => String}] + # @return [Array] def cli_args file_uri, config file = UriHelpers.uri_to_file(file_uri) args = [ @@ -71,6 +76,8 @@ def cli_args file_uri, config end # @param config [Hash{String => String}] + # @sg-ignore + # @return [Class] def formatter_class(config) if self.class.const_defined?('BlankRubocopFormatter') # @sg-ignore @@ -83,6 +90,7 @@ def formatter_class(config) end # @param value [Array, String] + # @return [String] def cop_list(value) value = value.join(',') if value.respond_to?(:join) return nil if value == '' || !value.is_a?(String) diff --git a/lib/solargraph/page.rb b/lib/solargraph/page.rb index 99b9e8f6d..34ae12fb0 100644 --- a/lib/solargraph/page.rb +++ b/lib/solargraph/page.rb @@ -27,8 +27,10 @@ def initialize locals, render_method end # @param text [String] + # @sg-ignore # @return [String] def htmlify text + # @type [String] YARD::Templates::Helpers::Markup::RDocMarkup.new(text).to_html end @@ -70,8 +72,10 @@ def initialize directory = VIEWS_PATH # @param template [String] # @param layout [Boolean] # @param locals [Hash] + # @sg-ignore # @return [String] def render template, layout: true, locals: {} + # @type [String] @render_method.call(template, layout: layout, locals: locals) end diff --git a/lib/solargraph/parser/flow_sensitive_typing.rb b/lib/solargraph/parser/flow_sensitive_typing.rb index 8fb26d498..33b295667 100644 --- a/lib/solargraph/parser/flow_sensitive_typing.rb +++ b/lib/solargraph/parser/flow_sensitive_typing.rb @@ -10,6 +10,7 @@ def initialize(locals, enclosing_breakable_pin = nil) end # @param and_node [Parser::AST::Node] + # @param true_ranges [Array] def process_and(and_node, true_ranges = []) lhs = and_node.children[0] rhs = and_node.children[1] diff --git a/lib/solargraph/source/chain/link.rb b/lib/solargraph/source/chain/link.rb index d5d63b025..716a55927 100644 --- a/lib/solargraph/source/chain/link.rb +++ b/lib/solargraph/source/chain/link.rb @@ -39,6 +39,7 @@ def resolve api_map, name_pin, locals end # debugging description of contents; not for machine use + # @return [String] def desc word end diff --git a/lib/solargraph/workspace.rb b/lib/solargraph/workspace.rb index aabadd333..ffd653d96 100644 --- a/lib/solargraph/workspace.rb +++ b/lib/solargraph/workspace.rb @@ -133,6 +133,7 @@ def rbs_collection_path @gem_rbs_collection ||= read_rbs_collection_path end + # @return [String, nil] def rbs_collection_config_path @rbs_collection_config_path ||= begin unless directory.empty? || directory == '*' From b4e09642932a6c765531e4c2d596c748bded417c Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 12 Jul 2025 10:58:39 -0400 Subject: [PATCH 129/561] Add stdlib deps to gemspec - needed for rbs collection to notice Drop rubygems as rbs collection won't let us shim core --- rbs_collection.yaml | 2 +- sig/shims/ast/{ => 0}/node.rbs | 0 sig/shims/rubygems/basic_specification.rbs | 3 --- sig/shims/rubygems/dependency.rbs | 5 ----- sig/shims/rubygems/errors.rbs | 17 ----------------- sig/shims/rubygems/spec_fetcher.rbs | 9 --------- sig/shims/rubygems/specification.rbs | 7 ------- solargraph.gemspec | 2 ++ 8 files changed, 3 insertions(+), 42 deletions(-) rename sig/shims/ast/{ => 0}/node.rbs (100%) delete mode 100644 sig/shims/rubygems/basic_specification.rbs delete mode 100644 sig/shims/rubygems/dependency.rbs delete mode 100644 sig/shims/rubygems/errors.rbs delete mode 100644 sig/shims/rubygems/spec_fetcher.rbs delete mode 100644 sig/shims/rubygems/specification.rbs diff --git a/rbs_collection.yaml b/rbs_collection.yaml index 551a475d6..450dea132 100644 --- a/rbs_collection.yaml +++ b/rbs_collection.yaml @@ -6,8 +6,8 @@ sources: revision: main repo_dir: gems - # You can specify local directories as sources also. - type: local + name: shims path: sig/shims # A directory to install the downloaded RBSs diff --git a/sig/shims/ast/node.rbs b/sig/shims/ast/0/node.rbs similarity index 100% rename from sig/shims/ast/node.rbs rename to sig/shims/ast/0/node.rbs diff --git a/sig/shims/rubygems/basic_specification.rbs b/sig/shims/rubygems/basic_specification.rbs deleted file mode 100644 index f254b1b36..000000000 --- a/sig/shims/rubygems/basic_specification.rbs +++ /dev/null @@ -1,3 +0,0 @@ -class Gem::BasicSpecification - def name: () -> String -end diff --git a/sig/shims/rubygems/dependency.rbs b/sig/shims/rubygems/dependency.rbs deleted file mode 100644 index 13f4549ca..000000000 --- a/sig/shims/rubygems/dependency.rbs +++ /dev/null @@ -1,5 +0,0 @@ -class Gem::Dependency - # Version of the gem - # - def version: () -> untyped -end diff --git a/sig/shims/rubygems/errors.rbs b/sig/shims/rubygems/errors.rbs deleted file mode 100644 index 0f107d78c..000000000 --- a/sig/shims/rubygems/errors.rbs +++ /dev/null @@ -1,17 +0,0 @@ -module Gem - class LoadError < ::LoadError - attr_accessor name: String - - attr_accessor requirement: untyped - end - - class MissingSpecError < Gem::LoadError - def initialize: (untyped name, untyped requirement, ?untyped? extra_message) -> void - - def message: () -> untyped - end - - class MissingSpecVersionError < MissingSpecError - def initialize: (untyped name, untyped requirement, untyped specs) -> void - end -end diff --git a/sig/shims/rubygems/spec_fetcher.rbs b/sig/shims/rubygems/spec_fetcher.rbs deleted file mode 100644 index 7a0297a98..000000000 --- a/sig/shims/rubygems/spec_fetcher.rbs +++ /dev/null @@ -1,9 +0,0 @@ -class Gem::SpecFetcher - include Gem::UserInteraction - - include Gem::Text - - def search_for_dependency: (untyped dependency, ?bool matching_platform) -> [::Array[[Gem::Dependency, untyped]], ::Array[untyped]] - - def spec_for_dependency: (untyped dependency, ?bool matching_platform) -> ::Array[untyped] -end diff --git a/sig/shims/rubygems/specification.rbs b/sig/shims/rubygems/specification.rbs deleted file mode 100644 index 3fb3c0386..000000000 --- a/sig/shims/rubygems/specification.rbs +++ /dev/null @@ -1,7 +0,0 @@ -class Gem::Specification < Gem::BasicSpecification - def self.find_by_name: (untyped name, *untyped requirements) -> instance - - def self.find_by_full_name: (untyped full_name) -> instance - - def self.find_by_path: (untyped path) -> instance -end diff --git a/solargraph.gemspec b/solargraph.gemspec index 5008b6247..e400ed9f2 100755 --- a/solargraph.gemspec +++ b/solargraph.gemspec @@ -23,6 +23,7 @@ Gem::Specification.new do |s| s.required_ruby_version = '>= 3.0' + s.add_runtime_dependency 'ast', '~> 2.4.3' s.add_runtime_dependency 'backport', '~> 1.2' s.add_runtime_dependency 'benchmark', '~> 0.4' s.add_runtime_dependency 'bundler', '~> 2.0' @@ -33,6 +34,7 @@ Gem::Specification.new do |s| s.add_runtime_dependency 'logger', '~> 1.6' s.add_runtime_dependency 'observer', '~> 0.1' s.add_runtime_dependency 'ostruct', '~> 0.6' + s.add_runtime_dependency 'open3', '~> 0.2.1' s.add_runtime_dependency 'parser', '~> 3.0' s.add_runtime_dependency 'prism', '~> 1.4' s.add_runtime_dependency 'rbs', '~> 3.3' From ac27324add971bf7f3ae5b22f57aa8d84813f03d Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 12 Jul 2025 11:14:28 -0400 Subject: [PATCH 130/561] Linting fixes --- lib/solargraph/gem_pins.rb | 1 - lib/solargraph/pin/base.rb | 4 ++-- lib/solargraph/pin/common.rb | 2 -- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/solargraph/gem_pins.rb b/lib/solargraph/gem_pins.rb index b92cbd6af..89927d41e 100644 --- a/lib/solargraph/gem_pins.rb +++ b/lib/solargraph/gem_pins.rb @@ -21,7 +21,6 @@ def self.build_yard_pins(gemspec) # @param pins [Array] def self.combine_method_pins_by_path(pins) - # bad_pins = pins.select { |pin| pin.is_a?(Pin::Method) && pin.path == 'StringIO.open' && pin.source == :rbs }; raise "wtf: #{bad_pins}" if bad_pins.length > 1 method_pins, alias_pins = pins.partition { |pin| pin.class == Pin::Method } by_path = method_pins.group_by(&:path) by_path.transform_values! do |pins| diff --git a/lib/solargraph/pin/base.rb b/lib/solargraph/pin/base.rb index 49e066b2c..15b145aee 100644 --- a/lib/solargraph/pin/base.rb +++ b/lib/solargraph/pin/base.rb @@ -13,10 +13,10 @@ class Base # @return [YARD::CodeObjects::Base] attr_reader :code_object - # @return [Solargraph::Location] + # @return [Solargraph::Location, nil] attr_reader :location - # @return [Solargraph::Location] + # @return [Solargraph::Location, nil] attr_reader :type_location # @return [String] diff --git a/lib/solargraph/pin/common.rb b/lib/solargraph/pin/common.rb index 6d9260abd..829dbe496 100644 --- a/lib/solargraph/pin/common.rb +++ b/lib/solargraph/pin/common.rb @@ -7,8 +7,6 @@ module Common attr_reader :location # @return [Pin::Closure, nil] - attr_reader :closure - def closure Solargraph.assert_or_log(:closure, "Closure not set on #{self.class} #{name.inspect} from #{source.inspect}") unless @closure @closure From 4196b1636924214efbf8998be9deb59cda511fc9 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 12 Jul 2025 11:45:56 -0400 Subject: [PATCH 131/561] Linting fixes --- lib/solargraph/api_map/index.rb | 3 +++ .../parser/flow_sensitive_typing.rb | 25 ++++++++++++++++++- lib/solargraph/source/chain/link.rb | 3 ++- 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/lib/solargraph/api_map/index.rb b/lib/solargraph/api_map/index.rb index 990fc7169..ea358297e 100644 --- a/lib/solargraph/api_map/index.rb +++ b/lib/solargraph/api_map/index.rb @@ -63,6 +63,7 @@ def superclass_references end # @param pins [Array] + # @return [self] def merge pins deep_clone.catalog pins end @@ -72,6 +73,7 @@ def merge pins attr_writer :pins, :pin_select_cache, :namespace_hash, :pin_class_hash, :path_pin_hash, :include_references, :extend_references, :prepend_references, :superclass_references + # @return [self] def deep_clone Index.allocate.tap do |copy| copy.pin_select_cache = {} @@ -87,6 +89,7 @@ def deep_clone end # @param new_pins [Array] + # @return [self] def catalog new_pins @pin_select_cache = {} pins.concat new_pins diff --git a/lib/solargraph/parser/flow_sensitive_typing.rb b/lib/solargraph/parser/flow_sensitive_typing.rb index 33b295667..58f149d73 100644 --- a/lib/solargraph/parser/flow_sensitive_typing.rb +++ b/lib/solargraph/parser/flow_sensitive_typing.rb @@ -4,6 +4,7 @@ class FlowSensitiveTyping include Solargraph::Parser::NodeMethods # @param locals [Array] + # @param enclosing_breakable_pin [Solargraph::Pin::Breakable, nil] def initialize(locals, enclosing_breakable_pin = nil) @locals = locals @enclosing_breakable_pin = enclosing_breakable_pin @@ -11,6 +12,8 @@ def initialize(locals, enclosing_breakable_pin = nil) # @param and_node [Parser::AST::Node] # @param true_ranges [Array] + # + # @return [void] def process_and(and_node, true_ranges = []) lhs = and_node.children[0] rhs = and_node.children[1] @@ -24,6 +27,8 @@ def process_and(and_node, true_ranges = []) end # @param if_node [Parser::AST::Node] + # + # @return [void] def process_if(if_node) # # See if we can refine a type based on the result of 'if foo.nil?' @@ -73,8 +78,11 @@ class << self # them based on the Closure and Location. # # @param pins [Array] + # @param name [String] # @param closure [Pin::Closure] # @param location [Location] + # + # @return [Array] def self.visible_pins(pins, name, closure, location) logger.debug { "FlowSensitiveTyping#visible_pins(name=#{name}, closure=#{closure}, location=#{location})" } pins_with_name = pins.select { |p| p.name == name } @@ -108,7 +116,10 @@ def self.visible_pins(pins, name, closure, location) private # @param pin [Pin::LocalVariable] - # @param if_node [Parser::AST::Node] + # @param downcast_type_name [String] + # @param presence [Range] + # + # @return [void] def add_downcast_local(pin, downcast_type_name, presence) # @todo Create pin#update method new_pin = Solargraph::Pin::LocalVariable.new( @@ -127,6 +138,7 @@ def add_downcast_local(pin, downcast_type_name, presence) # @param facts_by_pin [Hash{Pin::LocalVariable => Array String}>}] # @param presences [Array] + # # @return [void] def process_facts(facts_by_pin, presences) # @@ -143,6 +155,7 @@ def process_facts(facts_by_pin, presences) end # @param conditional_node [Parser::AST::Node] + # @param true_ranges [Array] def process_conditional(conditional_node, true_ranges) if conditional_node.type == :send process_isa(conditional_node, true_ranges) @@ -177,12 +190,20 @@ def parse_isa(isa_node) [isa_type_name, variable_name] end + # @param variable_name [String] + # @param position [Position] + # + # @return [Solargraph::Pin::LocalVariable, nil] def find_local(variable_name, position) pins = locals.select { |pin| pin.name == variable_name && pin.presence.include?(position) } return unless pins.length == 1 pins.first end + # @param isa_node [Parser::AST::Node] + # @param true_presences [Array] + # + # @return [void] def process_isa(isa_node, true_presences) isa_type_name, variable_name = parse_isa(isa_node) return if variable_name.nil? || variable_name.empty? @@ -198,6 +219,8 @@ def process_isa(isa_node, true_presences) end # @param node [Parser::AST::Node] + # + # @return [String, nil] def type_name(node) # e.g., # s(:const, nil, :Baz) diff --git a/lib/solargraph/source/chain/link.rb b/lib/solargraph/source/chain/link.rb index 716a55927..36a8d4e86 100644 --- a/lib/solargraph/source/chain/link.rb +++ b/lib/solargraph/source/chain/link.rb @@ -17,7 +17,6 @@ def initialize word = '' @word = word end - # @sg-ignore Fix "Not enough arguments to Module#protected" protected def equality_fields [self.class, word] end @@ -75,6 +74,8 @@ def nullable? end # debugging description of contents; not for machine use + # + # @return [String] def desc word end From da33ef57ef7a92b22a1c0509c4c225d14033576b Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 12 Jul 2025 12:26:09 -0400 Subject: [PATCH 132/561] Add rubocop-on-rbs and associated fixes --- .github/workflows/linting.yml | 2 +- .rubocop.yml | 1 + rbs/fills/tuple.rbs | 5 ++--- solargraph.gemspec | 1 + spec/spec_helper.rb | 2 ++ 5 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index 026e8c126..8dabe946b 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -65,7 +65,7 @@ jobs: reporter: github-pr-check skip_install: true use_bundler: true - rubocop_extensions: 'rubocop-rspec:gemfile rubocop-rake:gemfile rubocop-yard:gemfile' + rubocop_extensions: 'rubocop-rspec:gemfile rubocop-rake:gemfile rubocop-yard:gemfile rubocop-on-rbs:gemfile' rubocop_flags: '-c .rubocop.yml' fail_level: info rubocop_version: Gemfile diff --git a/.rubocop.yml b/.rubocop.yml index b95236b84..b18815cce 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -30,6 +30,7 @@ Metrics/MethodLength: Max: 25 plugins: + - rubocop-on-rbs - rubocop-rspec - rubocop-rake - rubocop-yard diff --git a/rbs/fills/tuple.rbs b/rbs/fills/tuple.rbs index 9a0a00d0e..f4e213355 100644 --- a/rbs/fills/tuple.rbs +++ b/rbs/fills/tuple.rbs @@ -45,7 +45,6 @@ module Solargraph | (7 index) -> H | (8 index) -> I | (9 index) -> J - | (0 index) -> K | (int index) -> nil # +# Bundler provides a consistent environment for Ruby projects by tracking and +# installing the exact gems and versions that are needed. +# +# Bundler is a part of Ruby's standard library. +# +# Bundler is used by creating *gemfiles* listing all the project dependencies +# and (optionally) their versions and then using +# +# require 'bundler/setup' +# +# or Bundler.setup to setup environment where only specified gems and their +# specified versions could be used. +# +# See [Bundler website](https://bundler.io/docs.html) for extensive +# documentation on gemfiles creation and Bundler usage. +# +# As a standard library inside project, Bundler could be used for introspection +# of loaded and required modules. +# +module Bundler + class Dsl + @source: untyped + + @sources: untyped + + @git_sources: untyped + + @dependencies: untyped + + @groups: untyped + + @install_conditionals: untyped + + @optional_groups: untyped + + @platforms: untyped + + @env: untyped + + @ruby_version: untyped + + @gemspecs: untyped + + @gemfile: untyped + + @gemfiles: untyped + + @valid_keys: untyped + + include RubyDsl + + def self.evaluate: (untyped gemfile, untyped lockfile, untyped unlock) -> untyped + + VALID_PLATFORMS: untyped + + VALID_KEYS: ::Array["group" | "groups" | "git" | "path" | "glob" | "name" | "branch" | "ref" | "tag" | "require" | "submodules" | "platform" | "platforms" | "source" | "install_if" | "force_ruby_platform"] + + GITHUB_PULL_REQUEST_URL: ::Regexp + + GITLAB_MERGE_REQUEST_URL: ::Regexp + + attr_reader gemspecs: untyped + + attr_reader gemfile: untyped + + attr_accessor dependencies: untyped + + def initialize: () -> void + + def eval_gemfile: (untyped gemfile, ?untyped? contents) -> untyped + + def gemspec: (?untyped? opts) -> void + + def gem: (untyped name, *untyped args) -> void + + def source: (untyped source, *untyped args) ?{ (?) -> untyped } -> void + + def git_source: (untyped name) ?{ (?) -> untyped } -> untyped + + def path: (untyped path, ?::Hash[untyped, untyped] options) ?{ (?) -> untyped } -> untyped + + def git: (untyped uri, ?::Hash[untyped, untyped] options) ?{ (?) -> untyped } -> untyped + + def github: (untyped repo, ?::Hash[untyped, untyped] options) ?{ () -> untyped } -> untyped + + def to_definition: (untyped lockfile, untyped unlock) -> untyped + + def group: (*untyped args) { () -> untyped } -> untyped + + def install_if: (*untyped args) { () -> untyped } -> untyped + + def platforms: (*untyped platforms) { () -> untyped } -> untyped + + alias platform platforms + + def env: (untyped name) { () -> untyped } -> untyped + + def plugin: (*untyped args) -> nil + + def method_missing: (untyped name, *untyped args) -> untyped + + def check_primary_source_safety: () -> untyped + + private + + def add_dependency: (untyped name, ?untyped? version, ?::Hash[untyped, untyped] options) -> (nil | untyped) + + def with_gemfile: (untyped gemfile) { (untyped) -> untyped } -> untyped + + def add_git_sources: () -> untyped + + def with_source: (untyped source) ?{ () -> untyped } -> untyped + + def normalize_hash: (untyped opts) -> untyped + + def valid_keys: () -> untyped + + def normalize_options: (untyped name, untyped version, untyped opts) -> untyped + + def normalize_group_options: (untyped opts, untyped groups) -> untyped + + def validate_keys: (untyped command, untyped opts, untyped valid_keys) -> (true | untyped) + + def normalize_source: (untyped source) -> untyped + + def deprecate_legacy_windows_platforms: (untyped platforms) -> (nil | untyped) + + def check_path_source_safety: () -> (nil | untyped) + + def check_rubygems_source_safety: () -> (untyped | nil) + + def multiple_global_source_warning: () -> untyped + + class DSLError < GemfileError + @status_code: untyped + + @description: untyped + + @dsl_path: untyped + + @backtrace: untyped + + @contents: untyped + + @to_s: untyped + + # @return [String] the description that should be presented to the user. + # + attr_reader description: untyped + + # @return [String] the path of the dsl file that raised the exception. + # + attr_reader dsl_path: untyped + + # @return [Exception] the backtrace of the exception raised by the + # evaluation of the dsl file. + # + attr_reader backtrace: untyped + + # @param [Exception] backtrace @see backtrace + # @param [String] dsl_path @see dsl_path + # + def initialize: (untyped description, untyped dsl_path, untyped backtrace, ?untyped? contents) -> void + + def status_code: () -> untyped + + # @return [String] the contents of the DSL that cause the exception to + # be raised. + # + def contents: () -> untyped + + # The message of the exception reports the content of podspec for the + # line that generated the original exception. + # + # @example Output + # + # Invalid podspec at `RestKit.podspec` - undefined method + # `exclude_header_search_paths=' for # + # + # from spec-repos/master/RestKit/0.9.3/RestKit.podspec:36 + # ------------------------------------------- + # # because it would break: #import + # > ns.exclude_header_search_paths = 'Code/RestKit.h' + # end + # ------------------------------------------- + # + # @return [String] the message of the exception. + # + def to_s: () -> untyped + + private + + def parse_line_number_from_description: () -> ::Array[untyped] + end + + def gemfile_root: () -> untyped + end +end diff --git a/rbs/fills/tuple.rbs b/rbs/fills/tuple/tuple.rbs similarity index 100% rename from rbs/fills/tuple.rbs rename to rbs/fills/tuple/tuple.rbs diff --git a/spec/convention/gemfile_spec.rb b/spec/convention/gemfile_spec.rb new file mode 100644 index 000000000..7cbb681a1 --- /dev/null +++ b/spec/convention/gemfile_spec.rb @@ -0,0 +1,21 @@ +describe Solargraph::Convention::Gemfile do + describe 'parsing Gemfiles' do + def type_checker(code) + Solargraph::TypeChecker.load_string(code, 'Gemfile', :strong) + end + + it 'typechecks valid files without error' do + checker = type_checker(%( + source 'https://rubygems.org' + + gemspec name: 'solargraph' + + # Local gemfile for development tools, etc. + local_gemfile = File.expand_path(".Gemfile", __dir__) + instance_eval File.read local_gemfile if File.exist? local_gemfile + )) + + expect(checker.problems).to be_empty + end + end +end From 8f443406e0f7e3d2d3677857fa32a8bdf48f0822 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 14 Jul 2025 11:20:14 -0400 Subject: [PATCH 171/561] Ensure conversions.pins is added --- lib/solargraph/rbs_map/core_map.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/solargraph/rbs_map/core_map.rb b/lib/solargraph/rbs_map/core_map.rb index e80520982..818d36404 100644 --- a/lib/solargraph/rbs_map/core_map.rb +++ b/lib/solargraph/rbs_map/core_map.rb @@ -23,6 +23,7 @@ def pins if cache @pins.replace cache else + @pins.concat conversions.pins Dir.glob(File.join(FILLS_DIRECTORY, '*')).each do |path| next unless File.directory?(path) fill_loader = RBS::EnvironmentLoader.new(repository: RBS::Repository.new(no_stdlib: false)) From 9e0fd38989dd795785c65c69850d0f856d0c7dae Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 14 Jul 2025 12:08:36 -0400 Subject: [PATCH 172/561] Define some types in dsl.rbs --- rbs/fills/bundler/dsl.rbs | 16 ++++++++-------- spec/convention/gemfile_spec.rb | 18 ++++++++++++++++++ 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/rbs/fills/bundler/dsl.rbs b/rbs/fills/bundler/dsl.rbs index 82dbe2ac4..d7c5b3442 100644 --- a/rbs/fills/bundler/dsl.rbs +++ b/rbs/fills/bundler/dsl.rbs @@ -70,11 +70,11 @@ module Bundler def eval_gemfile: (untyped gemfile, ?untyped? contents) -> untyped - def gemspec: (?untyped? opts) -> void + def gemspec: (?path: String, ?glob: String, ?name: String, ?development_group: Symbol) -> void def gem: (untyped name, *untyped args) -> void - def source: (untyped source, *untyped args) ?{ (?) -> untyped } -> void + def source: (String source, ?type: Symbol) ?{ (?) -> untyped } -> void def git_source: (untyped name) ?{ (?) -> untyped } -> untyped @@ -147,28 +147,28 @@ module Bundler # @return [String] the description that should be presented to the user. # - attr_reader description: untyped + attr_reader description: String # @return [String] the path of the dsl file that raised the exception. # - attr_reader dsl_path: untyped + attr_reader dsl_path: String # @return [Exception] the backtrace of the exception raised by the # evaluation of the dsl file. # - attr_reader backtrace: untyped + attr_reader backtrace: Exception # @param [Exception] backtrace @see backtrace # @param [String] dsl_path @see dsl_path # - def initialize: (untyped description, untyped dsl_path, untyped backtrace, ?untyped? contents) -> void + def initialize: (untyped description, String dsl_path, Exception backtrace, ?untyped? contents) -> void def status_code: () -> untyped # @return [String] the contents of the DSL that cause the exception to # be raised. # - def contents: () -> untyped + def contents: () -> String # The message of the exception reports the content of podspec for the # line that generated the original exception. @@ -188,7 +188,7 @@ module Bundler # # @return [String] the message of the exception. # - def to_s: () -> untyped + def to_s: () -> String private diff --git a/spec/convention/gemfile_spec.rb b/spec/convention/gemfile_spec.rb index 7cbb681a1..cefb6f1ad 100644 --- a/spec/convention/gemfile_spec.rb +++ b/spec/convention/gemfile_spec.rb @@ -8,6 +8,8 @@ def type_checker(code) checker = type_checker(%( source 'https://rubygems.org' + ruby "~> 3.3.5" + gemspec name: 'solargraph' # Local gemfile for development tools, etc. @@ -17,5 +19,21 @@ def type_checker(code) expect(checker.problems).to be_empty end + + it 'finds bad arguments to DSL methods' do + checker = type_checker(%( + source File + + gemspec bad_name: 'solargraph' + + # Local gemfile for development tools, etc. + local_gemfile = File.expand_path(".Gemfile", __dir__) + instance_eval File.read local_gemfile if File.exist? local_gemfile + )) + + expect(checker.problems.map(&:message).sort). + to eq(["Unrecognized keyword argument bad_name to Bundler::Dsl#gemspec", + "Wrong argument type for Bundler::Dsl#source: source expected String, received Class"].sort) + end end end From ab67325b21fac521b3b5d450113485e58ecbb867 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 14 Jul 2025 12:28:37 -0400 Subject: [PATCH 173/561] Fix typechecking error --- lib/solargraph/pin_cache.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/solargraph/pin_cache.rb b/lib/solargraph/pin_cache.rb index c78cb6088..a084fa0f9 100644 --- a/lib/solargraph/pin_cache.rb +++ b/lib/solargraph/pin_cache.rb @@ -116,8 +116,10 @@ def uncache_core(out: nil) uncache(core_path, out: out) end - def uncache_stdlib - uncache(stdlib_path) + # @param out [IO, nil] + # @return [void] + def uncache_stdlib(out: nil) + uncache(stdlib_path, out: out) end def uncache_gem(gemspec, out: nil) From 5b3d080add36ace27ca541b2c85b8eab60f73e51 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 14 Jul 2025 12:28:57 -0400 Subject: [PATCH 174/561] Fix typechecker to give expected errors for gemspec test (!) --- lib/solargraph/type_checker.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/solargraph/type_checker.rb b/lib/solargraph/type_checker.rb index aa215f97b..5a9c11905 100644 --- a/lib/solargraph/type_checker.rb +++ b/lib/solargraph/type_checker.rb @@ -324,10 +324,10 @@ def argument_problems_for chain, api_map, block_pin, locals, location end break if !rules.validate_calls? || base.links.first.is_a?(Solargraph::Source::Chain::ZSuper) - params = first_param_hash(pins) all_errors = [] pin.signatures.sort { |sig| sig.parameters.length }.each do |sig| + params = first_param_hash([sig]) errors = [] sig.parameters.each_with_index do |par, idx| # @todo add logic mapping up restarg parameters with @@ -467,7 +467,7 @@ def kwrestarg_problems_for(api_map, block_pin, locals, location, pin, params, kw result end - # @param pin [Pin::Method] + # @param pin [Pin::Callable] # @return [Hash{String => Hash{Symbol => String, ComplexType}}] def param_hash(pin) # @type [Hash{String => Hash{Symbol => String, ComplexType}}] @@ -494,7 +494,7 @@ def param_hash(pin) result end - # @param pins [Array] + # @param pins [Array] # @return [Hash{String => Hash{Symbol => String, ComplexType}}] def first_param_hash(pins) return {} if pins.empty? From 3f64bbe7645eb5ccdafb396cc4e1d9673f34b5ad Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 14 Jul 2025 13:56:03 -0400 Subject: [PATCH 175/561] Fix duplicate definitions issue on other core pins --- lib/solargraph/rbs_map/core_map.rb | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/lib/solargraph/rbs_map/core_map.rb b/lib/solargraph/rbs_map/core_map.rb index 818d36404..cb12b3caa 100644 --- a/lib/solargraph/rbs_map/core_map.rb +++ b/lib/solargraph/rbs_map/core_map.rb @@ -24,16 +24,15 @@ def pins @pins.replace cache else @pins.concat conversions.pins - Dir.glob(File.join(FILLS_DIRECTORY, '*')).each do |path| - next unless File.directory?(path) - fill_loader = RBS::EnvironmentLoader.new(repository: RBS::Repository.new(no_stdlib: false)) - fill_loader.add(path: Pathname(path)) - fill_conversions = Conversions.new(loader: fill_loader) - @pins.concat fill_conversions.pins - rescue RBS::DuplicatedDeclarationError => e - logger.debug "RBS already contains declarations in #{path}, skipping: #{e.message}" - end + + # Avoid RBS::DuplicatedDeclarationError by loading in a different EnvironmentLoader + fill_loader = RBS::EnvironmentLoader.new(core_root: nil, repository: RBS::Repository.new(no_stdlib: false)) + fill_loader.add(path: Pathname(FILLS_DIRECTORY)) + fill_conversions = Conversions.new(loader: fill_loader) + @pins.concat fill_conversions.pins + @pins.concat RbsMap::CoreFills::ALL + processed = ApiMap::Store.new(pins).pins.reject { |p| p.is_a?(Solargraph::Pin::Reference::Override) } @pins.replace processed From 8d6b6ea98086eec7929066c77a1048dec0ed68bf Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 14 Jul 2025 13:56:52 -0400 Subject: [PATCH 176/561] Fix related typechecker issues --- lib/solargraph/type_checker.rb | 131 ++++++++++++++++++--------------- 1 file changed, 73 insertions(+), 58 deletions(-) diff --git a/lib/solargraph/type_checker.rb b/lib/solargraph/type_checker.rb index 5a9c11905..0bcecdb00 100644 --- a/lib/solargraph/type_checker.rb +++ b/lib/solargraph/type_checker.rb @@ -147,33 +147,33 @@ def virtual_pin? pin # @return [Array] def method_param_type_problems_for pin stack = api_map.get_method_stack(pin.namespace, pin.name, scope: pin.scope) - params = first_param_hash(stack) result = [] - if rules.require_type_tags? - pin.signatures.each do |sig| - sig.parameters.each do |par| - break if par.decl == :restarg || par.decl == :kwrestarg || par.decl == :blockarg - unless params[par.name] - if pin.attribute? - inferred = pin.probe(api_map).self_to_type(pin.full_context) - if inferred.undefined? + pin.signatures.each do |sig| + params = param_details_from_stack(sig, stack) + if rules.require_type_tags? + sig.parameters.each do |par| + break if par.decl == :restarg || par.decl == :kwrestarg || par.decl == :blockarg + unless params[par.name] + if pin.attribute? + inferred = pin.probe(api_map).self_to_type(pin.full_context) + if inferred.undefined? + result.push Problem.new(pin.location, "Missing @param tag for #{par.name} on #{pin.path}", pin: pin) + end + else result.push Problem.new(pin.location, "Missing @param tag for #{par.name} on #{pin.path}", pin: pin) end - else - result.push Problem.new(pin.location, "Missing @param tag for #{par.name} on #{pin.path}", pin: pin) end end - end end - end - # @todo Should be able to probe type of name and data here - # @param name [String] - # @param data [Hash{Symbol => BasicObject}] - params.each_pair do |name, data| - # @type [ComplexType] - type = data[:qualified] - if type.undefined? - result.push Problem.new(pin.location, "Unresolved type #{data[:tagged]} for #{name} param on #{pin.path}", pin: pin) + # @todo Should be able to probe type of name and data here + # @param name [String] + # @param data [Hash{Symbol => BasicObject}] + params.each_pair do |name, data| + # @type [ComplexType] + type = data[:qualified] + if type.undefined? + result.push Problem.new(pin.location, "Unresolved type #{data[:tagged]} for #{name} param on #{pin.path}", pin: pin) + end end end result @@ -301,7 +301,7 @@ def argument_problems_for chain, api_map, block_pin, locals, location first_pin = pins.first if first_pin.is_a?(Pin::DelegatedMethod) && !first_pin.resolvable?(api_map) - # Do nothing, as we can't find the actual method implementation + # Do nothing, as we can't find the actual method implementation elsif first_pin.is_a?(Pin::Method) # @type [Pin::Method] pin = first_pin @@ -324,10 +324,9 @@ def argument_problems_for chain, api_map, block_pin, locals, location end break if !rules.validate_calls? || base.links.first.is_a?(Solargraph::Source::Chain::ZSuper) - all_errors = [] pin.signatures.sort { |sig| sig.parameters.length }.each do |sig| - params = first_param_hash([sig]) + params = param_details_from_stack(sig, pins) errors = [] sig.parameters.each_with_index do |par, idx| # @todo add logic mapping up restarg parameters with @@ -380,7 +379,7 @@ def argument_problems_for chain, api_map, block_pin, locals, location ptype = params.key?(par.name) ? params[par.name][:qualified] : ComplexType::UNDEFINED ptype = ptype.self_to_type(par.context) if ptype.nil? - # @todo Some level (strong, I guess) should require the param here + # @todo Some level (strong, I guess) should require the param here else argtype = argchain.infer(api_map, block_pin, locals) if argtype.defined? && ptype.defined? && !any_types_match?(api_map, ptype, argtype) @@ -429,7 +428,7 @@ def kwarg_problems_for sig, argchain, api_map, block_pin, locals, location, pin, if argchain data = params[par.name] if data.nil? - # @todo Some level (strong, I guess) should require the param here + # @todo Some level (strong, I guess) should require the param here else ptype = data[:qualified] unless ptype.undefined? @@ -467,9 +466,24 @@ def kwrestarg_problems_for(api_map, block_pin, locals, location, pin, params, kw result end - # @param pin [Pin::Callable] + # @param pin [Pin::Method] # @return [Hash{String => Hash{Symbol => String, ComplexType}}] - def param_hash(pin) + def add_restkwarg_param_tag_details(param_details, pin) + # see if we have additional tags to pay attention to from YARD - + # e.g., kwargs in a **restkwargs splat + tags = pin.docstring.tags(:param) + tags.each do |tag| + next if param_details.key? tag.name.to_s + next if tag.types.nil? + param_details[tag.name.to_s] = { + tagged: tag.types.join(', '), + qualified: Solargraph::ComplexType.try_parse(*tag.types).qualify(api_map, pin.full_context.namespace) + } + end + end + + # @param pin [Pin::Signature] + def signature_param_details(pin) # @type [Hash{String => Hash{Symbol => String, ComplexType}}] result = {} pin.parameters.each do |param| @@ -480,44 +494,45 @@ def param_hash(pin) qualified: type } end - # see if we have additional tags to pay attention to from YARD - - # e.g., kwargs in a **restkwargs splat - tags = pin.docstring.tags(:param) - tags.each do |tag| - next if result.key? tag.name.to_s - next if tag.types.nil? - result[tag.name.to_s] = { - tagged: tag.types.join(', '), - qualified: Solargraph::ComplexType.try_parse(*tag.types).qualify(api_map, pin.full_context.namespace) - } - end result end - # @param pins [Array] + # The original signature defines the parameters, but other + # signatures and method pins can help by adding type information + # + # @param param_details [Hash{String => Hash{Symbol => String, ComplexType}}] + # @param new_param_details [Hash{String => Hash{Symbol => String, ComplexType}}] + # @return [void] + def add_to_param_details(param_details, param_names, new_param_details) + new_param_details.each do |param_name, details| + next unless param_names.include?(param_name) + + param_details[param_name] ||= {} + param_details[param_name][:tagged] ||= details[:tagged] + param_details[param_name][:qualified] ||= details[:qualified] + end + end + + # @param signature [Pin::Signature] + # @param pins [Array] # @return [Hash{String => Hash{Symbol => String, ComplexType}}] - def first_param_hash(pins) - return {} if pins.empty? - first_pin_type = pins.first.typify(api_map) - first_pin = pins.first.proxy first_pin_type - param_names = first_pin.parameter_names - results = param_hash(first_pin) - pins[1..].each do |pin| - # @todo this assignment from parametric use of Hash should not lose its generic - # @type [Hash{String => Hash{Symbol => BasicObject}}] + def param_details_from_stack(signature, method_pin_stack) + signature_type = signature.typify(api_map) + signature = signature.proxy signature_type + param_details = signature_param_details(signature) + param_names = signature.parameter_names + + method_pin_stack.each do |method_pin| + add_restkwarg_param_tag_details(param_details, method_pin) # documentation of types in superclasses should fail back to # subclasses if the subclass hasn't documented something - superclass_results = param_hash(pin) - superclass_results.each do |param_name, details| - next unless param_names.include?(param_name) - - results[param_name] ||= {} - results[param_name][:tagged] ||= details[:tagged] - results[param_name][:qualified] ||= details[:qualified] + method_pin.signatures.each do |sig| + add_restkwarg_param_tag_details(param_details, sig) + add_to_param_details param_details, param_names, signature_param_details(sig) end end - results + param_details end # @param pin [Pin::Base] @@ -695,5 +710,5 @@ def without_ignored problems node && source_map.source.comments_for(node)&.include?('@sg-ignore') end end - end +end end From 8521e570dea35f68bbfeac88b2b5ce3a475c9396 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 14 Jul 2025 13:57:14 -0400 Subject: [PATCH 177/561] Ensure all types are rooted --- rbs/fills/bundler/dsl.rbs | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/rbs/fills/bundler/dsl.rbs b/rbs/fills/bundler/dsl.rbs index d7c5b3442..b63cd736c 100644 --- a/rbs/fills/bundler/dsl.rbs +++ b/rbs/fills/bundler/dsl.rbs @@ -70,11 +70,11 @@ module Bundler def eval_gemfile: (untyped gemfile, ?untyped? contents) -> untyped - def gemspec: (?path: String, ?glob: String, ?name: String, ?development_group: Symbol) -> void + def gemspec: (?path: ::String, ?glob: ::String, ?name: ::String, ?development_group: ::Symbol) -> void def gem: (untyped name, *untyped args) -> void - def source: (String source, ?type: Symbol) ?{ (?) -> untyped } -> void + def source: (::String source, ?type: ::Symbol) ?{ (?) -> untyped } -> void def git_source: (untyped name) ?{ (?) -> untyped } -> untyped @@ -145,30 +145,30 @@ module Bundler @to_s: untyped - # @return [String] the description that should be presented to the user. + # @return [::String] the description that should be presented to the user. # - attr_reader description: String + attr_reader description: ::String - # @return [String] the path of the dsl file that raised the exception. + # @return [::String] the path of the dsl file that raised the exception. # - attr_reader dsl_path: String + attr_reader dsl_path: ::String - # @return [Exception] the backtrace of the exception raised by the + # @return [::Exception] the backtrace of the exception raised by the # evaluation of the dsl file. # - attr_reader backtrace: Exception + attr_reader backtrace: ::Exception - # @param [Exception] backtrace @see backtrace - # @param [String] dsl_path @see dsl_path + # @param [::Exception] backtrace @see backtrace + # @param [::String] dsl_path @see dsl_path # - def initialize: (untyped description, String dsl_path, Exception backtrace, ?untyped? contents) -> void + def initialize: (untyped description, ::String dsl_path, ::Exception backtrace, ?untyped? contents) -> void def status_code: () -> untyped - # @return [String] the contents of the DSL that cause the exception to + # @return [::String] the contents of the DSL that cause the exception to # be raised. # - def contents: () -> String + def contents: () -> ::String # The message of the exception reports the content of podspec for the # line that generated the original exception. @@ -186,9 +186,9 @@ module Bundler # end # ------------------------------------------- # - # @return [String] the message of the exception. + # @return [::String] the message of the exception. # - def to_s: () -> String + def to_s: () -> ::String private From 8799369a461a8de9f421979b1537207470ce9595 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 14 Jul 2025 14:02:17 -0400 Subject: [PATCH 178/561] Restore whitespace --- lib/solargraph/type_checker.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/solargraph/type_checker.rb b/lib/solargraph/type_checker.rb index 0bcecdb00..fc00bdab2 100644 --- a/lib/solargraph/type_checker.rb +++ b/lib/solargraph/type_checker.rb @@ -379,7 +379,7 @@ def argument_problems_for chain, api_map, block_pin, locals, location ptype = params.key?(par.name) ? params[par.name][:qualified] : ComplexType::UNDEFINED ptype = ptype.self_to_type(par.context) if ptype.nil? - # @todo Some level (strong, I guess) should require the param here + # @todo Some level (strong, I guess) should require the param here else argtype = argchain.infer(api_map, block_pin, locals) if argtype.defined? && ptype.defined? && !any_types_match?(api_map, ptype, argtype) @@ -428,7 +428,7 @@ def kwarg_problems_for sig, argchain, api_map, block_pin, locals, location, pin, if argchain data = params[par.name] if data.nil? - # @todo Some level (strong, I guess) should require the param here + # @todo Some level (strong, I guess) should require the param here else ptype = data[:qualified] unless ptype.undefined? From 5f83ea37d0f5ea9bde47a2c762131096bf7bbc72 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 14 Jul 2025 14:53:55 -0400 Subject: [PATCH 179/561] Progress towards eventually typechecking 'ruby' directive --- lib/solargraph/rbs_map/conversions.rb | 10 ++++++- rbs/fills/bundler/ruby_dsl.rbs | 42 +++++++++++++++++++++++++++ spec/convention/gemfile_spec.rb | 10 +++++++ 3 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 rbs/fills/bundler/ruby_dsl.rbs diff --git a/lib/solargraph/rbs_map/conversions.rb b/lib/solargraph/rbs_map/conversions.rb index 6e50c022a..c9cdedbc5 100644 --- a/lib/solargraph/rbs_map/conversions.rb +++ b/lib/solargraph/rbs_map/conversions.rb @@ -476,7 +476,15 @@ def parts_of_function type, pin end if type.type.rest_positionals name = type.type.rest_positionals.name ? type.type.rest_positionals.name.to_s : "arg_#{arg_num += 1}" - parameters.push Solargraph::Pin::Parameter.new(decl: :restarg, name: name, closure: pin, source: :rbs, type_location: type_location) + inner_rest_positional_type = + ComplexType.try_parse(other_type_to_tag(type.type.rest_positionals.type)) + rest_positional_type = ComplexType::UniqueType.new('Array', + [], + [inner_rest_positional_type], + rooted: true, parameters_type: :list) + parameters.push Solargraph::Pin::Parameter.new(decl: :restarg, name: name, closure: pin, + source: :rbs, type_location: type_location, + return_type: rest_positional_type,) end type.type.trailing_positionals.each do |param| name = param.name ? param.name.to_s : "arg_#{arg_num += 1}" diff --git a/rbs/fills/bundler/ruby_dsl.rbs b/rbs/fills/bundler/ruby_dsl.rbs new file mode 100644 index 000000000..35b30c681 --- /dev/null +++ b/rbs/fills/bundler/ruby_dsl.rbs @@ -0,0 +1,42 @@ +# +# Bundler provides a consistent environment for Ruby projects by tracking and +# installing the exact gems and versions that are needed. +# +# Bundler is a part of Ruby's standard library. +# +# Bundler is used by creating *gemfiles* listing all the project dependencies +# and (optionally) their versions and then using +# +# require 'bundler/setup' +# +# or Bundler.setup to setup environment where only specified gems and their +# specified versions could be used. +# +# See [Bundler website](https://bundler.io/docs.html) for extensive +# documentation on gemfiles creation and Bundler usage. +# +# As a standard library inside project, Bundler could be used for introspection +# of loaded and required modules. +# +module Bundler + module RubyDsl + @ruby_version: untyped + + def ruby: (*::String ruby_version) -> void + + # Support the various file formats found in .ruby-version files. + # + # 3.2.2 + # ruby-3.2.2 + # + # Also supports .tool-versions files for asdf. Lines not starting with "ruby" are ignored. + # + # ruby 2.5.1 # comment is ignored + # ruby 2.5.1# close comment and extra spaces doesn't confuse + # + # Intentionally does not support `3.2.1@gemset` since rvm recommends using .ruby-gemset instead + # + # Loads the file relative to the dirname of the Gemfile itself. + def normalize_ruby_file: (::String filename) -> ::String + end +end diff --git a/spec/convention/gemfile_spec.rb b/spec/convention/gemfile_spec.rb index cefb6f1ad..0148d7e57 100644 --- a/spec/convention/gemfile_spec.rb +++ b/spec/convention/gemfile_spec.rb @@ -35,5 +35,15 @@ def type_checker(code) to eq(["Unrecognized keyword argument bad_name to Bundler::Dsl#gemspec", "Wrong argument type for Bundler::Dsl#source: source expected String, received Class"].sort) end + + # @todo add rest arg support to type checker + xit 'finds bad arguments to DSL ruby method' do + checker = type_checker(%( + ruby 123 + )) + + expect(checker.problems.map(&:message)). + to eq(["Wrong argument type for Bundler::Dsl#ruby: ruby_version expected String, received Integer"]) + end end end From e37757f670517b327926010575a78dfea4722449 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 14 Jul 2025 15:00:33 -0400 Subject: [PATCH 180/561] Fix annotations --- lib/solargraph/type_checker.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/solargraph/type_checker.rb b/lib/solargraph/type_checker.rb index fc00bdab2..a24be8044 100644 --- a/lib/solargraph/type_checker.rb +++ b/lib/solargraph/type_checker.rb @@ -467,7 +467,7 @@ def kwrestarg_problems_for(api_map, block_pin, locals, location, pin, params, kw end # @param pin [Pin::Method] - # @return [Hash{String => Hash{Symbol => String, ComplexType}}] + # @return [void] def add_restkwarg_param_tag_details(param_details, pin) # see if we have additional tags to pay attention to from YARD - # e.g., kwargs in a **restkwargs splat @@ -483,6 +483,7 @@ def add_restkwarg_param_tag_details(param_details, pin) end # @param pin [Pin::Signature] + # @return [Hash{String => Hash{Symbol => String, ComplexType}}] def signature_param_details(pin) # @type [Hash{String => Hash{Symbol => String, ComplexType}}] result = {} From 225987cc6077ee84382b4ad8518c8e35a0cb7368 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 14 Jul 2025 15:29:35 -0400 Subject: [PATCH 181/561] RuboCop fixes --- lib/solargraph/type_checker.rb | 2 +- spec/convention/gemfile_spec.rb | 15 ++++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/lib/solargraph/type_checker.rb b/lib/solargraph/type_checker.rb index a24be8044..ba2284b21 100644 --- a/lib/solargraph/type_checker.rb +++ b/lib/solargraph/type_checker.rb @@ -73,7 +73,7 @@ def load_string code, filename = nil, level = :normal end end - private + private # @return [Array] def method_tag_problems diff --git a/spec/convention/gemfile_spec.rb b/spec/convention/gemfile_spec.rb index 0148d7e57..62a346ca0 100644 --- a/spec/convention/gemfile_spec.rb +++ b/spec/convention/gemfile_spec.rb @@ -31,19 +31,20 @@ def type_checker(code) instance_eval File.read local_gemfile if File.exist? local_gemfile )) - expect(checker.problems.map(&:message).sort). - to eq(["Unrecognized keyword argument bad_name to Bundler::Dsl#gemspec", - "Wrong argument type for Bundler::Dsl#source: source expected String, received Class"].sort) + expect(checker.problems.map(&:message).sort) + .to eq(["Unrecognized keyword argument bad_name to Bundler::Dsl#gemspec", + "Wrong argument type for Bundler::Dsl#source: source expected String, received Class"].sort) end - # @todo add rest arg support to type checker - xit 'finds bad arguments to DSL ruby method' do + it 'finds bad arguments to DSL ruby method' do + pending 'missing support for restargs in the typechecker' + checker = type_checker(%( ruby 123 )) - expect(checker.problems.map(&:message)). - to eq(["Wrong argument type for Bundler::Dsl#ruby: ruby_version expected String, received Integer"]) + expect(checker.problems.map(&:message)) + .to eq(["Wrong argument type for Bundler::Dsl#ruby: ruby_version expected String, received Integer"]) end end end From f834f9eb0b930b36d291b418f0a89e2dca80b6b1 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 15 Jul 2025 13:24:49 -0400 Subject: [PATCH 182/561] Apply suggestion from @apiology --- lib/solargraph/convention.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/solargraph/convention.rb b/lib/solargraph/convention.rb index 798adf76d..7aeb0860d 100644 --- a/lib/solargraph/convention.rb +++ b/lib/solargraph/convention.rb @@ -6,7 +6,6 @@ module Solargraph # one of its sources. # module Convention - autoload :Base, 'solargraph/convention/base' autoload :Gemfile, 'solargraph/convention/gemfile' autoload :Gemspec, 'solargraph/convention/gemspec' From 50082bbaea2519897c55030a9d53a8920b80a4e6 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 15 Jul 2025 14:07:54 -0400 Subject: [PATCH 183/561] Update rubocop todo file --- .rubocop_todo.yml | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 54dca7c30..d1a04e53c 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,12 +1,12 @@ # This configuration was generated by # `rubocop --auto-gen-config` -# on 2025-07-09 18:34:33 UTC using RuboCop version 1.78.0. +# on 2025-07-15 18:07:35 UTC using RuboCop version 1.78.0. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. -# Offense count: 22 +# Offense count: 19 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: Include. # Include: **/*.gemspec @@ -23,7 +23,7 @@ Gemspec/DeprecatedAttributeAssignment: - 'solargraph.gemspec' - 'spec/fixtures/rdoc-lib/rdoc-lib.gemspec' -# Offense count: 10 +# Offense count: 13 # Configuration parameters: EnforcedStyle, AllowedGems, Include. # SupportedStyles: Gemfile, gems.rb, gemspec # Include: **/*.gemspec, **/Gemfile, **/gems.rb @@ -155,11 +155,12 @@ Layout/EndOfLine: - 'lib/solargraph/source/encoding_fixes.rb' - 'solargraph.gemspec' -# Offense count: 7 +# Offense count: 11 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowForAlignment, AllowBeforeTrailingComments, ForceEqualSignAlignment. Layout/ExtraSpacing: Exclude: + - 'Steepfile' - 'lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb' - 'lib/solargraph/pin/closure.rb' - 'lib/solargraph/pin/local_variable.rb' @@ -1716,7 +1717,7 @@ Style/StringConcatenation: - 'lib/solargraph/pin/namespace.rb' - 'solargraph.gemspec' -# Offense count: 617 +# Offense count: 614 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline. # SupportedStyles: single_quotes, double_quotes @@ -1891,10 +1892,3 @@ YARD/TagTypeSyntax: - 'lib/solargraph/language_server/host.rb' - 'lib/solargraph/parser/comment_ripper.rb' - 'lib/solargraph/type_checker.rb' - -# Offense count: 191 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, IgnoreCopDirectives, AllowedPatterns, SplitStrings. -# URISchemes: http, https -Layout/LineLength: - Max: 257 From 94b0ab48248c48387298bafd1da75b8ece73adf9 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 15 Jul 2025 14:13:14 -0400 Subject: [PATCH 184/561] Update rubocop todo file --- .rubocop_todo.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index d1a04e53c..4218789e2 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,6 +1,6 @@ # This configuration was generated by # `rubocop --auto-gen-config` -# on 2025-07-15 18:07:35 UTC using RuboCop version 1.78.0. +# on 2025-07-15 18:13:00 UTC using RuboCop version 1.78.0. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new @@ -1892,3 +1892,10 @@ YARD/TagTypeSyntax: - 'lib/solargraph/language_server/host.rb' - 'lib/solargraph/parser/comment_ripper.rb' - 'lib/solargraph/type_checker.rb' + +# Offense count: 191 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, IgnoreCopDirectives, AllowedPatterns, SplitStrings. +# URISchemes: http, https +Layout/LineLength: + Max: 257 From 6956c2a0731f0cedce7987805f2ccfabd888ddad Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 15 Jul 2025 14:35:51 -0400 Subject: [PATCH 185/561] Address issue with spec rubocop config file and rebuild todo --- .rubocop_todo.yml | 103 +++++++++++------- spec/diagnostics/rubocop_spec.rb | 31 ++++-- .../.rubocop.yml | 0 .../folder1/folder2/test.rb | 0 .../rubocop-validation-error/.rubocop.yml | 2 - 5 files changed, 85 insertions(+), 51 deletions(-) delete mode 100644 spec/fixtures/rubocop-subfolder-configuration/.rubocop.yml delete mode 100644 spec/fixtures/rubocop-subfolder-configuration/folder1/folder2/test.rb delete mode 100644 spec/fixtures/rubocop-validation-error/.rubocop.yml diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 4218789e2..2ce6703fc 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,6 +1,6 @@ # This configuration was generated by # `rubocop --auto-gen-config` -# on 2025-07-15 18:13:00 UTC using RuboCop version 1.78.0. +# on 2025-07-15 18:34:59 UTC using RuboCop version 1.78.0. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new @@ -58,12 +58,13 @@ Gemspec/RequiredRubyVersion: - 'spec/fixtures/rubocop-custom-version/specifications/rubocop-0.0.0.gemspec' - 'spec/fixtures/vendored/vendor/do_not_use.gemspec' -# Offense count: 5 +# Offense count: 8 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, IndentationWidth. # SupportedStyles: with_first_argument, with_fixed_indentation Layout/ArgumentAlignment: Exclude: + - 'lib/solargraph/convention/struct_definition.rb' - 'lib/solargraph/pin/callable.rb' - 'spec/source/source_chainer_spec.rb' @@ -75,10 +76,11 @@ Layout/BlockAlignment: Exclude: - 'spec/source_map/mapper_spec.rb' -# Offense count: 1 +# Offense count: 2 # This cop supports safe autocorrection (--autocorrect). Layout/ClosingHeredocIndentation: Exclude: + - 'spec/diagnostics/rubocop_spec.rb' - 'spec/rbs_map/conversions_spec.rb' # Offense count: 8 @@ -105,7 +107,7 @@ Layout/EmptyLineBetweenDefs: - 'lib/solargraph/language_server/message/initialize.rb' - 'lib/solargraph/pin/delegated_method.rb' -# Offense count: 12 +# Offense count: 13 # This cop supports safe autocorrection (--autocorrect). Layout/EmptyLines: Exclude: @@ -118,6 +120,7 @@ Layout/EmptyLines: - 'lib/solargraph/pin/delegated_method.rb' - 'lib/solargraph/rbs_map/conversions.rb' - 'spec/complex_type_spec.rb' + - 'spec/convention/struct_definition_spec.rb' - 'spec/pin/local_variable_spec.rb' - 'spec/pin/symbol_spec.rb' - 'spec/type_checker/levels/strict_spec.rb' @@ -211,7 +214,7 @@ Layout/FirstHashElementIndentation: - 'spec/language_server/message/text_document/type_definition_spec.rb' - 'spec/language_server/message/workspace/did_change_watched_files_spec.rb' -# Offense count: 1 +# Offense count: 4 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowMultipleStyles, EnforcedHashRocketStyle, EnforcedColonStyle, EnforcedLastArgumentHashStyle. # SupportedHashRocketStyles: key, separator, table @@ -219,12 +222,14 @@ Layout/FirstHashElementIndentation: # SupportedLastArgumentHashStyles: always_inspect, always_ignore, ignore_implicit, ignore_explicit Layout/HashAlignment: Exclude: + - 'lib/solargraph/convention/struct_definition.rb' - 'lib/solargraph/workspace/config.rb' -# Offense count: 2 +# Offense count: 3 # This cop supports safe autocorrection (--autocorrect). Layout/HeredocIndentation: Exclude: + - 'spec/diagnostics/rubocop_spec.rb' - 'spec/rbs_map/conversions_spec.rb' - 'spec/yard_map/mapper/to_method_spec.rb' @@ -329,7 +334,7 @@ Layout/SpaceAroundOperators: - 'spec/library_spec.rb' - 'spec/yard_map/mapper_spec.rb' -# Offense count: 108 +# Offense count: 105 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces. # SupportedStyles: space, no_space @@ -343,7 +348,7 @@ Layout/SpaceBeforeComma: Exclude: - 'spec/source/cursor_spec.rb' -# Offense count: 183 +# Offense count: 177 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces, SpaceBeforeBlockParameters. # SupportedStyles: space, no_space @@ -373,13 +378,22 @@ Layout/SpaceInsideParens: - 'lib/solargraph/pin/namespace.rb' - 'lib/solargraph/source_map.rb' -# Offense count: 3 +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle. +# SupportedStyles: final_newline, final_blank_line +Layout/TrailingEmptyLines: + Exclude: + - 'spec/convention/struct_definition_spec.rb' + +# Offense count: 5 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowInHeredoc. Layout/TrailingWhitespace: Exclude: - 'lib/solargraph/language_server/message/client/register_capability.rb' - 'spec/api_map/config_spec.rb' + - 'spec/convention/struct_definition_spec.rb' - 'spec/convention_spec.rb' # Offense count: 2 @@ -487,11 +501,10 @@ Lint/EmptyClass: - 'spec/fixtures/workspace_folders/folder1/app.rb' - 'spec/fixtures/workspace_folders/folder2/app.rb' -# Offense count: 2 +# Offense count: 1 # Configuration parameters: AllowComments. Lint/EmptyFile: Exclude: - - 'spec/fixtures/rubocop-subfolder-configuration/folder1/folder2/test.rb' - 'spec/fixtures/vendored/vendor/do_not_use.gemspec' # Offense count: 6 @@ -513,6 +526,12 @@ Lint/MissingSuper: - 'lib/solargraph/source/chain/literal.rb' - 'lib/solargraph/source/chain/or.rb' +# Offense count: 2 +# This cop supports unsafe autocorrection (--autocorrect-all). +Lint/NonAtomicFileOperation: + Exclude: + - 'spec/diagnostics/rubocop_spec.rb' + # Offense count: 4 # This cop supports safe autocorrection (--autocorrect). Lint/ParenthesesAsGroupedExpression: @@ -626,7 +645,7 @@ Lint/UselessMethodDefinition: Exclude: - 'lib/solargraph/pin/signature.rb' -# Offense count: 227 +# Offense count: 232 # Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes. Metrics/AbcSize: Max: 200 @@ -647,12 +666,12 @@ Metrics/BlockNesting: Metrics/ClassLength: Max: 580 -# Offense count: 111 +# Offense count: 113 # Configuration parameters: AllowedMethods, AllowedPatterns. Metrics/CyclomaticComplexity: Max: 42 -# Offense count: 50 +# Offense count: 52 # Configuration parameters: CountComments, Max, CountAsOne, AllowedMethods, AllowedPatterns. Metrics/MethodLength: Enabled: false @@ -668,7 +687,7 @@ Metrics/ParameterLists: Max: 9 MaxOptionalParameters: 5 -# Offense count: 88 +# Offense count: 89 # Configuration parameters: AllowedMethods, AllowedPatterns. Metrics/PerceivedComplexity: Max: 47 @@ -719,16 +738,19 @@ Naming/MethodParameterName: - 'lib/solargraph/yard_map/mapper/to_method.rb' - 'lib/solargraph/yard_map/to_method.rb' -# Offense count: 11 +# Offense count: 14 # Configuration parameters: Mode, AllowedMethods, AllowedPatterns, AllowBangMethods, WaywardPredicates. # AllowedMethods: call # WaywardPredicates: nonzero? Naming/PredicateMethod: Exclude: - 'lib/solargraph/api_map/store.rb' + - 'lib/solargraph/convention/data_definition.rb' + - 'lib/solargraph/convention/struct_definition.rb' - 'lib/solargraph/doc_map.rb' - 'lib/solargraph/language_server/progress.rb' - 'lib/solargraph/library.rb' + - 'lib/solargraph/parser/node_processor/base.rb' - 'lib/solargraph/parser/parser_gem/node_processors/send_node.rb' - 'lib/solargraph/pin/base.rb' - 'lib/solargraph/pin/local_variable.rb' @@ -770,6 +792,12 @@ RSpec/BeEq: - 'spec/complex_type_spec.rb' - 'spec/pin/method_spec.rb' +# Offense count: 2 +# This cop supports unsafe autocorrection (--autocorrect-all). +RSpec/BeEql: + Exclude: + - 'spec/convention/struct_definition_spec.rb' + # Offense count: 7 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. @@ -817,7 +845,7 @@ RSpec/DescribeClass: - 'spec/complex_type_spec.rb' - 'spec/source_map/node_processor_spec.rb' -# Offense count: 407 +# Offense count: 408 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: SkipBlocks, EnforcedStyle, OnlyStaticConstants. # SupportedStyles: described_class, explicit @@ -837,17 +865,18 @@ RSpec/EmptyLineAfterFinalLet: Exclude: - 'spec/workspace/config_spec.rb' -# Offense count: 928 +# Offense count: 941 # Configuration parameters: CountAsOne. RSpec/ExampleLength: Max: 59 -# Offense count: 4 +# Offense count: 10 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: CustomTransform, IgnoredWords, DisallowedExamples. # DisallowedExamples: works RSpec/ExampleWording: Exclude: + - 'spec/convention/struct_definition_spec.rb' - 'spec/pin/base_spec.rb' - 'spec/pin/method_spec.rb' @@ -923,12 +952,7 @@ RSpec/LetBeforeExamples: RSpec/MessageSpies: EnforcedStyle: receive -# Offense count: 2 -RSpec/MissingExampleGroupArgument: - Exclude: - - 'spec/diagnostics/rubocop_helpers_spec.rb' - -# Offense count: 471 +# Offense count: 481 RSpec/MultipleExpectations: Max: 14 @@ -989,12 +1013,7 @@ RSpec/ReceiveMessages: Exclude: - 'spec/language_server/host_spec.rb' -# Offense count: 1 -RSpec/RemoveConst: - Exclude: - - 'spec/diagnostics/rubocop_helpers_spec.rb' - -# Offense count: 48 +# Offense count: 50 RSpec/RepeatedDescription: Exclude: - 'spec/api_map_spec.rb' @@ -1217,7 +1236,7 @@ Style/ConditionalAssignment: - 'lib/solargraph/parser/parser_gem/node_processors/defs_node.rb' - 'lib/solargraph/source/chain/call.rb' -# Offense count: 140 +# Offense count: 142 # Configuration parameters: AllowedConstants. Style/Documentation: Enabled: false @@ -1383,7 +1402,7 @@ Style/MapToSet: - 'lib/solargraph/library.rb' - 'spec/source_map/clip_spec.rb' -# Offense count: 198 +# Offense count: 203 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: require_parentheses, require_no_parentheses, require_no_parentheses_except_multiline @@ -1598,7 +1617,7 @@ Style/RedundantRegexpArgument: Exclude: - 'lib/solargraph/api_map/index.rb' - 'lib/solargraph/workspace/config.rb' - - 'spec/diagnostics/rubocop_helpers_spec.rb' + - 'spec/diagnostics/rubocop_spec.rb' - 'spec/language_server/host_spec.rb' # Offense count: 18 @@ -1678,7 +1697,7 @@ Style/SafeNavigationChainLength: Exclude: - 'lib/solargraph/doc_map.rb' -# Offense count: 39 +# Offense count: 40 # This cop supports unsafe autocorrection (--autocorrect-all). Style/SlicingWithRange: Enabled: false @@ -1702,13 +1721,14 @@ Style/StderrPuts: - 'lib/solargraph/pin/base.rb' - 'lib/solargraph/shell.rb' -# Offense count: 10 +# Offense count: 11 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: Mode. Style/StringConcatenation: Exclude: - 'lib/solargraph/api_map.rb' - 'lib/solargraph/api_map/index.rb' + - 'lib/solargraph/convention/struct_definition.rb' - 'lib/solargraph/pin/base.rb' - 'lib/solargraph/pin/callable.rb' - 'lib/solargraph/pin/closure.rb' @@ -1717,7 +1737,7 @@ Style/StringConcatenation: - 'lib/solargraph/pin/namespace.rb' - 'solargraph.gemspec' -# Offense count: 614 +# Offense count: 617 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline. # SupportedStyles: single_quotes, double_quotes @@ -1777,7 +1797,7 @@ Style/TernaryParentheses: Exclude: - 'lib/solargraph/source_map/mapper.rb' -# Offense count: 19 +# Offense count: 16 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyleForMultiline. # SupportedStylesForMultiline: comma, consistent_comma, no_comma @@ -1793,7 +1813,6 @@ Style/TrailingCommaInArguments: - 'lib/solargraph/parser/parser_gem/node_processors/while_node.rb' - 'lib/solargraph/pin/method.rb' - 'lib/solargraph/rbs_map/conversions.rb' - - 'lib/solargraph/yard_map/mapper/to_constant.rb' - 'lib/solargraph/yard_map/mapper/to_method.rb' - 'lib/solargraph/yard_map/mapper/to_namespace.rb' @@ -1879,7 +1898,7 @@ YARD/CollectionType: Exclude: - 'lib/solargraph/range.rb' -# Offense count: 65 +# Offense count: 60 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStylePrototypeName. # SupportedStylesPrototypeName: before, after @@ -1893,9 +1912,9 @@ YARD/TagTypeSyntax: - 'lib/solargraph/parser/comment_ripper.rb' - 'lib/solargraph/type_checker.rb' -# Offense count: 191 +# Offense count: 195 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, IgnoreCopDirectives, AllowedPatterns, SplitStrings. # URISchemes: http, https Layout/LineLength: - Max: 257 + Max: 244 diff --git a/spec/diagnostics/rubocop_spec.rb b/spec/diagnostics/rubocop_spec.rb index 08891f053..aa279f66c 100644 --- a/spec/diagnostics/rubocop_spec.rb +++ b/spec/diagnostics/rubocop_spec.rb @@ -15,13 +15,30 @@ def bar expect(result).to be_a(Array) end - it 'handles validation errors' do - file = File.realpath(File.join('spec', 'fixtures', 'rubocop-validation-error', 'app.rb')) - source = Solargraph::Source.load(file) - rubocop = Solargraph::Diagnostics::Rubocop.new - expect { - rubocop.diagnose(source, nil) - }.to raise_error(Solargraph::DiagnosticsError) + context "with validation error" do + let(:fixture_path) do + File.absolute_path('spec/fixtures/rubocop-validation-error').gsub(/\\/, '/') + end + + around do |example| + config_file = File.join(fixture_path, '.rubocop.yml') + File.write(config_file, <<~YAML) + inherit_from: + - file_not_found.yml + YAML + example.run + ensure + File.delete(config_file) if File.exist?(config_file) + end + + it 'handles validation errors' do + file = File.realpath(File.join(fixture_path, 'app.rb')) + source = Solargraph::Source.load(file) + rubocop = Solargraph::Diagnostics::Rubocop.new + expect { + rubocop.diagnose(source, nil) + }.to raise_error(Solargraph::DiagnosticsError) + end end it 'calculates ranges' do diff --git a/spec/fixtures/rubocop-subfolder-configuration/.rubocop.yml b/spec/fixtures/rubocop-subfolder-configuration/.rubocop.yml deleted file mode 100644 index e69de29bb..000000000 diff --git a/spec/fixtures/rubocop-subfolder-configuration/folder1/folder2/test.rb b/spec/fixtures/rubocop-subfolder-configuration/folder1/folder2/test.rb deleted file mode 100644 index e69de29bb..000000000 diff --git a/spec/fixtures/rubocop-validation-error/.rubocop.yml b/spec/fixtures/rubocop-validation-error/.rubocop.yml deleted file mode 100644 index 12a649300..000000000 --- a/spec/fixtures/rubocop-validation-error/.rubocop.yml +++ /dev/null @@ -1,2 +0,0 @@ -inherit_from: - - file_not_found.yml From a84c9f038480c21c7f1fd5042a9f80752ac6c132 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 15 Jul 2025 14:40:19 -0400 Subject: [PATCH 186/561] Rebuild rubocop todo --- .rubocop_todo.yml | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 2ce6703fc..44985f8c3 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,6 +1,6 @@ # This configuration was generated by # `rubocop --auto-gen-config` -# on 2025-07-15 18:34:59 UTC using RuboCop version 1.78.0. +# on 2025-07-15 18:39:29 UTC using RuboCop version 1.78.0. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new @@ -845,7 +845,7 @@ RSpec/DescribeClass: - 'spec/complex_type_spec.rb' - 'spec/source_map/node_processor_spec.rb' -# Offense count: 408 +# Offense count: 412 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: SkipBlocks, EnforcedStyle, OnlyStaticConstants. # SupportedStyles: described_class, explicit @@ -952,6 +952,11 @@ RSpec/LetBeforeExamples: RSpec/MessageSpies: EnforcedStyle: receive +# Offense count: 2 +RSpec/MissingExampleGroupArgument: + Exclude: + - 'spec/diagnostics/rubocop_helpers_spec.rb' + # Offense count: 481 RSpec/MultipleExpectations: Max: 14 @@ -1013,6 +1018,11 @@ RSpec/ReceiveMessages: Exclude: - 'spec/language_server/host_spec.rb' +# Offense count: 1 +RSpec/RemoveConst: + Exclude: + - 'spec/diagnostics/rubocop_helpers_spec.rb' + # Offense count: 50 RSpec/RepeatedDescription: Exclude: @@ -1042,7 +1052,7 @@ RSpec/ScatteredLet: Exclude: - 'spec/complex_type_spec.rb' -# Offense count: 88 +# Offense count: 89 # Configuration parameters: Include, CustomTransform, IgnoreMethods, IgnoreMetadata. # Include: **/*_spec.rb RSpec/SpecFilePathFormat: @@ -1292,7 +1302,7 @@ Style/FloatDivision: - 'lib/solargraph/library.rb' - 'lib/solargraph/pin/search.rb' -# Offense count: 128 +# Offense count: 129 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: EnforcedStyle. # SupportedStyles: always, always_true, never @@ -1611,12 +1621,13 @@ Style/RedundantParentheses: - 'lib/solargraph/source.rb' - 'lib/solargraph/type_checker.rb' -# Offense count: 5 +# Offense count: 6 # This cop supports safe autocorrection (--autocorrect). Style/RedundantRegexpArgument: Exclude: - 'lib/solargraph/api_map/index.rb' - 'lib/solargraph/workspace/config.rb' + - 'spec/diagnostics/rubocop_helpers_spec.rb' - 'spec/diagnostics/rubocop_spec.rb' - 'spec/language_server/host_spec.rb' @@ -1737,7 +1748,7 @@ Style/StringConcatenation: - 'lib/solargraph/pin/namespace.rb' - 'solargraph.gemspec' -# Offense count: 617 +# Offense count: 621 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline. # SupportedStyles: single_quotes, double_quotes From 00772e1a677a1076a4dca4d5514c403dddd0fbfe Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 15 Jul 2025 15:09:38 -0400 Subject: [PATCH 187/561] Avoid overlapping chdir calls in multiple threads --- .../message/text_document/formatting.rb | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/solargraph/language_server/message/text_document/formatting.rb b/lib/solargraph/language_server/message/text_document/formatting.rb index 15117e0f4..aa8fba6db 100644 --- a/lib/solargraph/language_server/message/text_document/formatting.rb +++ b/lib/solargraph/language_server/message/text_document/formatting.rb @@ -19,8 +19,15 @@ def process require_rubocop(config['version']) options, paths = ::RuboCop::Options.new.parse(args) options[:stdin] = original - corrections = redirect_stdout do - ::RuboCop::Runner.new(options, ::RuboCop::ConfigStore.new).run(paths) + + # Ensure only one instance of RuboCop::Runner is running at + # a time - it uses 'chdir' to read config files with ERB, + # which can conflict with other chdirs. + @@rubocop_mutex ||= Mutex.new + corrections = @@rubocop_mutex.synchronize do + redirect_stdout do + ::RuboCop::Runner.new(options, ::RuboCop::ConfigStore.new).run(paths) + end end result = options[:stdin] From b6661cf73a3690e69215c903260002f3a85eeafe Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 15 Jul 2025 15:13:42 -0400 Subject: [PATCH 188/561] Force build --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0acaa61fe..98df20346 100755 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ Solargraph's behavior can be controlled via optional [configuration](https://sol ### Plugins -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. +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. For better Rails support, please consider using [solargraph-rails](https://github.com/iftheshoefritz/solargraph-rails/) From f0dea49c4c546682baa9534712cf7abf940df6c5 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 15 Jul 2025 15:26:28 -0400 Subject: [PATCH 189/561] Improve pin caching --- lib/solargraph/api_map.rb | 48 ++- lib/solargraph/doc_map.rb | 415 ++++--------------- lib/solargraph/environ.rb | 6 +- lib/solargraph/gem_pins.rb | 8 - lib/solargraph/library.rb | 6 +- lib/solargraph/pin_cache.rb | 507 +++++++++++++++++++----- lib/solargraph/rbs_map.rb | 61 ++- lib/solargraph/rbs_map/core_map.rb | 37 +- lib/solargraph/rbs_map/stdlib_map.rb | 13 +- lib/solargraph/shell.rb | 80 ++-- lib/solargraph/workspace.rb | 214 +++++++++- lib/solargraph/yardoc.rb | 42 +- spec/doc_map_spec.rb | 106 +++-- spec/gem_pins_spec.rb | 8 +- spec/type_checker/levels/normal_spec.rb | 4 +- spec/yard_map/mapper_spec.rb | 51 +-- 16 files changed, 983 insertions(+), 623 deletions(-) diff --git a/lib/solargraph/api_map.rb b/lib/solargraph/api_map.rb index 6a0edc5a4..83848cb71 100755 --- a/lib/solargraph/api_map.rb +++ b/lib/solargraph/api_map.rb @@ -94,11 +94,12 @@ def catalog bench end unresolved_requires = (bench.external_requires + implicit.requires + bench.workspace.config.required).to_a.compact.uniq recreate_docmap = @unresolved_requires != unresolved_requires || - @doc_map&.uncached_yard_gemspecs&.any? || - @doc_map&.uncached_rbs_collection_gemspecs&.any? || - @doc_map&.rbs_collection_path != bench.workspace.rbs_collection_path + workspace.rbs_collection_path != bench.workspace.rbs_collection_path || + @doc_map.any_uncached? + if recreate_docmap - @doc_map = DocMap.new(unresolved_requires, [], bench.workspace) # @todo Implement gem preferences + @doc_map = DocMap.new(unresolved_requires, bench.workspace) # @todo Implement gem preferences + @pin_cache = @doc_map.pin_cache @unresolved_requires = @doc_map.unresolved_requires end @cache.clear if store.update(@@core_map.pins, @doc_map.pins, implicit.pins, iced_pins, live_pins) @@ -122,16 +123,6 @@ def uncached_gemspecs @doc_map&.uncached_gemspecs || [] end - # @return [::Array] - def uncached_rbs_collection_gemspecs - @doc_map.uncached_rbs_collection_gemspecs - end - - # @return [::Array] - def uncached_yard_gemspecs - @doc_map.uncached_yard_gemspecs - end - # @return [Array] def core_pins @@core_map.pins @@ -186,12 +177,31 @@ def self.load directory api_map end - def cache_all!(out) - @doc_map.cache_all!(out) + # @param out [IO, nil] + # @return [void] + def cache_all_for_doc_map!(out) + @doc_map.cache_doc_map_gems!(out) + end + + # @param out [IO, nil] + # @param rebuild [Boolean] + # @return [void] + def cache_all_for_workspace!(out, rebuild: false) + workspace.cache_all_for_workspace!(out, rebuild: rebuild) + end + + # @return [Workspace] + def workspace + @doc_map.workspace end - def cache_gem(gemspec, rebuild: false, out: nil) - @doc_map.cache(gemspec, rebuild: rebuild, out: out) + # @param gemspec [Gem::Specification] + # @param rebuild [Boolean] + # @param only_if_used [Boolean] + # @param out [IO, nil] + # @return [void] + def cache_gem(gemspec, rebuild: false, only_if_used: false, out: nil) + @doc_map.cache(gemspec, rebuild: rebuild, out: out, only_if_used: only_if_used) end class << self @@ -215,7 +225,7 @@ def self.load_with_cache directory, out return api_map end - api_map.cache_all!(out) + api_map.cache_all_for_doc_map!(out) load(directory) end diff --git a/lib/solargraph/doc_map.rb b/lib/solargraph/doc_map.rb index d51fc3022..4595fa67e 100644 --- a/lib/solargraph/doc_map.rb +++ b/lib/solargraph/doc_map.rb @@ -4,7 +4,9 @@ require 'benchmark' module Solargraph - # A collection of pins generated from required gems. + # A collection of pins generated from required gems. Multiple can + # be created per workspace, to represent the pins available in + # different files based on their 'require' lines. # class DocMap include Logging @@ -13,99 +15,66 @@ class DocMap attr_reader :requires alias required requires - # @return [Array] - attr_reader :preferences - # @return [Array] attr_reader :pins - # @return [Array] - def uncached_gemspecs - uncached_yard_gemspecs.concat(uncached_rbs_collection_gemspecs) - .sort - .uniq { |gemspec| "#{gemspec.name}:#{gemspec.version}" } - end + attr_reader :pin_cache - # @return [Array] - attr_reader :uncached_yard_gemspecs + attr_reader :global_environ # @return [Array] - attr_reader :uncached_rbs_collection_gemspecs - - attr_reader :rbs_collection_path - - attr_reader :rbs_collection_config_path + def uncached_gemspecs + @uncached_gemspecs ||= [] + end - # @return [Workspace, nil] + # @return [Workspace] attr_reader :workspace - # @return [Environ] - attr_reader :environ - # @param requires [Array] - # @param preferences [Array] - # @param workspace [Workspace, nil] - def initialize(requires, preferences, workspace = nil) + # @param workspace [Workspace] + def initialize(requires, workspace) @requires = requires.compact - @preferences = preferences.compact @workspace = workspace - @rbs_collection_path = workspace&.rbs_collection_path - @rbs_collection_config_path = workspace&.rbs_collection_config_path - @environ = Convention.for_global(self) + @global_environ = Convention.for_global(self) load_serialized_gem_pins - pins.concat @environ.pins + pins.concat global_environ.pins end - def cache_all!(out) + def pin_cache + @pin_cache ||= workspace.fresh_pincache + end + + # @return [Array] + def yard_plugins + global_environ.yard_plugins + end + + def any_uncached? + uncached_gemspecs.any? + end + + # Cache all pins needed for the sources in this doc_map + # @param out [IO, nil] output stream for logging + # @return [void] + def cache_doc_map_gems!(out) # if we log at debug level: if logger.info? gem_desc = uncached_gemspecs.map { |gemspec| "#{gemspec.name}:#{gemspec.version}" }.join(', ') logger.info "Caching pins for gems: #{gem_desc}" unless uncached_gemspecs.empty? end - logger.debug { "Caching for YARD: #{uncached_yard_gemspecs.map(&:name)}" } - logger.debug { "Caching for RBS collection: #{uncached_rbs_collection_gemspecs.map(&:name)}" } + logger.debug { "Caching: #{uncached_gemspecs.map(&:name)}" } + PinCache.cache_core unless PinCache.has_core? load_serialized_gem_pins - uncached_gemspecs.each do |gemspec| - cache(gemspec, out: out) + existing_pin_count = pins.length + time = Benchmark.measure do + uncached_gemspecs.each do |gemspec| + cache(gemspec, out: out) + end end + pins_processed = pins.length - existing_pin_count + milliseconds = (time.real * 1000).round + out.puts "Built #{pins.length} gem pins in #{milliseconds} ms" if out && gemspecs.any? if milliseconds > 500 load_serialized_gem_pins - @uncached_rbs_collection_gemspecs = [] - @uncached_yard_gemspecs = [] - end - - def cache_yard_pins(gemspec, out) - pins = GemPins.build_yard_pins(gemspec) - PinCache.serialize_yard_gem(gemspec, pins) - logger.info { "Cached #{pins.length} YARD pins for gem #{gemspec.name}:#{gemspec.version}" } unless pins.empty? - end - - def cache_rbs_collection_pins(gemspec, out) - rbs_map = RbsMap.from_gemspec(gemspec, rbs_collection_path, rbs_collection_config_path) - pins = rbs_map.pins - rbs_version_cache_key = rbs_map.cache_key - # cache pins even if result is zero, so we don't retry building pins - pins ||= [] - PinCache.serialize_rbs_collection_gem(gemspec, rbs_version_cache_key, pins) - logger.info { "Cached #{pins.length} RBS collection pins for gem #{gemspec.name} #{gemspec.version} with cache_key #{rbs_version_cache_key.inspect}" unless pins.empty? } - end - - # @param gemspec [Gem::Specification] - def cache(gemspec, rebuild: false, out: nil) - build_yard = uncached_yard_gemspecs.include?(gemspec) || rebuild - build_rbs_collection = uncached_rbs_collection_gemspecs.include?(gemspec) || rebuild - if build_yard || build_rbs_collection - type = [] - type << 'YARD' if build_yard - type << 'RBS collection' if build_rbs_collection - out.puts("Caching #{type.join(' and ')} pins for gem #{gemspec.name}:#{gemspec.version}") if out - end - cache_yard_pins(gemspec, out) if build_yard - cache_rbs_collection_pins(gemspec, out) if build_rbs_collection - end - - # @return [Array] - def gemspecs - @gemspecs ||= required_gems_map.values.compact.flatten end # @return [Array] @@ -113,296 +82,78 @@ def unresolved_requires @unresolved_requires ||= required_gems_map.select { |_, gemspecs| gemspecs.nil? }.keys end - def self.all_yard_gems_in_memory - @yard_gems_in_memory ||= {} - end - - def self.all_rbs_collection_gems_in_memory - @rbs_collection_gems_in_memory ||= {} - end - - def yard_pins_in_memory - self.class.all_yard_gems_in_memory - end - - def rbs_collection_pins_in_memory - self.class.all_rbs_collection_gems_in_memory[rbs_collection_path] ||= {} - end - - def self.all_combined_pins_in_memory - @combined_pins_in_memory ||= {} - end - - def combined_pins_in_memory - self.class.all_combined_pins_in_memory + # @return [Array] + def gemspecs + @gemspecs ||= required_gems_map.values.compact.flatten end # @return [Set] def dependencies - @dependencies ||= (gemspecs.flat_map { |spec| fetch_dependencies(spec) } - gemspecs).to_set + @dependencies ||= (gemspecs.flat_map { |spec| workspace.fetch_dependencies(spec) } - gemspecs).to_set end private + # Cache gem documentation if needed for this doc_map + # + # @param gemspec [Gem::Specification] + # @param rebuild [Boolean] whether to rebuild the pins even if they are cached + # @param only_if_used [Boolean] + # @param out [IO, nil] output stream for logging + # + # @return [void] + def cache(gemspec, rebuild: false, only_if_used: false, out: nil) + return if only_if_used && !uncached_gemspecs.include?(gemspec) + + pin_cache.cache_gem(gemspec: gemspec, + rebuild: rebuild, + out: out) + end + # @return [void] - def load_serialized_gem_pins + def load_serialized_gem_pins(out: $stderr) @pins = [] - @uncached_yard_gemspecs = [] - @uncached_rbs_collection_gemspecs = [] with_gemspecs, without_gemspecs = required_gems_map.partition { |_, v| v } - paths = Hash[without_gemspecs].keys + # @type [Array] + missing_paths = Hash[without_gemspecs].keys + # @type [Array] gemspecs = Hash[with_gemspecs].values.flatten.compact + dependencies.to_a - paths.each do |path| - rbs_pins = deserialize_stdlib_rbs_map path + missing_paths.each do |path| + # this will load from disk if needed; no need to manage + # uncached_gemspecs to trigger that later + stdlib_name_guess = path.split('/').first + rbs_pins = pin_cache.cache_stdlib_rbs_map stdlib_name_guess if stdlib_name_guess + @pins.concat rbs_pins if rbs_pins end logger.debug { "DocMap#load_serialized_gem_pins: Combining pins..." } + existing_pin_count = pins.length time = Benchmark.measure do gemspecs.each do |gemspec| - pins = deserialize_combined_pin_cache gemspec - @pins.concat pins if pins + # only deserializes already-cached gems + pins = pin_cache.deserialize_combined_pin_cache gemspec + if pins + @pins.concat pins + else + uncached_gemspecs << gemspec + end end end - logger.info { "DocMap#load_serialized_gem_pins: Loaded and processed serialized pins together in #{time.real} seconds" } - @uncached_yard_gemspecs.uniq! - @uncached_rbs_collection_gemspecs.uniq! + pins_processed = pins.length - existing_pin_count + milliseconds = (time.real * 1000).round + out.puts "Deserialized #{pins.length} gem pins from #{PinCache.base_dir} in #{milliseconds} ms" if out && gemspecs.any? if milliseconds > 500 + uncached_gemspecs.uniq! { |gemspec| "#{gemspec.name}:#{gemspec.version}" } nil end # @return [Hash{String => Array}] def required_gems_map - @required_gems_map ||= requires.to_h { |path| [path, resolve_path_to_gemspecs(path)] } - end - - # @return [Hash{String => Gem::Specification}] - def preference_map - @preference_map ||= preferences.to_h { |gemspec| [gemspec.name, gemspec] } - end - - # @param gemspec [Gem::Specification] - # @return [Array] - def deserialize_yard_pin_cache gemspec - if yard_pins_in_memory.key?([gemspec.name, gemspec.version]) - return yard_pins_in_memory[[gemspec.name, gemspec.version]] - end - - cached = PinCache.deserialize_yard_gem(gemspec) - if cached - logger.info { "Loaded #{cached.length} cached YARD pins from #{gemspec.name}:#{gemspec.version}" } - yard_pins_in_memory[[gemspec.name, gemspec.version]] = cached - cached - else - logger.debug "No YARD pin cache for #{gemspec.name}:#{gemspec.version}" - @uncached_yard_gemspecs.push gemspec - nil - end + @required_gems_map ||= requires.to_h { |path| [path, workspace.resolve_path_to_gemspecs(path)] } end - # @param gemspec [Gem::Specification] - # @return [void] - def deserialize_combined_pin_cache(gemspec) - unless combined_pins_in_memory[[gemspec.name, gemspec.version]].nil? - return combined_pins_in_memory[[gemspec.name, gemspec.version]] - end - - rbs_map = RbsMap.from_gemspec(gemspec, rbs_collection_path, rbs_collection_config_path) - rbs_version_cache_key = rbs_map.cache_key - - cached = PinCache.deserialize_combined_gem(gemspec, rbs_version_cache_key) - if cached - logger.info { "Loaded #{cached.length} cached YARD pins from #{gemspec.name}:#{gemspec.version}" } - combined_pins_in_memory[[gemspec.name, gemspec.version]] = cached - return combined_pins_in_memory[[gemspec.name, gemspec.version]] - end - - rbs_collection_pins = deserialize_rbs_collection_cache gemspec, rbs_version_cache_key - - yard_pins = deserialize_yard_pin_cache gemspec - - if !rbs_collection_pins.nil? && !yard_pins.nil? - logger.debug { "Combining pins for #{gemspec.name}:#{gemspec.version}" } - combined_pins = GemPins.combine(yard_pins, rbs_collection_pins) - PinCache.serialize_combined_gem(gemspec, rbs_version_cache_key, combined_pins) - combined_pins_in_memory[[gemspec.name, gemspec.version]] = combined_pins - logger.info { "Generated #{combined_pins_in_memory[[gemspec.name, gemspec.version]].length} combined pins for #{gemspec.name} #{gemspec.version}" } - return combined_pins - end - - if !yard_pins.nil? - logger.debug { "Using only YARD pins for #{gemspec.name}:#{gemspec.version}" } - combined_pins_in_memory[[gemspec.name, gemspec.version]] = yard_pins - return combined_pins_in_memory[[gemspec.name, gemspec.version]] - elsif !rbs_collection_pins.nil? - logger.debug { "Using only RBS collection pins for #{gemspec.name}:#{gemspec.version}" } - combined_pins_in_memory[[gemspec.name, gemspec.version]] = rbs_collection_pins - return combined_pins_in_memory[[gemspec.name, gemspec.version]] - else - logger.debug { "Pins not yet cached for #{gemspec.name}:#{gemspec.version}" } - return nil - end - end - - # @param path [String] require path that might be in the RBS stdlib collection - # @return [void] - def deserialize_stdlib_rbs_map path - map = RbsMap::StdlibMap.load(path) - if map.resolved? - logger.debug { "Loading stdlib pins for #{path}" } - @pins.concat map.pins - logger.debug { "Loaded #{map.pins.length} stdlib pins for #{path}" } - map.pins - else - # @todo Temporarily ignoring unresolved `require 'set'` - logger.debug { "Require path #{path} could not be resolved in RBS" } unless path == 'set' - nil - end - end - - # @return [Array, nil] - def deserialize_rbs_collection_cache gemspec, rbs_version_cache_key - return if rbs_collection_pins_in_memory.key?([gemspec, rbs_version_cache_key]) - cached = PinCache.deserialize_rbs_collection_gem(gemspec, rbs_version_cache_key) - if cached - logger.info { "Loaded #{cached.length} pins from RBS collection cache for #{gemspec.name}:#{gemspec.version}" } unless cached.empty? - rbs_collection_pins_in_memory[[gemspec, rbs_version_cache_key]] = cached - cached - else - logger.debug "No RBS collection pin cache for #{gemspec.name} #{gemspec.version}" - @uncached_rbs_collection_gemspecs.push gemspec - nil - end - end - - # @param gemspec [Gem::Specification] - # @return [Boolean] - def try_gem_in_memory gemspec - gempins = DocMap.gems_in_memory[gemspec] - return false unless gempins - Solargraph.logger.debug "Found #{gemspec.name} #{gemspec.version} in memory" - @pins.concat gempins - true - end - - # @param path [String] - # @return [::Array, nil] - def resolve_path_to_gemspecs path - return nil if path.empty? - return gemspecs_required_from_bundler if path == 'bundler/require' - - gemspec = Gem::Specification.find_by_path(path) - if gemspec.nil? - gem_name_guess = path.split('/').first - begin - # this can happen when the gem is included via a local path in - # a Gemfile; Gem doesn't try to index the paths in that case. - # - # See if we can make a good guess: - potential_gemspec = Gem::Specification.find_by_name(gem_name_guess) - file = "lib/#{path}.rb" - gemspec = potential_gemspec if potential_gemspec.files.any? { |gemspec_file| file == gemspec_file } - rescue Gem::MissingSpecError - logger.debug { "Require path #{path} could not be resolved to a gem via find_by_path or guess of #{gem_name_guess}" } - [] - end - end - return nil if gemspec.nil? - [gemspec_or_preference(gemspec)] - end - - # @param gemspec [Gem::Specification] - # @return [Gem::Specification] - def gemspec_or_preference gemspec - return gemspec unless preference_map.key?(gemspec.name) - return gemspec if gemspec.version == preference_map[gemspec.name].version - - change_gemspec_version gemspec, preference_map[by_path.name].version - end - - # @param gemspec [Gem::Specification] - # @param version [Gem::Version] - # @return [Gem::Specification] - def change_gemspec_version gemspec, version - Gem::Specification.find_by_name(gemspec.name, "= #{version}") - rescue Gem::MissingSpecError - Solargraph.logger.info "Gem #{gemspec.name} version #{version} not found. Using #{gemspec.version} instead" - gemspec - end - - # @param gemspec [Gem::Specification] - # @return [Array] - def fetch_dependencies gemspec - # @param spec [Gem::Dependency] - only_runtime_dependencies(gemspec).each_with_object(Set.new) do |spec, deps| - Solargraph.logger.info "Adding #{spec.name} dependency for #{gemspec.name}" - dep = Gem.loaded_specs[spec.name] - # @todo is next line necessary? - dep ||= Gem::Specification.find_by_name(spec.name, spec.requirement) - deps.merge fetch_dependencies(dep) if deps.add?(dep) - rescue Gem::MissingSpecError - Solargraph.logger.warn "Gem dependency #{spec.name} #{spec.requirement} for #{gemspec.name} not found in RubyGems." - end.to_a - end - - # @param gemspec [Gem::Specification] - # @return [Array] - def only_runtime_dependencies gemspec - gemspec.dependencies - gemspec.development_dependencies - end - - def inspect self.class.inspect end - - def gemspecs_required_from_bundler - # @todo Handle projects with custom Bundler/Gemfile setups - return unless workspace.gemfile? - - if workspace.gemfile? && Bundler.definition&.lockfile&.to_s&.start_with?(workspace.directory) - # Find only the gems bundler is now using - Bundler.definition.locked_gems.specs.flat_map do |lazy_spec| - logger.info "Handling #{lazy_spec.name}:#{lazy_spec.version}" - [Gem::Specification.find_by_name(lazy_spec.name, lazy_spec.version)] - rescue Gem::MissingSpecError => e - logger.info("Could not find #{lazy_spec.name}:#{lazy_spec.version} with find_by_name, falling back to guess") - # can happen in local filesystem references - specs = resolve_path_to_gemspecs lazy_spec.name - logger.warn "Gem #{lazy_spec.name} #{lazy_spec.version} from bundle not found: #{e}" if specs.nil? - next specs - end.compact - else - logger.info 'Fetching gemspecs required from Bundler (bundler/require)' - gemspecs_required_from_external_bundle - end - end - - def gemspecs_required_from_external_bundle - logger.info 'Fetching gemspecs required from external bundle' - return [] unless workspace&.directory - - Solargraph.with_clean_env do - cmd = [ - 'ruby', '-e', - "require 'bundler'; require 'json'; Dir.chdir('#{workspace&.directory}') { puts Bundler.definition.locked_gems.specs.map { |spec| [spec.name, spec.version] }.to_h.to_json }" - ] - o, e, s = Open3.capture3(*cmd) - if s.success? - Solargraph.logger.debug "External bundle: #{o}" - hash = o && !o.empty? ? JSON.parse(o.split("\n").last) : {} - hash.flat_map do |name, version| - Gem::Specification.find_by_name(name, version) - rescue Gem::MissingSpecError => e - logger.info("Could not find #{name}:#{version} with find_by_name, falling back to guess") - # can happen in local filesystem references - specs = resolve_path_to_gemspecs name - logger.warn "Gem #{name} #{version} from bundle not found: #{e}" if specs.nil? - next specs - end.compact - else - Solargraph.logger.warn "Failed to load gems from bundle at #{workspace&.directory}: #{e}" - end - end - end end end diff --git a/lib/solargraph/environ.rb b/lib/solargraph/environ.rb index 6624ead45..392a24949 100644 --- a/lib/solargraph/environ.rb +++ b/lib/solargraph/environ.rb @@ -13,7 +13,7 @@ class Environ # @return [Array] attr_reader :domains - # @return [Array] + # @return [Array] attr_reader :pins # @param requires [Array] @@ -25,6 +25,10 @@ def initialize requires: [], domains: [], pins: [] @pins = pins end + def yard_plugins + @yard_plugins ||= [] + end + # @return [self] def clear domains.clear diff --git a/lib/solargraph/gem_pins.rb b/lib/solargraph/gem_pins.rb index b92cbd6af..15e696571 100644 --- a/lib/solargraph/gem_pins.rb +++ b/lib/solargraph/gem_pins.rb @@ -11,14 +11,6 @@ class << self include Logging end - # @param gemspec [Gem::Specification] - # @return [Array] - def self.build_yard_pins(gemspec) - Yardoc.cache(gemspec) unless Yardoc.cached?(gemspec) - yardoc = Yardoc.load!(gemspec) - YardMap::Mapper.new(yardoc, gemspec).map - end - # @param pins [Array] def self.combine_method_pins_by_path(pins) # bad_pins = pins.select { |pin| pin.is_a?(Pin::Method) && pin.path == 'StringIO.open' && pin.source == :rbs }; raise "wtf: #{bad_pins}" if bad_pins.length > 1 diff --git a/lib/solargraph/library.rb b/lib/solargraph/library.rb index 740d700b3..75f3a7fe0 100644 --- a/lib/solargraph/library.rb +++ b/lib/solargraph/library.rb @@ -622,8 +622,7 @@ def cache_next_gemspec end def cacheable_specs - cacheable = api_map.uncached_yard_gemspecs + - api_map.uncached_rbs_collection_gemspecs - + cacheable = api_map.uncached_gemspecs + queued_gemspec_cache - cache_errors.to_a return cacheable unless cacheable.empty? @@ -680,8 +679,7 @@ def sync_catalog api_map.catalog bench source_map_hash.values.each { |map| find_external_requires(map) } logger.info "Catalog complete (#{api_map.source_maps.length} files, #{api_map.pins.length} pins)" - logger.info "#{api_map.uncached_yard_gemspecs.length} uncached YARD gemspecs" - logger.info "#{api_map.uncached_rbs_collection_gemspecs.length} uncached RBS collection gemspecs" + logger.info "#{api_map.uncached_gemspecs.length} uncached gemspecs" cache_next_gemspec @sync_count = 0 end diff --git a/lib/solargraph/pin_cache.rb b/lib/solargraph/pin_cache.rb index 9013dd0d9..4b7276f19 100644 --- a/lib/solargraph/pin_cache.rb +++ b/lib/solargraph/pin_cache.rb @@ -1,128 +1,405 @@ require 'fileutils' require 'rbs' +require 'rubygems' module Solargraph - module PinCache - class << self - include Logging + class PinCache + include Logging + + attr_reader :directory, :rbs_collection_path, :rbs_collection_config_path, :yard_plugins + + # @param rbs_collection_path [String, nil] + # @param rbs_collection_config_path [String, nil] + # @param directory [String, nil] + # @param yard_plugins [Array] + def initialize(rbs_collection_path:, rbs_collection_config_path:, + directory:, + yard_plugins:) + @rbs_collection_path = rbs_collection_path + @rbs_collection_config_path = rbs_collection_config_path + @directory = directory + @yard_plugins = yard_plugins + end - # The base directory where cached YARD documentation and serialized pins are serialized - # - # @return [String] - def base_dir - # The directory is not stored in a variable so it can be overridden - # in specs. - ENV['SOLARGRAPH_CACHE'] || - (ENV['XDG_CACHE_HOME'] ? File.join(ENV['XDG_CACHE_HOME'], 'solargraph') : nil) || - File.join(Dir.home, '.cache', 'solargraph') - end + # @sg-ignore + # @param gemspec [Gem::Specification, Bundler::LazySpecification] + def cached?(gemspec) + rbs_version_cache_key = lookup_rbs_version_cache_key(gemspec) + has_combined_gem?(gemspec, rbs_version_cache_key) + end - # The working directory for the current Ruby, RBS, and Solargraph versions. - # - # @return [String] - def work_dir - # The directory is not stored in a variable so it can be overridden - # in specs. - File.join(base_dir, "ruby-#{RUBY_VERSION}", "rbs-#{RBS::VERSION}", "solargraph-#{Solargraph::VERSION}") - end + # @param gemspec [Gem::Specification, Bundler::LazySpecification] + # @param rebuild [Boolean] whether to rebuild the cache regardless of whether it already exists + # @param out [IO, nil] output stream for logging + # @return [void] + def cache_gem(gemspec:, rebuild: false, out: nil) + rbs_version_cache_key = lookup_rbs_version_cache_key(gemspec) + if rebuild + build_yard = true + build_rbs_collection = true + build_combined = true + else + build_yard = !has_yard_gem?(gemspec) + build_rbs_collection = !has_rbs_collection_pins?(gemspec, rbs_version_cache_key) + build_combined = !has_combined_gem?(gemspec, rbs_version_cache_key) || build_yard || build_rbs_collection + end + + build_yard = false if suppress_yard_cache?(gemspec, rbs_version_cache_key) + + return unless build_yard || build_rbs_collection || build_combined + + type = [] + type << 'YARD' if build_yard + rbs_source_desc = RbsMap.rbs_source_desc(rbs_version_cache_key) + type << rbs_source_desc if build_rbs_collection && !rbs_source_desc.nil? + # we'll build it anyway, but it won't take long to build with + # only a single source + type << 'combined' if build_combined && !rbs_source_desc.nil? + out.puts("Caching #{type.join(' and ')} pins for gem #{gemspec.name}:#{gemspec.version}") if out + + cache_yard_pins(gemspec, out) if build_yard + yard_pins = deserialize_yard_pin_cache(gemspec) + + cache_rbs_collection_pins(gemspec, out) if build_rbs_collection + rbs_collection_pins = deserialize_rbs_collection_cache(gemspec, rbs_version_cache_key) + + cache_combined_pins(gemspec, rbs_version_cache_key, yard_pins, rbs_collection_pins) if build_combined + end - def yardoc_path gemspec - File.join(base_dir, "yard-#{YARD::VERSION}", "#{gemspec.name}-#{gemspec.version}.yardoc") + def suppress_yard_cache?(gemspec, rbs_version_cache_key) + # TODO test this - saw: Caching YARD and RBS collection and combined pins for gem parser:3.3.8.0 + if gemspec == 'parser' && rbs_version_cache_key != CACHE_KEY_UNRESOLVED + # parser takes forever to build YARD pins, but has excellent RBS collection pins + return true end + false + end - def stdlib_path - File.join(work_dir, 'stdlib') + # @param path [String] require path that might be in the RBS stdlib collection + # @return [void] + def cache_stdlib_rbs_map path + # these are held in memory in RbsMap::StdlibMap + map = RbsMap::StdlibMap.load(path) + if map.resolved? + logger.debug { "Loading stdlib pins for #{path}" } + pins = map.pins + logger.debug { "Loaded #{pins.length} stdlib pins for #{path}" } + pins + else + # @todo Temporarily ignoring unresolved `require 'set'` + logger.debug { "Require path #{path} could not be resolved in RBS" } unless path == 'set' + nil end + end - def stdlib_require_path require - File.join(stdlib_path, "#{require}.ser") - end + # @param gemspec [Gem::Specification, Bundler::LazySpecification] + # @return [String] + def lookup_rbs_version_cache_key(gemspec) + rbs_map = RbsMap.from_gemspec(gemspec, rbs_collection_path, rbs_collection_config_path) + rbs_map.cache_key + end - def deserialize_stdlib_require require - load(stdlib_require_path(require)) - end + # @param gemspec [Gem::Specification] + # @param rbs_version_cache_key [String] + # @param yard_pins [Array] + # @param rbs_collection_pins [Array] + # @return [void] + def cache_combined_pins(gemspec, rbs_version_cache_key, yard_pins, rbs_collection_pins) + combined_pins = GemPins.combine(yard_pins, rbs_collection_pins) + serialize_combined_gem(gemspec, rbs_version_cache_key, combined_pins) + end - def serialize_stdlib_require require, pins - save(stdlib_require_path(require), pins) + # @param gemspec [Gem::Specification] + # @return [void] + def deserialize_combined_pin_cache(gemspec) + unless combined_pins_in_memory[[gemspec.name, gemspec.version]].nil? + return combined_pins_in_memory[[gemspec.name, gemspec.version]] end - def core_path - File.join(work_dir, 'core.ser') - end + rbs_version_cache_key = lookup_rbs_version_cache_key(gemspec) - def deserialize_core - load(core_path) + cached = load_combined_gem(gemspec, rbs_version_cache_key) + if cached + logger.info { "Loaded #{cached.length} cached YARD pins from #{gemspec.name}:#{gemspec.version}" } + combined_pins_in_memory[[gemspec.name, gemspec.version]] = cached + return combined_pins_in_memory[[gemspec.name, gemspec.version]] end + end - def serialize_core pins - save(core_path, pins) - end + # @param gemspec [Gem::Specification] + # @param out [IO, nil] + # @return [void] + def uncache_gem(gemspec, out: nil) + PinCache.uncache(yardoc_path(gemspec), out: out) + uncache_by_prefix(rbs_collection_pins_path_prefix(gemspec), out: out) + PinCache.uncache(yard_gem_path(gemspec), out: out) + uncache_by_prefix(combined_path_prefix(gemspec), out: out) + end - def yard_gem_path gemspec - File.join(work_dir, 'yard', "#{gemspec.name}-#{gemspec.version}.ser") - end + private - def deserialize_yard_gem(gemspec) - load(yard_gem_path(gemspec)) + # @param gemspec [Gem::Specification, Bundler::LazySpecification] + # @param out [IO, nil] + # @return [Array] + def cache_yard_pins(gemspec, out) + yardoc_dir = yardoc_path(gemspec) + unless Yardoc.docs_built?(yardoc_dir, gemspec) + Yardoc.build_docs(yardoc_dir, yard_plugins, gemspec) end + pins = Yardoc.build_pins(yardoc_dir, gemspec, out) + serialize_yard_gem(gemspec, pins) + logger.info { "Cached #{pins.length} YARD pins for gem #{gemspec.name}:#{gemspec.version}" } unless pins.empty? + pins + end - def serialize_yard_gem(gemspec, pins) - save(yard_gem_path(gemspec), pins) - end + # @return [Hash{Array(String, String) => Array}] gemspec name, version + def yard_pins_in_memory + PinCache.all_yard_pins_in_memory[yard_plugins] ||= {} + end - def has_yard?(gemspec) - exist?(yard_gem_path(gemspec)) - end + # @return [Hash{Array(String, String, String) => Array}] gemspec name, version and rbs version cache key + def rbs_collection_pins_in_memory + PinCache.all_rbs_collection_pins_in_memory ||= {} + end - def rbs_collection_path(gemspec, hash) - File.join(work_dir, 'rbs', "#{gemspec.name}-#{gemspec.version}-#{hash || 0}.ser") - end + # @return [Hash{Array(String, String, String) => Array}] + def combined_pins_in_memory + PinCache.all_combined_pins_in_memory[yard_plugins] ||= {} + end - def rbs_collection_path_prefix(gemspec) - File.join(work_dir, 'rbs', "#{gemspec.name}-#{gemspec.version}-") + # @param gemspec [Gem::Specification] + # @param out [IO, nil] + # @return [Array] + def cache_rbs_collection_pins(gemspec, out) + rbs_map = RbsMap.from_gemspec(gemspec, rbs_collection_path, rbs_collection_config_path) + pins = rbs_map.pins + rbs_version_cache_key = rbs_map.cache_key + # cache pins even if result is zero, so we don't retry building pins + pins ||= [] + serialize_rbs_collection_pins(gemspec, rbs_version_cache_key, pins) + logger.info { "Cached #{pins.length} RBS collection pins for gem #{gemspec.name} #{gemspec.version} with cache_key #{rbs_version_cache_key.inspect}" unless pins.empty? } + pins + end + + # @param gemspec [Gem::Specification] + # @return [Array] + def deserialize_yard_pin_cache gemspec + if yard_pins_in_memory.key?([gemspec.name, gemspec.version]) + return yard_pins_in_memory[[gemspec.name, gemspec.version]] end - def deserialize_rbs_collection_gem(gemspec, hash) - load(rbs_collection_path(gemspec, hash)) + cached = load_yard_gem(gemspec) + if cached + logger.info { "Loaded #{cached.length} cached YARD pins from #{gemspec.name}:#{gemspec.version}" } + yard_pins_in_memory[[gemspec.name, gemspec.version]] = cached + cached + else + logger.debug "No YARD pin cache for #{gemspec.name}:#{gemspec.version}" + nil end + end - def serialize_rbs_collection_gem(gemspec, hash, pins) - save(rbs_collection_path(gemspec, hash), pins) + # @param gemspec [Gem::Specification, Bundler::LazySpecification] + # @param rbs_version_cache_key [String] + # @return [Array, nil] + def deserialize_rbs_collection_cache gemspec, rbs_version_cache_key + return if rbs_collection_pins_in_memory.key?([gemspec.name, gemspec.version, rbs_version_cache_key]) + cached = load_rbs_collection_pins(gemspec, rbs_version_cache_key) + if cached + logger.info { "Loaded #{cached.length} pins from RBS collection cache for #{gemspec.name}:#{gemspec.version}" } unless cached.empty? + rbs_collection_pins_in_memory[[gemspec.name, gemspec.version, rbs_version_cache_key]] = cached + cached + else + logger.debug "No RBS collection pin cache for #{gemspec.name} #{gemspec.version}" + nil end + end + + # @return [Array] + def yard_path_components + ["yard-#{YARD::VERSION}", + yard_plugins.sort.uniq.join('-')] + end + + # @param gemspec [Gem::Specification] + # @return [String] + def yardoc_path gemspec + File.join(PinCache.base_dir, + *yard_path_components, + "#{gemspec.name}-#{gemspec.version}.yardoc") + end + + # @param gemspec [Gem::Specification] + # @return [String] + def yard_gem_path gemspec + File.join(PinCache.work_dir, *yard_path_components, "#{gemspec.name}-#{gemspec.version}.ser") + end + + # @param gemspec [Gem::Specification] + # @return [Array] + def load_yard_gem(gemspec) + PinCache.load(yard_gem_path(gemspec)) + end + + # @param gemspec [Gem::Specification] + # @param pins [Array] + # @return [void] + def serialize_yard_gem(gemspec, pins) + PinCache.save(yard_gem_path(gemspec), pins) + end + + # @param gemspec [Gem::Specification] + # @return [Boolean] + def has_yard_gem?(gemspec) + exist?(yard_gem_path(gemspec)) + end + + # @param gemspec [Gem::Specification] + # @return [Boolean] + def has_yardoc?(gemspec) + exist?(yardoc_path(gemspec)) + end + + # @param gemspec [Gem::Specification] + # @param hash [String, nil] + # @return [String] + def rbs_collection_pins_path(gemspec, hash) + rbs_collection_pins_path_prefix(gemspec) + "#{gemspec.name}-#{gemspec.version}-#{hash || 0}.ser" + end + + # @param gemspec [Gem::Specification] + # @return [String] + def rbs_collection_pins_path_prefix(gemspec) + File.join(PinCache.work_dir, 'rbs', "#{gemspec.name}-#{gemspec.version}-") + end + + # @param gemspec [Gem::Specification, Bundler::LazySpecification] + # @param hash [String] + # + # @return [Array] + def load_rbs_collection_pins(gemspec, hash) + PinCache.load(rbs_collection_pins_path(gemspec, hash)) + end + + # @param gemspec [Gem::Specification] + # @param hash [String, nil] + # @param pins [Array]n + # @return [void] + def serialize_rbs_collection_pins(gemspec, hash, pins) + PinCache.save(rbs_collection_pins_path(gemspec, hash), pins) + end + + # @param gemspec [Gem::Specification] + # @param hash [String, nil] + # @return [String] + def combined_path(gemspec, hash) + File.join(combined_path_prefix(gemspec) + "-#{hash || 0}.ser") + end + + # @param gemspec [Gem::Specification] + # @return [String] + def combined_path_prefix(gemspec) + File.join(PinCache.work_dir, 'combined', yard_plugins.sort.join('-'), "#{gemspec.name}-#{gemspec.version}") + end + + # @param gemspec [Gem::Specification] + # @param hash [String, nil] + # @param pins [Array] + # @return [void] + def serialize_combined_gem(gemspec, hash, pins) + PinCache.save(combined_path(gemspec, hash), pins) + end - def combined_path(gemspec, hash) - File.join(work_dir, 'combined', "#{gemspec.name}-#{gemspec.version}-#{hash || 0}.ser") + # @param gemspec [Gem::Specification, Bundler::LazySpecification] + # @param hash [String] + def has_combined_gem?(gemspec, hash) + exist?(combined_path(gemspec, hash)) + end + + # @param gemspec [Gem::Specification] + # @param hash [String, nil] + # @return [Array] + def load_combined_gem gemspec, hash + PinCache.load(combined_path(gemspec, hash)) + end + + # @param gemspec [Gem::Specification] + # @param hash [String] + def has_rbs_collection_pins?(gemspec, hash) + exist?(rbs_collection_pins_path(gemspec, hash)) + end + + include Logging + + # @param path [String] + def exist? *path + File.file? File.join(*path) + end + + # @return [void] + # @param path_segments [Array] + def uncache_by_prefix *path_segments, out: nil + path = File.join(*path_segments) + glob = "#{path}*" + out.puts "Clearing pin cache in #{glob}" unless out.nil? + Dir.glob(glob).each do |file| + next unless File.file?(file) + FileUtils.rm_rf file, secure: true + out.puts "Clearing pin cache in #{file}" unless out.nil? end + end + + class << self + include Logging - def combined_path_prefix(gemspec) - File.join(work_dir, 'combined', "#{gemspec.name}-#{gemspec.version}-") + # @return [Hash{Array => Hash{Array(String, String) => Array}}] yard plugins, then gemspec name and version + def all_yard_pins_in_memory + @all_yard_pins_in_memory ||= {} end - def serialize_combined_gem(gemspec, hash, pins) - save(combined_path(gemspec, hash), pins) + # @return [Hash{Array(String, String, String) => Array}] gemspec name, version and rbs version cache key + def all_rbs_collection_pins_in_memory + @all_rbs_collection_pins_in_memory ||= {} end - def deserialize_combined_gem gemspec, hash - load(combined_path(gemspec, hash)) + # @return [Hash{Array => Hash{Array(String, String) => Array}}] yard plugins, then gemspec name and version + def all_combined_pins_in_memory + @all_combined_pins_in_memory ||= {} end - def has_rbs_collection?(gemspec, hash) - exist?(rbs_collection_path(gemspec, hash)) + # The base directory where cached YARD documentation and serialized pins are serialized + # + # @return [String] + def base_dir + # The directory is not stored in a variable so it can be overridden + # in specs. + ENV['SOLARGRAPH_CACHE'] || + (ENV['XDG_CACHE_HOME'] ? File.join(ENV['XDG_CACHE_HOME'], 'solargraph') : nil) || + File.join(Dir.home, '.cache', 'solargraph') end - def uncache_core - uncache(core_path) + # @param path_segments [Array] + # @return [void] + def uncache *path_segments, out: nil + path = File.join(*path_segments) + if File.exist?(path) + FileUtils.rm_rf path, secure: true + out.puts "Clearing pin cache in #{path}" unless out.nil? + else + out.puts "Pin cache file #{path} does not exist" unless out.nil? + end end - def uncache_stdlib - uncache(stdlib_path) + # @param out [IO, nil] + # @return [void] + def uncache_core(out: nil) + uncache(core_path, out: out) end - def uncache_gem(gemspec, out: nil) - uncache(yardoc_path(gemspec), out: out) - uncache_by_prefix(rbs_collection_path_prefix(gemspec), out: out) - uncache(yard_gem_path(gemspec), out: out) - uncache_by_prefix(combined_path_prefix(gemspec), out: out) + # @param out [IO, nil] + # @return [void] + def uncache_stdlib(out: nil) + uncache(stdlib_path, out: out) end # @return [void] @@ -130,7 +407,19 @@ def clear FileUtils.rm_rf base_dir, secure: true end - private + # The working directory for the current Ruby, RBS, and Solargraph versions. + # + # @return [String] + def work_dir + # The directory is not stored in a variable so it can be overridden + # in specs. + File.join(base_dir, "ruby-#{RUBY_VERSION}", "rbs-#{RBS::VERSION}", "solargraph-#{Solargraph::VERSION}") + end + + # @return [String] + def core_path + File.join(work_dir, 'core.ser') + end # @param file [String] # @return [Array, nil] @@ -143,11 +432,7 @@ def load file nil end - def exist? *path - File.file? join(*path) - end - - # @param path [Array] + # @param file [String] # @param pins [Array] # @return [void] def save file, pins @@ -158,27 +443,41 @@ def save file, pins logger.debug { "Cache#save: Saved #{pins.length} pins to #{file}" } end - # @return [void] - # @param path_segments [Array] - def uncache *path_segments, out: nil - path = File.join(*path_segments) - if File.exist?(path) - FileUtils.rm_rf path, secure: true - out.puts "Clearing pin cache in #{path}" unless out.nil? - end + def has_core? + File.file?(core_path) + end + + def cache_core(out: $stderr) + RbsMap::CoreMap.new.cache_core(out: out) + end + + # @return [Array] + def load_core + load(core_path) end + # @param pins [Array] # @return [void] - # @param path_segments [Array] - def uncache_by_prefix *path_segments, out: nil - path = File.join(*path_segments) - glob = "#{path}*" - out.puts "Clearing pin cache in #{glob}" unless out.nil? - Dir.glob(glob).each do |file| - next unless File.file?(file) - FileUtils.rm_rf file, secure: true - out.puts "Clearing pin cache in #{file}" unless out.nil? - end + def serialize_core pins, out: $stderr + save(core_path, pins) + end + + def stdlib_path + File.join(work_dir, 'stdlib') + end + + def stdlib_require_path require + File.join(stdlib_path, "#{require}.ser") + end + + # @param require [String] + # @return [Array] + def load_stdlib_require require + load(stdlib_require_path(require)) + end + + def serialize_stdlib_require require, pins + save(stdlib_require_path(require), pins) end end end diff --git a/lib/solargraph/rbs_map.rb b/lib/solargraph/rbs_map.rb index c2673c71b..4b2ce50bb 100644 --- a/lib/solargraph/rbs_map.rb +++ b/lib/solargraph/rbs_map.rb @@ -24,7 +24,7 @@ class RbsMap # @param library [String] # @param version [String, nil] # @param rbs_collection_paths [Array] - def initialize library, version = nil, rbs_collection_config_path: nil, rbs_collection_paths: [] + def initialize library, version = nil, rbs_collection_config_path: nil, rbs_collection_paths: [], out: $stderr if rbs_collection_config_path.nil? && !rbs_collection_paths.empty? raise 'Please provide rbs_collection_config_path if you provide rbs_collection_paths' end @@ -35,8 +35,24 @@ def initialize library, version = nil, rbs_collection_config_path: nil, rbs_coll add_library loader, library, version end - def loader - @loader ||= RBS::EnvironmentLoader.new(core_root: nil, repository: repository) + + CACHE_KEY_GEM_EXPORT = 'gem-export' + CACHE_KEY_UNRESOLVED = 'unresolved' + CACHE_KEY_STDLIB = 'stdlib' + CACHE_KEY_LOCAL = 'local' + + def self.rbs_source_desc(cache_key) + if cache_key == CACHE_KEY_GEM_EXPORT + "RBS gem export" + elsif cache_key == CACHE_KEY_UNRESOLVED + nil + elsif cache_key == CACHE_KEY_STDLIB + "RBS standard library" + elsif cache_key == CACHE_KEY_LOCAL + "local RBS shims" + else + "RBS collection" + end end # @return string representing the version of the RBS info fetched @@ -46,6 +62,7 @@ def loader def cache_key @hextdigest ||= begin data = nil + gem_config = nil if rbs_collection_config_path lockfile_path = RBS::Collection::Config.to_lockfile_path(Pathname.new(rbs_collection_config_path)) if lockfile_path.exist? @@ -54,16 +71,19 @@ def cache_key data = gem_config&.to_s end end - if data.nil? || data.empty? - if resolved? - # definitely came from the gem itself and not elsewhere - - # only one version per gem - 'gem-export' + if gem_config.nil? + CACHE_KEY_UNRESOLVED + else + source = gem_config.fetch('source', {}).fetch('type', nil) + if source == 'rubygems' + CACHE_KEY_GEM_EXPORT + elsif source == 'local' + CACHE_KEY_LOCAL + elsif source == 'stdlib' + CACHE_KEY_STDLIB else - 'unresolved' + Digest::SHA1.hexdigest(data) end - else - Digest::SHA1.hexdigest(data) end end end @@ -80,8 +100,14 @@ def self.from_gemspec gemspec, rbs_collection_path, rbs_collection_config_path rbs_collection_config_path: rbs_collection_config_path) end - def pins - @pins ||= resolved? ? conversions.pins : [] + # @return [Array] + def pins(out: $stderr) + @pins ||= if resolved? + loader.libs.each { |lib| log_caching(lib, out: $stderr) } + conversions.pins + else + [] + end end # @generic T @@ -128,12 +154,17 @@ def conversions @conversions ||= Conversions.new(loader: loader) end + # @return [void] + def log_caching(gemspec, out:); end + # @param loader [RBS::EnvironmentLoader] # @param library [String] # @return [Boolean] true if adding the library succeeded - def add_library loader, library, version + def add_library loader, library, version, out: $stderr + raise "wtf - why was library not a string? #{library}" unless library.is_a?(String) @resolved = if loader.has_library?(library: library, version: version) - loader.add library: library, version: version + # we find our own dependencies from gemfile.lock + loader.add library: library, version: version, resolve_dependencies: false logger.debug { "#{short_name} successfully loaded library #{library}:#{version}" } true else diff --git a/lib/solargraph/rbs_map/core_map.rb b/lib/solargraph/rbs_map/core_map.rb index 0d265d773..ade16d932 100644 --- a/lib/solargraph/rbs_map/core_map.rb +++ b/lib/solargraph/rbs_map/core_map.rb @@ -14,27 +14,36 @@ def resolved? def initialize; end - def pins + # @return [Array] + def pins(out: $stderr) return @pins if @pins + @pins = cache_core(out: out) + end - @pins = [] - cache = PinCache.deserialize_core + def cache_core(out: $stderr) + new_pins = [] + cache = PinCache.load_core if cache - @pins.replace cache + return cache else - loader.add(path: Pathname(FILLS_DIRECTORY)) - @pins = conversions.pins - @pins.concat RbsMap::CoreFills::ALL - processed = ApiMap::Store.new(pins).pins.reject { |p| p.is_a?(Solargraph::Pin::Reference::Override) } - @pins.replace processed + new_pins.concat conversions.pins + + # Avoid RBS::DuplicatedDeclarationError by loading in a different EnvironmentLoader + fill_loader = RBS::EnvironmentLoader.new(core_root: nil, repository: RBS::Repository.new(no_stdlib: false)) + fill_loader.add(path: Pathname(FILLS_DIRECTORY)) + out.puts "Caching RBS pins for Ruby core" if out + fill_conversions = Conversions.new(loader: fill_loader) + new_pins.concat fill_conversions.pins + + new_pins.concat RbsMap::CoreFills::ALL + + processed = ApiMap::Store.new(new_pins).pins.reject { |p| p.is_a?(Solargraph::Pin::Reference::Override) } + new_pins.replace processed - PinCache.serialize_core @pins + PinCache.serialize_core new_pins end - @pins - end + new_pins - def loader - @loader ||= RBS::EnvironmentLoader.new(repository: RBS::Repository.new(no_stdlib: false)) end private diff --git a/lib/solargraph/rbs_map/stdlib_map.rb b/lib/solargraph/rbs_map/stdlib_map.rb index b6804157f..b4b88fd69 100644 --- a/lib/solargraph/rbs_map/stdlib_map.rb +++ b/lib/solargraph/rbs_map/stdlib_map.rb @@ -9,12 +9,21 @@ class RbsMap class StdlibMap < RbsMap include Logging + def log_level + # TODO: track down remaining unfound requires + :info + end + # @type [Hash{String => RbsMap}] @stdlib_maps_hash = {} + def log_caching(gemspec, out: $stderr) + out.puts("Caching RBS pins for standard library #{gemspec.name}") + end + # @param library [String] - def initialize library - cached_pins = PinCache.deserialize_stdlib_require library + def initialize library, out: $stderr + cached_pins = PinCache.load_stdlib_require library if cached_pins @pins = cached_pins @resolved = true diff --git a/lib/solargraph/shell.rb b/lib/solargraph/shell.rb index 8f02f6ec9..d3b79d700 100755 --- a/lib/solargraph/shell.rb +++ b/lib/solargraph/shell.rb @@ -101,9 +101,38 @@ def clear # @param gem [String] # @param version [String, nil] def cache gem, version = nil - api_map = Solargraph::ApiMap.load(Dir.pwd) - spec = Gem::Specification.find_by_name(gem, version) - api_map.cache_gem(spec, rebuild: options[:rebuild], out: $stdout) + gems(gem + version ? "=#{version}" : '') + # " + end + + desc 'gems [GEM[=VERSION]]', 'Cache documentation for installed gems' + option :rebuild, type: :boolean, desc: 'Rebuild existing documentation', default: false + # @param names [Array] + # @return [void] + def gems *names + api_map = ApiMap.load('.') + if names.empty? + api_map.cache_all_for_workspace!($stdout, rebuild: options.rebuild) + else + STDERR.puts("Caching these gems: #{names}") + names.each do |name| + if gem == 'core' + PinCache.cache_core(out: $stdout) + next + end + + if gem == 'stdlib' + PinCache.cache_stdlib(out: $stdout) + next + end + + gemspec = api_map.find_gem(*name.split('=')) + api_map.cache_gem(gemspec, rebuild: options.rebuild, out: $stdout) + rescue Gem::MissingSpecError + warn "Gem '#{name}' not found" + end + STDERR.puts "Documentation cached for #{names.count} gems." + end end desc 'uncache GEM [...GEM]', "Delete specific cached gem documentation" @@ -116,6 +145,7 @@ def cache gem, version = nil # @return [void] def uncache *gems raise ArgumentError, 'No gems specified.' if gems.empty? + workspace = Workspace.new('.') gems.each do |gem| if gem == 'core' PinCache.uncache_core @@ -128,27 +158,7 @@ def uncache *gems end spec = Gem::Specification.find_by_name(gem) - PinCache.uncache_gem(spec, out: $stdout) - end - end - - desc 'gems [GEM[=VERSION]]', 'Cache documentation for installed gems' - option :rebuild, type: :boolean, desc: 'Rebuild existing documentation', default: false - # @param names [Array] - # @return [void] - def gems *names - api_map = ApiMap.load('.') - if names.empty? - Gem::Specification.to_a.each { |spec| do_cache spec, api_map } - STDERR.puts "Documentation cached for all #{Gem::Specification.count} gems." - else - names.each do |name| - spec = Gem::Specification.find_by_name(*name.split('=')) - do_cache spec, api_map - rescue Gem::MissingSpecError - warn "Gem '#{name}' not found" - end - STDERR.puts "Documentation cached for #{names.count} gems." + workspace.uncache_gem(spec, out: $stdout) end end @@ -257,12 +267,24 @@ def pin_description pin desc end - # @param gemspec [Gem::Specification] + # @param type [ComplexType] # @return [void] - def do_cache gemspec, api_map - # @todo if the rebuild: option is passed as a positional arg, - # typecheck doesn't complain on the below line - api_map.cache_gem(gemspec, rebuild: options.rebuild, out: $stdout) + def print_type(type) + if options[:rbs] + puts type.to_rbs + else + puts type.rooted_tag + end + end + + # @param pin [Solargraph::Pin::Base] + # @return [void] + def print_pin(pin) + if options[:rbs] + puts pin.to_rbs + else + puts pin.inspect + end end end end diff --git a/lib/solargraph/workspace.rb b/lib/solargraph/workspace.rb index aabadd333..dccc65b52 100644 --- a/lib/solargraph/workspace.rb +++ b/lib/solargraph/workspace.rb @@ -9,11 +9,15 @@ module Solargraph # in an associated Library or ApiMap. # class Workspace + include Logging + autoload :Config, 'solargraph/workspace/config' # @return [String] attr_reader :directory + attr_reader :preferences + # The require paths associated with the workspace. # # @return [Array] @@ -34,6 +38,8 @@ def initialize directory = '', config = nil, server = {} @gemnames = [] @require_paths = generate_require_paths require_plugins + # @todo implement preferences + @preferences = [] end # @return [Solargraph::Workspace::Config] @@ -41,6 +47,106 @@ def config @config ||= Solargraph::Workspace::Config.new(directory) end + # @return [Solargraph::PinCache] + def pin_cache + @pin_cache ||= Solargraph::PinCache.new(rbs_collection_path: rbs_collection_path, + rbs_collection_config_path: rbs_collection_config_path, + yard_plugins: yard_plugins, + directory: directory) + end + + # @return [Environ] + def global_environ + # empty docmap, since the result needs to work in any possible + # context here + @environ ||= Convention.for_global(DocMap.new([], self)) + end + + # @param gemspec [Gem::Specification, Bundler::LazySpecification] + # @param out [IO, nil] output stream for logging + # @param rebuild [Boolean] whether to rebuild the pins even if they are cached + # + # @return [void] + def cache_gem(gemspec, out: nil, rebuild: false) + pin_cache.cache_gem(gemspec: gemspec, out: out, rebuild: rebuild) + end + + # @param gemspec [Gem::Specification, Bundler::LazySpecification] + # @param out [IO, nil] output stream for logging + # + # @return [void] + def uncache_gem(gemspec, out: nil) + pin_cache.uncache_gem(gemspec, out: out) + end + + def fresh_pincache + PinCache.new(rbs_collection_path: rbs_collection_path, + rbs_collection_config_path: rbs_collection_config_path, + yard_plugins: yard_plugins, + directory: directory) + end + + # @return [Array] + def yard_plugins + @yard_plugins ||= global_environ.yard_plugins.sort.uniq + end + + # @param path [String] + # @return [::Array, nil] + def resolve_path_to_gemspecs path + return nil if path.empty? + # TODO there should be a distinction between everything and the non-require: false stuff + return gemspecs_required_from_bundler if path == 'bundler/require' + + gemspecs = gemspecs_required_from_bundler + # @type [Gem::Specification, nil] + gemspec = gemspecs.find { |gemspec| gemspec.name == path } + if gemspec.nil? + gem_name_guess = path.split('/').first + begin + # this can happen when the gem is included via a local path in + # a Gemfile; Gem doesn't try to index the paths in that case. + # + # See if we can make a good guess: + potential_gemspec = gemspecs.find { |gemspec| gemspec.name == gem_name_guess } + + return nil if potential_gemspec.nil? + + file = "lib/#{path}.rb" + gemspec = potential_gemspec if potential_gemspec&.files&.any? { |gemspec_file| file == gemspec_file } + rescue Gem::MissingSpecError + logger.debug { "Require path #{path} could not be resolved to a gem via find_by_path or guess of #{gem_name_guess}" } + [] + end + end + return nil if gemspec.nil? + [gemspec_or_preference(gemspec)] + end + + # @param gemspec [Gem::Specification] + # @return [Array] + def fetch_dependencies gemspec + gemspecs = gemspecs_required_from_bundler + + # @param spec [Gem::Dependency] + only_runtime_dependencies(gemspec).each_with_object(Set.new) do |spec, deps| + Solargraph.logger.info "Adding #{spec.name} dependency for #{gemspec.name}" + # @type [Gem::Specification, nil] + dep = gemspecs.find { |dep| dep.name == spec.name } + # @todo is next line necessary? + dep ||= Gem::Specification.find_by_name(spec.name, spec.requirement) + deps.merge fetch_dependencies(dep) if deps.add?(dep) + rescue Gem::MissingSpecError + Solargraph.logger.warn "Gem dependency #{spec.name} #{spec.requirement} for #{gemspec.name} not found in RubyGems." + end.to_a + end + + # @param gemspec [Gem::Specification] + # @return [Array] + def only_runtime_dependencies gemspec + gemspec.dependencies - gemspec.development_dependencies + end + # Merge the source. A merge will update the existing source for the file # or add it to the sources if the workspace is configured to include it. # The source is ignored if the configuration excludes it. @@ -134,12 +240,28 @@ def rbs_collection_path end def rbs_collection_config_path - @rbs_collection_config_path ||= begin - unless directory.empty? || directory == '*' - yaml_file = File.join(directory, 'rbs_collection.yaml') - yaml_file if File.file?(yaml_file) + @rbs_collection_config_path ||= + begin + unless directory.empty? || directory == '*' + yaml_file = File.join(directory, 'rbs_collection.yaml') + yaml_file if File.file?(yaml_file) + end + end + end + + # @param out [IO, nil] output stream for logging + # @param rebuild [Boolean] whether to rebuild the pins even if they are cached + # @return [void] + def cache_all_for_workspace!(out, rebuild: false) + PinCache.cache_core(out: $stdout) unless PinCache.has_core? + # @type [Array] + specs = gemspecs_required_from_bundler + specs.each do |spec| + unless pin_cache.cached?(spec) + pin_cache.cache_gem(gemspec: spec, rebuild: rebuild, out: out) end end + out.puts "Documentation cached for all #{specs.length} gems." end # Synchronize the workspace from the provided updater. @@ -155,6 +277,8 @@ def command_path server['commandPath'] || 'solargraph' end + private + # True if the workspace has a root Gemfile. # # @todo Handle projects with custom Bundler/Gemfile setups (see DocMap#gemspecs_required_from_bundler) @@ -163,7 +287,87 @@ def gemfile? directory && File.file?(File.join(directory, 'Gemfile')) end - private + # @return [Array] + def gemspecs_required_from_bundler + @gemspecs_required_from_bundler ||= + begin + if directory && Bundler.definition&.lockfile&.to_s&.start_with?(directory) + # Find only the gems bundler is now using + Bundler.definition.locked_gems.specs.flat_map do |lazy_spec| + logger.info "Handling #{lazy_spec.name}:#{lazy_spec.version}" + [Gem::Specification.find_by_name(lazy_spec.name, lazy_spec.version)] + rescue Gem::MissingSpecError => e + logger.info("Could not find #{lazy_spec.name}:#{lazy_spec.version} with find_by_name, falling back to guess") + # can happen in local filesystem references + specs = resolve_path_to_gemspecs lazy_spec.name + logger.warn "Gem #{lazy_spec.name} #{lazy_spec.version} from bundle not found: #{e}" if specs.nil? + next specs + end.compact + else + logger.info 'Fetching gemspecs required from Bundler (bundler/require)' + gemspecs_required_from_external_bundle + end + end + end + + # @return [Array] + def gemspecs_required_from_external_bundle + return [] unless directory + + @gemspecs_required_from_external_bundle ||= + begin + logger.info 'Fetching gemspecs required from external bundle' + + Solargraph.with_clean_env do + cmd = [ + 'ruby', '-e', + "require 'bundler'; require 'json'; Dir.chdir('#{directory}') { puts Bundler.definition.locked_gems.specs.map { |spec| [spec.name, spec.version] }.to_h.to_json }" + ] + o, e, s = Open3.capture3(*cmd) + if s.success? + Solargraph.logger.debug "External bundle: #{o}" + hash = o && !o.empty? ? JSON.parse(o.split("\n").last) : {} + hash.flat_map do |name, version| + Gem::Specification.find_by_name(name, version) + rescue Gem::MissingSpecError => e + logger.info("Could not find #{name}:#{version} with find_by_name, falling back to guess") + # can happen in local filesystem references + specs = Gem::Specification.find_by_path(name) + specs ||= Gem::Specification.find_by_name(name) + logger.warn "Gem #{name} #{version} from bundle not found: #{e}" if specs.nil? + next specs + end.compact + else + Solargraph.logger.warn e + raise BundleNotFoundError, "Failed to load gems from bundle at #{directory}" + end + end + end + end + + # @return [Hash{String => Gem::Specification}] + def preference_map + @preference_map ||= preferences.to_h { |gemspec| [gemspec.name, gemspec] } + end + + # @param gemspec [Gem::Specification] + # @return [Gem::Specification] + def gemspec_or_preference gemspec + return gemspec unless preference_map.key?(gemspec.name) + return gemspec if gemspec.version == preference_map[gemspec.name].version + + change_gemspec_version gemspec, preference_map[by_path.name].version + end + + # @param gemspec [Gem::Specification] + # @param version [Gem::Version] + # @return [Gem::Specification] + def change_gemspec_version gemspec, version + Gem::Specification.find_by_name(gemspec.name, "= #{version}") + rescue Gem::MissingSpecError + Solargraph.logger.info "Gem #{gemspec.name} version #{version} not found. Using #{gemspec.version} instead" + gemspec + end # The language server configuration (or an empty hash if the workspace was # not initialized from a server). diff --git a/lib/solargraph/yardoc.rb b/lib/solargraph/yardoc.rb index 59ce428a8..95b427f47 100644 --- a/lib/solargraph/yardoc.rb +++ b/lib/solargraph/yardoc.rb @@ -6,27 +6,41 @@ module Solargraph module Yardoc module_function - # Build and cache a gem's yardoc and return the path. If the cache already - # exists, do nothing and return the path. + # Build and save a gem's yardoc into a given path. # # @param gemspec [Gem::Specification] - # @return [String] The path to the cached yardoc. - def cache(gemspec) - path = PinCache.yardoc_path gemspec - return path if cached?(gemspec) + # + # @return [void] + def build_docs(yardoc_path, yard_plugins, gemspec) + return if docs_built?(yardoc_path, gemspec) - Solargraph.logger.info "Caching yardoc for #{gemspec.name} #{gemspec.version}" - Dir.chdir gemspec.gem_dir do - `yardoc --db #{path} --no-output --plugin solargraph` + Solargraph.logger.info "Saving yardoc for #{gemspec.name} #{gemspec.version} into #{yardoc_path}" + cmd = "yardoc --db #{yardoc_path} --no-output --plugin solargraph" + yard_plugins.each { |plugin| cmd << " --plugin #{plugin}" } + Solargraph.logger.debug { "Running: #{cmd}" } + # @todo set these up to run in parallel + # + # @sg-ignore RBS gem doesn't reflect that Open3.* also include + # kwopts from Process.spawn() + stdout_and_stderr_str, status = Open3.capture2e(cmd, chdir: gemspec.gem_dir) + unless status.success? + Solargraph.logger.warn { "YARD failed running #{cmd.inspect} in #{gemspec.gem_dir}" } + Solargraph.logger.info stdout_and_stderr_str end - path + end + + # @param gemspec [Gem::Specification] + # @return [Array] + def build_pins(yardoc_path, gemspec, out) + yardoc = load!(yardoc_path, gemspec) + YardMap::Mapper.new(yardoc, gemspec).map end # True if the gem yardoc is cached. # # @param gemspec [Gem::Specification] - def cached?(gemspec) - yardoc = File.join(PinCache.yardoc_path(gemspec), 'complete') + def docs_built?(yardoc_path, gemspec) + yardoc = File.join(yardoc_path, 'complete') File.exist?(yardoc) end @@ -43,8 +57,8 @@ def processing?(gemspec) # # @param gemspec [Gem::Specification] # @return [Array] - def load!(gemspec) - YARD::Registry.load! PinCache.yardoc_path gemspec + def load!(yardoc_path, gemspec) + YARD::Registry.load! yardoc_path YARD::Registry.all end end diff --git a/spec/doc_map_spec.rb b/spec/doc_map_spec.rb index cc53ef383..cde804cfc 100644 --- a/spec/doc_map_spec.rb +++ b/spec/doc_map_spec.rb @@ -1,61 +1,89 @@ # frozen_string_literal: true describe Solargraph::DocMap do - before :all do - # We use ast here because it's a known dependency. - gemspec = Gem::Specification.find_by_name('ast') - yard_pins = Solargraph::GemPins.build_yard_pins(gemspec) - Solargraph::PinCache.serialize_yard_gem(gemspec, yard_pins) + let(:workspace) do + Solargraph::Workspace.new(Dir.pwd) end - it 'generates pins from gems' do - doc_map = Solargraph::DocMap.new(['ast'], []) - doc_map.cache_all!($stderr) - node_pin = doc_map.pins.find { |pin| pin.path == 'AST::Node' } - expect(node_pin).to be_a(Solargraph::Pin::Namespace) + subject(:doc_map) do + dm = Solargraph::DocMap.new(requires, workspace) + dm.cache_doc_map_gems!($stderr) + dm end - it 'tracks unresolved requires' do - doc_map = Solargraph::DocMap.new(['not_a_gem'], []) - expect(doc_map.unresolved_requires).to eq(['not_a_gem']) - end + let(:plain_doc_map) { Solargraph::DocMap.new([], workspace) } + + context 'with a require in solargraph test bundle' do + let(:requires) do + ['ast'] + end - it 'tracks uncached_gemspecs' do - gemspec = Gem::Specification.new do |spec| - spec.name = 'not_a_gem' - spec.version = '1.0.0' + it 'generates pins from gems' do + node_pin = doc_map.pins.find { |pin| pin.path == 'AST::Node' } + expect(node_pin).to be_a(Solargraph::Pin::Namespace) end - allow(Gem::Specification).to receive(:find_by_path).and_return(gemspec) - doc_map = Solargraph::DocMap.new(['not_a_gem'], [gemspec]) - expect(doc_map.uncached_yard_gemspecs).to eq([gemspec]) - expect(doc_map.uncached_rbs_collection_gemspecs).to eq([gemspec]) end - it 'imports all gems when bundler/require used' do - workspace = Solargraph::Workspace.new(Dir.pwd) - plain_doc_map = Solargraph::DocMap.new([], [], workspace) - doc_map_with_bundler_require = Solargraph::DocMap.new(['bundler/require'], [], workspace) + context 'with an invalid require' do + let(:requires) do + ['not_a_gem'] + end + + it 'tracks unresolved requires' do + expect(doc_map.unresolved_requires).to eq(['not_a_gem']) + end + + xit 'tracks uncached_gemspecs' do + gemspec = Gem::Specification.new do |spec| + spec.name = 'not_a_gem' + spec.version = '1.0.0' + end + allow(Gem::Specification).to receive(:find_by_path).and_return(gemspec) + doc_map = Solargraph::DocMap.new(['not_a_gem'], workspace) + expect(doc_map.uncached_gemspecs).to eq([gemspec]) + end + end - expect(doc_map_with_bundler_require.pins.length - plain_doc_map.pins.length).to be_positive + context 'with require as bundle/require' do + it 'imports all gems when bundler/require used' do + doc_map_with_bundler_require = Solargraph::DocMap.new(['bundler/require'], workspace) + expect(doc_map_with_bundler_require.pins.length - plain_doc_map.pins.length).to be_positive + end end - it 'does not warn for redundant requires' do - # Requiring 'set' is unnecessary because it's already included in core. It - # might make sense to log redundant requires, but a warning is overkill. - expect(Solargraph.logger).not_to receive(:warn).with(/path set/) - Solargraph::DocMap.new(['set'], []) + context 'with a require not needed by Ruby core' do + let(:requires) { ['set'] } + + it 'does not warn' do + # Requiring 'set' is unnecessary because it's already included in core. It + # might make sense to log redundant requires, but a warning is overkill. + expect(Solargraph.logger).not_to receive(:warn).with(/path set/) + doc_map + end end - it 'ignores nil requires' do - expect { Solargraph::DocMap.new([nil], []) }.not_to raise_error + context 'with a nil require' do + let(:requires) { [nil] } + + it 'does not raise error' do + expect { doc_map }.not_to raise_error + end end - it 'ignores empty requires' do - expect { Solargraph::DocMap.new([''], []) }.not_to raise_error + context 'with an empty require' do + let(:requires) { [''] } + + it 'does not raise error' do + expect { doc_map }.not_to raise_error + end end - it 'collects dependencies' do - doc_map = Solargraph::DocMap.new(['rspec'], []) - expect(doc_map.dependencies.map(&:name)).to include('rspec-core') + context 'with a require that has dependencies' do + let(:requires) { ['rspec'] } + let(:example_dependency) { 'rspec-core' } + + it 'collects dependencies' do + expect(doc_map.dependencies.map(&:name)).to include(example_dependency) + end end end diff --git a/spec/gem_pins_spec.rb b/spec/gem_pins_spec.rb index 94044c52a..8ec370b56 100644 --- a/spec/gem_pins_spec.rb +++ b/spec/gem_pins_spec.rb @@ -3,11 +3,11 @@ describe Solargraph::GemPins do it 'can merge YARD and RBS' do gemspec = Gem::Specification.find_by_name('rbs') - yard_pins = Solargraph::GemPins.build_yard_pins(gemspec) - rbs_map = Solargraph::RbsMap.from_gemspec(gemspec, nil, nil) - pins = Solargraph::GemPins.combine yard_pins, rbs_map.pins + workspace = Solargraph::Workspace.new(Dir.pwd) + doc_map = Solargraph::DocMap.new(['rbs'], workspace) + doc_map.cache_doc_map_gems!($stderr) - core_root = pins.find { |pin| pin.path == 'RBS::EnvironmentLoader#core_root' } + core_root = doc_map.pins.find { |pin| pin.path == 'RBS::EnvironmentLoader#core_root' } expect(core_root.return_type.to_s).to eq('Pathname, nil') expect(core_root.location.filename).to end_with('environment_loader.rb') end diff --git a/spec/type_checker/levels/normal_spec.rb b/spec/type_checker/levels/normal_spec.rb index 62970fbd9..0f9607a75 100644 --- a/spec/type_checker/levels/normal_spec.rb +++ b/spec/type_checker/levels/normal_spec.rb @@ -221,9 +221,9 @@ def bar; end # @todo This test uses kramdown-parser-gfm because it's a gem dependency known to # lack typed methods. A better test wouldn't depend on the state of # vendored code. + workspace = Solargraph::Workspace.new(Dir.pwd) gemspec = Gem::Specification.find_by_name('kramdown-parser-gfm') - yard_pins = Solargraph::GemPins.build_yard_pins(gemspec) - Solargraph::PinCache.serialize_yard_gem(gemspec, yard_pins) + workspace.cache_gem(gemspec) checker = type_checker(%( require 'kramdown-parser-gfm' diff --git a/spec/yard_map/mapper_spec.rb b/spec/yard_map/mapper_spec.rb index 75fce9684..acfd201b1 100644 --- a/spec/yard_map/mapper_spec.rb +++ b/spec/yard_map/mapper_spec.rb @@ -1,4 +1,16 @@ describe Solargraph::YardMap::Mapper do + before :all do + Solargraph::ApiMap.load_with_cache('.', $stderr) + end + + let(:workspace) { Solargraph::Workspace.new(Dir.pwd) } + + def pins_with(require) + doc_map = Solargraph::DocMap.new([require], workspace) + doc_map.cache_doc_map_gems!($stderr) + doc_map.pins + end + it 'converts nil docstrings to empty strings' do dir = File.absolute_path(File.join('spec', 'fixtures', 'yard_map')) Dir.chdir dir do @@ -14,50 +26,33 @@ it 'marks explicit methods' do # Using rspec-expectations because it's a known dependency - rspec = Gem::Specification.find_by_name('rspec-expectations') - Solargraph::Yardoc.cache(rspec) - Solargraph::Yardoc.load!(rspec) - pins = Solargraph::YardMap::Mapper.new(YARD::Registry.all).map - pin = pins.find { |pin| pin.path == 'RSpec::Matchers#be_truthy' } + pin = pins_with('rspec-expectations').find { |pin| pin.path == 'RSpec::Matchers#be_truthy' } + expect(pin).not_to be_nil expect(pin.explicit?).to be(true) end it 'marks correct return type from Logger.new' do # Using logger because it's a known dependency - logger = Gem::Specification.find_by_name('logger') - Solargraph::Yardoc.cache(logger) - registry = Solargraph::Yardoc.load!(logger) - pins = Solargraph::YardMap::Mapper.new(registry).map - pins = pins.select { |pin| pin.path == 'Logger.new' } + pins = pins_with('logger').select { |pin| pin.path == 'Logger.new' } expect(pins.map(&:return_type).uniq.map(&:to_s)).to eq(['self']) end it 'marks correct return type from RuboCop::Options.new' do # Using rubocop because it's a known dependency - rubocop = Gem::Specification.find_by_name('rubocop') - Solargraph::Yardoc.cache(rubocop) - Solargraph::Yardoc.load!(rubocop) - pins = Solargraph::YardMap::Mapper.new(YARD::Registry.all).map - pins = pins.select { |pin| pin.path == 'RuboCop::Options.new' } + pins = pins_with('rubocop').select { |pin| pin.path == 'RuboCop::Options.new' } expect(pins.map(&:return_type).uniq.map(&:to_s)).to eq(['self']) expect(pins.flat_map(&:signatures).map(&:return_type).uniq.map(&:to_s)).to eq(['self']) end it 'marks non-explicit methods' do # Using rspec-expectations because it's a known dependency - rspec = Gem::Specification.find_by_name('rspec-expectations') - Solargraph::Yardoc.load!(rspec) - pins = Solargraph::YardMap::Mapper.new(YARD::Registry.all).map - pin = pins.find { |pin| pin.path == 'RSpec::Matchers#expect' } + pin = pins_with('rspec-expectations').find { |pin| pin.path == 'RSpec::Matchers#expect' } expect(pin.explicit?).to be(false) end it 'adds superclass references' do # Asssuming the yard gem exists because it's a known dependency - gemspec = Gem::Specification.find_by_name('yard') - Solargraph::Yardoc.cache(gemspec) - pins = Solargraph::YardMap::Mapper.new(Solargraph::Yardoc.load!(gemspec)).map - pin = pins.find do |pin| + pin = pins_with('yard').find do |pin| pin.is_a?(Solargraph::Pin::Reference::Superclass) && pin.name == 'YARD::CodeObjects::NamespaceObject' end expect(pin.closure.path).to eq('YARD::CodeObjects::ClassObject') @@ -65,10 +60,7 @@ it 'adds include references' do # Asssuming the ast gem exists because it's a known dependency - gemspec = Gem::Specification.find_by_name('ast') - Solargraph::Yardoc.cache(gemspec) - pins = Solargraph::YardMap::Mapper.new(Solargraph::Yardoc.load!(gemspec)).map - inc= pins.find do |pin| + inc = pins_with('ast').find do |pin| pin.is_a?(Solargraph::Pin::Reference::Include) && pin.name == 'AST::Processor::Mixin' && pin.closure.path == 'AST::Processor' end expect(inc).to be_a(Solargraph::Pin::Reference::Include) @@ -76,10 +68,7 @@ it 'adds extend references' do # Asssuming the yard gem exists because it's a known dependency - gemspec = Gem::Specification.find_by_name('yard') - Solargraph::Yardoc.cache(gemspec) - pins = Solargraph::YardMap::Mapper.new(Solargraph::Yardoc.load!(gemspec)).map - ext = pins.find do |pin| + ext = pins_with('yard').find do |pin| pin.is_a?(Solargraph::Pin::Reference::Extend) && pin.name == 'Enumerable' && pin.closure.path == 'YARD::Registry' end expect(ext).to be_a(Solargraph::Pin::Reference::Extend) From ea9504c5caa3dcb12e8fd14875c72084b46752c7 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 15 Jul 2025 15:35:37 -0400 Subject: [PATCH 190/561] RuboCop fix --- spec/doc_map_spec.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/doc_map_spec.rb b/spec/doc_map_spec.rb index cde804cfc..e909e65b1 100644 --- a/spec/doc_map_spec.rb +++ b/spec/doc_map_spec.rb @@ -1,16 +1,16 @@ # frozen_string_literal: true describe Solargraph::DocMap do - let(:workspace) do - Solargraph::Workspace.new(Dir.pwd) - end - subject(:doc_map) do dm = Solargraph::DocMap.new(requires, workspace) dm.cache_doc_map_gems!($stderr) dm end + let(:workspace) do + Solargraph::Workspace.new(Dir.pwd) + end + let(:plain_doc_map) { Solargraph::DocMap.new([], workspace) } context 'with a require in solargraph test bundle' do From 2e4eccdefec6a70a501922b7237a7fbd7fca1bad Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 15 Jul 2025 16:07:49 -0400 Subject: [PATCH 191/561] Rubocop and Solargraph fixes --- lib/solargraph/api_map.rb | 3 +++ lib/solargraph/doc_map.rb | 14 ++++++++---- lib/solargraph/gem_pins.rb | 1 - lib/solargraph/library.rb | 7 +++++- lib/solargraph/pin_cache.rb | 24 ++++++++++++++++---- lib/solargraph/rbs_map.rb | 44 +++++++++++++++++++++++++++---------- lib/solargraph/workspace.rb | 17 +++++++++----- lib/solargraph/yardoc.rb | 18 ++++++++++----- 8 files changed, 94 insertions(+), 34 deletions(-) diff --git a/lib/solargraph/api_map.rb b/lib/solargraph/api_map.rb index 83848cb71..c668a624f 100755 --- a/lib/solargraph/api_map.rb +++ b/lib/solargraph/api_map.rb @@ -22,6 +22,9 @@ class ApiMap # @return [Array] attr_reader :missing_docs + # @return [Solargraph::PinCache] + attr_reader :pin_cache + # @param pins [Array] def initialize pins: [] @source_map_hash = {} diff --git a/lib/solargraph/doc_map.rb b/lib/solargraph/doc_map.rb index 4595fa67e..a79afac2e 100644 --- a/lib/solargraph/doc_map.rb +++ b/lib/solargraph/doc_map.rb @@ -18,8 +18,6 @@ class DocMap # @return [Array] attr_reader :pins - attr_reader :pin_cache - attr_reader :global_environ # @return [Array] @@ -40,6 +38,7 @@ def initialize(requires, workspace) pins.concat global_environ.pins end + # @return [Solargraph::PinCache] def pin_cache @pin_cache ||= workspace.fresh_pincache end @@ -73,7 +72,9 @@ def cache_doc_map_gems!(out) end pins_processed = pins.length - existing_pin_count milliseconds = (time.real * 1000).round - out.puts "Built #{pins.length} gem pins in #{milliseconds} ms" if out && gemspecs.any? if milliseconds > 500 + if (milliseconds > 500) && uncached_gemspecs.any? && out && uncached_gemspecs.any? + out.puts "Built #{pins.length} gem pins in #{milliseconds} ms" + end load_serialized_gem_pins end @@ -110,12 +111,15 @@ def cache(gemspec, rebuild: false, only_if_used: false, out: nil) out: out) end + # @param out [IO, nil] # @return [void] def load_serialized_gem_pins(out: $stderr) @pins = [] with_gemspecs, without_gemspecs = required_gems_map.partition { |_, v| v } + # @sg-ignore Need support for RBS duck interfaces like _ToHash # @type [Array] missing_paths = Hash[without_gemspecs].keys + # @sg-ignore Need support for RBS duck interfaces like _ToHash # @type [Array] gemspecs = Hash[with_gemspecs].values.flatten.compact + dependencies.to_a @@ -142,7 +146,9 @@ def load_serialized_gem_pins(out: $stderr) end pins_processed = pins.length - existing_pin_count milliseconds = (time.real * 1000).round - out.puts "Deserialized #{pins.length} gem pins from #{PinCache.base_dir} in #{milliseconds} ms" if out && gemspecs.any? if milliseconds > 500 + if (milliseconds > 500) && out && gemspecs.any? + out.puts "Deserialized #{pins.length} gem pins from #{PinCache.base_dir} in #{milliseconds} ms" + end uncached_gemspecs.uniq! { |gemspec| "#{gemspec.name}:#{gemspec.version}" } nil end diff --git a/lib/solargraph/gem_pins.rb b/lib/solargraph/gem_pins.rb index 15e696571..486a9aeea 100644 --- a/lib/solargraph/gem_pins.rb +++ b/lib/solargraph/gem_pins.rb @@ -13,7 +13,6 @@ class << self # @param pins [Array] def self.combine_method_pins_by_path(pins) - # bad_pins = pins.select { |pin| pin.is_a?(Pin::Method) && pin.path == 'StringIO.open' && pin.source == :rbs }; raise "wtf: #{bad_pins}" if bad_pins.length > 1 method_pins, alias_pins = pins.partition { |pin| pin.class == Pin::Method } by_path = method_pins.group_by(&:path) by_path.transform_values! do |pins| diff --git a/lib/solargraph/library.rb b/lib/solargraph/library.rb index 75f3a7fe0..4ea1ddaab 100644 --- a/lib/solargraph/library.rb +++ b/lib/solargraph/library.rb @@ -511,6 +511,11 @@ def external_requires private + # @return [PinCache] + def pin_cache + api_map.pin_cache + end + # @return [Hash{String => Set}] def source_map_external_require_hash @source_map_external_require_hash ||= {} @@ -595,7 +600,7 @@ def cache_next_gemspec pending = api_map.uncached_gemspecs.length - cache_errors.length - 1 - if Yardoc.processing?(spec) + if pin_cache.yardoc_processing?(spec) logger.info "Enqueuing cache of #{spec.name} #{spec.version} (already being processed)" queued_gemspec_cache.push(spec) return if pending - queued_gemspec_cache.length < 1 diff --git a/lib/solargraph/pin_cache.rb b/lib/solargraph/pin_cache.rb index 4b7276f19..600998efc 100644 --- a/lib/solargraph/pin_cache.rb +++ b/lib/solargraph/pin_cache.rb @@ -66,9 +66,11 @@ def cache_gem(gemspec:, rebuild: false, out: nil) cache_combined_pins(gemspec, rbs_version_cache_key, yard_pins, rbs_collection_pins) if build_combined end + # @param gemspec [Gem::Specification, Bundler::LazySpecification] + # @param rbs_version_cache_key [String] def suppress_yard_cache?(gemspec, rbs_version_cache_key) - # TODO test this - saw: Caching YARD and RBS collection and combined pins for gem parser:3.3.8.0 - if gemspec == 'parser' && rbs_version_cache_key != CACHE_KEY_UNRESOLVED + # TODO: test this - saw: Caching YARD and RBS collection and combined pins for gem parser:3.3.8.0 + if gemspec == 'parser' && rbs_version_cache_key != RbsMap::CACHE_KEY_UNRESOLVED # parser takes forever to build YARD pins, but has excellent RBS collection pins return true end @@ -93,6 +95,7 @@ def cache_stdlib_rbs_map path end # @param gemspec [Gem::Specification, Bundler::LazySpecification] + # # @return [String] def lookup_rbs_version_cache_key(gemspec) rbs_map = RbsMap.from_gemspec(gemspec, rbs_collection_path, rbs_collection_config_path) @@ -110,7 +113,7 @@ def cache_combined_pins(gemspec, rbs_version_cache_key, yard_pins, rbs_collectio end # @param gemspec [Gem::Specification] - # @return [void] + # @return [Array] def deserialize_combined_pin_cache(gemspec) unless combined_pins_in_memory[[gemspec.name, gemspec.version]].nil? return combined_pins_in_memory[[gemspec.name, gemspec.version]] @@ -122,7 +125,6 @@ def deserialize_combined_pin_cache(gemspec) if cached logger.info { "Loaded #{cached.length} cached YARD pins from #{gemspec.name}:#{gemspec.version}" } combined_pins_in_memory[[gemspec.name, gemspec.version]] = cached - return combined_pins_in_memory[[gemspec.name, gemspec.version]] end end @@ -136,6 +138,11 @@ def uncache_gem(gemspec, out: nil) uncache_by_prefix(combined_path_prefix(gemspec), out: out) end + # @param gemspec [Gem::Specification, Bundler::LazySpecification] + def yardoc_processing?(gemspec) + Yardoc.processing?(yardoc_path(gemspec)) + end + private # @param gemspec [Gem::Specification, Bundler::LazySpecification] @@ -447,6 +454,8 @@ def has_core? File.file?(core_path) end + # @param out [IO, nil] + # @return [Array] def cache_core(out: $stderr) RbsMap::CoreMap.new.cache_core(out: out) end @@ -457,15 +466,19 @@ def load_core end # @param pins [Array] + # @param out [IO, nil] # @return [void] def serialize_core pins, out: $stderr save(core_path, pins) end + # @return [String] def stdlib_path File.join(work_dir, 'stdlib') end + # @param require [String] + # @return [String] def stdlib_require_path require File.join(stdlib_path, "#{require}.ser") end @@ -476,6 +489,9 @@ def load_stdlib_require require load(stdlib_require_path(require)) end + # @param require [String] + # @param pins [Array] + # @return [void] def serialize_stdlib_require require, pins save(stdlib_require_path(require), pins) end diff --git a/lib/solargraph/rbs_map.rb b/lib/solargraph/rbs_map.rb index 4b2ce50bb..a6e41cfef 100644 --- a/lib/solargraph/rbs_map.rb +++ b/lib/solargraph/rbs_map.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true +require 'digest' require 'pathname' require 'rbs' @@ -23,7 +24,9 @@ class RbsMap # @param library [String] # @param version [String, nil] - # @param rbs_collection_paths [Array] + # @param rbs_collection_config_path [String, nil] + # @param rbs_collection_paths [Array] + # @param out [IO, nil] where to log messages def initialize library, version = nil, rbs_collection_config_path: nil, rbs_collection_paths: [], out: $stderr if rbs_collection_config_path.nil? && !rbs_collection_paths.empty? raise 'Please provide rbs_collection_config_path if you provide rbs_collection_paths' @@ -35,33 +38,37 @@ def initialize library, version = nil, rbs_collection_config_path: nil, rbs_coll add_library loader, library, version end - CACHE_KEY_GEM_EXPORT = 'gem-export' CACHE_KEY_UNRESOLVED = 'unresolved' CACHE_KEY_STDLIB = 'stdlib' CACHE_KEY_LOCAL = 'local' + # @param cache_key [String] + # @return [String, nil] a description of the source of the RBS info def self.rbs_source_desc(cache_key) - if cache_key == CACHE_KEY_GEM_EXPORT + case cache_key + when CACHE_KEY_GEM_EXPORT "RBS gem export" - elsif cache_key == CACHE_KEY_UNRESOLVED + when CACHE_KEY_UNRESOLVED nil - elsif cache_key == CACHE_KEY_STDLIB + when CACHE_KEY_STDLIB "RBS standard library" - elsif cache_key == CACHE_KEY_LOCAL + when CACHE_KEY_LOCAL "local RBS shims" else "RBS collection" end end - # @return string representing the version of the RBS info fetched + # @return [String] representing the version of the RBS info fetched # for the given library. Must change when the RBS info is # updated upstream for the same library and version. May change # if the config for where information comes form changes. + # @sg-ignore Solargraph::RbsMap#cache_key return type could not be inferred def cache_key @hextdigest ||= begin data = nil + # @type gem_config [nil, Hash{String => Hash{String => String}}] gem_config = nil if rbs_collection_config_path lockfile_path = RBS::Collection::Config.to_lockfile_path(Pathname.new(rbs_collection_config_path)) @@ -74,12 +81,14 @@ def cache_key if gem_config.nil? CACHE_KEY_UNRESOLVED else - source = gem_config.fetch('source', {}).fetch('type', nil) - if source == 'rubygems' + # @type [String] + source = gem_config.dig('source', 'type') + case source + when 'rubygems' CACHE_KEY_GEM_EXPORT - elsif source == 'local' + when 'local' CACHE_KEY_LOCAL - elsif source == 'stdlib' + when 'stdlib' CACHE_KEY_STDLIB else Digest::SHA1.hexdigest(data) @@ -88,6 +97,10 @@ def cache_key end end + # @param gemspec [Gem::Specification, Bundler::LazySpecification] + # @param rbs_collection_path [String, nil] + # @param rbs_collection_config_path [String, nil] + # @return [RbsMap] def self.from_gemspec gemspec, rbs_collection_path, rbs_collection_config_path rbs_map = RbsMap.new(gemspec.name, gemspec.version, rbs_collection_paths: [rbs_collection_path].compact, @@ -100,6 +113,7 @@ def self.from_gemspec gemspec, rbs_collection_path, rbs_collection_config_path rbs_collection_config_path: rbs_collection_config_path) end + # @param out [IO, nil] where to log messages # @return [Array] def pins(out: $stderr) @pins ||= if resolved? @@ -129,6 +143,7 @@ def resolved? @resolved end + # @return [RBS::Repository] def repository @repository ||= RBS::Repository.new(no_stdlib: false).tap do |repo| @rbs_collection_paths.each do |dir| @@ -146,22 +161,27 @@ def self.load library private + # @return [RBS::EnvironmentLoader] def loader @loader ||= RBS::EnvironmentLoader.new(core_root: nil, repository: repository) end + # @return [Conversions] def conversions @conversions ||= Conversions.new(loader: loader) end + # @param gemspec [RBS::EnvironmentLoader::Library] + # @param out [IO, nil] where to log messages # @return [void] def log_caching(gemspec, out:); end # @param loader [RBS::EnvironmentLoader] # @param library [String] + # @param version [String, nil] the version of the library to load, or nil for any + # @param out [IO, nil] where to log messages # @return [Boolean] true if adding the library succeeded def add_library loader, library, version, out: $stderr - raise "wtf - why was library not a string? #{library}" unless library.is_a?(String) @resolved = if loader.has_library?(library: library, version: version) # we find our own dependencies from gemfile.lock loader.add library: library, version: version, resolve_dependencies: false diff --git a/lib/solargraph/workspace.rb b/lib/solargraph/workspace.rb index dccc65b52..ed4012a4b 100644 --- a/lib/solargraph/workspace.rb +++ b/lib/solargraph/workspace.rb @@ -2,6 +2,7 @@ require 'open3' require 'json' +require 'yaml' module Solargraph # A workspace consists of the files in a project's directory and the @@ -49,10 +50,7 @@ def config # @return [Solargraph::PinCache] def pin_cache - @pin_cache ||= Solargraph::PinCache.new(rbs_collection_path: rbs_collection_path, - rbs_collection_config_path: rbs_collection_config_path, - yard_plugins: yard_plugins, - directory: directory) + @pin_cache ||= fresh_pincache end # @return [Environ] @@ -79,6 +77,7 @@ def uncache_gem(gemspec, out: nil) pin_cache.uncache_gem(gemspec, out: out) end + # @return [Solargraph::PinCache] def fresh_pincache PinCache.new(rbs_collection_path: rbs_collection_path, rbs_collection_config_path: rbs_collection_config_path, @@ -95,7 +94,7 @@ def yard_plugins # @return [::Array, nil] def resolve_path_to_gemspecs path return nil if path.empty? - # TODO there should be a distinction between everything and the non-require: false stuff + # TODO: there should be a distinction between everything and the non-require: false stuff return gemspecs_required_from_bundler if path == 'bundler/require' gemspecs = gemspecs_required_from_bundler @@ -113,6 +112,7 @@ def resolve_path_to_gemspecs path return nil if potential_gemspec.nil? file = "lib/#{path}.rb" + # @sg-ignore Unresolved call to files gemspec = potential_gemspec if potential_gemspec&.files&.any? { |gemspec_file| file == gemspec_file } rescue Gem::MissingSpecError logger.debug { "Require path #{path} could not be resolved to a gem via find_by_path or guess of #{gem_name_guess}" } @@ -239,6 +239,8 @@ def rbs_collection_path @gem_rbs_collection ||= read_rbs_collection_path end + # @return [String, nil] + # @sg-ignore Solargraph::Workspace#rbs_collection_config_path return type could not be inferred def rbs_collection_config_path @rbs_collection_config_path ||= begin @@ -291,7 +293,7 @@ def gemfile? def gemspecs_required_from_bundler @gemspecs_required_from_bundler ||= begin - if directory && Bundler.definition&.lockfile&.to_s&.start_with?(directory) + if directory && Bundler.definition&.lockfile&.to_s&.start_with?(directory) # rubocop:disable Style/SafeNavigationChainLength # Find only the gems bundler is now using Bundler.definition.locked_gems.specs.flat_map do |lazy_spec| logger.info "Handling #{lazy_spec.name}:#{lazy_spec.version}" @@ -323,6 +325,7 @@ def gemspecs_required_from_external_bundle 'ruby', '-e', "require 'bundler'; require 'json'; Dir.chdir('#{directory}') { puts Bundler.definition.locked_gems.specs.map { |spec| [spec.name, spec.version] }.to_h.to_json }" ] + # @sg-ignore Unresolved call to capture3 o, e, s = Open3.capture3(*cmd) if s.success? Solargraph.logger.debug "External bundle: #{o}" @@ -409,6 +412,7 @@ def generate_require_paths # workspace code, but this is how Gem::Specification.load does it # anyway. cmd = ['ruby', '-e', "require 'rubygems'; require 'json'; spec = eval(File.read('#{file}'), TOPLEVEL_BINDING, '#{file}'); return unless Gem::Specification === spec; puts({name: spec.name, paths: spec.require_paths}.to_json)"] + # @sg-ignore Unresolved call to capture3 o, e, s = Open3.capture3(*cmd) if s.success? begin @@ -453,6 +457,7 @@ def require_plugins def read_rbs_collection_path return unless rbs_collection_config_path + # @sg-ignore Unresolved call to load_file path = YAML.load_file(rbs_collection_config_path)&.fetch('path') # make fully qualified File.expand_path(path, directory) diff --git a/lib/solargraph/yardoc.rb b/lib/solargraph/yardoc.rb index 95b427f47..7609019e8 100644 --- a/lib/solargraph/yardoc.rb +++ b/lib/solargraph/yardoc.rb @@ -8,6 +8,8 @@ module Yardoc # Build and save a gem's yardoc into a given path. # + # @param yardoc_path [String] + # @param yard_plugins [Array] # @param gemspec [Gem::Specification] # # @return [void] @@ -23,13 +25,14 @@ def build_docs(yardoc_path, yard_plugins, gemspec) # @sg-ignore RBS gem doesn't reflect that Open3.* also include # kwopts from Process.spawn() stdout_and_stderr_str, status = Open3.capture2e(cmd, chdir: gemspec.gem_dir) - unless status.success? - Solargraph.logger.warn { "YARD failed running #{cmd.inspect} in #{gemspec.gem_dir}" } - Solargraph.logger.info stdout_and_stderr_str - end + return if status.success? + Solargraph.logger.warn { "YARD failed running #{cmd.inspect} in #{gemspec.gem_dir}" } + Solargraph.logger.info stdout_and_stderr_str end + # @param yardoc_path [String] the path to the yardoc cache # @param gemspec [Gem::Specification] + # @param out [IO, nil] where to log messages # @return [Array] def build_pins(yardoc_path, gemspec, out) yardoc = load!(yardoc_path, gemspec) @@ -38,6 +41,7 @@ def build_pins(yardoc_path, gemspec, out) # True if the gem yardoc is cached. # + # @param yardoc_path [String] # @param gemspec [Gem::Specification] def docs_built?(yardoc_path, gemspec) yardoc = File.join(yardoc_path, 'complete') @@ -46,8 +50,9 @@ def docs_built?(yardoc_path, gemspec) # True if another process is currently building the yardoc cache. # - def processing?(gemspec) - yardoc = File.join(PinCache.yardoc_path(gemspec), 'processing') + # @param yardoc_path [String] + def processing?(yardoc_path) + yardoc = File.join(yardoc_path, 'processing') File.exist?(yardoc) end @@ -55,6 +60,7 @@ def processing?(gemspec) # # @note This method modifies the global YARD registry. # + # @param yardoc_path [String] # @param gemspec [Gem::Specification] # @return [Array] def load!(yardoc_path, gemspec) From 949e27530bc494be86b7aa5c229fbf81b6d955ef Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 15 Jul 2025 16:31:40 -0400 Subject: [PATCH 192/561] RuboCop/Solargraph fixes --- lib/solargraph/api_map.rb | 22 +++++++++++------ lib/solargraph/doc_map.rb | 2 +- lib/solargraph/environ.rb | 1 + lib/solargraph/gem_pins.rb | 4 +++- lib/solargraph/library.rb | 11 +++++++++ lib/solargraph/rbs_map/core_map.rb | 35 +++++++++++++++------------- lib/solargraph/rbs_map/stdlib_map.rb | 1 + lib/solargraph/shell.rb | 12 +++++----- spec/yard_map/mapper_spec.rb | 2 +- 9 files changed, 58 insertions(+), 32 deletions(-) diff --git a/lib/solargraph/api_map.rb b/lib/solargraph/api_map.rb index c668a624f..9894cd19b 100755 --- a/lib/solargraph/api_map.rb +++ b/lib/solargraph/api_map.rb @@ -39,15 +39,18 @@ def initialize pins: [] # just caches), please also change `equality_fields` below. # + # @param other [Object] def eql?(other) self.class == other.class && equality_fields == other.equality_fields end + # @param other [Object] def ==(other) self.eql?(other) end + # @return [Integer] def hash equality_fields.hash end @@ -117,9 +120,8 @@ def catalog bench [self.class, @source_map_hash, implicit, @doc_map, @unresolved_requires] end - def doc_map - @doc_map ||= DocMap.new([], []) - end + # @return [DocMap, nil] + attr_reader :doc_map # @return [::Array] def uncached_gemspecs @@ -183,7 +185,7 @@ def self.load directory # @param out [IO, nil] # @return [void] def cache_all_for_doc_map!(out) - @doc_map.cache_doc_map_gems!(out) + @doc_map&.cache_doc_map_gems!(out) end # @param out [IO, nil] @@ -195,7 +197,7 @@ def cache_all_for_workspace!(out, rebuild: false) # @return [Workspace] def workspace - @doc_map.workspace + @doc_map&.workspace end # @param gemspec [Gem::Specification] @@ -204,7 +206,7 @@ def workspace # @param out [IO, nil] # @return [void] def cache_gem(gemspec, rebuild: false, only_if_used: false, out: nil) - @doc_map.cache(gemspec, rebuild: rebuild, out: out, only_if_used: only_if_used) + @doc_map&.cache(gemspec, rebuild: rebuild, out: out, only_if_used: only_if_used) end class << self @@ -366,6 +368,9 @@ def get_instance_variable_pins(namespace, scope = :instance) end # @see Solargraph::Parser::FlowSensitiveTyping#visible_pins + # @param (see Solargraph::Parser::FlowSensitiveTyping#visible_pins) + # @return (see Solargraph::Parser::FlowSensitiveTyping#visible_pins) + # @sg-ignore Missing @return tag for Solargraph::ApiMap#visible_pins def visible_pins(*args, **kwargs, &blk) Solargraph::Parser::FlowSensitiveTyping.visible_pins(*args, **kwargs, &blk) end @@ -527,6 +532,9 @@ def get_complex_type_methods complex_type, context = '', internal = false # @param rooted_tag [String] Parameterized namespace, fully qualified # @param name [String] Method name to look up # @param scope [Symbol] :instance or :class + # @param visibility [Array] :public, :protected, and/or :private + # @param preserve_generics [Boolean] True to preserve any + # unresolved generic parameters, false to erase them # @return [Array] def get_method_stack rooted_tag, name, scope: :instance, visibility: [:private, :protected, :public], preserve_generics: false rooted_type = ComplexType.parse(rooted_tag) @@ -824,7 +832,7 @@ def qualify_lower namespace, context qualify namespace, context.split('::')[0..-2].join('::') end - # @param fq_tag [String] + # @param fq_sub_tag [String] # @return [String, nil] def qualify_superclass fq_sub_tag fq_sub_type = ComplexType.try_parse(fq_sub_tag) diff --git a/lib/solargraph/doc_map.rb b/lib/solargraph/doc_map.rb index a79afac2e..97302bb17 100644 --- a/lib/solargraph/doc_map.rb +++ b/lib/solargraph/doc_map.rb @@ -29,7 +29,7 @@ def uncached_gemspecs attr_reader :workspace # @param requires [Array] - # @param workspace [Workspace] + # @param workspace [Workspace, nil] def initialize(requires, workspace) @requires = requires.compact @workspace = workspace diff --git a/lib/solargraph/environ.rb b/lib/solargraph/environ.rb index 392a24949..86e5a8f0f 100644 --- a/lib/solargraph/environ.rb +++ b/lib/solargraph/environ.rb @@ -25,6 +25,7 @@ def initialize requires: [], domains: [], pins: [] @pins = pins end + # @return [Array] def yard_plugins @yard_plugins ||= [] end diff --git a/lib/solargraph/gem_pins.rb b/lib/solargraph/gem_pins.rb index 486a9aeea..9ef123719 100644 --- a/lib/solargraph/gem_pins.rb +++ b/lib/solargraph/gem_pins.rb @@ -12,6 +12,7 @@ class << self end # @param pins [Array] + # @return [Array] def self.combine_method_pins_by_path(pins) method_pins, alias_pins = pins.partition { |pin| pin.class == Pin::Method } by_path = method_pins.group_by(&:path) @@ -21,6 +22,7 @@ def self.combine_method_pins_by_path(pins) by_path.values + alias_pins end + # @return [Pin::Method, nil] def self.combine_method_pins(*pins) out = pins.reduce(nil) do |memo, pin| next pin if memo.nil? @@ -37,7 +39,7 @@ def self.combine_method_pins(*pins) end # @param yard_pins [Array] - # @param rbs_map [RbsMap] + # @param rbs_pins [Array] # @return [Array] def self.combine(yard_pins, rbs_pins) in_yard = Set.new diff --git a/lib/solargraph/library.rb b/lib/solargraph/library.rb index 4ea1ddaab..b194725d7 100644 --- a/lib/solargraph/library.rb +++ b/lib/solargraph/library.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true +require 'rubygems' require 'pathname' require 'observer' require 'open3' @@ -595,12 +596,14 @@ def cache_errors def cache_next_gemspec return if @cache_progress + # @type [Gem::Specification] spec = cacheable_specs.first return end_cache_progress unless spec pending = api_map.uncached_gemspecs.length - cache_errors.length - 1 if pin_cache.yardoc_processing?(spec) + # @sg-ignore Unresolved call to name logger.info "Enqueuing cache of #{spec.name} #{spec.version} (already being processed)" queued_gemspec_cache.push(spec) return if pending - queued_gemspec_cache.length < 1 @@ -608,14 +611,19 @@ def cache_next_gemspec catalog sync_catalog else + # @sg-ignore Unresolved call to name logger.info "Caching #{spec.name} #{spec.version}" Thread.new do + # @sg-ignore Unresolved call to name report_cache_progress spec.name, pending + # @sg-ignore Unresolved call to capture3 _o, e, s = Open3.capture3(workspace.command_path, 'cache', spec.name, spec.version.to_s) if s.success? + # @sg-ignore Unresolved call to name logger.info "Cached #{spec.name} #{spec.version}" else cache_errors.add spec + # @sg-ignore Unresolved call to name logger.warn "Error caching gemspec #{spec.name} #{spec.version}" logger.warn e end @@ -626,6 +634,7 @@ def cache_next_gemspec end end + # @return [Array] def cacheable_specs cacheable = api_map.uncached_gemspecs + queued_gemspec_cache - @@ -635,6 +644,7 @@ def cacheable_specs queued_gemspec_cache end + # @return [Array] def queued_gemspec_cache @queued_gemspec_cache ||= [] end @@ -676,6 +686,7 @@ def end_cache_progress @total = nil end + # @return [void] def sync_catalog return if @sync_count == 0 diff --git a/lib/solargraph/rbs_map/core_map.rb b/lib/solargraph/rbs_map/core_map.rb index ade16d932..44df866e4 100644 --- a/lib/solargraph/rbs_map/core_map.rb +++ b/lib/solargraph/rbs_map/core_map.rb @@ -14,44 +14,47 @@ def resolved? def initialize; end + # @param out [IO, nil] output stream for logging # @return [Array] def pins(out: $stderr) return @pins if @pins @pins = cache_core(out: out) end + # @param out [IO, nil] output stream for logging + # @return [Array] def cache_core(out: $stderr) new_pins = [] cache = PinCache.load_core - if cache - return cache - else - new_pins.concat conversions.pins + return cache if cache - # Avoid RBS::DuplicatedDeclarationError by loading in a different EnvironmentLoader - fill_loader = RBS::EnvironmentLoader.new(core_root: nil, repository: RBS::Repository.new(no_stdlib: false)) - fill_loader.add(path: Pathname(FILLS_DIRECTORY)) - out.puts "Caching RBS pins for Ruby core" if out - fill_conversions = Conversions.new(loader: fill_loader) - new_pins.concat fill_conversions.pins + new_pins.concat conversions.pins - new_pins.concat RbsMap::CoreFills::ALL + # Avoid RBS::DuplicatedDeclarationError by loading in a different EnvironmentLoader + fill_loader = RBS::EnvironmentLoader.new(core_root: nil, repository: RBS::Repository.new(no_stdlib: false)) + fill_loader.add(path: Pathname(FILLS_DIRECTORY)) + out&.puts "Caching RBS pins for Ruby core" + fill_conversions = Conversions.new(loader: fill_loader) + new_pins.concat fill_conversions.pins - processed = ApiMap::Store.new(new_pins).pins.reject { |p| p.is_a?(Solargraph::Pin::Reference::Override) } - new_pins.replace processed + new_pins.concat RbsMap::CoreFills::ALL - PinCache.serialize_core new_pins - end - new_pins + processed = ApiMap::Store.new(new_pins).pins.reject { |p| p.is_a?(Solargraph::Pin::Reference::Override) } + new_pins.replace processed + PinCache.serialize_core new_pins + + new_pins end private + # @return [RBS::EnvironmentLoader] def loader @loader ||= RBS::EnvironmentLoader.new(repository: RBS::Repository.new(no_stdlib: false)) end + # @return [Conversions] def conversions @conversions ||= Conversions.new(loader: loader) end diff --git a/lib/solargraph/rbs_map/stdlib_map.rb b/lib/solargraph/rbs_map/stdlib_map.rb index b4b88fd69..4db30137f 100644 --- a/lib/solargraph/rbs_map/stdlib_map.rb +++ b/lib/solargraph/rbs_map/stdlib_map.rb @@ -9,6 +9,7 @@ class RbsMap class StdlibMap < RbsMap include Logging + # @return [Symbol] def log_level # TODO: track down remaining unfound requires :info diff --git a/lib/solargraph/shell.rb b/lib/solargraph/shell.rb index d3b79d700..4d617dbfd 100755 --- a/lib/solargraph/shell.rb +++ b/lib/solargraph/shell.rb @@ -3,6 +3,7 @@ require 'benchmark' require 'thor' require 'yard' +require 'yaml' module Solargraph class Shell < Thor @@ -36,6 +37,7 @@ def socket Signal.trap("TERM") do Backport.stop end + # @sg-ignore https://github.com/castwide/backport/pull/5 Backport.prepare_tcp_server host: options[:host], port: port, adapter: Solargraph::LanguageServer::Transport::Adapter STDERR.puts "Solargraph is listening PORT=#{port} PID=#{Process.pid}" end @@ -52,6 +54,7 @@ def stdio Signal.trap("TERM") do Backport.stop end + # @sg-ignore https://github.com/castwide/backport/pull/5 Backport.prepare_stdio_server adapter: Solargraph::LanguageServer::Transport::Adapter STDERR.puts "Solargraph is listening on stdio PID=#{Process.pid}" end @@ -64,6 +67,7 @@ def stdio def config(directory = '.') matches = [] if options[:extensions] + # @sg-ignore Unresolved call to each Gem::Specification.each do |g| if g.name.match(/^solargraph\-[A-Za-z0-9_\-]*?\-ext/) require g.name @@ -78,6 +82,7 @@ def config(directory = '.') end end File.open(File.join(directory, '.solargraph.yml'), 'w') do |file| + # @sg-ignore Unresolved call to to_yaml file.puts conf.to_yaml end STDOUT.puts "Configuration file initialized." @@ -116,16 +121,11 @@ def gems *names else STDERR.puts("Caching these gems: #{names}") names.each do |name| - if gem == 'core' + if name == 'core' PinCache.cache_core(out: $stdout) next end - if gem == 'stdlib' - PinCache.cache_stdlib(out: $stdout) - next - end - gemspec = api_map.find_gem(*name.split('=')) api_map.cache_gem(gemspec, rebuild: options.rebuild, out: $stdout) rescue Gem::MissingSpecError diff --git a/spec/yard_map/mapper_spec.rb b/spec/yard_map/mapper_spec.rb index acfd201b1..548b4ba31 100644 --- a/spec/yard_map/mapper_spec.rb +++ b/spec/yard_map/mapper_spec.rb @@ -1,5 +1,5 @@ describe Solargraph::YardMap::Mapper do - before :all do + before do Solargraph::ApiMap.load_with_cache('.', $stderr) end From 75c71e53c6ce464be9f273f1b4c85c8d82972c15 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 15 Jul 2025 20:06:11 -0400 Subject: [PATCH 193/561] Fix log message --- lib/solargraph/doc_map.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/solargraph/doc_map.rb b/lib/solargraph/doc_map.rb index 97302bb17..99451a878 100644 --- a/lib/solargraph/doc_map.rb +++ b/lib/solargraph/doc_map.rb @@ -73,7 +73,7 @@ def cache_doc_map_gems!(out) pins_processed = pins.length - existing_pin_count milliseconds = (time.real * 1000).round if (milliseconds > 500) && uncached_gemspecs.any? && out && uncached_gemspecs.any? - out.puts "Built #{pins.length} gem pins in #{milliseconds} ms" + out.puts "Built #{pins_processed} gem pins in #{milliseconds} ms" end load_serialized_gem_pins end From 602a9717ffed6a58619427cae0a02de989381568 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 15 Jul 2025 20:06:30 -0400 Subject: [PATCH 194/561] Make workspace required in DocMap --- lib/solargraph/doc_map.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/solargraph/doc_map.rb b/lib/solargraph/doc_map.rb index 99451a878..d7bed4cc2 100644 --- a/lib/solargraph/doc_map.rb +++ b/lib/solargraph/doc_map.rb @@ -29,7 +29,7 @@ def uncached_gemspecs attr_reader :workspace # @param requires [Array] - # @param workspace [Workspace, nil] + # @param workspace [Workspace] def initialize(requires, workspace) @requires = requires.compact @workspace = workspace From bdccccdaf38ba946a408e2a04bf14045e6cb7660 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 16 Jul 2025 09:10:17 -0400 Subject: [PATCH 195/561] Overcommit tweaks --- .overcommit.yml | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/.overcommit.yml b/.overcommit.yml index 215770aed..85c85487f 100644 --- a/.overcommit.yml +++ b/.overcommit.yml @@ -19,15 +19,16 @@ PreCommit: RuboCop: enabled: true - on_warn: fail # Treat all warnings as failures Solargraph: enabled: true + + FixMe: + enabled: true exclude: - - 'spec/**/*' - - lib/solargraph/rails/annotations/**/* - - vendor/**/* - - ".bundle/**/*" + # don't freak out over line below + - '.overcommit.yml' + keywords: ['BROKEN', 'BUG', 'FIXME', 'HACK', 'OPTIMIZE', 'REVIEW', 'TODO', 'WTF', 'XXX'] # creates false positives in CI AuthorName: @@ -37,6 +38,19 @@ PreCommit: AuthorEmail: enabled: false + ALL: + # if you make non-trivial changes to a file, please leave it + # better than you found it + problem_on_unmodified_line: fail + on_warn: fail + exclude: + - 'spec/**/*' + - lib/solargraph/rails/annotations/**/* + - vendor/**/* + - ".bundle/**/*" + - 'core.*' + + # # TrailingWhitespace: # enabled: true From d8e2830d7ddce0e59cdfc6b9e4acf42990e89c88 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 16 Jul 2025 09:54:15 -0400 Subject: [PATCH 196/561] Reenable spec --- spec/doc_map_spec.rb | 40 +++++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/spec/doc_map_spec.rb b/spec/doc_map_spec.rb index e909e65b1..3a506a87e 100644 --- a/spec/doc_map_spec.rb +++ b/spec/doc_map_spec.rb @@ -1,10 +1,17 @@ # frozen_string_literal: true +require 'bundler' + describe Solargraph::DocMap do subject(:doc_map) do - dm = Solargraph::DocMap.new(requires, workspace) - dm.cache_doc_map_gems!($stderr) - dm + Solargraph::DocMap.new(requires, workspace) + end + + let(:pre_cache) { true } + let(:requires) { [] } + + before do + doc_map.cache_doc_map_gems!($stderr) if pre_cache end let(:workspace) do @@ -32,15 +39,26 @@ it 'tracks unresolved requires' do expect(doc_map.unresolved_requires).to eq(['not_a_gem']) end + end + + context 'with an uncached but valid gemspec' do + let(:uncached_lazy_gemspec) do + Bundler::LazySpecification.new('uncached_gem', '1.0.0', 'ruby') + end + let(:requires) { ['uncached_gem'] } + let(:workspace) { instance_double(Solargraph::Workspace) } + let(:pincache) { instance_double(Solargraph::PinCache) } + let(:pre_cache) { false } + + before do + allow(workspace).to receive(:resolve_path_to_gemspecs).with('uncached_gem').and_return([uncached_lazy_gemspec]) + allow(workspace).to receive(:fetch_dependencies).with(uncached_lazy_gemspec).and_return([]) + allow(workspace).to receive(:fresh_pincache).and_return(pincache) + allow(pincache).to receive(:deserialize_combined_pin_cache).with(uncached_lazy_gemspec).and_return(nil) + end - xit 'tracks uncached_gemspecs' do - gemspec = Gem::Specification.new do |spec| - spec.name = 'not_a_gem' - spec.version = '1.0.0' - end - allow(Gem::Specification).to receive(:find_by_path).and_return(gemspec) - doc_map = Solargraph::DocMap.new(['not_a_gem'], workspace) - expect(doc_map.uncached_gemspecs).to eq([gemspec]) + it 'tracks uncached_gemspecs' do + expect(doc_map.uncached_gemspecs).to eq([uncached_lazy_gemspec]) end end From 7ed864672910b2879c3e28c39a0b588394558c8a Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 16 Jul 2025 10:16:29 -0400 Subject: [PATCH 197/561] Consolidate and fix names in workflows --- .github/workflows/linting.yml | 29 ++++++++++++++++++++++++++ .github/workflows/rubocop.yml | 36 --------------------------------- .github/workflows/typecheck.yml | 3 ++- 3 files changed, 31 insertions(+), 37 deletions(-) delete mode 100644 .github/workflows/rubocop.yml diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index bab5a28f7..99f2df787 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -17,6 +17,7 @@ on: permissions: pull-requests: write + contents: read jobs: overcommit: @@ -73,3 +74,31 @@ jobs: fail_level: info rubocop_version: Gemfile level: info + rubocop_todo: + name: Rubocop / .rubocop_todo.yml check + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: 3.4 + bundler-cache: false + + - name: Install gems + run: bundle install + + - name: Run RuboCop + run: bundle exec rubocop + + - name: Run RuboCop against todo file + run: | + bundle exec rubocop --auto-gen-config + if [ -n "$(git status --porcelain)" ] + then + git status --porcelain + git diff -u . + >&2 echo "Please fix deltas if bad or run 'bundle exec rubocop --auto-gen-config' and push up changes if good" + exit 1 + fi diff --git a/.github/workflows/rubocop.yml b/.github/workflows/rubocop.yml deleted file mode 100644 index e11526a06..000000000 --- a/.github/workflows/rubocop.yml +++ /dev/null @@ -1,36 +0,0 @@ -# This workflow uses actions that are not certified by GitHub. -# They are provided by a third-party and are governed by -# separate terms of service, privacy policy, and support -# documentation. -# This workflow will download a prebuilt Ruby version, install dependencies and run tests with rspec. -# For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby - -name: RuboCop - -on: - push: - branches: [ master ] - pull_request: - branches: [ master ] - -permissions: - contents: read - -jobs: - full: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - name: Set up Ruby - uses: ruby/setup-ruby@v1 - with: - ruby-version: 3.4 - bundler-cache: false - - - name: Install gems - run: bundle install - - - name: Run RuboCop - # Use -c flag so we ignore config files used within rubocop-related fixtures - run: bundle exec rubocop -c .rubocop.yml diff --git a/.github/workflows/typecheck.yml b/.github/workflows/typecheck.yml index 3680b02b4..0ae8a3d8a 100644 --- a/.github/workflows/typecheck.yml +++ b/.github/workflows/typecheck.yml @@ -17,7 +17,8 @@ permissions: contents: read jobs: - test: + solargraph_typed: + name: Solargraph / typed runs-on: ubuntu-latest From e27974bb9576ed4d716e62804ad6e787f7c24e53 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 16 Jul 2025 10:33:56 -0400 Subject: [PATCH 198/561] Rebaseline todo file --- .rubocop_todo.yml | 80 +++++++++++++++++------------------------------ 1 file changed, 29 insertions(+), 51 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 44985f8c3..47d41e0ba 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,6 +1,6 @@ # This configuration was generated by # `rubocop --auto-gen-config` -# on 2025-07-15 18:39:29 UTC using RuboCop version 1.78.0. +# on 2025-07-16 14:33:20 UTC using RuboCop version 1.78.0. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new @@ -58,13 +58,12 @@ Gemspec/RequiredRubyVersion: - 'spec/fixtures/rubocop-custom-version/specifications/rubocop-0.0.0.gemspec' - 'spec/fixtures/vendored/vendor/do_not_use.gemspec' -# Offense count: 8 +# Offense count: 5 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, IndentationWidth. # SupportedStyles: with_first_argument, with_fixed_indentation Layout/ArgumentAlignment: Exclude: - - 'lib/solargraph/convention/struct_definition.rb' - 'lib/solargraph/pin/callable.rb' - 'spec/source/source_chainer_spec.rb' @@ -107,7 +106,7 @@ Layout/EmptyLineBetweenDefs: - 'lib/solargraph/language_server/message/initialize.rb' - 'lib/solargraph/pin/delegated_method.rb' -# Offense count: 13 +# Offense count: 12 # This cop supports safe autocorrection (--autocorrect). Layout/EmptyLines: Exclude: @@ -120,7 +119,6 @@ Layout/EmptyLines: - 'lib/solargraph/pin/delegated_method.rb' - 'lib/solargraph/rbs_map/conversions.rb' - 'spec/complex_type_spec.rb' - - 'spec/convention/struct_definition_spec.rb' - 'spec/pin/local_variable_spec.rb' - 'spec/pin/symbol_spec.rb' - 'spec/type_checker/levels/strict_spec.rb' @@ -214,7 +212,7 @@ Layout/FirstHashElementIndentation: - 'spec/language_server/message/text_document/type_definition_spec.rb' - 'spec/language_server/message/workspace/did_change_watched_files_spec.rb' -# Offense count: 4 +# Offense count: 1 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowMultipleStyles, EnforcedHashRocketStyle, EnforcedColonStyle, EnforcedLastArgumentHashStyle. # SupportedHashRocketStyles: key, separator, table @@ -222,7 +220,6 @@ Layout/FirstHashElementIndentation: # SupportedLastArgumentHashStyles: always_inspect, always_ignore, ignore_implicit, ignore_explicit Layout/HashAlignment: Exclude: - - 'lib/solargraph/convention/struct_definition.rb' - 'lib/solargraph/workspace/config.rb' # Offense count: 3 @@ -334,7 +331,7 @@ Layout/SpaceAroundOperators: - 'spec/library_spec.rb' - 'spec/yard_map/mapper_spec.rb' -# Offense count: 105 +# Offense count: 108 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces. # SupportedStyles: space, no_space @@ -348,7 +345,7 @@ Layout/SpaceBeforeComma: Exclude: - 'spec/source/cursor_spec.rb' -# Offense count: 177 +# Offense count: 183 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces, SpaceBeforeBlockParameters. # SupportedStyles: space, no_space @@ -378,22 +375,13 @@ Layout/SpaceInsideParens: - 'lib/solargraph/pin/namespace.rb' - 'lib/solargraph/source_map.rb' -# Offense count: 1 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle. -# SupportedStyles: final_newline, final_blank_line -Layout/TrailingEmptyLines: - Exclude: - - 'spec/convention/struct_definition_spec.rb' - -# Offense count: 5 +# Offense count: 3 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowInHeredoc. Layout/TrailingWhitespace: Exclude: - 'lib/solargraph/language_server/message/client/register_capability.rb' - 'spec/api_map/config_spec.rb' - - 'spec/convention/struct_definition_spec.rb' - 'spec/convention_spec.rb' # Offense count: 2 @@ -645,7 +633,7 @@ Lint/UselessMethodDefinition: Exclude: - 'lib/solargraph/pin/signature.rb' -# Offense count: 232 +# Offense count: 227 # Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes. Metrics/AbcSize: Max: 200 @@ -666,12 +654,12 @@ Metrics/BlockNesting: Metrics/ClassLength: Max: 580 -# Offense count: 113 +# Offense count: 111 # Configuration parameters: AllowedMethods, AllowedPatterns. Metrics/CyclomaticComplexity: Max: 42 -# Offense count: 52 +# Offense count: 50 # Configuration parameters: CountComments, Max, CountAsOne, AllowedMethods, AllowedPatterns. Metrics/MethodLength: Enabled: false @@ -687,7 +675,7 @@ Metrics/ParameterLists: Max: 9 MaxOptionalParameters: 5 -# Offense count: 89 +# Offense count: 88 # Configuration parameters: AllowedMethods, AllowedPatterns. Metrics/PerceivedComplexity: Max: 47 @@ -738,19 +726,16 @@ Naming/MethodParameterName: - 'lib/solargraph/yard_map/mapper/to_method.rb' - 'lib/solargraph/yard_map/to_method.rb' -# Offense count: 14 +# Offense count: 11 # Configuration parameters: Mode, AllowedMethods, AllowedPatterns, AllowBangMethods, WaywardPredicates. # AllowedMethods: call # WaywardPredicates: nonzero? Naming/PredicateMethod: Exclude: - 'lib/solargraph/api_map/store.rb' - - 'lib/solargraph/convention/data_definition.rb' - - 'lib/solargraph/convention/struct_definition.rb' - 'lib/solargraph/doc_map.rb' - 'lib/solargraph/language_server/progress.rb' - 'lib/solargraph/library.rb' - - 'lib/solargraph/parser/node_processor/base.rb' - 'lib/solargraph/parser/parser_gem/node_processors/send_node.rb' - 'lib/solargraph/pin/base.rb' - 'lib/solargraph/pin/local_variable.rb' @@ -792,12 +777,6 @@ RSpec/BeEq: - 'spec/complex_type_spec.rb' - 'spec/pin/method_spec.rb' -# Offense count: 2 -# This cop supports unsafe autocorrection (--autocorrect-all). -RSpec/BeEql: - Exclude: - - 'spec/convention/struct_definition_spec.rb' - # Offense count: 7 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. @@ -845,7 +824,7 @@ RSpec/DescribeClass: - 'spec/complex_type_spec.rb' - 'spec/source_map/node_processor_spec.rb' -# Offense count: 412 +# Offense count: 407 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: SkipBlocks, EnforcedStyle, OnlyStaticConstants. # SupportedStyles: described_class, explicit @@ -865,18 +844,17 @@ RSpec/EmptyLineAfterFinalLet: Exclude: - 'spec/workspace/config_spec.rb' -# Offense count: 941 +# Offense count: 928 # Configuration parameters: CountAsOne. RSpec/ExampleLength: Max: 59 -# Offense count: 10 +# Offense count: 4 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: CustomTransform, IgnoredWords, DisallowedExamples. # DisallowedExamples: works RSpec/ExampleWording: Exclude: - - 'spec/convention/struct_definition_spec.rb' - 'spec/pin/base_spec.rb' - 'spec/pin/method_spec.rb' @@ -957,7 +935,7 @@ RSpec/MissingExampleGroupArgument: Exclude: - 'spec/diagnostics/rubocop_helpers_spec.rb' -# Offense count: 481 +# Offense count: 471 RSpec/MultipleExpectations: Max: 14 @@ -1023,7 +1001,7 @@ RSpec/RemoveConst: Exclude: - 'spec/diagnostics/rubocop_helpers_spec.rb' -# Offense count: 50 +# Offense count: 48 RSpec/RepeatedDescription: Exclude: - 'spec/api_map_spec.rb' @@ -1052,7 +1030,7 @@ RSpec/ScatteredLet: Exclude: - 'spec/complex_type_spec.rb' -# Offense count: 89 +# Offense count: 88 # Configuration parameters: Include, CustomTransform, IgnoreMethods, IgnoreMetadata. # Include: **/*_spec.rb RSpec/SpecFilePathFormat: @@ -1246,7 +1224,7 @@ Style/ConditionalAssignment: - 'lib/solargraph/parser/parser_gem/node_processors/defs_node.rb' - 'lib/solargraph/source/chain/call.rb' -# Offense count: 142 +# Offense count: 140 # Configuration parameters: AllowedConstants. Style/Documentation: Enabled: false @@ -1302,7 +1280,7 @@ Style/FloatDivision: - 'lib/solargraph/library.rb' - 'lib/solargraph/pin/search.rb' -# Offense count: 129 +# Offense count: 128 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: EnforcedStyle. # SupportedStyles: always, always_true, never @@ -1412,7 +1390,7 @@ Style/MapToSet: - 'lib/solargraph/library.rb' - 'spec/source_map/clip_spec.rb' -# Offense count: 203 +# Offense count: 198 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: require_parentheses, require_no_parentheses, require_no_parentheses_except_multiline @@ -1708,7 +1686,7 @@ Style/SafeNavigationChainLength: Exclude: - 'lib/solargraph/doc_map.rb' -# Offense count: 40 +# Offense count: 39 # This cop supports unsafe autocorrection (--autocorrect-all). Style/SlicingWithRange: Enabled: false @@ -1732,14 +1710,13 @@ Style/StderrPuts: - 'lib/solargraph/pin/base.rb' - 'lib/solargraph/shell.rb' -# Offense count: 11 +# Offense count: 10 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: Mode. Style/StringConcatenation: Exclude: - 'lib/solargraph/api_map.rb' - 'lib/solargraph/api_map/index.rb' - - 'lib/solargraph/convention/struct_definition.rb' - 'lib/solargraph/pin/base.rb' - 'lib/solargraph/pin/callable.rb' - 'lib/solargraph/pin/closure.rb' @@ -1748,7 +1725,7 @@ Style/StringConcatenation: - 'lib/solargraph/pin/namespace.rb' - 'solargraph.gemspec' -# Offense count: 621 +# Offense count: 615 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline. # SupportedStyles: single_quotes, double_quotes @@ -1808,7 +1785,7 @@ Style/TernaryParentheses: Exclude: - 'lib/solargraph/source_map/mapper.rb' -# Offense count: 16 +# Offense count: 19 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyleForMultiline. # SupportedStylesForMultiline: comma, consistent_comma, no_comma @@ -1824,6 +1801,7 @@ Style/TrailingCommaInArguments: - 'lib/solargraph/parser/parser_gem/node_processors/while_node.rb' - 'lib/solargraph/pin/method.rb' - 'lib/solargraph/rbs_map/conversions.rb' + - 'lib/solargraph/yard_map/mapper/to_constant.rb' - 'lib/solargraph/yard_map/mapper/to_method.rb' - 'lib/solargraph/yard_map/mapper/to_namespace.rb' @@ -1909,7 +1887,7 @@ YARD/CollectionType: Exclude: - 'lib/solargraph/range.rb' -# Offense count: 60 +# Offense count: 65 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStylePrototypeName. # SupportedStylesPrototypeName: before, after @@ -1923,9 +1901,9 @@ YARD/TagTypeSyntax: - 'lib/solargraph/parser/comment_ripper.rb' - 'lib/solargraph/type_checker.rb' -# Offense count: 195 +# Offense count: 191 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, IgnoreCopDirectives, AllowedPatterns, SplitStrings. # URISchemes: http, https Layout/LineLength: - Max: 244 + Max: 257 From 9c09e4ce6b34e2d23254b6d33effac9ff546e260 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 16 Jul 2025 10:35:33 -0400 Subject: [PATCH 199/561] Rebaseline todo file --- .rubocop_todo.yml | 1537 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 1425 insertions(+), 112 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 47d41e0ba..fd0f08b3c 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,6 +1,6 @@ # This configuration was generated by -# `rubocop --auto-gen-config` -# on 2025-07-16 14:33:20 UTC using RuboCop version 1.78.0. +# `rubocop --auto-gen-config --no-exclude-limit --no-auto-gen-timestamp` +# using RuboCop version 1.78.0. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new @@ -95,7 +95,24 @@ Layout/CommentIndentation: # Offense count: 23 # This cop supports safe autocorrection (--autocorrect). Layout/ElseAlignment: - Enabled: false + Exclude: + - 'lib/solargraph.rb' + - 'lib/solargraph/api_map/store.rb' + - 'lib/solargraph/diagnostics/rubocop.rb' + - 'lib/solargraph/language_server/message/extended/check_gem_version.rb' + - 'lib/solargraph/library.rb' + - 'lib/solargraph/parser/parser_gem/node_methods.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/block_node.rb' + - 'lib/solargraph/pin/method.rb' + - 'lib/solargraph/pin/namespace.rb' + - 'lib/solargraph/rbs_map.rb' + - 'lib/solargraph/shell.rb' + - 'lib/solargraph/source/chain/call.rb' + - 'lib/solargraph/source_map/clip.rb' + - 'lib/solargraph/source_map/mapper.rb' + - 'lib/solargraph/type_checker.rb' + - 'lib/solargraph/type_checker/rules.rb' + - 'lib/solargraph/yard_map/mapper.rb' # Offense count: 3 # This cop supports safe autocorrection (--autocorrect). @@ -144,7 +161,24 @@ Layout/EmptyLinesAroundModuleBody: # Configuration parameters: EnforcedStyleAlignWith, Severity. # SupportedStylesAlignWith: keyword, variable, start_of_line Layout/EndAlignment: - Enabled: false + Exclude: + - 'lib/solargraph.rb' + - 'lib/solargraph/api_map/store.rb' + - 'lib/solargraph/diagnostics/rubocop.rb' + - 'lib/solargraph/language_server/message/extended/check_gem_version.rb' + - 'lib/solargraph/library.rb' + - 'lib/solargraph/parser/parser_gem/node_methods.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/block_node.rb' + - 'lib/solargraph/pin/method.rb' + - 'lib/solargraph/pin/namespace.rb' + - 'lib/solargraph/rbs_map.rb' + - 'lib/solargraph/shell.rb' + - 'lib/solargraph/source/chain/call.rb' + - 'lib/solargraph/source_map/clip.rb' + - 'lib/solargraph/source_map/mapper.rb' + - 'lib/solargraph/type_checker.rb' + - 'lib/solargraph/type_checker/rules.rb' + - 'lib/solargraph/yard_map/mapper.rb' # Offense count: 4 # Configuration parameters: EnforcedStyle. @@ -234,7 +268,26 @@ Layout/HeredocIndentation: # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: Width, AllowedPatterns. Layout/IndentationWidth: - Enabled: false + Exclude: + - 'lib/solargraph.rb' + - 'lib/solargraph/api_map/store.rb' + - 'lib/solargraph/diagnostics/rubocop.rb' + - 'lib/solargraph/language_server/message/extended/check_gem_version.rb' + - 'lib/solargraph/library.rb' + - 'lib/solargraph/parser/parser_gem/node_methods.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/block_node.rb' + - 'lib/solargraph/pin/method.rb' + - 'lib/solargraph/pin/namespace.rb' + - 'lib/solargraph/rbs_map.rb' + - 'lib/solargraph/shell.rb' + - 'lib/solargraph/source/chain/call.rb' + - 'lib/solargraph/source_map/clip.rb' + - 'lib/solargraph/source_map/mapper.rb' + - 'lib/solargraph/type_checker.rb' + - 'lib/solargraph/type_checker/rules.rb' + - 'lib/solargraph/yard_map/mapper.rb' + - 'spec/api_map/config_spec.rb' + - 'spec/source_map/mapper_spec.rb' # Offense count: 5 # This cop supports safe autocorrection (--autocorrect). @@ -337,7 +390,39 @@ Layout/SpaceAroundOperators: # SupportedStyles: space, no_space # SupportedStylesForEmptyBraces: space, no_space Layout/SpaceBeforeBlockBraces: - Enabled: false + Exclude: + - 'lib/solargraph/api_map.rb' + - 'lib/solargraph/api_map/store.rb' + - 'lib/solargraph/diagnostics/rubocop.rb' + - 'lib/solargraph/diagnostics/update_errors.rb' + - 'lib/solargraph/language_server/host.rb' + - 'lib/solargraph/library.rb' + - 'lib/solargraph/parser/node_processor/base.rb' + - 'lib/solargraph/parser/parser_gem/node_methods.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/and_node.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/if_node.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/send_node.rb' + - 'lib/solargraph/pin/base.rb' + - 'lib/solargraph/source.rb' + - 'lib/solargraph/source/chain/call.rb' + - 'lib/solargraph/source/chain/class_variable.rb' + - 'lib/solargraph/source/chain/constant.rb' + - 'lib/solargraph/source/chain/global_variable.rb' + - 'lib/solargraph/source/chain/instance_variable.rb' + - 'lib/solargraph/source/chain/variable.rb' + - 'lib/solargraph/source/source_chainer.rb' + - 'lib/solargraph/source_map/clip.rb' + - 'lib/solargraph/source_map/mapper.rb' + - 'lib/solargraph/workspace/config.rb' + - 'spec/language_server/host_spec.rb' + - 'spec/language_server/protocol_spec.rb' + - 'spec/library_spec.rb' + - 'spec/pin/constant_spec.rb' + - 'spec/pin/instance_variable_spec.rb' + - 'spec/rbs_map/core_map_spec.rb' + - 'spec/source_map/mapper_spec.rb' + - 'spec/source_map_spec.rb' + - 'spec/source_spec.rb' # Offense count: 1 # This cop supports safe autocorrection (--autocorrect). @@ -351,7 +436,29 @@ Layout/SpaceBeforeComma: # SupportedStyles: space, no_space # SupportedStylesForEmptyBraces: space, no_space Layout/SpaceInsideBlockBraces: - Enabled: false + Exclude: + - 'lib/solargraph/api_map.rb' + - 'lib/solargraph/api_map/store.rb' + - 'lib/solargraph/diagnostics/update_errors.rb' + - 'lib/solargraph/language_server/host.rb' + - 'lib/solargraph/parser/node_processor/base.rb' + - 'lib/solargraph/parser/parser_gem/node_methods.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/and_node.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/if_node.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/send_node.rb' + - 'lib/solargraph/source/chain/call.rb' + - 'lib/solargraph/source/chain/class_variable.rb' + - 'lib/solargraph/source/chain/global_variable.rb' + - 'lib/solargraph/source/chain/instance_variable.rb' + - 'lib/solargraph/source/chain/variable.rb' + - 'lib/solargraph/source/source_chainer.rb' + - 'lib/solargraph/source_map/mapper.rb' + - 'spec/language_server/protocol_spec.rb' + - 'spec/library_spec.rb' + - 'spec/pin/constant_spec.rb' + - 'spec/rbs_map/core_map_spec.rb' + - 'spec/source_map/mapper_spec.rb' + - 'spec/source_spec.rb' # Offense count: 10 # This cop supports safe autocorrection (--autocorrect). @@ -606,7 +713,30 @@ Lint/UnusedBlockArgument: # Configuration parameters: AutoCorrect, AllowUnusedKeywordArguments, IgnoreEmptyMethods, IgnoreNotImplementedMethods, NotImplementedExceptions. # NotImplementedExceptions: NotImplementedError Lint/UnusedMethodArgument: - Enabled: false + Exclude: + - 'lib/solargraph.rb' + - 'lib/solargraph/complex_type/type_methods.rb' + - 'lib/solargraph/convention/base.rb' + - 'lib/solargraph/diagnostics/base.rb' + - 'lib/solargraph/diagnostics/update_errors.rb' + - 'lib/solargraph/doc_map.rb' + - 'lib/solargraph/pin/namespace.rb' + - 'lib/solargraph/rbs_map/conversions.rb' + - 'lib/solargraph/source.rb' + - 'lib/solargraph/source/chain.rb' + - 'lib/solargraph/source/chain/block_symbol.rb' + - 'lib/solargraph/source/chain/block_variable.rb' + - 'lib/solargraph/source/chain/call.rb' + - 'lib/solargraph/source/chain/class_variable.rb' + - 'lib/solargraph/source/chain/constant.rb' + - 'lib/solargraph/source/chain/global_variable.rb' + - 'lib/solargraph/source/chain/hash.rb' + - 'lib/solargraph/source/chain/head.rb' + - 'lib/solargraph/source/chain/instance_variable.rb' + - 'lib/solargraph/source/chain/link.rb' + - 'lib/solargraph/source/chain/literal.rb' + - 'lib/solargraph/source/chain/variable.rb' + - 'lib/solargraph/source/chain/z_super.rb' # Offense count: 1 # This cop supports safe autocorrection (--autocorrect). @@ -619,7 +749,30 @@ Lint/UselessAccessModifier: # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AutoCorrect. Lint/UselessAssignment: - Enabled: false + Exclude: + - 'lib/solargraph/api_map.rb' + - 'lib/solargraph/doc_map.rb' + - 'lib/solargraph/language_server/message/extended/check_gem_version.rb' + - 'lib/solargraph/language_server/message/extended/document_gems.rb' + - 'lib/solargraph/language_server/message/text_document/completion.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/send_node.rb' + - 'lib/solargraph/pin/method.rb' + - 'lib/solargraph/pin/parameter.rb' + - 'lib/solargraph/rbs_map/conversions.rb' + - 'lib/solargraph/source.rb' + - 'lib/solargraph/source/chain/call.rb' + - 'lib/solargraph/source/source_chainer.rb' + - 'lib/solargraph/source_map/mapper.rb' + - 'lib/solargraph/type_checker.rb' + - 'spec/fixtures/long_squiggly_heredoc.rb' + - 'spec/fixtures/rubocop-unused-variable-error/app.rb' + - 'spec/fixtures/unicode.rb' + - 'spec/language_server/host_spec.rb' + - 'spec/language_server/protocol_spec.rb' + - 'spec/library_spec.rb' + - 'spec/pin/namespace_spec.rb' + - 'spec/source/chain/call_spec.rb' + - 'spec/source_map/mapper_spec.rb' # Offense count: 2 Lint/UselessConstantScoping: @@ -634,76 +787,330 @@ Lint/UselessMethodDefinition: - 'lib/solargraph/pin/signature.rb' # Offense count: 227 -# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes. +# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes, Max. Metrics/AbcSize: - Max: 200 + Exclude: + - 'lib/solargraph/api_map.rb' + - 'lib/solargraph/api_map/index.rb' + - 'lib/solargraph/api_map/source_to_yard.rb' + - 'lib/solargraph/api_map/store.rb' + - 'lib/solargraph/complex_type.rb' + - 'lib/solargraph/complex_type/type_methods.rb' + - 'lib/solargraph/complex_type/unique_type.rb' + - 'lib/solargraph/convention/struct_definition.rb' + - 'lib/solargraph/convention/struct_definition/struct_definition_node.rb' + - 'lib/solargraph/diagnostics/require_not_found.rb' + - 'lib/solargraph/diagnostics/rubocop.rb' + - 'lib/solargraph/diagnostics/type_check.rb' + - 'lib/solargraph/diagnostics/update_errors.rb' + - 'lib/solargraph/doc_map.rb' + - 'lib/solargraph/gem_pins.rb' + - 'lib/solargraph/language_server/host.rb' + - 'lib/solargraph/language_server/host/diagnoser.rb' + - 'lib/solargraph/language_server/message/base.rb' + - 'lib/solargraph/language_server/message/completion_item/resolve.rb' + - 'lib/solargraph/language_server/message/extended/check_gem_version.rb' + - 'lib/solargraph/language_server/message/extended/document.rb' + - 'lib/solargraph/language_server/message/initialize.rb' + - 'lib/solargraph/language_server/message/text_document/completion.rb' + - 'lib/solargraph/language_server/message/text_document/definition.rb' + - 'lib/solargraph/language_server/message/text_document/document_symbol.rb' + - 'lib/solargraph/language_server/message/text_document/formatting.rb' + - 'lib/solargraph/language_server/message/text_document/hover.rb' + - 'lib/solargraph/language_server/message/text_document/prepare_rename.rb' + - 'lib/solargraph/language_server/message/text_document/references.rb' + - 'lib/solargraph/language_server/message/text_document/rename.rb' + - 'lib/solargraph/language_server/message/text_document/signature_help.rb' + - 'lib/solargraph/language_server/message/text_document/type_definition.rb' + - 'lib/solargraph/language_server/message/workspace/did_change_configuration.rb' + - 'lib/solargraph/language_server/message/workspace/did_change_watched_files.rb' + - 'lib/solargraph/library.rb' + - 'lib/solargraph/page.rb' + - 'lib/solargraph/parser/comment_ripper.rb' + - 'lib/solargraph/parser/flow_sensitive_typing.rb' + - 'lib/solargraph/parser/parser_gem/class_methods.rb' + - 'lib/solargraph/parser/parser_gem/node_chainer.rb' + - 'lib/solargraph/parser/parser_gem/node_methods.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/alias_node.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/args_node.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/block_node.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/cvasgn_node.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/def_node.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/defs_node.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/gvasgn_node.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/namespace_node.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/orasgn_node.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/resbody_node.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/send_node.rb' + - 'lib/solargraph/pin/base.rb' + - 'lib/solargraph/pin/base_variable.rb' + - 'lib/solargraph/pin/block.rb' + - 'lib/solargraph/pin/callable.rb' + - 'lib/solargraph/pin/documenting.rb' + - 'lib/solargraph/pin/method.rb' + - 'lib/solargraph/pin/namespace.rb' + - 'lib/solargraph/pin/parameter.rb' + - 'lib/solargraph/pin/search.rb' + - 'lib/solargraph/pin/signature.rb' + - 'lib/solargraph/position.rb' + - 'lib/solargraph/range.rb' + - 'lib/solargraph/rbs_map/conversions.rb' + - 'lib/solargraph/shell.rb' + - 'lib/solargraph/source.rb' + - 'lib/solargraph/source/chain.rb' + - 'lib/solargraph/source/chain/array.rb' + - 'lib/solargraph/source/chain/call.rb' + - 'lib/solargraph/source/chain/constant.rb' + - 'lib/solargraph/source/change.rb' + - 'lib/solargraph/source/cursor.rb' + - 'lib/solargraph/source/source_chainer.rb' + - 'lib/solargraph/source_map.rb' + - 'lib/solargraph/source_map/clip.rb' + - 'lib/solargraph/source_map/mapper.rb' + - 'lib/solargraph/type_checker.rb' + - 'lib/solargraph/type_checker/checks.rb' + - 'lib/solargraph/workspace.rb' + - 'lib/solargraph/yard_map/mapper.rb' + - 'lib/solargraph/yard_map/mapper/to_method.rb' + - 'lib/solargraph/yard_map/to_method.rb' # Offense count: 12 -# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns, inherit_mode. +# Configuration parameters: CountComments, Max, CountAsOne, AllowedMethods, AllowedPatterns, inherit_mode. # AllowedMethods: refine Metrics/BlockLength: - Max: 54 + Exclude: + - 'lib/solargraph/api_map.rb' + - 'lib/solargraph/api_map/source_to_yard.rb' + - 'lib/solargraph/complex_type.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/send_node.rb' + - 'lib/solargraph/source/chain/call.rb' + - 'lib/solargraph/type_checker.rb' # Offense count: 13 -# Configuration parameters: CountBlocks, CountModifierForms. +# Configuration parameters: CountBlocks, CountModifierForms, Max. Metrics/BlockNesting: - Max: 6 + Exclude: + - 'lib/solargraph/source/source_chainer.rb' + - 'lib/solargraph/source_map/clip.rb' + - 'lib/solargraph/type_checker.rb' # Offense count: 30 -# Configuration parameters: CountComments, CountAsOne. +# Configuration parameters: CountComments, Max, CountAsOne. Metrics/ClassLength: - Max: 580 - -# Offense count: 111 -# Configuration parameters: AllowedMethods, AllowedPatterns. -Metrics/CyclomaticComplexity: - Max: 42 - -# Offense count: 50 -# Configuration parameters: CountComments, Max, CountAsOne, AllowedMethods, AllowedPatterns. -Metrics/MethodLength: - Enabled: false - -# Offense count: 4 -# Configuration parameters: CountComments, CountAsOne. -Metrics/ModuleLength: - Max: 168 - -# Offense count: 14 -# Configuration parameters: CountKeywordArgs. -Metrics/ParameterLists: - Max: 9 - MaxOptionalParameters: 5 - -# Offense count: 88 -# Configuration parameters: AllowedMethods, AllowedPatterns. -Metrics/PerceivedComplexity: - Max: 47 - -# Offense count: 5 -Naming/AccessorMethodName: Exclude: - 'lib/solargraph/api_map.rb' + - 'lib/solargraph/api_map/index.rb' - 'lib/solargraph/api_map/store.rb' - - 'lib/solargraph/language_server/message/base.rb' - -# Offense count: 1 -# Configuration parameters: AsciiConstants. -Naming/AsciiIdentifiers: - Exclude: - - 'spec/fixtures/unicode.rb' - -# Offense count: 1 -# Configuration parameters: ForbiddenDelimiters. -# ForbiddenDelimiters: (?i-mx:(^|\s)(EO[A-Z]{1}|END)(\s|$)) -Naming/HeredocDelimiterNaming: - Exclude: - - 'spec/yard_map/mapper/to_method_spec.rb' - -# Offense count: 9 -# This cop supports unsafe autocorrection (--autocorrect-all). -# Configuration parameters: EnforcedStyleForLeadingUnderscores. -# SupportedStylesForLeadingUnderscores: disallowed, required, optional + - 'lib/solargraph/complex_type.rb' + - 'lib/solargraph/complex_type/unique_type.rb' + - 'lib/solargraph/doc_map.rb' + - 'lib/solargraph/language_server/host.rb' + - 'lib/solargraph/language_server/message/initialize.rb' + - 'lib/solargraph/library.rb' + - 'lib/solargraph/parser/flow_sensitive_typing.rb' + - 'lib/solargraph/parser/parser_gem/node_chainer.rb' + - 'lib/solargraph/parser/parser_gem/node_methods.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/send_node.rb' + - 'lib/solargraph/pin/base.rb' + - 'lib/solargraph/pin/callable.rb' + - 'lib/solargraph/pin/method.rb' + - 'lib/solargraph/pin/parameter.rb' + - 'lib/solargraph/pin_cache.rb' + - 'lib/solargraph/rbs_map/conversions.rb' + - 'lib/solargraph/shell.rb' + - 'lib/solargraph/source.rb' + - 'lib/solargraph/source/chain.rb' + - 'lib/solargraph/source/chain/call.rb' + - 'lib/solargraph/source/source_chainer.rb' + - 'lib/solargraph/source_map.rb' + - 'lib/solargraph/source_map/clip.rb' + - 'lib/solargraph/source_map/mapper.rb' + - 'lib/solargraph/type_checker.rb' + - 'lib/solargraph/workspace.rb' + - 'lib/solargraph/workspace/config.rb' + +# Offense count: 111 +# Configuration parameters: AllowedMethods, AllowedPatterns, Max. +Metrics/CyclomaticComplexity: + Exclude: + - 'lib/solargraph/api_map.rb' + - 'lib/solargraph/api_map/index.rb' + - 'lib/solargraph/api_map/source_to_yard.rb' + - 'lib/solargraph/api_map/store.rb' + - 'lib/solargraph/complex_type.rb' + - 'lib/solargraph/complex_type/type_methods.rb' + - 'lib/solargraph/complex_type/unique_type.rb' + - 'lib/solargraph/convention/struct_definition/struct_definition_node.rb' + - 'lib/solargraph/diagnostics/require_not_found.rb' + - 'lib/solargraph/doc_map.rb' + - 'lib/solargraph/gem_pins.rb' + - 'lib/solargraph/language_server/host.rb' + - 'lib/solargraph/language_server/message/completion_item/resolve.rb' + - 'lib/solargraph/language_server/message/extended/check_gem_version.rb' + - 'lib/solargraph/language_server/message/initialize.rb' + - 'lib/solargraph/language_server/message/text_document/hover.rb' + - 'lib/solargraph/language_server/message/workspace/did_change_configuration.rb' + - 'lib/solargraph/library.rb' + - 'lib/solargraph/parser/flow_sensitive_typing.rb' + - 'lib/solargraph/parser/parser_gem/node_chainer.rb' + - 'lib/solargraph/parser/parser_gem/node_methods.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/send_node.rb' + - 'lib/solargraph/pin/base.rb' + - 'lib/solargraph/pin/base_variable.rb' + - 'lib/solargraph/pin/block.rb' + - 'lib/solargraph/pin/callable.rb' + - 'lib/solargraph/pin/method.rb' + - 'lib/solargraph/pin/parameter.rb' + - 'lib/solargraph/pin/signature.rb' + - 'lib/solargraph/rbs_map.rb' + - 'lib/solargraph/rbs_map/conversions.rb' + - 'lib/solargraph/shell.rb' + - 'lib/solargraph/source.rb' + - 'lib/solargraph/source/chain.rb' + - 'lib/solargraph/source/chain/array.rb' + - 'lib/solargraph/source/chain/call.rb' + - 'lib/solargraph/source/chain/constant.rb' + - 'lib/solargraph/source/change.rb' + - 'lib/solargraph/source/source_chainer.rb' + - 'lib/solargraph/source_map.rb' + - 'lib/solargraph/source_map/clip.rb' + - 'lib/solargraph/source_map/mapper.rb' + - 'lib/solargraph/type_checker.rb' + - 'lib/solargraph/type_checker/checks.rb' + - 'lib/solargraph/workspace.rb' + - 'lib/solargraph/yard_map/mapper.rb' + - 'lib/solargraph/yard_map/mapper/to_method.rb' + +# Offense count: 50 +# Configuration parameters: CountComments, Max, CountAsOne, AllowedMethods, AllowedPatterns. +Metrics/MethodLength: + Exclude: + - 'lib/solargraph/api_map.rb' + - 'lib/solargraph/api_map/source_to_yard.rb' + - 'lib/solargraph/complex_type.rb' + - 'lib/solargraph/complex_type/unique_type.rb' + - 'lib/solargraph/convention/struct_definition.rb' + - 'lib/solargraph/doc_map.rb' + - 'lib/solargraph/language_server/host.rb' + - 'lib/solargraph/language_server/message/extended/check_gem_version.rb' + - 'lib/solargraph/language_server/message/initialize.rb' + - 'lib/solargraph/language_server/message/text_document/completion.rb' + - 'lib/solargraph/language_server/message/text_document/formatting.rb' + - 'lib/solargraph/language_server/message/text_document/hover.rb' + - 'lib/solargraph/library.rb' + - 'lib/solargraph/parser/parser_gem/node_chainer.rb' + - 'lib/solargraph/parser/parser_gem/node_methods.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/def_node.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/send_node.rb' + - 'lib/solargraph/pin/method.rb' + - 'lib/solargraph/rbs_map/conversions.rb' + - 'lib/solargraph/source.rb' + - 'lib/solargraph/source/chain.rb' + - 'lib/solargraph/source/chain/call.rb' + - 'lib/solargraph/source/source_chainer.rb' + - 'lib/solargraph/source_map/clip.rb' + - 'lib/solargraph/source_map/mapper.rb' + - 'lib/solargraph/type_checker.rb' + - 'lib/solargraph/yard_map/mapper.rb' + - 'lib/solargraph/yard_map/mapper/to_method.rb' + +# Offense count: 4 +# Configuration parameters: CountComments, Max, CountAsOne. +Metrics/ModuleLength: + Exclude: + - 'lib/solargraph/complex_type/type_methods.rb' + - 'lib/solargraph/parser/parser_gem/node_methods.rb' + - 'lib/solargraph/pin_cache.rb' + +# Offense count: 14 +# Configuration parameters: Max, CountKeywordArgs, MaxOptionalParameters. +Metrics/ParameterLists: + Exclude: + - 'lib/solargraph/api_map.rb' + - 'lib/solargraph/pin/base.rb' + - 'lib/solargraph/pin/callable.rb' + - 'lib/solargraph/pin/method.rb' + - 'lib/solargraph/type_checker.rb' + - 'lib/solargraph/yard_map/mapper/to_method.rb' + - 'lib/solargraph/yard_map/to_method.rb' + +# Offense count: 88 +# Configuration parameters: AllowedMethods, AllowedPatterns, Max. +Metrics/PerceivedComplexity: + Exclude: + - 'lib/solargraph/api_map.rb' + - 'lib/solargraph/api_map/index.rb' + - 'lib/solargraph/api_map/source_to_yard.rb' + - 'lib/solargraph/complex_type.rb' + - 'lib/solargraph/complex_type/type_methods.rb' + - 'lib/solargraph/complex_type/unique_type.rb' + - 'lib/solargraph/doc_map.rb' + - 'lib/solargraph/language_server/host.rb' + - 'lib/solargraph/language_server/message/extended/check_gem_version.rb' + - 'lib/solargraph/language_server/message/initialize.rb' + - 'lib/solargraph/language_server/message/text_document/hover.rb' + - 'lib/solargraph/language_server/message/workspace/did_change_configuration.rb' + - 'lib/solargraph/library.rb' + - 'lib/solargraph/parser/flow_sensitive_typing.rb' + - 'lib/solargraph/parser/parser_gem/node_chainer.rb' + - 'lib/solargraph/parser/parser_gem/node_methods.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/send_node.rb' + - 'lib/solargraph/pin/base.rb' + - 'lib/solargraph/pin/base_variable.rb' + - 'lib/solargraph/pin/block.rb' + - 'lib/solargraph/pin/method.rb' + - 'lib/solargraph/pin/parameter.rb' + - 'lib/solargraph/rbs_map.rb' + - 'lib/solargraph/rbs_map/conversions.rb' + - 'lib/solargraph/shell.rb' + - 'lib/solargraph/source.rb' + - 'lib/solargraph/source/chain.rb' + - 'lib/solargraph/source/chain/array.rb' + - 'lib/solargraph/source/chain/call.rb' + - 'lib/solargraph/source/chain/constant.rb' + - 'lib/solargraph/source/change.rb' + - 'lib/solargraph/source/source_chainer.rb' + - 'lib/solargraph/source_map.rb' + - 'lib/solargraph/source_map/clip.rb' + - 'lib/solargraph/source_map/mapper.rb' + - 'lib/solargraph/type_checker.rb' + - 'lib/solargraph/type_checker/checks.rb' + - 'lib/solargraph/workspace.rb' + - 'lib/solargraph/yard_map/mapper.rb' + - 'lib/solargraph/yard_map/mapper/to_method.rb' + +# Offense count: 5 +Naming/AccessorMethodName: + Exclude: + - 'lib/solargraph/api_map.rb' + - 'lib/solargraph/api_map/store.rb' + - 'lib/solargraph/language_server/message/base.rb' + +# Offense count: 1 +# Configuration parameters: AsciiConstants. +Naming/AsciiIdentifiers: + Exclude: + - 'spec/fixtures/unicode.rb' + +# Offense count: 1 +# Configuration parameters: ForbiddenDelimiters. +# ForbiddenDelimiters: (?i-mx:(^|\s)(EO[A-Z]{1}|END)(\s|$)) +Naming/HeredocDelimiterNaming: + Exclude: + - 'spec/yard_map/mapper/to_method_spec.rb' + +# Offense count: 9 +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: EnforcedStyleForLeadingUnderscores. +# SupportedStylesForLeadingUnderscores: disallowed, required, optional Naming/MemoizedInstanceVariableName: Exclude: - 'lib/solargraph/complex_type/type_methods.rb' @@ -829,7 +1236,77 @@ RSpec/DescribeClass: # Configuration parameters: SkipBlocks, EnforcedStyle, OnlyStaticConstants. # SupportedStyles: described_class, explicit RSpec/DescribedClass: - Enabled: false + Exclude: + - 'spec/api_map/cache_spec.rb' + - 'spec/api_map/source_to_yard_spec.rb' + - 'spec/api_map/store_spec.rb' + - 'spec/api_map_spec.rb' + - 'spec/diagnostics/base_spec.rb' + - 'spec/diagnostics/require_not_found_spec.rb' + - 'spec/diagnostics/rubocop_helpers_spec.rb' + - 'spec/diagnostics/rubocop_spec.rb' + - 'spec/diagnostics/type_check_spec.rb' + - 'spec/diagnostics/update_errors_spec.rb' + - 'spec/diagnostics_spec.rb' + - 'spec/doc_map_spec.rb' + - 'spec/gem_pins_spec.rb' + - 'spec/language_server/host/diagnoser_spec.rb' + - 'spec/language_server/host/dispatch_spec.rb' + - 'spec/language_server/host/message_worker_spec.rb' + - 'spec/language_server/host_spec.rb' + - 'spec/language_server/message/completion_item/resolve_spec.rb' + - 'spec/language_server/message/extended/check_gem_version_spec.rb' + - 'spec/language_server/message/initialize_spec.rb' + - 'spec/language_server/message/text_document/definition_spec.rb' + - 'spec/language_server/message/text_document/formatting_spec.rb' + - 'spec/language_server/message/text_document/hover_spec.rb' + - 'spec/language_server/message/text_document/rename_spec.rb' + - 'spec/language_server/message/text_document/type_definition_spec.rb' + - 'spec/language_server/message/workspace/did_change_watched_files_spec.rb' + - 'spec/language_server/message_spec.rb' + - 'spec/language_server/protocol_spec.rb' + - 'spec/language_server/transport/data_reader_spec.rb' + - 'spec/language_server/uri_helpers_spec.rb' + - 'spec/library_spec.rb' + - 'spec/logging_spec.rb' + - 'spec/parser/node_methods_spec.rb' + - 'spec/parser/node_processor_spec.rb' + - 'spec/parser_spec.rb' + - 'spec/pin/base_spec.rb' + - 'spec/pin/delegated_method_spec.rb' + - 'spec/pin/instance_variable_spec.rb' + - 'spec/pin/keyword_spec.rb' + - 'spec/pin/local_variable_spec.rb' + - 'spec/pin/method_spec.rb' + - 'spec/pin/namespace_spec.rb' + - 'spec/pin/parameter_spec.rb' + - 'spec/pin/search_spec.rb' + - 'spec/pin/symbol_spec.rb' + - 'spec/position_spec.rb' + - 'spec/rbs_map/conversions_spec.rb' + - 'spec/rbs_map/core_map_spec.rb' + - 'spec/rbs_map/stdlib_map_spec.rb' + - 'spec/rbs_map_spec.rb' + - 'spec/source/chain/class_variable_spec.rb' + - 'spec/source/chain/global_variable_spec.rb' + - 'spec/source/chain/head_spec.rb' + - 'spec/source/chain/instance_variable_spec.rb' + - 'spec/source/chain/z_super_spec.rb' + - 'spec/source/change_spec.rb' + - 'spec/source/source_chainer_spec.rb' + - 'spec/source/updater_spec.rb' + - 'spec/source_map/mapper_spec.rb' + - 'spec/source_map_spec.rb' + - 'spec/source_spec.rb' + - 'spec/type_checker/checks_spec.rb' + - 'spec/type_checker/levels/normal_spec.rb' + - 'spec/type_checker/levels/strict_spec.rb' + - 'spec/type_checker/rules_spec.rb' + - 'spec/type_checker_spec.rb' + - 'spec/workspace/config_spec.rb' + - 'spec/workspace_spec.rb' + - 'spec/yard_map/mapper/to_method_spec.rb' + - 'spec/yard_map/mapper_spec.rb' # Offense count: 1 # This cop supports unsafe autocorrection (--autocorrect-all). @@ -845,9 +1322,80 @@ RSpec/EmptyLineAfterFinalLet: - 'spec/workspace/config_spec.rb' # Offense count: 928 -# Configuration parameters: CountAsOne. +# Configuration parameters: Max, CountAsOne. RSpec/ExampleLength: - Max: 59 + Exclude: + - 'spec/api_map/config_spec.rb' + - 'spec/api_map/source_to_yard_spec.rb' + - 'spec/api_map/store_spec.rb' + - 'spec/api_map_spec.rb' + - 'spec/complex_type_spec.rb' + - 'spec/diagnostics/rubocop_spec.rb' + - 'spec/diagnostics/type_check_spec.rb' + - 'spec/diagnostics/update_errors_spec.rb' + - 'spec/doc_map_spec.rb' + - 'spec/gem_pins_spec.rb' + - 'spec/language_server/host/dispatch_spec.rb' + - 'spec/language_server/host/message_worker_spec.rb' + - 'spec/language_server/host_spec.rb' + - 'spec/language_server/message/completion_item/resolve_spec.rb' + - 'spec/language_server/message/extended/check_gem_version_spec.rb' + - 'spec/language_server/message/initialize_spec.rb' + - 'spec/language_server/message/text_document/definition_spec.rb' + - 'spec/language_server/message/text_document/formatting_spec.rb' + - 'spec/language_server/message/text_document/hover_spec.rb' + - 'spec/language_server/message/text_document/rename_spec.rb' + - 'spec/language_server/message/text_document/type_definition_spec.rb' + - 'spec/language_server/message/workspace/did_change_configuration_spec.rb' + - 'spec/language_server/message/workspace/did_change_watched_files_spec.rb' + - 'spec/language_server/protocol_spec.rb' + - 'spec/language_server/transport/adapter_spec.rb' + - 'spec/language_server/transport/data_reader_spec.rb' + - 'spec/library_spec.rb' + - 'spec/logging_spec.rb' + - 'spec/parser/flow_sensitive_typing_spec.rb' + - 'spec/parser/node_chainer_spec.rb' + - 'spec/parser/node_methods_spec.rb' + - 'spec/parser/node_processor_spec.rb' + - 'spec/pin/base_spec.rb' + - 'spec/pin/base_variable_spec.rb' + - 'spec/pin/constant_spec.rb' + - 'spec/pin/delegated_method_spec.rb' + - 'spec/pin/local_variable_spec.rb' + - 'spec/pin/method_spec.rb' + - 'spec/pin/namespace_spec.rb' + - 'spec/pin/parameter_spec.rb' + - 'spec/pin/search_spec.rb' + - 'spec/rbs_map/core_map_spec.rb' + - 'spec/rbs_map/stdlib_map_spec.rb' + - 'spec/rbs_map_spec.rb' + - 'spec/source/chain/call_spec.rb' + - 'spec/source/chain/class_variable_spec.rb' + - 'spec/source/chain/constant_spec.rb' + - 'spec/source/chain/global_variable_spec.rb' + - 'spec/source/chain/head_spec.rb' + - 'spec/source/chain/instance_variable_spec.rb' + - 'spec/source/chain/z_super_spec.rb' + - 'spec/source/chain_spec.rb' + - 'spec/source/change_spec.rb' + - 'spec/source/cursor_spec.rb' + - 'spec/source/source_chainer_spec.rb' + - 'spec/source/updater_spec.rb' + - 'spec/source_map/clip_spec.rb' + - 'spec/source_map/mapper_spec.rb' + - 'spec/source_map/node_processor_spec.rb' + - 'spec/source_map_spec.rb' + - 'spec/source_spec.rb' + - 'spec/type_checker/checks_spec.rb' + - 'spec/type_checker/levels/normal_spec.rb' + - 'spec/type_checker/levels/strict_spec.rb' + - 'spec/type_checker/levels/strong_spec.rb' + - 'spec/type_checker/levels/typed_spec.rb' + - 'spec/type_checker/rules_spec.rb' + - 'spec/type_checker_spec.rb' + - 'spec/workspace_spec.rb' + - 'spec/yard_map/mapper/to_method_spec.rb' + - 'spec/yard_map/mapper_spec.rb' # Offense count: 4 # This cop supports safe autocorrection (--autocorrect). @@ -936,13 +1484,76 @@ RSpec/MissingExampleGroupArgument: - 'spec/diagnostics/rubocop_helpers_spec.rb' # Offense count: 471 +# Configuration parameters: Max. RSpec/MultipleExpectations: - Max: 14 + Exclude: + - 'spec/api_map/cache_spec.rb' + - 'spec/api_map/config_spec.rb' + - 'spec/api_map/source_to_yard_spec.rb' + - 'spec/api_map/store_spec.rb' + - 'spec/api_map_spec.rb' + - 'spec/complex_type_spec.rb' + - 'spec/diagnostics/rubocop_spec.rb' + - 'spec/diagnostics/type_check_spec.rb' + - 'spec/diagnostics/update_errors_spec.rb' + - 'spec/diagnostics_spec.rb' + - 'spec/doc_map_spec.rb' + - 'spec/gem_pins_spec.rb' + - 'spec/language_server/host/message_worker_spec.rb' + - 'spec/language_server/host_spec.rb' + - 'spec/language_server/message/completion_item/resolve_spec.rb' + - 'spec/language_server/message/initialize_spec.rb' + - 'spec/language_server/message/text_document/rename_spec.rb' + - 'spec/language_server/message/workspace/did_change_watched_files_spec.rb' + - 'spec/language_server/protocol_spec.rb' + - 'spec/language_server/transport/adapter_spec.rb' + - 'spec/language_server/transport/data_reader_spec.rb' + - 'spec/library_spec.rb' + - 'spec/parser/flow_sensitive_typing_spec.rb' + - 'spec/parser/node_chainer_spec.rb' + - 'spec/parser/node_methods_spec.rb' + - 'spec/parser/node_processor_spec.rb' + - 'spec/pin/base_spec.rb' + - 'spec/pin/base_variable_spec.rb' + - 'spec/pin/constant_spec.rb' + - 'spec/pin/instance_variable_spec.rb' + - 'spec/pin/local_variable_spec.rb' + - 'spec/pin/method_spec.rb' + - 'spec/pin/namespace_spec.rb' + - 'spec/pin/parameter_spec.rb' + - 'spec/position_spec.rb' + - 'spec/rbs_map/core_map_spec.rb' + - 'spec/rbs_map/stdlib_map_spec.rb' + - 'spec/rbs_map_spec.rb' + - 'spec/source/chain/call_spec.rb' + - 'spec/source/chain/class_variable_spec.rb' + - 'spec/source/chain/global_variable_spec.rb' + - 'spec/source/chain/head_spec.rb' + - 'spec/source/chain/instance_variable_spec.rb' + - 'spec/source/chain_spec.rb' + - 'spec/source/cursor_spec.rb' + - 'spec/source/source_chainer_spec.rb' + - 'spec/source_map/clip_spec.rb' + - 'spec/source_map/mapper_spec.rb' + - 'spec/source_map/node_processor_spec.rb' + - 'spec/source_map_spec.rb' + - 'spec/source_spec.rb' + - 'spec/type_checker/checks_spec.rb' + - 'spec/type_checker/levels/normal_spec.rb' + - 'spec/type_checker/levels/strict_spec.rb' + - 'spec/type_checker/levels/strong_spec.rb' + - 'spec/type_checker/levels/typed_spec.rb' + - 'spec/type_checker/rules_spec.rb' + - 'spec/workspace/config_spec.rb' + - 'spec/workspace_spec.rb' + - 'spec/yard_map/mapper/to_method_spec.rb' + - 'spec/yard_map/mapper_spec.rb' # Offense count: 1 -# Configuration parameters: AllowedGroups. +# Configuration parameters: Max, AllowedGroups. RSpec/NestedGroups: - Max: 4 + Exclude: + - 'spec/complex_type_spec.rb' # Offense count: 8 # Configuration parameters: AllowedPatterns. @@ -1013,28 +1624,117 @@ RSpec/RepeatedDescription: - 'spec/source_map/mapper_spec.rb' - 'spec/type_checker/levels/normal_spec.rb' - 'spec/type_checker/levels/strict_spec.rb' - -# Offense count: 30 -RSpec/RepeatedExample: - Exclude: - - 'spec/api_map_spec.rb' - - 'spec/parser/node_methods_spec.rb' - - 'spec/source/cursor_spec.rb' - - 'spec/source_map/clip_spec.rb' - - 'spec/type_checker/levels/strict_spec.rb' - -# Offense count: 2 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AutoCorrect. -RSpec/ScatteredLet: - Exclude: - - 'spec/complex_type_spec.rb' - -# Offense count: 88 -# Configuration parameters: Include, CustomTransform, IgnoreMethods, IgnoreMetadata. -# Include: **/*_spec.rb -RSpec/SpecFilePathFormat: - Enabled: false + +# Offense count: 30 +RSpec/RepeatedExample: + Exclude: + - 'spec/api_map_spec.rb' + - 'spec/parser/node_methods_spec.rb' + - 'spec/source/cursor_spec.rb' + - 'spec/source_map/clip_spec.rb' + - 'spec/type_checker/levels/strict_spec.rb' + +# Offense count: 2 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: AutoCorrect. +RSpec/ScatteredLet: + Exclude: + - 'spec/complex_type_spec.rb' + +# Offense count: 88 +# Configuration parameters: Include, CustomTransform, IgnoreMethods, IgnoreMetadata. +# Include: **/*_spec.rb +RSpec/SpecFilePathFormat: + Exclude: + - '**/spec/routing/**/*' + - 'spec/api_map/cache_spec.rb' + - 'spec/api_map/config_spec.rb' + - 'spec/api_map/source_to_yard_spec.rb' + - 'spec/api_map/store_spec.rb' + - 'spec/api_map_spec.rb' + - 'spec/convention_spec.rb' + - 'spec/diagnostics/base_spec.rb' + - 'spec/diagnostics/require_not_found_spec.rb' + - 'spec/diagnostics/rubocop_helpers_spec.rb' + - 'spec/diagnostics/rubocop_spec.rb' + - 'spec/diagnostics/type_check_spec.rb' + - 'spec/diagnostics/update_errors_spec.rb' + - 'spec/diagnostics_spec.rb' + - 'spec/doc_map_spec.rb' + - 'spec/gem_pins_spec.rb' + - 'spec/language_server/host/diagnoser_spec.rb' + - 'spec/language_server/host/dispatch_spec.rb' + - 'spec/language_server/host/message_worker_spec.rb' + - 'spec/language_server/host_spec.rb' + - 'spec/language_server/message/completion_item/resolve_spec.rb' + - 'spec/language_server/message/extended/check_gem_version_spec.rb' + - 'spec/language_server/message/initialize_spec.rb' + - 'spec/language_server/message/text_document/definition_spec.rb' + - 'spec/language_server/message/text_document/formatting_spec.rb' + - 'spec/language_server/message/text_document/hover_spec.rb' + - 'spec/language_server/message/text_document/rename_spec.rb' + - 'spec/language_server/message/text_document/type_definition_spec.rb' + - 'spec/language_server/message/workspace/did_change_configuration_spec.rb' + - 'spec/language_server/message/workspace/did_change_watched_files_spec.rb' + - 'spec/language_server/message_spec.rb' + - 'spec/language_server/transport/adapter_spec.rb' + - 'spec/language_server/transport/data_reader_spec.rb' + - 'spec/language_server/uri_helpers_spec.rb' + - 'spec/library_spec.rb' + - 'spec/logging_spec.rb' + - 'spec/parser/flow_sensitive_typing_spec.rb' + - 'spec/parser/node_methods_spec.rb' + - 'spec/parser/node_processor_spec.rb' + - 'spec/parser_spec.rb' + - 'spec/pin/base_spec.rb' + - 'spec/pin/base_variable_spec.rb' + - 'spec/pin/block_spec.rb' + - 'spec/pin/constant_spec.rb' + - 'spec/pin/delegated_method_spec.rb' + - 'spec/pin/documenting_spec.rb' + - 'spec/pin/instance_variable_spec.rb' + - 'spec/pin/keyword_spec.rb' + - 'spec/pin/local_variable_spec.rb' + - 'spec/pin/method_spec.rb' + - 'spec/pin/namespace_spec.rb' + - 'spec/pin/parameter_spec.rb' + - 'spec/pin/search_spec.rb' + - 'spec/pin/symbol_spec.rb' + - 'spec/position_spec.rb' + - 'spec/rbs_map/conversions_spec.rb' + - 'spec/rbs_map/core_map_spec.rb' + - 'spec/rbs_map/stdlib_map_spec.rb' + - 'spec/rbs_map_spec.rb' + - 'spec/source/chain/array_spec.rb' + - 'spec/source/chain/call_spec.rb' + - 'spec/source/chain/class_variable_spec.rb' + - 'spec/source/chain/constant_spec.rb' + - 'spec/source/chain/global_variable_spec.rb' + - 'spec/source/chain/head_spec.rb' + - 'spec/source/chain/instance_variable_spec.rb' + - 'spec/source/chain/link_spec.rb' + - 'spec/source/chain/literal_spec.rb' + - 'spec/source/chain/z_super_spec.rb' + - 'spec/source/chain_spec.rb' + - 'spec/source/change_spec.rb' + - 'spec/source/cursor_spec.rb' + - 'spec/source/source_chainer_spec.rb' + - 'spec/source/updater_spec.rb' + - 'spec/source_map/clip_spec.rb' + - 'spec/source_map/mapper_spec.rb' + - 'spec/source_map_spec.rb' + - 'spec/source_spec.rb' + - 'spec/type_checker/checks_spec.rb' + - 'spec/type_checker/levels/normal_spec.rb' + - 'spec/type_checker/levels/strict_spec.rb' + - 'spec/type_checker/levels/strong_spec.rb' + - 'spec/type_checker/levels/typed_spec.rb' + - 'spec/type_checker/rules_spec.rb' + - 'spec/type_checker_spec.rb' + - 'spec/workspace/config_spec.rb' + - 'spec/workspace_spec.rb' + - 'spec/yard_map/mapper/to_method_spec.rb' + - 'spec/yard_map/mapper_spec.rb' # Offense count: 1 RSpec/StubbedMock: @@ -1136,7 +1836,33 @@ Style/ArgumentsForwarding: # FunctionalMethods: let, let!, subject, watch # AllowedMethods: lambda, proc, it Style/BlockDelimiters: - Enabled: false + Exclude: + - 'lib/solargraph/api_map/source_to_yard.rb' + - 'lib/solargraph/api_map/store.rb' + - 'lib/solargraph/language_server/host.rb' + - 'lib/solargraph/parser/parser_gem/node_methods.rb' + - 'lib/solargraph/shell.rb' + - 'lib/solargraph/source.rb' + - 'lib/solargraph/source_map/clip.rb' + - 'lib/solargraph/source_map/mapper.rb' + - 'spec/diagnostics/rubocop_spec.rb' + - 'spec/language_server/host_spec.rb' + - 'spec/language_server/message/extended/check_gem_version_spec.rb' + - 'spec/language_server/message/workspace/did_change_watched_files_spec.rb' + - 'spec/language_server/protocol_spec.rb' + - 'spec/language_server/transport/adapter_spec.rb' + - 'spec/language_server/transport/data_reader_spec.rb' + - 'spec/library_spec.rb' + - 'spec/parser/node_processor_spec.rb' + - 'spec/pin/documenting_spec.rb' + - 'spec/position_spec.rb' + - 'spec/source/chain_spec.rb' + - 'spec/source/source_chainer_spec.rb' + - 'spec/source_map/mapper_spec.rb' + - 'spec/source_spec.rb' + - 'spec/type_checker_spec.rb' + - 'spec/workspace_spec.rb' + - 'spec/yard_map/mapper/to_method_spec.rb' # Offense count: 7 # This cop supports unsafe autocorrection (--autocorrect-all). @@ -1214,20 +1940,157 @@ Style/ConcatArrayLiterals: Exclude: - 'lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb' -# Offense count: 4 +# Offense count: 3 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, SingleLineConditionsOnly, IncludeTernaryExpressions. # SupportedStyles: assign_to_condition, assign_inside_condition Style/ConditionalAssignment: Exclude: - - 'lib/solargraph/api_map/source_to_yard.rb' - 'lib/solargraph/parser/parser_gem/node_processors/defs_node.rb' - 'lib/solargraph/source/chain/call.rb' # Offense count: 140 # Configuration parameters: AllowedConstants. Style/Documentation: - Enabled: false + Exclude: + - 'spec/**/*' + - 'test/**/*' + - 'lib/solargraph/api_map/cache.rb' + - 'lib/solargraph/api_map/index.rb' + - 'lib/solargraph/api_map/source_to_yard.rb' + - 'lib/solargraph/convention/gemfile.rb' + - 'lib/solargraph/convention/gemspec.rb' + - 'lib/solargraph/convention/rakefile.rb' + - 'lib/solargraph/convention/struct_definition.rb' + - 'lib/solargraph/converters/dd.rb' + - 'lib/solargraph/converters/dl.rb' + - 'lib/solargraph/converters/dt.rb' + - 'lib/solargraph/diagnostics/update_errors.rb' + - 'lib/solargraph/language_server/message/base.rb' + - 'lib/solargraph/language_server/message/cancel_request.rb' + - 'lib/solargraph/language_server/message/client.rb' + - 'lib/solargraph/language_server/message/client/register_capability.rb' + - 'lib/solargraph/language_server/message/completion_item.rb' + - 'lib/solargraph/language_server/message/exit_notification.rb' + - 'lib/solargraph/language_server/message/extended/document.rb' + - 'lib/solargraph/language_server/message/extended/search.rb' + - 'lib/solargraph/language_server/message/initialize.rb' + - 'lib/solargraph/language_server/message/initialized.rb' + - 'lib/solargraph/language_server/message/method_not_found.rb' + - 'lib/solargraph/language_server/message/method_not_implemented.rb' + - 'lib/solargraph/language_server/message/shutdown.rb' + - 'lib/solargraph/language_server/message/text_document.rb' + - 'lib/solargraph/language_server/message/text_document/base.rb' + - 'lib/solargraph/language_server/message/text_document/code_action.rb' + - 'lib/solargraph/language_server/message/text_document/completion.rb' + - 'lib/solargraph/language_server/message/text_document/definition.rb' + - 'lib/solargraph/language_server/message/text_document/did_change.rb' + - 'lib/solargraph/language_server/message/text_document/did_close.rb' + - 'lib/solargraph/language_server/message/text_document/did_open.rb' + - 'lib/solargraph/language_server/message/text_document/did_save.rb' + - 'lib/solargraph/language_server/message/text_document/document_highlight.rb' + - 'lib/solargraph/language_server/message/text_document/document_symbol.rb' + - 'lib/solargraph/language_server/message/text_document/folding_range.rb' + - 'lib/solargraph/language_server/message/text_document/formatting.rb' + - 'lib/solargraph/language_server/message/text_document/hover.rb' + - 'lib/solargraph/language_server/message/text_document/on_type_formatting.rb' + - 'lib/solargraph/language_server/message/text_document/prepare_rename.rb' + - 'lib/solargraph/language_server/message/text_document/references.rb' + - 'lib/solargraph/language_server/message/text_document/rename.rb' + - 'lib/solargraph/language_server/message/text_document/signature_help.rb' + - 'lib/solargraph/language_server/message/text_document/type_definition.rb' + - 'lib/solargraph/language_server/message/workspace.rb' + - 'lib/solargraph/language_server/message/workspace/did_change_configuration.rb' + - 'lib/solargraph/language_server/message/workspace/did_change_watched_files.rb' + - 'lib/solargraph/language_server/message/workspace/did_change_workspace_folders.rb' + - 'lib/solargraph/language_server/message/workspace/workspace_symbol.rb' + - 'lib/solargraph/language_server/request.rb' + - 'lib/solargraph/language_server/transport/data_reader.rb' + - 'lib/solargraph/logging.rb' + - 'lib/solargraph/page.rb' + - 'lib/solargraph/parser.rb' + - 'lib/solargraph/parser/comment_ripper.rb' + - 'lib/solargraph/parser/flow_sensitive_typing.rb' + - 'lib/solargraph/parser/node_methods.rb' + - 'lib/solargraph/parser/node_processor/base.rb' + - 'lib/solargraph/parser/parser_gem.rb' + - 'lib/solargraph/parser/parser_gem/class_methods.rb' + - 'lib/solargraph/parser/parser_gem/node_methods.rb' + - 'lib/solargraph/parser/parser_gem/node_processors.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/alias_node.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/and_node.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/args_node.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/begin_node.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/block_node.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/casgn_node.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/cvasgn_node.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/def_node.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/defs_node.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/gvasgn_node.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/if_node.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/namespace_node.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/orasgn_node.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/resbody_node.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/send_node.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/sym_node.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/until_node.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/while_node.rb' + - 'lib/solargraph/parser/snippet.rb' + - 'lib/solargraph/pin/base_variable.rb' + - 'lib/solargraph/pin/block.rb' + - 'lib/solargraph/pin/callable.rb' + - 'lib/solargraph/pin/closure.rb' + - 'lib/solargraph/pin/common.rb' + - 'lib/solargraph/pin/constant.rb' + - 'lib/solargraph/pin/instance_variable.rb' + - 'lib/solargraph/pin/keyword.rb' + - 'lib/solargraph/pin/local_variable.rb' + - 'lib/solargraph/pin/namespace.rb' + - 'lib/solargraph/pin/parameter.rb' + - 'lib/solargraph/pin/proxy_type.rb' + - 'lib/solargraph/pin/reference.rb' + - 'lib/solargraph/pin/reference/override.rb' + - 'lib/solargraph/pin/reference/require.rb' + - 'lib/solargraph/pin/search.rb' + - 'lib/solargraph/pin/signature.rb' + - 'lib/solargraph/pin/singleton.rb' + - 'lib/solargraph/pin/symbol.rb' + - 'lib/solargraph/pin/until.rb' + - 'lib/solargraph/pin/while.rb' + - 'lib/solargraph/pin_cache.rb' + - 'lib/solargraph/rbs_map.rb' + - 'lib/solargraph/repro.rb' + - 'lib/solargraph/server_methods.rb' + - 'lib/solargraph/shell.rb' + - 'lib/solargraph/source/chain/array.rb' + - 'lib/solargraph/source/chain/block_symbol.rb' + - 'lib/solargraph/source/chain/block_variable.rb' + - 'lib/solargraph/source/chain/class_variable.rb' + - 'lib/solargraph/source/chain/constant.rb' + - 'lib/solargraph/source/chain/global_variable.rb' + - 'lib/solargraph/source/chain/hash.rb' + - 'lib/solargraph/source/chain/if.rb' + - 'lib/solargraph/source/chain/instance_variable.rb' + - 'lib/solargraph/source/chain/link.rb' + - 'lib/solargraph/source/chain/literal.rb' + - 'lib/solargraph/source/chain/or.rb' + - 'lib/solargraph/source/chain/q_call.rb' + - 'lib/solargraph/source/chain/variable.rb' + - 'lib/solargraph/source/chain/z_super.rb' + - 'lib/solargraph/source/encoding_fixes.rb' + - 'lib/solargraph/source_map/data.rb' + - 'lib/solargraph/yard_map/cache.rb' + - 'lib/solargraph/yard_map/helpers.rb' + - 'lib/solargraph/yard_map/mapper.rb' + - 'lib/solargraph/yard_map/mapper/to_constant.rb' + - 'lib/solargraph/yard_map/mapper/to_method.rb' + - 'lib/solargraph/yard_map/mapper/to_namespace.rb' + - 'lib/solargraph/yard_map/to_method.rb' # Offense count: 1 # This cop supports safe autocorrection (--autocorrect). @@ -1285,7 +2148,136 @@ Style/FloatDivision: # Configuration parameters: EnforcedStyle. # SupportedStyles: always, always_true, never Style/FrozenStringLiteralComment: - Enabled: false + Exclude: + - '**/*.arb' + - 'Gemfile' + - 'Rakefile' + - 'Steepfile' + - 'bin/solargraph' + - 'lib/solargraph/converters/dd.rb' + - 'lib/solargraph/converters/dl.rb' + - 'lib/solargraph/converters/dt.rb' + - 'lib/solargraph/converters/misc.rb' + - 'lib/solargraph/parser.rb' + - 'lib/solargraph/parser/comment_ripper.rb' + - 'lib/solargraph/parser/flow_sensitive_typing.rb' + - 'lib/solargraph/parser/node_methods.rb' + - 'lib/solargraph/parser/parser_gem.rb' + - 'lib/solargraph/parser/snippet.rb' + - 'lib/solargraph/pin/breakable.rb' + - 'lib/solargraph/pin/signature.rb' + - 'lib/solargraph/pin_cache.rb' + - 'lib/solargraph/repro.rb' + - 'lib/solargraph/source/chain/array.rb' + - 'lib/solargraph/source/chain/q_call.rb' + - 'lib/solargraph/yard_map/helpers.rb' + - 'solargraph.gemspec' + - 'spec/api_map/cache_spec.rb' + - 'spec/api_map/config_spec.rb' + - 'spec/api_map/source_to_yard_spec.rb' + - 'spec/api_map_spec.rb' + - 'spec/complex_type_spec.rb' + - 'spec/convention_spec.rb' + - 'spec/diagnostics/base_spec.rb' + - 'spec/diagnostics/require_not_found_spec.rb' + - 'spec/diagnostics/rubocop_helpers_spec.rb' + - 'spec/diagnostics/type_check_spec.rb' + - 'spec/diagnostics/update_errors_spec.rb' + - 'spec/diagnostics_spec.rb' + - 'spec/fixtures/formattable.rb' + - 'spec/fixtures/long_squiggly_heredoc.rb' + - 'spec/fixtures/rdoc-lib/Gemfile' + - 'spec/fixtures/rdoc-lib/lib/example.rb' + - 'spec/fixtures/rdoc-lib/rdoc-lib.gemspec' + - 'spec/fixtures/rubocop-custom-version/specifications/rubocop-0.0.0.gemspec' + - 'spec/fixtures/rubocop-validation-error/app.rb' + - 'spec/fixtures/unicode.rb' + - 'spec/fixtures/workspace-with-gemfile/Gemfile' + - 'spec/fixtures/workspace-with-gemfile/app.rb' + - 'spec/fixtures/workspace-with-gemfile/lib/other.rb' + - 'spec/fixtures/workspace-with-gemfile/lib/thing.rb' + - 'spec/fixtures/workspace/app.rb' + - 'spec/fixtures/workspace/lib/other.rb' + - 'spec/fixtures/workspace/lib/something.rb' + - 'spec/fixtures/workspace/lib/thing.rb' + - 'spec/fixtures/workspace_folders/folder1/app.rb' + - 'spec/fixtures/workspace_folders/folder2/app.rb' + - 'spec/fixtures/yard_map/attr.rb' + - 'spec/language_server/host/diagnoser_spec.rb' + - 'spec/language_server/host/dispatch_spec.rb' + - 'spec/language_server/host/message_worker_spec.rb' + - 'spec/language_server/host_spec.rb' + - 'spec/language_server/message/completion_item/resolve_spec.rb' + - 'spec/language_server/message/extended/check_gem_version_spec.rb' + - 'spec/language_server/message/initialize_spec.rb' + - 'spec/language_server/message/text_document/definition_spec.rb' + - 'spec/language_server/message/text_document/formatting_spec.rb' + - 'spec/language_server/message/text_document/hover_spec.rb' + - 'spec/language_server/message/text_document/type_definition_spec.rb' + - 'spec/language_server/message/workspace/did_change_watched_files_spec.rb' + - 'spec/language_server/message_spec.rb' + - 'spec/language_server/protocol_spec.rb' + - 'spec/language_server/transport/adapter_spec.rb' + - 'spec/language_server/transport/data_reader_spec.rb' + - 'spec/language_server/uri_helpers_spec.rb' + - 'spec/library_spec.rb' + - 'spec/logging_spec.rb' + - 'spec/parser/node_chainer_spec.rb' + - 'spec/parser/node_methods_spec.rb' + - 'spec/parser/node_processor_spec.rb' + - 'spec/parser_spec.rb' + - 'spec/pin/base_spec.rb' + - 'spec/pin/base_variable_spec.rb' + - 'spec/pin/block_spec.rb' + - 'spec/pin/class_variable_spec.rb' + - 'spec/pin/constant_spec.rb' + - 'spec/pin/delegated_method_spec.rb' + - 'spec/pin/documenting_spec.rb' + - 'spec/pin/instance_variable_spec.rb' + - 'spec/pin/keyword_spec.rb' + - 'spec/pin/local_variable_spec.rb' + - 'spec/pin/method_spec.rb' + - 'spec/pin/namespace_spec.rb' + - 'spec/pin/parameter_spec.rb' + - 'spec/pin/search_spec.rb' + - 'spec/pin/symbol_spec.rb' + - 'spec/position_spec.rb' + - 'spec/rbs_map/conversions_spec.rb' + - 'spec/rbs_map/core_map_spec.rb' + - 'spec/rbs_map/stdlib_map_spec.rb' + - 'spec/rbs_map_spec.rb' + - 'spec/source/chain/array_spec.rb' + - 'spec/source/chain/call_spec.rb' + - 'spec/source/chain/class_variable_spec.rb' + - 'spec/source/chain/constant_spec.rb' + - 'spec/source/chain/global_variable_spec.rb' + - 'spec/source/chain/head_spec.rb' + - 'spec/source/chain/instance_variable_spec.rb' + - 'spec/source/chain/link_spec.rb' + - 'spec/source/chain/literal_spec.rb' + - 'spec/source/chain/z_super_spec.rb' + - 'spec/source/chain_spec.rb' + - 'spec/source/change_spec.rb' + - 'spec/source/cursor_spec.rb' + - 'spec/source/source_chainer_spec.rb' + - 'spec/source/updater_spec.rb' + - 'spec/source_map/clip_spec.rb' + - 'spec/source_map/mapper_spec.rb' + - 'spec/source_map/node_processor_spec.rb' + - 'spec/source_map_spec.rb' + - 'spec/source_spec.rb' + - 'spec/spec_helper.rb' + - 'spec/type_checker/checks_spec.rb' + - 'spec/type_checker/levels/normal_spec.rb' + - 'spec/type_checker/levels/strict_spec.rb' + - 'spec/type_checker/levels/strong_spec.rb' + - 'spec/type_checker/levels/typed_spec.rb' + - 'spec/type_checker/rules_spec.rb' + - 'spec/type_checker_spec.rb' + - 'spec/workspace/config_spec.rb' + - 'spec/workspace_spec.rb' + - 'spec/yard_map/mapper/to_method_spec.rb' + - 'spec/yard_map/mapper_spec.rb' # Offense count: 11 # This cop supports unsafe autocorrection (--autocorrect-all). @@ -1355,11 +2347,45 @@ Style/IfInsideElse: - 'lib/solargraph/parser/parser_gem/node_methods.rb' - 'lib/solargraph/source_map/clip.rb' - 'lib/solargraph/type_checker.rb' - -# Offense count: 46 -# This cop supports safe autocorrection (--autocorrect). -Style/IfUnlessModifier: - Enabled: false + +# Offense count: 63 +# This cop supports safe autocorrection (--autocorrect). +Style/IfUnlessModifier: + Exclude: + - 'lib/solargraph/api_map.rb' + - 'lib/solargraph/api_map/index.rb' + - 'lib/solargraph/complex_type.rb' + - 'lib/solargraph/complex_type/unique_type.rb' + - 'lib/solargraph/doc_map.rb' + - 'lib/solargraph/language_server/message/completion_item/resolve.rb' + - 'lib/solargraph/language_server/message/initialize.rb' + - 'lib/solargraph/language_server/message/text_document/completion.rb' + - 'lib/solargraph/language_server/message/text_document/hover.rb' + - 'lib/solargraph/library.rb' + - 'lib/solargraph/parser/parser_gem/class_methods.rb' + - 'lib/solargraph/parser/parser_gem/node_chainer.rb' + - 'lib/solargraph/parser/parser_gem/node_methods.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb' + - 'lib/solargraph/pin/base.rb' + - 'lib/solargraph/pin/callable.rb' + - 'lib/solargraph/pin/common.rb' + - 'lib/solargraph/pin/constant.rb' + - 'lib/solargraph/pin/method.rb' + - 'lib/solargraph/pin/parameter.rb' + - 'lib/solargraph/range.rb' + - 'lib/solargraph/rbs_map/conversions.rb' + - 'lib/solargraph/source/chain.rb' + - 'lib/solargraph/source/chain/call.rb' + - 'lib/solargraph/source/change.rb' + - 'lib/solargraph/source/cursor.rb' + - 'lib/solargraph/source/source_chainer.rb' + - 'lib/solargraph/source_map.rb' + - 'lib/solargraph/source_map/clip.rb' + - 'lib/solargraph/source_map/mapper.rb' + - 'lib/solargraph/type_checker.rb' + - 'lib/solargraph/workspace.rb' + - 'lib/solargraph/workspace/config.rb' + - 'lib/solargraph/yard_map/mapper/to_method.rb' # Offense count: 2 # This cop supports safe autocorrection (--autocorrect). @@ -1395,7 +2421,64 @@ Style/MapToSet: # Configuration parameters: EnforcedStyle. # SupportedStyles: require_parentheses, require_no_parentheses, require_no_parentheses_except_multiline Style/MethodDefParentheses: - Enabled: false + Exclude: + - 'lib/solargraph.rb' + - 'lib/solargraph/api_map.rb' + - 'lib/solargraph/api_map/index.rb' + - 'lib/solargraph/api_map/store.rb' + - 'lib/solargraph/complex_type.rb' + - 'lib/solargraph/complex_type/type_methods.rb' + - 'lib/solargraph/complex_type/unique_type.rb' + - 'lib/solargraph/convention.rb' + - 'lib/solargraph/convention/struct_definition.rb' + - 'lib/solargraph/convention/struct_definition/struct_assignment_node.rb' + - 'lib/solargraph/convention/struct_definition/struct_definition_node.rb' + - 'lib/solargraph/diagnostics/rubocop_helpers.rb' + - 'lib/solargraph/doc_map.rb' + - 'lib/solargraph/equality.rb' + - 'lib/solargraph/gem_pins.rb' + - 'lib/solargraph/language_server/host/message_worker.rb' + - 'lib/solargraph/language_server/host/sources.rb' + - 'lib/solargraph/language_server/message/text_document/formatting.rb' + - 'lib/solargraph/location.rb' + - 'lib/solargraph/parser/comment_ripper.rb' + - 'lib/solargraph/parser/flow_sensitive_typing.rb' + - 'lib/solargraph/parser/node_methods.rb' + - 'lib/solargraph/parser/node_processor/base.rb' + - 'lib/solargraph/parser/parser_gem/flawed_builder.rb' + - 'lib/solargraph/parser/parser_gem/node_chainer.rb' + - 'lib/solargraph/parser/parser_gem/node_methods.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/args_node.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/namespace_node.rb' + - 'lib/solargraph/pin/base.rb' + - 'lib/solargraph/pin/base_variable.rb' + - 'lib/solargraph/pin/block.rb' + - 'lib/solargraph/pin/callable.rb' + - 'lib/solargraph/pin/closure.rb' + - 'lib/solargraph/pin/delegated_method.rb' + - 'lib/solargraph/pin/local_variable.rb' + - 'lib/solargraph/pin/method.rb' + - 'lib/solargraph/pin/parameter.rb' + - 'lib/solargraph/pin_cache.rb' + - 'lib/solargraph/position.rb' + - 'lib/solargraph/range.rb' + - 'lib/solargraph/rbs_map/conversions.rb' + - 'lib/solargraph/shell.rb' + - 'lib/solargraph/source.rb' + - 'lib/solargraph/source/chain/call.rb' + - 'lib/solargraph/source/chain/constant.rb' + - 'lib/solargraph/source_map.rb' + - 'lib/solargraph/source_map/mapper.rb' + - 'lib/solargraph/type_checker.rb' + - 'lib/solargraph/type_checker/checks.rb' + - 'lib/solargraph/yardoc.rb' + - 'spec/fixtures/rdoc-lib/lib/example.rb' + - 'spec/source_map_spec.rb' + - 'spec/spec_helper.rb' + - 'spec/type_checker/levels/normal_spec.rb' + - 'spec/type_checker/levels/strict_spec.rb' + - 'spec/type_checker/levels/strong_spec.rb' + - 'spec/type_checker/levels/typed_spec.rb' # Offense count: 1 Style/MultilineBlockChain: @@ -1489,16 +2572,35 @@ Style/Next: # Offense count: 11 # This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: Strict, AllowedNumbers, AllowedPatterns. +# Configuration parameters: MinDigits, Strict, AllowedNumbers, AllowedPatterns. Style/NumericLiterals: - MinDigits: 6 + Exclude: + - 'lib/solargraph/language_server/error_codes.rb' + - 'spec/language_server/protocol_spec.rb' # Offense count: 31 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: EnforcedStyle, AllowedMethods, AllowedPatterns. # SupportedStyles: predicate, comparison Style/NumericPredicate: - Enabled: false + Exclude: + - 'spec/**/*' + - 'lib/solargraph/api_map.rb' + - 'lib/solargraph/api_map/index.rb' + - 'lib/solargraph/api_map/store.rb' + - 'lib/solargraph/complex_type.rb' + - 'lib/solargraph/complex_type/type_methods.rb' + - 'lib/solargraph/complex_type/unique_type.rb' + - 'lib/solargraph/language_server/message/extended/check_gem_version.rb' + - 'lib/solargraph/library.rb' + - 'lib/solargraph/parser/comment_ripper.rb' + - 'lib/solargraph/pin/delegated_method.rb' + - 'lib/solargraph/pin/method.rb' + - 'lib/solargraph/shell.rb' + - 'lib/solargraph/source/chain/array.rb' + - 'lib/solargraph/source/change.rb' + - 'lib/solargraph/source/source_chainer.rb' + - 'lib/solargraph/workspace.rb' # Offense count: 1 Style/OpenStructUse: @@ -1689,7 +2791,24 @@ Style/SafeNavigationChainLength: # Offense count: 39 # This cop supports unsafe autocorrection (--autocorrect-all). Style/SlicingWithRange: - Enabled: false + Exclude: + - 'lib/solargraph/api_map.rb' + - 'lib/solargraph/complex_type/unique_type.rb' + - 'lib/solargraph/convention/struct_definition/struct_definition_node.rb' + - 'lib/solargraph/diagnostics/rubocop_helpers.rb' + - 'lib/solargraph/library.rb' + - 'lib/solargraph/parser/parser_gem/node_chainer.rb' + - 'lib/solargraph/parser/parser_gem/node_methods.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/send_node.rb' + - 'lib/solargraph/pin/method.rb' + - 'lib/solargraph/pin/namespace.rb' + - 'lib/solargraph/source.rb' + - 'lib/solargraph/source/chain/constant.rb' + - 'lib/solargraph/source/change.rb' + - 'lib/solargraph/source/cursor.rb' + - 'lib/solargraph/source/source_chainer.rb' + - 'lib/solargraph/source_map/mapper.rb' + - 'lib/solargraph/type_checker/checks.rb' # Offense count: 6 # This cop supports safe autocorrection (--autocorrect). @@ -1730,7 +2849,98 @@ Style/StringConcatenation: # Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline. # SupportedStyles: single_quotes, double_quotes Style/StringLiterals: - Enabled: false + Exclude: + - 'Gemfile' + - 'Rakefile' + - 'Steepfile' + - 'lib/solargraph/api_map/index.rb' + - 'lib/solargraph/complex_type.rb' + - 'lib/solargraph/complex_type/unique_type.rb' + - 'lib/solargraph/doc_map.rb' + - 'lib/solargraph/language_server/host.rb' + - 'lib/solargraph/language_server/message/extended/document_gems.rb' + - 'lib/solargraph/language_server/message/extended/download_core.rb' + - 'lib/solargraph/language_server/message/initialize.rb' + - 'lib/solargraph/language_server/message/text_document/completion.rb' + - 'lib/solargraph/library.rb' + - 'lib/solargraph/parser/parser_gem/class_methods.rb' + - 'lib/solargraph/parser/parser_gem/node_methods.rb' + - 'lib/solargraph/pin/base.rb' + - 'lib/solargraph/pin/conversions.rb' + - 'lib/solargraph/pin/method.rb' + - 'lib/solargraph/pin/parameter.rb' + - 'lib/solargraph/rbs_map/conversions.rb' + - 'lib/solargraph/repro.rb' + - 'lib/solargraph/server_methods.rb' + - 'lib/solargraph/shell.rb' + - 'lib/solargraph/workspace.rb' + - 'lib/solargraph/yard_map/mapper/to_method.rb' + - 'lib/solargraph/yard_tags.rb' + - 'solargraph.gemspec' + - 'spec/api_map/cache_spec.rb' + - 'spec/api_map/config_spec.rb' + - 'spec/api_map/source_to_yard_spec.rb' + - 'spec/complex_type_spec.rb' + - 'spec/diagnostics/base_spec.rb' + - 'spec/diagnostics/require_not_found_spec.rb' + - 'spec/diagnostics/rubocop_helpers_spec.rb' + - 'spec/diagnostics/rubocop_spec.rb' + - 'spec/diagnostics/type_check_spec.rb' + - 'spec/diagnostics/update_errors_spec.rb' + - 'spec/diagnostics_spec.rb' + - 'spec/fixtures/rdoc-lib/rdoc-lib.gemspec' + - 'spec/language_server/host/diagnoser_spec.rb' + - 'spec/language_server/host/dispatch_spec.rb' + - 'spec/language_server/host/message_worker_spec.rb' + - 'spec/language_server/host_spec.rb' + - 'spec/language_server/message/completion_item/resolve_spec.rb' + - 'spec/language_server/message/extended/check_gem_version_spec.rb' + - 'spec/language_server/message/initialize_spec.rb' + - 'spec/language_server/message/text_document/rename_spec.rb' + - 'spec/language_server/message_spec.rb' + - 'spec/language_server/protocol_spec.rb' + - 'spec/language_server/transport/adapter_spec.rb' + - 'spec/language_server/transport/data_reader_spec.rb' + - 'spec/library_spec.rb' + - 'spec/logging_spec.rb' + - 'spec/parser/node_chainer_spec.rb' + - 'spec/parser/node_methods_spec.rb' + - 'spec/parser_spec.rb' + - 'spec/pin/base_spec.rb' + - 'spec/pin/base_variable_spec.rb' + - 'spec/pin/constant_spec.rb' + - 'spec/pin/instance_variable_spec.rb' + - 'spec/pin/keyword_spec.rb' + - 'spec/pin/local_variable_spec.rb' + - 'spec/pin/namespace_spec.rb' + - 'spec/pin/symbol_spec.rb' + - 'spec/position_spec.rb' + - 'spec/rbs_map/conversions_spec.rb' + - 'spec/rbs_map/core_map_spec.rb' + - 'spec/rbs_map/stdlib_map_spec.rb' + - 'spec/source/chain/array_spec.rb' + - 'spec/source/chain/call_spec.rb' + - 'spec/source/chain/class_variable_spec.rb' + - 'spec/source/chain/constant_spec.rb' + - 'spec/source/chain/global_variable_spec.rb' + - 'spec/source/chain/head_spec.rb' + - 'spec/source/chain/instance_variable_spec.rb' + - 'spec/source/chain/link_spec.rb' + - 'spec/source/chain/literal_spec.rb' + - 'spec/source/chain/z_super_spec.rb' + - 'spec/source/chain_spec.rb' + - 'spec/source/change_spec.rb' + - 'spec/source/cursor_spec.rb' + - 'spec/source/source_chainer_spec.rb' + - 'spec/source/updater_spec.rb' + - 'spec/source_map/clip_spec.rb' + - 'spec/source_map/mapper_spec.rb' + - 'spec/source_map_spec.rb' + - 'spec/source_spec.rb' + - 'spec/type_checker/levels/strict_spec.rb' + - 'spec/workspace/config_spec.rb' + - 'spec/workspace_spec.rb' + - 'spec/yard_map/mapper/to_method_spec.rb' # Offense count: 4 # This cop supports safe autocorrection (--autocorrect). @@ -1892,7 +3102,45 @@ YARD/CollectionType: # Configuration parameters: EnforcedStylePrototypeName. # SupportedStylesPrototypeName: before, after YARD/MismatchName: - Enabled: false + Exclude: + - 'lib/solargraph/api_map.rb' + - 'lib/solargraph/complex_type.rb' + - 'lib/solargraph/complex_type/unique_type.rb' + - 'lib/solargraph/convention.rb' + - 'lib/solargraph/convention/struct_definition.rb' + - 'lib/solargraph/doc_map.rb' + - 'lib/solargraph/gem_pins.rb' + - 'lib/solargraph/language_server/host.rb' + - 'lib/solargraph/language_server/host/dispatch.rb' + - 'lib/solargraph/language_server/message/text_document/formatting.rb' + - 'lib/solargraph/language_server/request.rb' + - 'lib/solargraph/parser/flow_sensitive_typing.rb' + - 'lib/solargraph/parser/parser_gem/node_methods.rb' + - 'lib/solargraph/parser/region.rb' + - 'lib/solargraph/pin/base.rb' + - 'lib/solargraph/pin/base_variable.rb' + - 'lib/solargraph/pin/block.rb' + - 'lib/solargraph/pin/callable.rb' + - 'lib/solargraph/pin/closure.rb' + - 'lib/solargraph/pin/delegated_method.rb' + - 'lib/solargraph/pin/method.rb' + - 'lib/solargraph/pin/namespace.rb' + - 'lib/solargraph/pin/parameter.rb' + - 'lib/solargraph/pin/proxy_type.rb' + - 'lib/solargraph/pin/reference.rb' + - 'lib/solargraph/pin/symbol.rb' + - 'lib/solargraph/pin/until.rb' + - 'lib/solargraph/pin/while.rb' + - 'lib/solargraph/pin_cache.rb' + - 'lib/solargraph/rbs_map.rb' + - 'lib/solargraph/rbs_map/conversions.rb' + - 'lib/solargraph/shell.rb' + - 'lib/solargraph/source/chain.rb' + - 'lib/solargraph/source/chain/call.rb' + - 'lib/solargraph/source/chain/z_super.rb' + - 'lib/solargraph/type_checker.rb' + - 'lib/solargraph/yard_map/mapper/to_constant.rb' + - 'lib/solargraph/yard_map/mapper/to_namespace.rb' # Offense count: 4 YARD/TagTypeSyntax: @@ -1903,7 +3151,72 @@ YARD/TagTypeSyntax: # Offense count: 191 # This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, IgnoreCopDirectives, AllowedPatterns, SplitStrings. +# Configuration parameters: Max, AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, IgnoreCopDirectives, AllowedPatterns, SplitStrings. # URISchemes: http, https Layout/LineLength: - Max: 257 + Exclude: + - 'lib/solargraph/api_map.rb' + - 'lib/solargraph/api_map/source_to_yard.rb' + - 'lib/solargraph/api_map/store.rb' + - 'lib/solargraph/complex_type.rb' + - 'lib/solargraph/complex_type/unique_type.rb' + - 'lib/solargraph/doc_map.rb' + - 'lib/solargraph/gem_pins.rb' + - 'lib/solargraph/language_server/host.rb' + - 'lib/solargraph/language_server/message/extended/check_gem_version.rb' + - 'lib/solargraph/language_server/message/extended/download_core.rb' + - 'lib/solargraph/language_server/message/initialize.rb' + - 'lib/solargraph/language_server/message/text_document/completion.rb' + - 'lib/solargraph/language_server/message/text_document/definition.rb' + - 'lib/solargraph/language_server/message/text_document/document_highlight.rb' + - 'lib/solargraph/language_server/message/text_document/prepare_rename.rb' + - 'lib/solargraph/language_server/message/text_document/references.rb' + - 'lib/solargraph/language_server/message/text_document/rename.rb' + - 'lib/solargraph/language_server/message/workspace/did_change_watched_files.rb' + - 'lib/solargraph/library.rb' + - 'lib/solargraph/parser/comment_ripper.rb' + - 'lib/solargraph/parser/flow_sensitive_typing.rb' + - 'lib/solargraph/parser/node_processor/base.rb' + - 'lib/solargraph/parser/parser_gem/node_chainer.rb' + - 'lib/solargraph/parser/parser_gem/node_methods.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/and_node.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/if_node.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb' + - 'lib/solargraph/parser/parser_gem/node_processors/send_node.rb' + - 'lib/solargraph/pin/base.rb' + - 'lib/solargraph/pin/callable.rb' + - 'lib/solargraph/pin/common.rb' + - 'lib/solargraph/pin/documenting.rb' + - 'lib/solargraph/pin/method.rb' + - 'lib/solargraph/pin/parameter.rb' + - 'lib/solargraph/rbs_map/conversions.rb' + - 'lib/solargraph/rbs_map/core_fills.rb' + - 'lib/solargraph/shell.rb' + - 'lib/solargraph/source.rb' + - 'lib/solargraph/source/chain.rb' + - 'lib/solargraph/source/chain/call.rb' + - 'lib/solargraph/source/chain/if.rb' + - 'lib/solargraph/source/chain/instance_variable.rb' + - 'lib/solargraph/source/chain/variable.rb' + - 'lib/solargraph/source/cursor.rb' + - 'lib/solargraph/source/encoding_fixes.rb' + - 'lib/solargraph/source/source_chainer.rb' + - 'lib/solargraph/source_map.rb' + - 'lib/solargraph/source_map/clip.rb' + - 'lib/solargraph/source_map/mapper.rb' + - 'lib/solargraph/type_checker.rb' + - 'lib/solargraph/workspace.rb' + - 'lib/solargraph/workspace/config.rb' + - 'lib/solargraph/yard_map/mapper/to_method.rb' + - 'spec/api_map_spec.rb' + - 'spec/complex_type_spec.rb' + - 'spec/language_server/message/completion_item/resolve_spec.rb' + - 'spec/language_server/message/extended/check_gem_version_spec.rb' + - 'spec/language_server/message/text_document/definition_spec.rb' + - 'spec/language_server/protocol_spec.rb' + - 'spec/pin/parameter_spec.rb' + - 'spec/source/chain_spec.rb' + - 'spec/source_map/clip_spec.rb' + - 'spec/source_map_spec.rb' + - 'spec/workspace_spec.rb' From 00c051621aa873388b3727ce87235d5b7de2f6f6 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 16 Jul 2025 11:12:18 -0400 Subject: [PATCH 200/561] Rebaseline todo file --- .rubocop.yml | 1 + .rubocop_todo.yml | 108 ++++++++++++++++++++++++++++++---------------- 2 files changed, 72 insertions(+), 37 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 3b4198618..65cae16b1 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -17,6 +17,7 @@ AllCops: - "spec/fixtures/invalid_byte.rb" - "spec/fixtures/invalid_node_comment.rb" - "spec/fixtures/invalid_utf8.rb" + - "spec/fixtures/rubocop-unused-variable-error/app.rb" - "vendor/**/*" - "vendor/**/.*" TargetRubyVersion: 3.0 diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index fd0f08b3c..d9559e334 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -58,12 +58,13 @@ Gemspec/RequiredRubyVersion: - 'spec/fixtures/rubocop-custom-version/specifications/rubocop-0.0.0.gemspec' - 'spec/fixtures/vendored/vendor/do_not_use.gemspec' -# Offense count: 5 +# Offense count: 8 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, IndentationWidth. # SupportedStyles: with_first_argument, with_fixed_indentation Layout/ArgumentAlignment: Exclude: + - 'lib/solargraph/convention/struct_definition.rb' - 'lib/solargraph/pin/callable.rb' - 'spec/source/source_chainer_spec.rb' @@ -123,7 +124,7 @@ Layout/EmptyLineBetweenDefs: - 'lib/solargraph/language_server/message/initialize.rb' - 'lib/solargraph/pin/delegated_method.rb' -# Offense count: 12 +# Offense count: 13 # This cop supports safe autocorrection (--autocorrect). Layout/EmptyLines: Exclude: @@ -136,6 +137,7 @@ Layout/EmptyLines: - 'lib/solargraph/pin/delegated_method.rb' - 'lib/solargraph/rbs_map/conversions.rb' - 'spec/complex_type_spec.rb' + - 'spec/convention/struct_definition_spec.rb' - 'spec/pin/local_variable_spec.rb' - 'spec/pin/symbol_spec.rb' - 'spec/type_checker/levels/strict_spec.rb' @@ -246,7 +248,7 @@ Layout/FirstHashElementIndentation: - 'spec/language_server/message/text_document/type_definition_spec.rb' - 'spec/language_server/message/workspace/did_change_watched_files_spec.rb' -# Offense count: 1 +# Offense count: 4 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowMultipleStyles, EnforcedHashRocketStyle, EnforcedColonStyle, EnforcedLastArgumentHashStyle. # SupportedHashRocketStyles: key, separator, table @@ -254,6 +256,7 @@ Layout/FirstHashElementIndentation: # SupportedLastArgumentHashStyles: always_inspect, always_ignore, ignore_implicit, ignore_explicit Layout/HashAlignment: Exclude: + - 'lib/solargraph/convention/struct_definition.rb' - 'lib/solargraph/workspace/config.rb' # Offense count: 3 @@ -384,7 +387,7 @@ Layout/SpaceAroundOperators: - 'spec/library_spec.rb' - 'spec/yard_map/mapper_spec.rb' -# Offense count: 108 +# Offense count: 105 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces. # SupportedStyles: space, no_space @@ -397,7 +400,6 @@ Layout/SpaceBeforeBlockBraces: - 'lib/solargraph/diagnostics/update_errors.rb' - 'lib/solargraph/language_server/host.rb' - 'lib/solargraph/library.rb' - - 'lib/solargraph/parser/node_processor/base.rb' - 'lib/solargraph/parser/parser_gem/node_methods.rb' - 'lib/solargraph/parser/parser_gem/node_processors/and_node.rb' - 'lib/solargraph/parser/parser_gem/node_processors/if_node.rb' @@ -430,7 +432,7 @@ Layout/SpaceBeforeComma: Exclude: - 'spec/source/cursor_spec.rb' -# Offense count: 183 +# Offense count: 177 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces, SpaceBeforeBlockParameters. # SupportedStyles: space, no_space @@ -441,7 +443,6 @@ Layout/SpaceInsideBlockBraces: - 'lib/solargraph/api_map/store.rb' - 'lib/solargraph/diagnostics/update_errors.rb' - 'lib/solargraph/language_server/host.rb' - - 'lib/solargraph/parser/node_processor/base.rb' - 'lib/solargraph/parser/parser_gem/node_methods.rb' - 'lib/solargraph/parser/parser_gem/node_processors/and_node.rb' - 'lib/solargraph/parser/parser_gem/node_processors/if_node.rb' @@ -482,13 +483,22 @@ Layout/SpaceInsideParens: - 'lib/solargraph/pin/namespace.rb' - 'lib/solargraph/source_map.rb' -# Offense count: 3 +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle. +# SupportedStyles: final_newline, final_blank_line +Layout/TrailingEmptyLines: + Exclude: + - 'spec/convention/struct_definition_spec.rb' + +# Offense count: 5 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowInHeredoc. Layout/TrailingWhitespace: Exclude: - 'lib/solargraph/language_server/message/client/register_capability.rb' - 'spec/api_map/config_spec.rb' + - 'spec/convention/struct_definition_spec.rb' - 'spec/convention_spec.rb' # Offense count: 2 @@ -745,7 +755,7 @@ Lint/UselessAccessModifier: Exclude: - 'lib/solargraph/api_map.rb' -# Offense count: 40 +# Offense count: 39 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AutoCorrect. Lint/UselessAssignment: @@ -765,7 +775,6 @@ Lint/UselessAssignment: - 'lib/solargraph/source_map/mapper.rb' - 'lib/solargraph/type_checker.rb' - 'spec/fixtures/long_squiggly_heredoc.rb' - - 'spec/fixtures/rubocop-unused-variable-error/app.rb' - 'spec/fixtures/unicode.rb' - 'spec/language_server/host_spec.rb' - 'spec/language_server/protocol_spec.rb' @@ -786,7 +795,7 @@ Lint/UselessMethodDefinition: Exclude: - 'lib/solargraph/pin/signature.rb' -# Offense count: 227 +# Offense count: 232 # Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes, Max. Metrics/AbcSize: Exclude: @@ -797,6 +806,7 @@ Metrics/AbcSize: - 'lib/solargraph/complex_type.rb' - 'lib/solargraph/complex_type/type_methods.rb' - 'lib/solargraph/complex_type/unique_type.rb' + - 'lib/solargraph/convention/data_definition.rb' - 'lib/solargraph/convention/struct_definition.rb' - 'lib/solargraph/convention/struct_definition/struct_definition_node.rb' - 'lib/solargraph/diagnostics/require_not_found.rb' @@ -875,6 +885,7 @@ Metrics/AbcSize: - 'lib/solargraph/type_checker.rb' - 'lib/solargraph/type_checker/checks.rb' - 'lib/solargraph/workspace.rb' + - 'lib/solargraph/yard_map/helpers.rb' - 'lib/solargraph/yard_map/mapper.rb' - 'lib/solargraph/yard_map/mapper/to_method.rb' - 'lib/solargraph/yard_map/to_method.rb' @@ -934,7 +945,7 @@ Metrics/ClassLength: - 'lib/solargraph/workspace.rb' - 'lib/solargraph/workspace/config.rb' -# Offense count: 111 +# Offense count: 113 # Configuration parameters: AllowedMethods, AllowedPatterns, Max. Metrics/CyclomaticComplexity: Exclude: @@ -945,6 +956,8 @@ Metrics/CyclomaticComplexity: - 'lib/solargraph/complex_type.rb' - 'lib/solargraph/complex_type/type_methods.rb' - 'lib/solargraph/complex_type/unique_type.rb' + - 'lib/solargraph/convention/data_definition/data_definition_node.rb' + - 'lib/solargraph/convention/struct_definition.rb' - 'lib/solargraph/convention/struct_definition/struct_definition_node.rb' - 'lib/solargraph/diagnostics/require_not_found.rb' - 'lib/solargraph/doc_map.rb' @@ -987,7 +1000,7 @@ Metrics/CyclomaticComplexity: - 'lib/solargraph/yard_map/mapper.rb' - 'lib/solargraph/yard_map/mapper/to_method.rb' -# Offense count: 50 +# Offense count: 52 # Configuration parameters: CountComments, Max, CountAsOne, AllowedMethods, AllowedPatterns. Metrics/MethodLength: Exclude: @@ -995,6 +1008,7 @@ Metrics/MethodLength: - 'lib/solargraph/api_map/source_to_yard.rb' - 'lib/solargraph/complex_type.rb' - 'lib/solargraph/complex_type/unique_type.rb' + - 'lib/solargraph/convention/data_definition.rb' - 'lib/solargraph/convention/struct_definition.rb' - 'lib/solargraph/doc_map.rb' - 'lib/solargraph/language_server/host.rb' @@ -1041,7 +1055,7 @@ Metrics/ParameterLists: - 'lib/solargraph/yard_map/mapper/to_method.rb' - 'lib/solargraph/yard_map/to_method.rb' -# Offense count: 88 +# Offense count: 89 # Configuration parameters: AllowedMethods, AllowedPatterns, Max. Metrics/PerceivedComplexity: Exclude: @@ -1051,6 +1065,7 @@ Metrics/PerceivedComplexity: - 'lib/solargraph/complex_type.rb' - 'lib/solargraph/complex_type/type_methods.rb' - 'lib/solargraph/complex_type/unique_type.rb' + - 'lib/solargraph/convention/struct_definition.rb' - 'lib/solargraph/doc_map.rb' - 'lib/solargraph/language_server/host.rb' - 'lib/solargraph/language_server/message/extended/check_gem_version.rb' @@ -1133,16 +1148,19 @@ Naming/MethodParameterName: - 'lib/solargraph/yard_map/mapper/to_method.rb' - 'lib/solargraph/yard_map/to_method.rb' -# Offense count: 11 +# Offense count: 14 # Configuration parameters: Mode, AllowedMethods, AllowedPatterns, AllowBangMethods, WaywardPredicates. # AllowedMethods: call # WaywardPredicates: nonzero? Naming/PredicateMethod: Exclude: - 'lib/solargraph/api_map/store.rb' + - 'lib/solargraph/convention/data_definition.rb' + - 'lib/solargraph/convention/struct_definition.rb' - 'lib/solargraph/doc_map.rb' - 'lib/solargraph/language_server/progress.rb' - 'lib/solargraph/library.rb' + - 'lib/solargraph/parser/node_processor/base.rb' - 'lib/solargraph/parser/parser_gem/node_processors/send_node.rb' - 'lib/solargraph/pin/base.rb' - 'lib/solargraph/pin/local_variable.rb' @@ -1184,6 +1202,12 @@ RSpec/BeEq: - 'spec/complex_type_spec.rb' - 'spec/pin/method_spec.rb' +# Offense count: 2 +# This cop supports unsafe autocorrection (--autocorrect-all). +RSpec/BeEql: + Exclude: + - 'spec/convention/struct_definition_spec.rb' + # Offense count: 7 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. @@ -1231,7 +1255,7 @@ RSpec/DescribeClass: - 'spec/complex_type_spec.rb' - 'spec/source_map/node_processor_spec.rb' -# Offense count: 407 +# Offense count: 412 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: SkipBlocks, EnforcedStyle, OnlyStaticConstants. # SupportedStyles: described_class, explicit @@ -1321,7 +1345,7 @@ RSpec/EmptyLineAfterFinalLet: Exclude: - 'spec/workspace/config_spec.rb' -# Offense count: 928 +# Offense count: 941 # Configuration parameters: Max, CountAsOne. RSpec/ExampleLength: Exclude: @@ -1330,6 +1354,7 @@ RSpec/ExampleLength: - 'spec/api_map/store_spec.rb' - 'spec/api_map_spec.rb' - 'spec/complex_type_spec.rb' + - 'spec/convention/struct_definition_spec.rb' - 'spec/diagnostics/rubocop_spec.rb' - 'spec/diagnostics/type_check_spec.rb' - 'spec/diagnostics/update_errors_spec.rb' @@ -1397,12 +1422,13 @@ RSpec/ExampleLength: - 'spec/yard_map/mapper/to_method_spec.rb' - 'spec/yard_map/mapper_spec.rb' -# Offense count: 4 +# Offense count: 10 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: CustomTransform, IgnoredWords, DisallowedExamples. # DisallowedExamples: works RSpec/ExampleWording: Exclude: + - 'spec/convention/struct_definition_spec.rb' - 'spec/pin/base_spec.rb' - 'spec/pin/method_spec.rb' @@ -1483,7 +1509,7 @@ RSpec/MissingExampleGroupArgument: Exclude: - 'spec/diagnostics/rubocop_helpers_spec.rb' -# Offense count: 471 +# Offense count: 481 # Configuration parameters: Max. RSpec/MultipleExpectations: Exclude: @@ -1493,6 +1519,7 @@ RSpec/MultipleExpectations: - 'spec/api_map/store_spec.rb' - 'spec/api_map_spec.rb' - 'spec/complex_type_spec.rb' + - 'spec/convention/struct_definition_spec.rb' - 'spec/diagnostics/rubocop_spec.rb' - 'spec/diagnostics/type_check_spec.rb' - 'spec/diagnostics/update_errors_spec.rb' @@ -1612,7 +1639,7 @@ RSpec/RemoveConst: Exclude: - 'spec/diagnostics/rubocop_helpers_spec.rb' -# Offense count: 48 +# Offense count: 50 RSpec/RepeatedDescription: Exclude: - 'spec/api_map_spec.rb' @@ -1641,7 +1668,7 @@ RSpec/ScatteredLet: Exclude: - 'spec/complex_type_spec.rb' -# Offense count: 88 +# Offense count: 89 # Configuration parameters: Include, CustomTransform, IgnoreMethods, IgnoreMetadata. # Include: **/*_spec.rb RSpec/SpecFilePathFormat: @@ -1652,6 +1679,7 @@ RSpec/SpecFilePathFormat: - 'spec/api_map/source_to_yard_spec.rb' - 'spec/api_map/store_spec.rb' - 'spec/api_map_spec.rb' + - 'spec/convention/struct_definition_spec.rb' - 'spec/convention_spec.rb' - 'spec/diagnostics/base_spec.rb' - 'spec/diagnostics/require_not_found_spec.rb' @@ -1949,7 +1977,7 @@ Style/ConditionalAssignment: - 'lib/solargraph/parser/parser_gem/node_processors/defs_node.rb' - 'lib/solargraph/source/chain/call.rb' -# Offense count: 140 +# Offense count: 142 # Configuration parameters: AllowedConstants. Style/Documentation: Exclude: @@ -1958,6 +1986,7 @@ Style/Documentation: - 'lib/solargraph/api_map/cache.rb' - 'lib/solargraph/api_map/index.rb' - 'lib/solargraph/api_map/source_to_yard.rb' + - 'lib/solargraph/convention/data_definition.rb' - 'lib/solargraph/convention/gemfile.rb' - 'lib/solargraph/convention/gemspec.rb' - 'lib/solargraph/convention/rakefile.rb' @@ -2143,7 +2172,7 @@ Style/FloatDivision: - 'lib/solargraph/library.rb' - 'lib/solargraph/pin/search.rb' -# Offense count: 128 +# Offense count: 129 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: EnforcedStyle. # SupportedStyles: always, always_true, never @@ -2177,6 +2206,7 @@ Style/FrozenStringLiteralComment: - 'spec/api_map/source_to_yard_spec.rb' - 'spec/api_map_spec.rb' - 'spec/complex_type_spec.rb' + - 'spec/convention/struct_definition_spec.rb' - 'spec/convention_spec.rb' - 'spec/diagnostics/base_spec.rb' - 'spec/diagnostics/require_not_found_spec.rb' @@ -2385,6 +2415,7 @@ Style/IfUnlessModifier: - 'lib/solargraph/type_checker.rb' - 'lib/solargraph/workspace.rb' - 'lib/solargraph/workspace/config.rb' + - 'lib/solargraph/yard_map/helpers.rb' - 'lib/solargraph/yard_map/mapper/to_method.rb' # Offense count: 2 @@ -2416,7 +2447,7 @@ Style/MapToSet: - 'lib/solargraph/library.rb' - 'spec/source_map/clip_spec.rb' -# Offense count: 198 +# Offense count: 203 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: require_parentheses, require_no_parentheses, require_no_parentheses_except_multiline @@ -2430,6 +2461,9 @@ Style/MethodDefParentheses: - 'lib/solargraph/complex_type/type_methods.rb' - 'lib/solargraph/complex_type/unique_type.rb' - 'lib/solargraph/convention.rb' + - 'lib/solargraph/convention/data_definition.rb' + - 'lib/solargraph/convention/data_definition/data_assignment_node.rb' + - 'lib/solargraph/convention/data_definition/data_definition_node.rb' - 'lib/solargraph/convention/struct_definition.rb' - 'lib/solargraph/convention/struct_definition/struct_assignment_node.rb' - 'lib/solargraph/convention/struct_definition/struct_definition_node.rb' @@ -2449,7 +2483,6 @@ Style/MethodDefParentheses: - 'lib/solargraph/parser/parser_gem/node_chainer.rb' - 'lib/solargraph/parser/parser_gem/node_methods.rb' - 'lib/solargraph/parser/parser_gem/node_processors/args_node.rb' - - 'lib/solargraph/parser/parser_gem/node_processors/namespace_node.rb' - 'lib/solargraph/pin/base.rb' - 'lib/solargraph/pin/base_variable.rb' - 'lib/solargraph/pin/block.rb' @@ -2471,6 +2504,7 @@ Style/MethodDefParentheses: - 'lib/solargraph/source_map/mapper.rb' - 'lib/solargraph/type_checker.rb' - 'lib/solargraph/type_checker/checks.rb' + - 'lib/solargraph/yard_map/helpers.rb' - 'lib/solargraph/yardoc.rb' - 'spec/fixtures/rdoc-lib/lib/example.rb' - 'spec/source_map_spec.rb' @@ -2788,12 +2822,13 @@ Style/SafeNavigationChainLength: Exclude: - 'lib/solargraph/doc_map.rb' -# Offense count: 39 +# Offense count: 40 # This cop supports unsafe autocorrection (--autocorrect-all). Style/SlicingWithRange: Exclude: - 'lib/solargraph/api_map.rb' - 'lib/solargraph/complex_type/unique_type.rb' + - 'lib/solargraph/convention/data_definition/data_definition_node.rb' - 'lib/solargraph/convention/struct_definition/struct_definition_node.rb' - 'lib/solargraph/diagnostics/rubocop_helpers.rb' - 'lib/solargraph/library.rb' @@ -2829,13 +2864,14 @@ Style/StderrPuts: - 'lib/solargraph/pin/base.rb' - 'lib/solargraph/shell.rb' -# Offense count: 10 +# Offense count: 11 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: Mode. Style/StringConcatenation: Exclude: - 'lib/solargraph/api_map.rb' - 'lib/solargraph/api_map/index.rb' + - 'lib/solargraph/convention/struct_definition.rb' - 'lib/solargraph/pin/base.rb' - 'lib/solargraph/pin/callable.rb' - 'lib/solargraph/pin/closure.rb' @@ -2844,7 +2880,7 @@ Style/StringConcatenation: - 'lib/solargraph/pin/namespace.rb' - 'solargraph.gemspec' -# Offense count: 615 +# Offense count: 621 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline. # SupportedStyles: single_quotes, double_quotes @@ -2856,6 +2892,7 @@ Style/StringLiterals: - 'lib/solargraph/api_map/index.rb' - 'lib/solargraph/complex_type.rb' - 'lib/solargraph/complex_type/unique_type.rb' + - 'lib/solargraph/convention/struct_definition.rb' - 'lib/solargraph/doc_map.rb' - 'lib/solargraph/language_server/host.rb' - 'lib/solargraph/language_server/message/extended/document_gems.rb' @@ -2881,6 +2918,7 @@ Style/StringLiterals: - 'spec/api_map/config_spec.rb' - 'spec/api_map/source_to_yard_spec.rb' - 'spec/complex_type_spec.rb' + - 'spec/convention/struct_definition_spec.rb' - 'spec/diagnostics/base_spec.rb' - 'spec/diagnostics/require_not_found_spec.rb' - 'spec/diagnostics/rubocop_helpers_spec.rb' @@ -2995,7 +3033,7 @@ Style/TernaryParentheses: Exclude: - 'lib/solargraph/source_map/mapper.rb' -# Offense count: 19 +# Offense count: 16 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyleForMultiline. # SupportedStylesForMultiline: comma, consistent_comma, no_comma @@ -3011,7 +3049,6 @@ Style/TrailingCommaInArguments: - 'lib/solargraph/parser/parser_gem/node_processors/while_node.rb' - 'lib/solargraph/pin/method.rb' - 'lib/solargraph/rbs_map/conversions.rb' - - 'lib/solargraph/yard_map/mapper/to_constant.rb' - 'lib/solargraph/yard_map/mapper/to_method.rb' - 'lib/solargraph/yard_map/mapper/to_namespace.rb' @@ -3097,7 +3134,7 @@ YARD/CollectionType: Exclude: - 'lib/solargraph/range.rb' -# Offense count: 65 +# Offense count: 60 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStylePrototypeName. # SupportedStylesPrototypeName: before, after @@ -3107,7 +3144,7 @@ YARD/MismatchName: - 'lib/solargraph/complex_type.rb' - 'lib/solargraph/complex_type/unique_type.rb' - 'lib/solargraph/convention.rb' - - 'lib/solargraph/convention/struct_definition.rb' + - 'lib/solargraph/convention/data_definition.rb' - 'lib/solargraph/doc_map.rb' - 'lib/solargraph/gem_pins.rb' - 'lib/solargraph/language_server/host.rb' @@ -3133,14 +3170,11 @@ YARD/MismatchName: - 'lib/solargraph/pin/while.rb' - 'lib/solargraph/pin_cache.rb' - 'lib/solargraph/rbs_map.rb' - - 'lib/solargraph/rbs_map/conversions.rb' - 'lib/solargraph/shell.rb' - 'lib/solargraph/source/chain.rb' - 'lib/solargraph/source/chain/call.rb' - 'lib/solargraph/source/chain/z_super.rb' - 'lib/solargraph/type_checker.rb' - - 'lib/solargraph/yard_map/mapper/to_constant.rb' - - 'lib/solargraph/yard_map/mapper/to_namespace.rb' # Offense count: 4 YARD/TagTypeSyntax: @@ -3149,7 +3183,7 @@ YARD/TagTypeSyntax: - 'lib/solargraph/parser/comment_ripper.rb' - 'lib/solargraph/type_checker.rb' -# Offense count: 191 +# Offense count: 195 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: Max, AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, IgnoreCopDirectives, AllowedPatterns, SplitStrings. # URISchemes: http, https @@ -3160,6 +3194,7 @@ Layout/LineLength: - 'lib/solargraph/api_map/store.rb' - 'lib/solargraph/complex_type.rb' - 'lib/solargraph/complex_type/unique_type.rb' + - 'lib/solargraph/convention/data_definition.rb' - 'lib/solargraph/doc_map.rb' - 'lib/solargraph/gem_pins.rb' - 'lib/solargraph/language_server/host.rb' @@ -3176,7 +3211,6 @@ Layout/LineLength: - 'lib/solargraph/library.rb' - 'lib/solargraph/parser/comment_ripper.rb' - 'lib/solargraph/parser/flow_sensitive_typing.rb' - - 'lib/solargraph/parser/node_processor/base.rb' - 'lib/solargraph/parser/parser_gem/node_chainer.rb' - 'lib/solargraph/parser/parser_gem/node_methods.rb' - 'lib/solargraph/parser/parser_gem/node_processors/and_node.rb' From 5119a50a0c04916cc403932ecd0e5ded51bbcbcf Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 16 Jul 2025 11:28:48 -0400 Subject: [PATCH 201/561] Force build --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 98df20346..00ac781a9 100755 --- a/README.md +++ b/README.md @@ -51,6 +51,8 @@ Plug-ins and extensions are available for the following editors: Solargraph's behavior can be controlled via optional [configuration](https://solargraph.org/guides/configuration) files. The highest priority file is a `.solargraph.yml` file at the root of the project. If not present, any global configuration at `~/.config/solargraph/config.yml` will apply. The path to the global configuration can be overridden with the `SOLARGRAPH_GLOBAL_CONFIG` environment variable. +Use `bundle exec solargraph init` to create a configuration file. + ### Plugins 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. From 0aeaee093cfeca81f1836e95af6433740e2448ce Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 16 Jul 2025 11:35:15 -0400 Subject: [PATCH 202/561] Rebaseline todo file --- .rubocop.yml | 1 - .rubocop_todo.yml | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 65cae16b1..3b4198618 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -17,7 +17,6 @@ AllCops: - "spec/fixtures/invalid_byte.rb" - "spec/fixtures/invalid_node_comment.rb" - "spec/fixtures/invalid_utf8.rb" - - "spec/fixtures/rubocop-unused-variable-error/app.rb" - "vendor/**/*" - "vendor/**/.*" TargetRubyVersion: 3.0 diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index d9559e334..29efb4040 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -755,7 +755,7 @@ Lint/UselessAccessModifier: Exclude: - 'lib/solargraph/api_map.rb' -# Offense count: 39 +# Offense count: 40 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AutoCorrect. Lint/UselessAssignment: @@ -775,6 +775,7 @@ Lint/UselessAssignment: - 'lib/solargraph/source_map/mapper.rb' - 'lib/solargraph/type_checker.rb' - 'spec/fixtures/long_squiggly_heredoc.rb' + - 'spec/fixtures/rubocop-unused-variable-error/app.rb' - 'spec/fixtures/unicode.rb' - 'spec/language_server/host_spec.rb' - 'spec/language_server/protocol_spec.rb' From cfdfa6d05ae1120ddfcc7ab9860c3649fdabe998 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 16 Jul 2025 11:44:02 -0400 Subject: [PATCH 203/561] Force use of single config file --- .github/workflows/linting.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index 99f2df787..9e570f4b5 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -90,7 +90,7 @@ jobs: run: bundle install - name: Run RuboCop - run: bundle exec rubocop + run: bundle exec rubocop -c .rubocop.yml - name: Run RuboCop against todo file run: | @@ -99,6 +99,6 @@ jobs: then git status --porcelain git diff -u . - >&2 echo "Please fix deltas if bad or run 'bundle exec rubocop --auto-gen-config' and push up changes if good" + >&2 echo "Please fix deltas if bad or run 'bundle exec rubocop --auto-gen-config --no-exclude-limit --no-auto-gen-timestamp' and push up changes if good" exit 1 fi From c2f1b35623aea26a39af104f15d9d5e27c77b366 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 16 Jul 2025 11:52:20 -0400 Subject: [PATCH 204/561] Fix todo generation command --- .github/workflows/linting.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index 9e570f4b5..637f3a319 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -94,7 +94,7 @@ jobs: - name: Run RuboCop against todo file run: | - bundle exec rubocop --auto-gen-config + bundle exec rubocop --auto-gen-config --no-exclude-limit --no-auto-gen-timestamp if [ -n "$(git status --porcelain)" ] then git status --porcelain From 3f91a339234335f3da1269ebc788a3fbfd5f81cc Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 16 Jul 2025 12:37:40 -0400 Subject: [PATCH 205/561] Add AllowUnusedKeywordArguments for Lint/UnusedMethodArgument --- .rubocop.yml | 3 +++ .rubocop_todo.yml | 17 +++++------------ 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 3b4198618..64b4efed9 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -27,6 +27,9 @@ Style/MethodDefParentheses: Layout/EmptyLineAfterGuardClause: Enabled: false +Lint/UnusedMethodArgument: + AllowUnusedKeywordArguments: true + Metrics/MethodLength: Max: 25 diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 29efb4040..81e489a9c 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -192,12 +192,11 @@ Layout/EndOfLine: - 'lib/solargraph/source/encoding_fixes.rb' - 'solargraph.gemspec' -# Offense count: 11 +# Offense count: 7 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowForAlignment, AllowBeforeTrailingComments, ForceEqualSignAlignment. Layout/ExtraSpacing: Exclude: - - 'Steepfile' - 'lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb' - 'lib/solargraph/pin/closure.rb' - 'lib/solargraph/pin/local_variable.rb' @@ -1978,7 +1977,7 @@ Style/ConditionalAssignment: - 'lib/solargraph/parser/parser_gem/node_processors/defs_node.rb' - 'lib/solargraph/source/chain/call.rb' -# Offense count: 142 +# Offense count: 141 # Configuration parameters: AllowedConstants. Style/Documentation: Exclude: @@ -2094,7 +2093,6 @@ Style/Documentation: - 'lib/solargraph/pin/while.rb' - 'lib/solargraph/pin_cache.rb' - 'lib/solargraph/rbs_map.rb' - - 'lib/solargraph/repro.rb' - 'lib/solargraph/server_methods.rb' - 'lib/solargraph/shell.rb' - 'lib/solargraph/source/chain/array.rb' @@ -2173,7 +2171,7 @@ Style/FloatDivision: - 'lib/solargraph/library.rb' - 'lib/solargraph/pin/search.rb' -# Offense count: 129 +# Offense count: 127 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: EnforcedStyle. # SupportedStyles: always, always_true, never @@ -2182,7 +2180,6 @@ Style/FrozenStringLiteralComment: - '**/*.arb' - 'Gemfile' - 'Rakefile' - - 'Steepfile' - 'bin/solargraph' - 'lib/solargraph/converters/dd.rb' - 'lib/solargraph/converters/dl.rb' @@ -2197,7 +2194,6 @@ Style/FrozenStringLiteralComment: - 'lib/solargraph/pin/breakable.rb' - 'lib/solargraph/pin/signature.rb' - 'lib/solargraph/pin_cache.rb' - - 'lib/solargraph/repro.rb' - 'lib/solargraph/source/chain/array.rb' - 'lib/solargraph/source/chain/q_call.rb' - 'lib/solargraph/yard_map/helpers.rb' @@ -2678,12 +2674,11 @@ Style/RedundantArgument: Exclude: - 'lib/solargraph/source_map/mapper.rb' -# Offense count: 3 +# Offense count: 2 # This cop supports safe autocorrection (--autocorrect). Style/RedundantAssignment: Exclude: - 'lib/solargraph/language_server/host/dispatch.rb' - - 'lib/solargraph/repro.rb' - 'lib/solargraph/workspace/config.rb' # Offense count: 8 @@ -2881,7 +2876,7 @@ Style/StringConcatenation: - 'lib/solargraph/pin/namespace.rb' - 'solargraph.gemspec' -# Offense count: 621 +# Offense count: 613 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline. # SupportedStyles: single_quotes, double_quotes @@ -2889,7 +2884,6 @@ Style/StringLiterals: Exclude: - 'Gemfile' - 'Rakefile' - - 'Steepfile' - 'lib/solargraph/api_map/index.rb' - 'lib/solargraph/complex_type.rb' - 'lib/solargraph/complex_type/unique_type.rb' @@ -2908,7 +2902,6 @@ Style/StringLiterals: - 'lib/solargraph/pin/method.rb' - 'lib/solargraph/pin/parameter.rb' - 'lib/solargraph/rbs_map/conversions.rb' - - 'lib/solargraph/repro.rb' - 'lib/solargraph/server_methods.rb' - 'lib/solargraph/shell.rb' - 'lib/solargraph/workspace.rb' From 8104466c25e0657cbc820d55d7d6d120268bf5df Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 16 Jul 2025 12:55:42 -0400 Subject: [PATCH 206/561] Rubocop fixes --- lib/solargraph/pin_cache.rb | 145 ++++++++++++++++++++------- lib/solargraph/rbs_map.rb | 14 +-- lib/solargraph/rbs_map/core_map.rb | 6 +- lib/solargraph/rbs_map/stdlib_map.rb | 3 +- lib/solargraph/workspace.rb | 6 +- lib/solargraph/yardoc.rb | 2 +- spec/gem_pins_spec.rb | 1 - spec/yard_map/mapper_spec.rb | 2 +- 8 files changed, 123 insertions(+), 56 deletions(-) diff --git a/lib/solargraph/pin_cache.rb b/lib/solargraph/pin_cache.rb index 600998efc..86aeeeb83 100644 --- a/lib/solargraph/pin_cache.rb +++ b/lib/solargraph/pin_cache.rb @@ -34,36 +34,20 @@ def cached?(gemspec) # @return [void] def cache_gem(gemspec:, rebuild: false, out: nil) rbs_version_cache_key = lookup_rbs_version_cache_key(gemspec) - if rebuild - build_yard = true - build_rbs_collection = true - build_combined = true - else - build_yard = !has_yard_gem?(gemspec) - build_rbs_collection = !has_rbs_collection_pins?(gemspec, rbs_version_cache_key) - build_combined = !has_combined_gem?(gemspec, rbs_version_cache_key) || build_yard || build_rbs_collection - end - build_yard = false if suppress_yard_cache?(gemspec, rbs_version_cache_key) + build_yard, build_rbs_collection, build_combined = + calculate_build_needs(gemspec, + rebuild: rebuild, + rbs_version_cache_key: rbs_version_cache_key) return unless build_yard || build_rbs_collection || build_combined - type = [] - type << 'YARD' if build_yard - rbs_source_desc = RbsMap.rbs_source_desc(rbs_version_cache_key) - type << rbs_source_desc if build_rbs_collection && !rbs_source_desc.nil? - # we'll build it anyway, but it won't take long to build with - # only a single source - type << 'combined' if build_combined && !rbs_source_desc.nil? - out.puts("Caching #{type.join(' and ')} pins for gem #{gemspec.name}:#{gemspec.version}") if out - - cache_yard_pins(gemspec, out) if build_yard - yard_pins = deserialize_yard_pin_cache(gemspec) - - cache_rbs_collection_pins(gemspec, out) if build_rbs_collection - rbs_collection_pins = deserialize_rbs_collection_cache(gemspec, rbs_version_cache_key) - - cache_combined_pins(gemspec, rbs_version_cache_key, yard_pins, rbs_collection_pins) if build_combined + build_combine_and_cache(gemspec, + rbs_version_cache_key, + build_yard: build_yard, + build_rbs_collection: build_rbs_collection, + build_combined: build_combined, + out: out) end # @param gemspec [Gem::Specification, Bundler::LazySpecification] @@ -145,15 +129,85 @@ def yardoc_processing?(gemspec) private + # @param gemspec [Gem::Specification, Bundler::LazySpecification] + # @param rebuild [Boolean] whether to rebuild the cache regardless of whether it already exists + # @param rbs_version_cache_key [String, nil] the cache key for the gem in the RBS collection + # + # @return [Array(Boolean, Boolean, Boolean)] whether to build YARD + # pins, RBS collection pins, and combined pins + def calculate_build_needs(gemspec, rebuild:, rbs_version_cache_key:) + if rebuild + build_yard = true + build_rbs_collection = true + build_combined = true + else + build_yard = !has_yard_gem?(gemspec) + build_rbs_collection = !has_rbs_collection_pins?(gemspec, rbs_version_cache_key) + build_combined = !has_combined_gem?(gemspec, rbs_version_cache_key) || build_yard || build_rbs_collection + end + + build_yard = false if suppress_yard_cache?(gemspec, rbs_version_cache_key) + + [build_yard, build_rbs_collection, build_combined] + end + + # @param gemspec [Gem::Specification, Bundler::LazySpecification] + # @param rbs_version_cache_key [String, nil] + # @param build_yard [Boolean] + # @param build_rbs_collection [Boolean] + # @param build_combined [Boolean] + # @param out [IO, nil] + # + # @return [void] + def build_combine_and_cache(gemspec, + rbs_version_cache_key, + build_yard:, + build_rbs_collection:, + build_combined:, + out:) + log_cache_info(gemspec, rbs_version_cache_key, + build_yard: build_yard, + build_rbs_collection: build_rbs_collection, + build_combined: build_combined, + out: out) + cache_yard_pins(gemspec, out) if build_yard + yard_pins = deserialize_yard_pin_cache(gemspec) + cache_rbs_collection_pins(gemspec, out) if build_rbs_collection + rbs_collection_pins = deserialize_rbs_collection_cache(gemspec, rbs_version_cache_key) + cache_combined_pins(gemspec, rbs_version_cache_key, yard_pins, rbs_collection_pins) if build_combined + end + + # @param gemspec [Gem::Specification, Bundler::LazySpecification] + # @param rbs_version_cache_key [String, nil] + # @param build_yard [Boolean] + # @param build_rbs_collection [Boolean] + # @param build_combined [Boolean] + # @param out [IO, nil] + # + # @return [void] + def log_cache_info(gemspec, + rbs_version_cache_key, + build_yard:, + build_rbs_collection:, + build_combined:, + out:) + type = [] + type << 'YARD' if build_yard + rbs_source_desc = RbsMap.rbs_source_desc(rbs_version_cache_key) + type << rbs_source_desc if build_rbs_collection && !rbs_source_desc.nil? + # we'll build it anyway, but it won't take long to build with + # only a single source + type << 'combined' if build_combined && !rbs_source_desc.nil? + out.puts("Caching #{type.join(' and ')} pins for gem #{gemspec.name}:#{gemspec.version}") if out + end + # @param gemspec [Gem::Specification, Bundler::LazySpecification] # @param out [IO, nil] # @return [Array] def cache_yard_pins(gemspec, out) yardoc_dir = yardoc_path(gemspec) - unless Yardoc.docs_built?(yardoc_dir, gemspec) - Yardoc.build_docs(yardoc_dir, yard_plugins, gemspec) - end - pins = Yardoc.build_pins(yardoc_dir, gemspec, out) + Yardoc.build_docs(yardoc_dir, yard_plugins, gemspec) unless Yardoc.docs_built?(yardoc_dir, gemspec) + pins = Yardoc.build_pins(yardoc_dir, gemspec, out: out) serialize_yard_gem(gemspec, pins) logger.info { "Cached #{pins.length} YARD pins for gem #{gemspec.name}:#{gemspec.version}" } unless pins.empty? pins @@ -175,16 +229,21 @@ def combined_pins_in_memory end # @param gemspec [Gem::Specification] - # @param out [IO, nil] + # @param _out [IO, nil] # @return [Array] - def cache_rbs_collection_pins(gemspec, out) + def cache_rbs_collection_pins(gemspec, _out) rbs_map = RbsMap.from_gemspec(gemspec, rbs_collection_path, rbs_collection_config_path) pins = rbs_map.pins rbs_version_cache_key = rbs_map.cache_key # cache pins even if result is zero, so we don't retry building pins pins ||= [] serialize_rbs_collection_pins(gemspec, rbs_version_cache_key, pins) - logger.info { "Cached #{pins.length} RBS collection pins for gem #{gemspec.name} #{gemspec.version} with cache_key #{rbs_version_cache_key.inspect}" unless pins.empty? } + logger.info do + unless pins.empty? + "Cached #{pins.length} RBS collection pins for gem #{gemspec.name} #{gemspec.version} with " \ + "cache_key #{rbs_version_cache_key.inspect}" + end + end pins end @@ -213,7 +272,11 @@ def deserialize_rbs_collection_cache gemspec, rbs_version_cache_key return if rbs_collection_pins_in_memory.key?([gemspec.name, gemspec.version, rbs_version_cache_key]) cached = load_rbs_collection_pins(gemspec, rbs_version_cache_key) if cached - logger.info { "Loaded #{cached.length} pins from RBS collection cache for #{gemspec.name}:#{gemspec.version}" } unless cached.empty? + unless cached.empty? + logger.info do + "Loaded #{cached.length} pins from RBS collection cache for #{gemspec.name}:#{gemspec.version}" + end + end rbs_collection_pins_in_memory[[gemspec.name, gemspec.version, rbs_version_cache_key]] = cached cached else @@ -359,17 +422,22 @@ def uncache_by_prefix *path_segments, out: nil class << self include Logging - # @return [Hash{Array => Hash{Array(String, String) => Array}}] yard plugins, then gemspec name and version + # @return [Hash{Array => Hash{Array(String, String) => Array}}] yard + # plugins, then gemspec name and version def all_yard_pins_in_memory @all_yard_pins_in_memory ||= {} end - # @return [Hash{Array(String, String, String) => Array}] gemspec name, version and rbs version cache key + # @return [Hash{Array(String, String, String) => + # Array}] gemspec name, version and rbs version + # cache key def all_rbs_collection_pins_in_memory @all_rbs_collection_pins_in_memory ||= {} end - # @return [Hash{Array => Hash{Array(String, String) => Array}}] yard plugins, then gemspec name and version + # @return [Hash{Array => Hash{Array(String, String) => + # Array}}] yard plugins, then gemspec name and + # version def all_combined_pins_in_memory @all_combined_pins_in_memory ||= {} end @@ -466,9 +534,8 @@ def load_core end # @param pins [Array] - # @param out [IO, nil] # @return [void] - def serialize_core pins, out: $stderr + def serialize_core pins save(core_path, pins) end diff --git a/lib/solargraph/rbs_map.rb b/lib/solargraph/rbs_map.rb index a6e41cfef..f56ad4729 100644 --- a/lib/solargraph/rbs_map.rb +++ b/lib/solargraph/rbs_map.rb @@ -45,18 +45,18 @@ def initialize library, version = nil, rbs_collection_config_path: nil, rbs_coll # @param cache_key [String] # @return [String, nil] a description of the source of the RBS info - def self.rbs_source_desc(cache_key) + def self.rbs_source_desc cache_key case cache_key when CACHE_KEY_GEM_EXPORT - "RBS gem export" + 'RBS gem export' when CACHE_KEY_UNRESOLVED nil when CACHE_KEY_STDLIB - "RBS standard library" + 'RBS standard library' when CACHE_KEY_LOCAL - "local RBS shims" + 'local RBS shims' else - "RBS collection" + 'RBS collection' end end @@ -115,7 +115,7 @@ def self.from_gemspec gemspec, rbs_collection_path, rbs_collection_config_path # @param out [IO, nil] where to log messages # @return [Array] - def pins(out: $stderr) + def pins out: $stderr @pins ||= if resolved? loader.libs.each { |lib| log_caching(lib, out: $stderr) } conversions.pins @@ -174,7 +174,7 @@ def conversions # @param gemspec [RBS::EnvironmentLoader::Library] # @param out [IO, nil] where to log messages # @return [void] - def log_caching(gemspec, out:); end + def log_caching gemspec, out:; end # @param loader [RBS::EnvironmentLoader] # @param library [String] diff --git a/lib/solargraph/rbs_map/core_map.rb b/lib/solargraph/rbs_map/core_map.rb index 44df866e4..853c927fa 100644 --- a/lib/solargraph/rbs_map/core_map.rb +++ b/lib/solargraph/rbs_map/core_map.rb @@ -16,14 +16,14 @@ def initialize; end # @param out [IO, nil] output stream for logging # @return [Array] - def pins(out: $stderr) + def pins out: $stderr return @pins if @pins @pins = cache_core(out: out) end # @param out [IO, nil] output stream for logging # @return [Array] - def cache_core(out: $stderr) + def cache_core out: $stderr new_pins = [] cache = PinCache.load_core return cache if cache @@ -33,7 +33,7 @@ def cache_core(out: $stderr) # Avoid RBS::DuplicatedDeclarationError by loading in a different EnvironmentLoader fill_loader = RBS::EnvironmentLoader.new(core_root: nil, repository: RBS::Repository.new(no_stdlib: false)) fill_loader.add(path: Pathname(FILLS_DIRECTORY)) - out&.puts "Caching RBS pins for Ruby core" + out&.puts 'Caching RBS pins for Ruby core' fill_conversions = Conversions.new(loader: fill_loader) new_pins.concat fill_conversions.pins diff --git a/lib/solargraph/rbs_map/stdlib_map.rb b/lib/solargraph/rbs_map/stdlib_map.rb index 4db30137f..4aeaaf402 100644 --- a/lib/solargraph/rbs_map/stdlib_map.rb +++ b/lib/solargraph/rbs_map/stdlib_map.rb @@ -18,11 +18,12 @@ def log_level # @type [Hash{String => RbsMap}] @stdlib_maps_hash = {} - def log_caching(gemspec, out: $stderr) + def log_caching gemspec, out: $stderr out.puts("Caching RBS pins for standard library #{gemspec.name}") end # @param library [String] + # @param out [IO, nil] where to log messages def initialize library, out: $stderr cached_pins = PinCache.load_stdlib_require library if cached_pins diff --git a/lib/solargraph/workspace.rb b/lib/solargraph/workspace.rb index ed4012a4b..a4832c6f6 100644 --- a/lib/solargraph/workspace.rb +++ b/lib/solargraph/workspace.rb @@ -65,7 +65,7 @@ def global_environ # @param rebuild [Boolean] whether to rebuild the pins even if they are cached # # @return [void] - def cache_gem(gemspec, out: nil, rebuild: false) + def cache_gem gemspec, out: nil, rebuild: false pin_cache.cache_gem(gemspec: gemspec, out: out, rebuild: rebuild) end @@ -73,7 +73,7 @@ def cache_gem(gemspec, out: nil, rebuild: false) # @param out [IO, nil] output stream for logging # # @return [void] - def uncache_gem(gemspec, out: nil) + def uncache_gem gemspec, out: nil pin_cache.uncache_gem(gemspec, out: out) end @@ -254,7 +254,7 @@ def rbs_collection_config_path # @param out [IO, nil] output stream for logging # @param rebuild [Boolean] whether to rebuild the pins even if they are cached # @return [void] - def cache_all_for_workspace!(out, rebuild: false) + def cache_all_for_workspace! out, rebuild: false PinCache.cache_core(out: $stdout) unless PinCache.has_core? # @type [Array] specs = gemspecs_required_from_bundler diff --git a/lib/solargraph/yardoc.rb b/lib/solargraph/yardoc.rb index 7609019e8..67fa7aa79 100644 --- a/lib/solargraph/yardoc.rb +++ b/lib/solargraph/yardoc.rb @@ -34,7 +34,7 @@ def build_docs(yardoc_path, yard_plugins, gemspec) # @param gemspec [Gem::Specification] # @param out [IO, nil] where to log messages # @return [Array] - def build_pins(yardoc_path, gemspec, out) + def build_pins(yardoc_path, gemspec, out: $stderr) yardoc = load!(yardoc_path, gemspec) YardMap::Mapper.new(yardoc, gemspec).map end diff --git a/spec/gem_pins_spec.rb b/spec/gem_pins_spec.rb index 8ec370b56..ecab06001 100644 --- a/spec/gem_pins_spec.rb +++ b/spec/gem_pins_spec.rb @@ -2,7 +2,6 @@ describe Solargraph::GemPins do it 'can merge YARD and RBS' do - gemspec = Gem::Specification.find_by_name('rbs') workspace = Solargraph::Workspace.new(Dir.pwd) doc_map = Solargraph::DocMap.new(['rbs'], workspace) doc_map.cache_doc_map_gems!($stderr) diff --git a/spec/yard_map/mapper_spec.rb b/spec/yard_map/mapper_spec.rb index 548b4ba31..d4d1127ad 100644 --- a/spec/yard_map/mapper_spec.rb +++ b/spec/yard_map/mapper_spec.rb @@ -5,7 +5,7 @@ let(:workspace) { Solargraph::Workspace.new(Dir.pwd) } - def pins_with(require) + def pins_with require doc_map = Solargraph::DocMap.new([require], workspace) doc_map.cache_doc_map_gems!($stderr) doc_map.pins From 6566e07356d550edab9e2b322f8ae6c5aa262d2e Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 16 Jul 2025 12:55:59 -0400 Subject: [PATCH 207/561] Change workflow name --- .github/workflows/linting.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index 637f3a319..ff46725de 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -75,7 +75,7 @@ jobs: rubocop_version: Gemfile level: info rubocop_todo: - name: Rubocop / .rubocop_todo.yml check + name: .rubocop_todo.yml runs-on: ubuntu-latest steps: From f7b7809015a7d6e5e96b0e19283b8bbf1813cc6d Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 16 Jul 2025 13:18:03 -0400 Subject: [PATCH 208/561] RuboCop fix --- spec/doc_map_spec.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/doc_map_spec.rb b/spec/doc_map_spec.rb index 3a506a87e..23d1e332c 100644 --- a/spec/doc_map_spec.rb +++ b/spec/doc_map_spec.rb @@ -10,16 +10,16 @@ let(:pre_cache) { true } let(:requires) { [] } - before do - doc_map.cache_doc_map_gems!($stderr) if pre_cache - end - let(:workspace) do Solargraph::Workspace.new(Dir.pwd) end let(:plain_doc_map) { Solargraph::DocMap.new([], workspace) } + before do + doc_map.cache_doc_map_gems!($stderr) if pre_cache + end + context 'with a require in solargraph test bundle' do let(:requires) do ['ast'] From 2d83333d1b037e739ad5672cd1874cdb2fa59d9c Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 16 Jul 2025 13:59:32 -0400 Subject: [PATCH 209/561] Align trickier rules with current code quality for outside PRs --- .overcommit.yml | 2 +- .rubocop.yml | 14 ++++++- .rubocop_todo.yml | 104 ++-------------------------------------------- 3 files changed, 17 insertions(+), 103 deletions(-) diff --git a/.overcommit.yml b/.overcommit.yml index 85c85487f..e4f93d68a 100644 --- a/.overcommit.yml +++ b/.overcommit.yml @@ -28,7 +28,7 @@ PreCommit: exclude: # don't freak out over line below - '.overcommit.yml' - keywords: ['BROKEN', 'BUG', 'FIXME', 'HACK', 'OPTIMIZE', 'REVIEW', 'TODO', 'WTF', 'XXX'] + keywords: ['FIXME', 'TODO', 'XXX'] # creates false positives in CI AuthorName: diff --git a/.rubocop.yml b/.rubocop.yml index 64b4efed9..2a0396d93 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -30,8 +30,20 @@ Layout/EmptyLineAfterGuardClause: Lint/UnusedMethodArgument: AllowUnusedKeywordArguments: true +Metrics/ParameterLists: + Max: 7 + CountKeywordArgs: false + + +# +# Set a relaxed standard on harder-to-address item for ease of +# contribution - if you are good at refactoring, we welcome PRs to +# improve existing code! +# +Metrics/AbcSize: + Max: 65 Metrics/MethodLength: - Max: 25 + Max: 60 plugins: - rubocop-rspec diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 81e489a9c..8cf432cbb 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -795,100 +795,27 @@ Lint/UselessMethodDefinition: Exclude: - 'lib/solargraph/pin/signature.rb' -# Offense count: 232 +# Offense count: 22 # Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes, Max. Metrics/AbcSize: Exclude: - 'lib/solargraph/api_map.rb' - - 'lib/solargraph/api_map/index.rb' - 'lib/solargraph/api_map/source_to_yard.rb' - - 'lib/solargraph/api_map/store.rb' - 'lib/solargraph/complex_type.rb' - - 'lib/solargraph/complex_type/type_methods.rb' - - 'lib/solargraph/complex_type/unique_type.rb' - - 'lib/solargraph/convention/data_definition.rb' - - 'lib/solargraph/convention/struct_definition.rb' - - 'lib/solargraph/convention/struct_definition/struct_definition_node.rb' - - 'lib/solargraph/diagnostics/require_not_found.rb' - - 'lib/solargraph/diagnostics/rubocop.rb' - - 'lib/solargraph/diagnostics/type_check.rb' - - 'lib/solargraph/diagnostics/update_errors.rb' - 'lib/solargraph/doc_map.rb' - - 'lib/solargraph/gem_pins.rb' - 'lib/solargraph/language_server/host.rb' - - 'lib/solargraph/language_server/host/diagnoser.rb' - - 'lib/solargraph/language_server/message/base.rb' - - 'lib/solargraph/language_server/message/completion_item/resolve.rb' - - 'lib/solargraph/language_server/message/extended/check_gem_version.rb' - - 'lib/solargraph/language_server/message/extended/document.rb' - 'lib/solargraph/language_server/message/initialize.rb' - - 'lib/solargraph/language_server/message/text_document/completion.rb' - - 'lib/solargraph/language_server/message/text_document/definition.rb' - - 'lib/solargraph/language_server/message/text_document/document_symbol.rb' - - 'lib/solargraph/language_server/message/text_document/formatting.rb' - - 'lib/solargraph/language_server/message/text_document/hover.rb' - - 'lib/solargraph/language_server/message/text_document/prepare_rename.rb' - - 'lib/solargraph/language_server/message/text_document/references.rb' - - 'lib/solargraph/language_server/message/text_document/rename.rb' - - 'lib/solargraph/language_server/message/text_document/signature_help.rb' - - 'lib/solargraph/language_server/message/text_document/type_definition.rb' - - 'lib/solargraph/language_server/message/workspace/did_change_configuration.rb' - - 'lib/solargraph/language_server/message/workspace/did_change_watched_files.rb' - 'lib/solargraph/library.rb' - - 'lib/solargraph/page.rb' - - 'lib/solargraph/parser/comment_ripper.rb' - - 'lib/solargraph/parser/flow_sensitive_typing.rb' - - 'lib/solargraph/parser/parser_gem/class_methods.rb' - 'lib/solargraph/parser/parser_gem/node_chainer.rb' - - 'lib/solargraph/parser/parser_gem/node_methods.rb' - - 'lib/solargraph/parser/parser_gem/node_processors/alias_node.rb' - - 'lib/solargraph/parser/parser_gem/node_processors/args_node.rb' - - 'lib/solargraph/parser/parser_gem/node_processors/block_node.rb' - - 'lib/solargraph/parser/parser_gem/node_processors/cvasgn_node.rb' - - 'lib/solargraph/parser/parser_gem/node_processors/def_node.rb' - - 'lib/solargraph/parser/parser_gem/node_processors/defs_node.rb' - - 'lib/solargraph/parser/parser_gem/node_processors/gvasgn_node.rb' - - 'lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb' - - 'lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb' - - 'lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb' - - 'lib/solargraph/parser/parser_gem/node_processors/namespace_node.rb' - - 'lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb' - - 'lib/solargraph/parser/parser_gem/node_processors/orasgn_node.rb' - - 'lib/solargraph/parser/parser_gem/node_processors/resbody_node.rb' - 'lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb' - 'lib/solargraph/parser/parser_gem/node_processors/send_node.rb' - - 'lib/solargraph/pin/base.rb' - - 'lib/solargraph/pin/base_variable.rb' - - 'lib/solargraph/pin/block.rb' - - 'lib/solargraph/pin/callable.rb' - - 'lib/solargraph/pin/documenting.rb' - 'lib/solargraph/pin/method.rb' - - 'lib/solargraph/pin/namespace.rb' - - 'lib/solargraph/pin/parameter.rb' - - 'lib/solargraph/pin/search.rb' - - 'lib/solargraph/pin/signature.rb' - - 'lib/solargraph/position.rb' - - 'lib/solargraph/range.rb' - 'lib/solargraph/rbs_map/conversions.rb' - - 'lib/solargraph/shell.rb' - - 'lib/solargraph/source.rb' - - 'lib/solargraph/source/chain.rb' - - 'lib/solargraph/source/chain/array.rb' - 'lib/solargraph/source/chain/call.rb' - - 'lib/solargraph/source/chain/constant.rb' - - 'lib/solargraph/source/change.rb' - - 'lib/solargraph/source/cursor.rb' - 'lib/solargraph/source/source_chainer.rb' - - 'lib/solargraph/source_map.rb' - 'lib/solargraph/source_map/clip.rb' - 'lib/solargraph/source_map/mapper.rb' - 'lib/solargraph/type_checker.rb' - - 'lib/solargraph/type_checker/checks.rb' - - 'lib/solargraph/workspace.rb' - - 'lib/solargraph/yard_map/helpers.rb' - - 'lib/solargraph/yard_map/mapper.rb' - - 'lib/solargraph/yard_map/mapper/to_method.rb' - - 'lib/solargraph/yard_map/to_method.rb' # Offense count: 12 # Configuration parameters: CountComments, Max, CountAsOne, AllowedMethods, AllowedPatterns, inherit_mode. @@ -1000,40 +927,17 @@ Metrics/CyclomaticComplexity: - 'lib/solargraph/yard_map/mapper.rb' - 'lib/solargraph/yard_map/mapper/to_method.rb' -# Offense count: 52 +# Offense count: 7 # Configuration parameters: CountComments, Max, CountAsOne, AllowedMethods, AllowedPatterns. Metrics/MethodLength: Exclude: - 'lib/solargraph/api_map.rb' - - 'lib/solargraph/api_map/source_to_yard.rb' - 'lib/solargraph/complex_type.rb' - - 'lib/solargraph/complex_type/unique_type.rb' - - 'lib/solargraph/convention/data_definition.rb' - 'lib/solargraph/convention/struct_definition.rb' - - 'lib/solargraph/doc_map.rb' - - 'lib/solargraph/language_server/host.rb' - - 'lib/solargraph/language_server/message/extended/check_gem_version.rb' - - 'lib/solargraph/language_server/message/initialize.rb' - - 'lib/solargraph/language_server/message/text_document/completion.rb' - - 'lib/solargraph/language_server/message/text_document/formatting.rb' - - 'lib/solargraph/language_server/message/text_document/hover.rb' - - 'lib/solargraph/library.rb' - 'lib/solargraph/parser/parser_gem/node_chainer.rb' - - 'lib/solargraph/parser/parser_gem/node_methods.rb' - - 'lib/solargraph/parser/parser_gem/node_processors/def_node.rb' - - 'lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb' - - 'lib/solargraph/parser/parser_gem/node_processors/send_node.rb' - - 'lib/solargraph/pin/method.rb' - - 'lib/solargraph/rbs_map/conversions.rb' - - 'lib/solargraph/source.rb' - - 'lib/solargraph/source/chain.rb' - 'lib/solargraph/source/chain/call.rb' - - 'lib/solargraph/source/source_chainer.rb' - - 'lib/solargraph/source_map/clip.rb' - 'lib/solargraph/source_map/mapper.rb' - 'lib/solargraph/type_checker.rb' - - 'lib/solargraph/yard_map/mapper.rb' - - 'lib/solargraph/yard_map/mapper/to_method.rb' # Offense count: 4 # Configuration parameters: CountComments, Max, CountAsOne. @@ -1043,14 +947,12 @@ Metrics/ModuleLength: - 'lib/solargraph/parser/parser_gem/node_methods.rb' - 'lib/solargraph/pin_cache.rb' -# Offense count: 14 +# Offense count: 6 # Configuration parameters: Max, CountKeywordArgs, MaxOptionalParameters. Metrics/ParameterLists: Exclude: - 'lib/solargraph/api_map.rb' - - 'lib/solargraph/pin/base.rb' - 'lib/solargraph/pin/callable.rb' - - 'lib/solargraph/pin/method.rb' - 'lib/solargraph/type_checker.rb' - 'lib/solargraph/yard_map/mapper/to_method.rb' - 'lib/solargraph/yard_map/to_method.rb' From c1496631951875329a9209f1f77da0e5446d7eda Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 16 Jul 2025 14:08:01 -0400 Subject: [PATCH 210/561] Align trickier rules with current code quality for outside PRs --- .rubocop.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.rubocop.yml b/.rubocop.yml index 2a0396d93..2332648fd 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -44,6 +44,8 @@ Metrics/AbcSize: Max: 65 Metrics/MethodLength: Max: 60 +Metrics/ClassLength: + Max: 500 plugins: - rubocop-rspec From a8ea42a2d5d0dfc065cbfd191b3033c67092c92d Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 16 Jul 2025 14:08:49 -0400 Subject: [PATCH 211/561] Align trickier rules with current code quality for outside PRs --- .rubocop_todo.yml | 28 +--------------------------- 1 file changed, 1 insertion(+), 27 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 8cf432cbb..5ca1b33ce 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -837,40 +837,14 @@ Metrics/BlockNesting: - 'lib/solargraph/source_map/clip.rb' - 'lib/solargraph/type_checker.rb' -# Offense count: 30 +# Offense count: 4 # Configuration parameters: CountComments, Max, CountAsOne. Metrics/ClassLength: Exclude: - 'lib/solargraph/api_map.rb' - - 'lib/solargraph/api_map/index.rb' - - 'lib/solargraph/api_map/store.rb' - - 'lib/solargraph/complex_type.rb' - - 'lib/solargraph/complex_type/unique_type.rb' - - 'lib/solargraph/doc_map.rb' - 'lib/solargraph/language_server/host.rb' - - 'lib/solargraph/language_server/message/initialize.rb' - - 'lib/solargraph/library.rb' - - 'lib/solargraph/parser/flow_sensitive_typing.rb' - - 'lib/solargraph/parser/parser_gem/node_chainer.rb' - - 'lib/solargraph/parser/parser_gem/node_methods.rb' - - 'lib/solargraph/parser/parser_gem/node_processors/send_node.rb' - - 'lib/solargraph/pin/base.rb' - - 'lib/solargraph/pin/callable.rb' - - 'lib/solargraph/pin/method.rb' - - 'lib/solargraph/pin/parameter.rb' - - 'lib/solargraph/pin_cache.rb' - 'lib/solargraph/rbs_map/conversions.rb' - - 'lib/solargraph/shell.rb' - - 'lib/solargraph/source.rb' - - 'lib/solargraph/source/chain.rb' - - 'lib/solargraph/source/chain/call.rb' - - 'lib/solargraph/source/source_chainer.rb' - - 'lib/solargraph/source_map.rb' - - 'lib/solargraph/source_map/clip.rb' - - 'lib/solargraph/source_map/mapper.rb' - 'lib/solargraph/type_checker.rb' - - 'lib/solargraph/workspace.rb' - - 'lib/solargraph/workspace/config.rb' # Offense count: 113 # Configuration parameters: AllowedMethods, AllowedPatterns, Max. From c365097eca3d11acba46725fc3e92a857f7a5c3d Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 16 Jul 2025 14:13:56 -0400 Subject: [PATCH 212/561] Fix RuboCop issue --- spec/doc_map_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/doc_map_spec.rb b/spec/doc_map_spec.rb index 23d1e332c..0f056e07c 100644 --- a/spec/doc_map_spec.rb +++ b/spec/doc_map_spec.rb @@ -46,11 +46,11 @@ Bundler::LazySpecification.new('uncached_gem', '1.0.0', 'ruby') end let(:requires) { ['uncached_gem'] } - let(:workspace) { instance_double(Solargraph::Workspace) } - let(:pincache) { instance_double(Solargraph::PinCache) } let(:pre_cache) { false } + let(:workspace) { instance_double(Solargraph::Workspace) } before do + pincache = instance_double(Solargraph::PinCache) allow(workspace).to receive(:resolve_path_to_gemspecs).with('uncached_gem').and_return([uncached_lazy_gemspec]) allow(workspace).to receive(:fetch_dependencies).with(uncached_lazy_gemspec).and_return([]) allow(workspace).to receive(:fresh_pincache).and_return(pincache) From 9d116be1802ee6511f8a2211f34d5ffce2669746 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 16 Jul 2025 14:17:18 -0400 Subject: [PATCH 213/561] Fix RuboCop issue --- lib/solargraph/yardoc.rb | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/lib/solargraph/yardoc.rb b/lib/solargraph/yardoc.rb index 67fa7aa79..0fa0cd3b8 100644 --- a/lib/solargraph/yardoc.rb +++ b/lib/solargraph/yardoc.rb @@ -8,16 +8,16 @@ module Yardoc # Build and save a gem's yardoc into a given path. # - # @param yardoc_path [String] + # @param gem_yardoc_path [String] the path to the yardoc cache of a particular gem # @param yard_plugins [Array] # @param gemspec [Gem::Specification] # # @return [void] - def build_docs(yardoc_path, yard_plugins, gemspec) - return if docs_built?(yardoc_path, gemspec) + def build_docs(gem_yardoc_path, yard_plugins, gemspec) + return if docs_built?(gem_yardoc_path) Solargraph.logger.info "Saving yardoc for #{gemspec.name} #{gemspec.version} into #{yardoc_path}" - cmd = "yardoc --db #{yardoc_path} --no-output --plugin solargraph" + cmd = "yardoc --db #{gem_yardoc_path} --no-output --plugin solargraph" yard_plugins.each { |plugin| cmd << " --plugin #{plugin}" } Solargraph.logger.debug { "Running: #{cmd}" } # @todo set these up to run in parallel @@ -30,29 +30,28 @@ def build_docs(yardoc_path, yard_plugins, gemspec) Solargraph.logger.info stdout_and_stderr_str end - # @param yardoc_path [String] the path to the yardoc cache + # @param gem_yardoc_path [String] the path to the yardoc cache of a particular gem # @param gemspec [Gem::Specification] # @param out [IO, nil] where to log messages # @return [Array] - def build_pins(yardoc_path, gemspec, out: $stderr) - yardoc = load!(yardoc_path, gemspec) + def build_pins(gem_yardoc_path, gemspec, out: $stderr) + yardoc = load!(gem_yardoc_path) YardMap::Mapper.new(yardoc, gemspec).map end # True if the gem yardoc is cached. # - # @param yardoc_path [String] - # @param gemspec [Gem::Specification] - def docs_built?(yardoc_path, gemspec) - yardoc = File.join(yardoc_path, 'complete') + # @param gem_yardoc_path [String] + def docs_built?(gem_yardoc_path) + yardoc = File.join(gem_yardoc_path, 'complete') File.exist?(yardoc) end # True if another process is currently building the yardoc cache. # - # @param yardoc_path [String] - def processing?(yardoc_path) - yardoc = File.join(yardoc_path, 'processing') + # @param gem_yardoc_path [String] the path to the yardoc cache of a particular gem + def processing?(gem_yardoc_path) + yardoc = File.join(gem_yardoc_path, 'processing') File.exist?(yardoc) end @@ -60,11 +59,10 @@ def processing?(yardoc_path) # # @note This method modifies the global YARD registry. # - # @param yardoc_path [String] - # @param gemspec [Gem::Specification] + # @param gem_yardoc_path [String] the path to the yardoc cache of a particular gem # @return [Array] - def load!(yardoc_path, gemspec) - YARD::Registry.load! yardoc_path + def load!(gem_yardoc_path) + YARD::Registry.load! gem_yardoc_path YARD::Registry.all end end From 5de2b7ca5019956e7eaa177d051e8ea31add36f2 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 16 Jul 2025 14:27:42 -0400 Subject: [PATCH 214/561] Fix type issue --- lib/solargraph/pin_cache.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/solargraph/pin_cache.rb b/lib/solargraph/pin_cache.rb index 86aeeeb83..9d0998e6d 100644 --- a/lib/solargraph/pin_cache.rb +++ b/lib/solargraph/pin_cache.rb @@ -205,9 +205,9 @@ def log_cache_info(gemspec, # @param out [IO, nil] # @return [Array] def cache_yard_pins(gemspec, out) - yardoc_dir = yardoc_path(gemspec) - Yardoc.build_docs(yardoc_dir, yard_plugins, gemspec) unless Yardoc.docs_built?(yardoc_dir, gemspec) - pins = Yardoc.build_pins(yardoc_dir, gemspec, out: out) + gem_yardoc_path = yardoc_path(gemspec) + Yardoc.build_docs(gem_yardoc_path, yard_plugins, gemspec) unless Yardoc.docs_built?(gem_yardoc_path) + pins = Yardoc.build_pins(gem_yardoc_path, gemspec, out: out) serialize_yard_gem(gemspec, pins) logger.info { "Cached #{pins.length} YARD pins for gem #{gemspec.name}:#{gemspec.version}" } unless pins.empty? pins From e532d91d5f2dfca0e63cccc0a8d032c737778f67 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 16 Jul 2025 14:32:19 -0400 Subject: [PATCH 215/561] Use RSpec as a pre-push hook --- .overcommit.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.overcommit.yml b/.overcommit.yml index e4f93d68a..40747ce51 100644 --- a/.overcommit.yml +++ b/.overcommit.yml @@ -50,6 +50,9 @@ PreCommit: - ".bundle/**/*" - 'core.*' +PrePush: + RSpec: + enabled: true # # TrailingWhitespace: From f030cc61c588c97df72817944b66664cba2877b2 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 16 Jul 2025 14:44:23 -0400 Subject: [PATCH 216/561] Fix type issue --- lib/solargraph/yardoc.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/solargraph/yardoc.rb b/lib/solargraph/yardoc.rb index 0fa0cd3b8..cbc2af910 100644 --- a/lib/solargraph/yardoc.rb +++ b/lib/solargraph/yardoc.rb @@ -16,7 +16,7 @@ module Yardoc def build_docs(gem_yardoc_path, yard_plugins, gemspec) return if docs_built?(gem_yardoc_path) - Solargraph.logger.info "Saving yardoc for #{gemspec.name} #{gemspec.version} into #{yardoc_path}" + Solargraph.logger.info "Saving yardoc for #{gemspec.name} #{gemspec.version} into #{gem_yardoc_path}" cmd = "yardoc --db #{gem_yardoc_path} --no-output --plugin solargraph" yard_plugins.each { |plugin| cmd << " --plugin #{plugin}" } Solargraph.logger.debug { "Running: #{cmd}" } From 5fae72c5b162581aec15d0643e6d859634549df1 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 16 Jul 2025 14:51:01 -0400 Subject: [PATCH 217/561] Fix parser exclusion code --- lib/solargraph/pin_cache.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/solargraph/pin_cache.rb b/lib/solargraph/pin_cache.rb index 9d0998e6d..5c1516179 100644 --- a/lib/solargraph/pin_cache.rb +++ b/lib/solargraph/pin_cache.rb @@ -53,8 +53,7 @@ def cache_gem(gemspec:, rebuild: false, out: nil) # @param gemspec [Gem::Specification, Bundler::LazySpecification] # @param rbs_version_cache_key [String] def suppress_yard_cache?(gemspec, rbs_version_cache_key) - # TODO: test this - saw: Caching YARD and RBS collection and combined pins for gem parser:3.3.8.0 - if gemspec == 'parser' && rbs_version_cache_key != RbsMap::CACHE_KEY_UNRESOLVED + if gemspec.name == 'parser' && rbs_version_cache_key != RbsMap::CACHE_KEY_UNRESOLVED # parser takes forever to build YARD pins, but has excellent RBS collection pins return true end From f26aae30dec00b9e8ba9a8bce79e93fb310743e3 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 16 Jul 2025 14:53:58 -0400 Subject: [PATCH 218/561] Fix gems built message --- lib/solargraph/doc_map.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/solargraph/doc_map.rb b/lib/solargraph/doc_map.rb index d7bed4cc2..b1f565039 100644 --- a/lib/solargraph/doc_map.rb +++ b/lib/solargraph/doc_map.rb @@ -64,16 +64,14 @@ def cache_doc_map_gems!(out) logger.debug { "Caching: #{uncached_gemspecs.map(&:name)}" } PinCache.cache_core unless PinCache.has_core? load_serialized_gem_pins - existing_pin_count = pins.length time = Benchmark.measure do uncached_gemspecs.each do |gemspec| cache(gemspec, out: out) end end - pins_processed = pins.length - existing_pin_count milliseconds = (time.real * 1000).round if (milliseconds > 500) && uncached_gemspecs.any? && out && uncached_gemspecs.any? - out.puts "Built #{pins_processed} gem pins in #{milliseconds} ms" + out.puts "Built #{uncached_gemspecs.length} gems in #{milliseconds} ms" end load_serialized_gem_pins end From 37401bc1870543be146a52487945e9d38112574d Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 16 Jul 2025 14:59:16 -0400 Subject: [PATCH 219/561] Fix parser exclusion code --- lib/solargraph/pin_cache.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/solargraph/pin_cache.rb b/lib/solargraph/pin_cache.rb index 5c1516179..0e5760e76 100644 --- a/lib/solargraph/pin_cache.rb +++ b/lib/solargraph/pin_cache.rb @@ -170,7 +170,8 @@ def build_combine_and_cache(gemspec, build_combined: build_combined, out: out) cache_yard_pins(gemspec, out) if build_yard - yard_pins = deserialize_yard_pin_cache(gemspec) + # this can be nil even if we aren't told to build it - see suppress_yard_cache? + yard_pins = deserialize_yard_pin_cache(gemspec) || [] cache_rbs_collection_pins(gemspec, out) if build_rbs_collection rbs_collection_pins = deserialize_rbs_collection_cache(gemspec, rbs_version_cache_key) cache_combined_pins(gemspec, rbs_version_cache_key, yard_pins, rbs_collection_pins) if build_combined From 20aebce8b567c015f7674030ec0ccc0b8a158476 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 16 Jul 2025 16:06:43 -0400 Subject: [PATCH 220/561] Fix parser-related type issues --- .../parser/parser_gem/class_methods.rb | 24 +-- rbs_collection.yaml | 6 +- sig/shims/parser/3.2.0.1/manifest.yaml | 7 + sig/shims/parser/3.2.0.1/parser.rbs | 199 ++++++++++++++++++ sig/shims/parser/3.2.0.1/polyfill.rbs | 4 + 5 files changed, 225 insertions(+), 15 deletions(-) create mode 100644 sig/shims/parser/3.2.0.1/manifest.yaml create mode 100644 sig/shims/parser/3.2.0.1/parser.rbs create mode 100644 sig/shims/parser/3.2.0.1/polyfill.rbs diff --git a/lib/solargraph/parser/parser_gem/class_methods.rb b/lib/solargraph/parser/parser_gem/class_methods.rb index 58ca8056b..f86759c04 100644 --- a/lib/solargraph/parser/parser_gem/class_methods.rb +++ b/lib/solargraph/parser/parser_gem/class_methods.rb @@ -1,15 +1,8 @@ # frozen_string_literal: true require 'prism' - -# Awaiting ability to use a version containing https://github.com/whitequark/parser/pull/1076 -# -# @!parse -# class ::Parser::Base < ::Parser::Builder -# # @return [Integer] -# def version; end -# end -# class ::Parser::CurrentRuby < ::Parser::Base; end +require 'ast' +require 'parser' module Solargraph module Parser @@ -66,6 +59,9 @@ def references source, name # @return [Array(Integer, Integer), Array(nil, nil)] extract_offset = ->(code, offset) { [soff = code.index(name, offset), soff + name.length] } end + # @sg-ignore Wrong argument type for + # Solargraph::Parser::ParserGem::ClassMethods#inner_node_references: + # top expected AST::Node, received Parser::AST::Node, nil inner_node_references(name, source.node).map do |n| rng = Range.from_node(n) offset = Position.to_offset(source.code, rng.start) @@ -81,8 +77,8 @@ def references source, name end # @param name [String] - # @param top [AST::Node] - # @return [Array] + # @param top [Parser::AST::Node] + # @return [Array] def inner_node_references name, top result = [] if top.is_a?(AST::Node) && top.to_s.include?(":#{name}") @@ -118,7 +114,7 @@ def version parser.version end - # @param node [BasicObject] + # @param node [Object] # @return [Boolean] def is_ast_node? node node.is_a?(::Parser::AST::Node) @@ -135,6 +131,9 @@ def node_range node # @param node [Parser::AST::Node] # @return [Array] def string_ranges node + # @sg-ignore Wrong argument type for + # Solargraph::Parser::ParserGem::ClassMethods#is_ast_node?: + # node expected Object, received Parser::AST::Node return [] unless is_ast_node?(node) result = [] if node.type == :str @@ -145,6 +144,7 @@ def string_ranges node end if node.type == :dstr && node.children.last.nil? last = node.children[-2] + # @sg-ignore Unresolved call to nil? unless last.nil? rng = Range.from_node(last) pos = Position.new(rng.ending.line, rng.ending.column - 1) diff --git a/rbs_collection.yaml b/rbs_collection.yaml index 66e30ecfe..4f62fa6e4 100644 --- a/rbs_collection.yaml +++ b/rbs_collection.yaml @@ -1,14 +1,14 @@ # Download sources sources: + - type: local + path: sig/shims + - type: git name: ruby/gem_rbs_collection remote: https://github.com/ruby/gem_rbs_collection.git revision: main repo_dir: gems -# You can specify local directories as sources also. -# - type: local -# path: path/to/your/local/repository # A directory to install the downloaded RBSs path: .gem_rbs_collection diff --git a/sig/shims/parser/3.2.0.1/manifest.yaml b/sig/shims/parser/3.2.0.1/manifest.yaml new file mode 100644 index 000000000..f00038381 --- /dev/null +++ b/sig/shims/parser/3.2.0.1/manifest.yaml @@ -0,0 +1,7 @@ +# manifest.yaml describes dependencies which do not appear in the gemspec. +# If this gem includes such dependencies, comment-out the following lines and +# declare the dependencies. +# If all dependencies appear in the gemspec, you should remove this file. +# +dependencies: + - name: ast diff --git a/sig/shims/parser/3.2.0.1/parser.rbs b/sig/shims/parser/3.2.0.1/parser.rbs new file mode 100644 index 000000000..065fcf5ca --- /dev/null +++ b/sig/shims/parser/3.2.0.1/parser.rbs @@ -0,0 +1,199 @@ +module Parser + CurrentRuby: Parser::Base + + class SyntaxError < StandardError + end + class UnknownEncodingInMagicComment < StandardError + end + + class Base < Racc::Parser + def version: -> Integer + def self.parse: (String string, ?String file, ?Integer line) -> Parser::AST::Node? + def self.parse_with_comments: (String string, ?String file, ?Integer line) -> [Parser::AST::Node?, Array[Source::Comment]] + def parse: (Parser::Source::Buffer source_buffer) -> Parser::AST::Node? + end + + class Ruby18 < Base + end + class Ruby19 < Base + end + class Ruby20 < Base + end + class Ruby21 < Base + end + class Ruby22 < Base + end + class Ruby23 < Base + end + class Ruby24 < Base + end + class Ruby25 < Base + end + class Ruby26 < Base + end + class Ruby27 < Base + end + class Ruby30 < Base + end + class Ruby31 < Base + end + class Ruby32 < Base + end + class Ruby33 < Base + end + + module AST + class Node < ::AST::Node + attr_reader location: Source::Map + alias loc location + end + + class Processor + module Mixin + def process: (Node? node) -> Node? + end + + include Mixin + end + end + + module Source + class Range + attr_reader source_buffer: Buffer + attr_reader begin_pos: Integer + attr_reader end_pos: Integer + def begin: () -> Range + def end: () -> Range + def size: () -> Integer + alias length size + def line: () -> Integer + alias first_line line + def column: () -> Integer + def last_line: () -> Integer + def last_column: () -> Integer + def column_range: () -> ::Range[Integer] + def source_line: () -> String + def source: () -> String + def with: (?begin_pos: Integer, ?end_pos: Integer) -> Range + def adjust: (?begin_pos: Integer, ?end_pos: Integer) -> Range + def resize: (Integer new_size) -> Range + def join: (Range other) -> Range + def intersect: (Range other) -> Range? + def disjoint?: (Range other) -> bool + def overlaps?: (Range other) -> bool + def contains?: (Range other) -> bool + def contained?: (Range other) -> bool + def crossing?: (Range other) -> bool + def empty?: () -> bool + end + + ## + # A buffer with source code. {Buffer} contains the source code itself, + # associated location information (name and first line), and takes care + # of encoding. + # + # A source buffer is immutable once populated. + # + # @!attribute [r] name + # Buffer name. If the buffer was created from a file, the name corresponds + # to relative path to the file. + # @return [String] buffer name + # + # @!attribute [r] first_line + # First line of the buffer, 1 by default. + # @return [Integer] first line + # + # @api public + # + class Buffer + attr_reader name: String + attr_reader first_line: Integer + + def self.recognize_encoding: (String) -> Encoding + def self.reencode_string: (String) -> String + + def initialize: (untyped name, ?Integer first_line, ?source: untyped) -> void + def read: () -> self + def source: () -> String + def source=: (String) -> String + def raw_source: (String) -> String + def decompose_position: (Integer) -> [Integer, Integer] + def source_lines: () -> Array[String] + def source_line: (Integer) -> String + def line_range: (Integer) -> ::Range[Integer] + def source_range: () -> ::Range[Integer] + def last_line: () -> Integer + end + + class TreeRewriter + def replace: (Range range, String content) -> self + def remove: (Range range) -> self + def insert_before: (Range range, String content) -> self + def insert_after: (Range range, String content) -> self + end + + class Map + attr_reader node: AST::Node | nil + attr_reader expression: Range + def line: () -> Integer + def first_line: () -> Integer + def last_line: () -> Integer + def column: () -> Integer + def last_column: () -> Integer + end + + class Map::Collection < Map + attr_reader begin: Range? + attr_reader end: Range? + end + + class Map::Condition < Map + attr_reader keyword: Range + attr_reader begin: Range? + attr_reader else: Range? + attr_reader end: Range + end + + class Map::Heredoc < Map + attr_reader heredoc_body: Range + attr_reader heredoc_end: Range + end + + class Map::Keyword < Map + attr_reader keyword: Range + attr_reader begin: Range? + attr_reader end: Range? + end + + class Map::MethodDefinition < Map + attr_reader keyword: Range + attr_reader operator: Range? + attr_reader name: Range? + attr_reader end: Range? + attr_reader assignment: Range? + end + + class Map::Operator < Map + attr_reader operator: Range? + end + + class Map::Send < Map + attr_reader dot: Range? + attr_reader selector: Range + attr_reader operator: Range? + attr_reader begin: Range? + attr_reader end: Range? + end + + class Map::Ternary < Map + attr_reader question: Range? + attr_reader colon: Range + end + + class Comment + attr_reader text: String + attr_reader location: Map + alias loc location + end + end +end diff --git a/sig/shims/parser/3.2.0.1/polyfill.rbs b/sig/shims/parser/3.2.0.1/polyfill.rbs new file mode 100644 index 000000000..2e8c12487 --- /dev/null +++ b/sig/shims/parser/3.2.0.1/polyfill.rbs @@ -0,0 +1,4 @@ +module Racc + class Parser + end +end From 233f6b00810f7add9f14ba8f182f7485fcdd9b7c Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 17 Jul 2025 09:43:11 -0400 Subject: [PATCH 221/561] Fix hole in type checking evaluation The call to `bar()` in `[ bar('string') ].compact` is not currently type-checked due to a missed spot in `NodeMethods#call_nodes_from(node)` --- .../parser/parser_gem/node_methods.rb | 1 + spec/parser/node_methods_spec.rb | 24 +++++++++++++ spec/type_checker/levels/strict_spec.rb | 35 ++++++++++++++++++- 3 files changed, 59 insertions(+), 1 deletion(-) diff --git a/lib/solargraph/parser/parser_gem/node_methods.rb b/lib/solargraph/parser/parser_gem/node_methods.rb index b716b352d..af5c62cca 100644 --- a/lib/solargraph/parser/parser_gem/node_methods.rb +++ b/lib/solargraph/parser/parser_gem/node_methods.rb @@ -179,6 +179,7 @@ def call_nodes_from node node.children[1..-1].each { |child| result.concat call_nodes_from(child) } elsif node.type == :send result.push node + result.concat call_nodes_from(node.children.first) node.children[2..-1].each { |child| result.concat call_nodes_from(child) } elsif [:super, :zsuper].include?(node.type) result.push node diff --git a/spec/parser/node_methods_spec.rb b/spec/parser/node_methods_spec.rb index eb026725b..cfd5c614a 100644 --- a/spec/parser/node_methods_spec.rb +++ b/spec/parser/node_methods_spec.rb @@ -440,5 +440,29 @@ def super_with_block calls = Solargraph::Parser::NodeMethods.call_nodes_from(source.node) expect(calls).to be_one end + + it 'handles chained calls' do + source = Solargraph::Source.load_string(%( + Foo.new.bar('string') + )) + calls = Solargraph::Parser::NodeMethods.call_nodes_from(source.node) + expect(calls.length).to eq(2) + end + + it 'handles calls from inside array literals' do + source = Solargraph::Source.load_string(%( + [ Foo.new.bar('string') ] + )) + calls = Solargraph::Parser::NodeMethods.call_nodes_from(source.node) + expect(calls.length).to eq(2) + end + + it 'handles calls from inside array literals that are chained' do + source = Solargraph::Source.load_string(%( + [ Foo.new.bar('string') ].compact + )) + calls = Solargraph::Parser::NodeMethods.call_nodes_from(source.node) + expect(calls.length).to eq(3) + end end end diff --git a/spec/type_checker/levels/strict_spec.rb b/spec/type_checker/levels/strict_spec.rb index b198cec89..25890683b 100644 --- a/spec/type_checker/levels/strict_spec.rb +++ b/spec/type_checker/levels/strict_spec.rb @@ -115,6 +115,39 @@ def bar(baz); end expect(checker.problems.first.message).to include('Wrong argument type') end + it 'reports mismatched argument types in chained calls' do + checker = type_checker(%( + # @param baz [Integer] + # @return [String] + def bar(baz); "foo"; end + bar('string').upcase + )) + expect(checker.problems).to be_one + expect(checker.problems.first.message).to include('Wrong argument type') + end + + it 'reports mismatched argument types in calls inside array literals' do + checker = type_checker(%( + # @param baz [Integer] + # @return [String] + def bar(baz); "foo"; end + [ bar('string') ] + )) + expect(checker.problems).to be_one + expect(checker.problems.first.message).to include('Wrong argument type') + end + + it 'reports mismatched argument types in calls inside array literals used in a chain' do + checker = type_checker(%( + # @param baz [Integer] + # @return [String] + def bar(baz); "foo"; end + [ bar('string') ].compact + )) + expect(checker.problems).to be_one + expect(checker.problems.first.message).to include('Wrong argument type') + end + xit 'complains about calling a private method from an illegal place' xit 'complains about calling a non-existent method' @@ -126,7 +159,7 @@ def foo(a) a[0] = :something end )) - expect(checker.problems.map(&:problems)).to eq(['Wrong argument type']) + expect(checker.problems.map(&:message)).to eq(['Wrong argument type']) end it 'complains about dereferencing a non-existent tuple slot' From 9fd1e3501eceb035fd0ef382b11b35b50c36bc24 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 17 Jul 2025 12:52:35 -0400 Subject: [PATCH 222/561] Avoid over-reporting call issues --- lib/solargraph/type_checker.rb | 225 ++++++++++++++++--------------- spec/parser/node_methods_spec.rb | 16 +++ 2 files changed, 134 insertions(+), 107 deletions(-) diff --git a/lib/solargraph/type_checker.rb b/lib/solargraph/type_checker.rb index aa215f97b..f867ba04f 100644 --- a/lib/solargraph/type_checker.rb +++ b/lib/solargraph/type_checker.rb @@ -284,127 +284,138 @@ def call_problems # @param chain [Solargraph::Source::Chain] # @param api_map [Solargraph::ApiMap] - # @param block_pin [Solargraph::Pin::Base] + # @param closure_pin [Solargraph::Pin::Closure] # @param locals [Array] # @param location [Solargraph::Location] # @return [Array] - def argument_problems_for chain, api_map, block_pin, locals, location + def argument_problems_for chain, api_map, closure_pin, locals, location result = [] base = chain - until base.links.length == 1 && base.undefined? - last_base_link = base.links.last - break unless last_base_link.is_a?(Solargraph::Source::Chain::Call) - - arguments = last_base_link.arguments - - pins = base.define(api_map, block_pin, locals) - - first_pin = pins.first - if first_pin.is_a?(Pin::DelegatedMethod) && !first_pin.resolvable?(api_map) - # Do nothing, as we can't find the actual method implementation - elsif first_pin.is_a?(Pin::Method) - # @type [Pin::Method] - pin = first_pin - ap = if base.links.last.is_a?(Solargraph::Source::Chain::ZSuper) - arity_problems_for(pin, fake_args_for(block_pin), location) - elsif pin.path == 'Class#new' - fqns = if base.links.one? - block_pin.namespace + # @type last_base_link [Solargraph::Source::Chain::Call] + last_base_link = base.links.last + return [] unless last_base_link.is_a?(Solargraph::Source::Chain::Call) + + + arguments = last_base_link.arguments + + pins = base.define(api_map, closure_pin, locals) + + first_pin = pins.first + if first_pin.is_a?(Pin::DelegatedMethod) && !first_pin.resolvable?(api_map) + # Do nothing, as we can't find the actual method implementation + return [] + elsif first_pin.is_a?(Pin::Method) + # @type [Pin::Method] + pin = first_pin + ap = if base.links.last.is_a?(Solargraph::Source::Chain::ZSuper) + arity_problems_for(pin, fake_args_for(closure_pin), location) + elsif pin.path == 'Class#new' + fqns = if base.links.one? + closure_pin.namespace + else + base.base.infer(api_map, closure_pin, locals).namespace + end + init = api_map.get_method_stack(fqns, 'initialize').first + + init ? arity_problems_for(init, arguments, location) : [] + else + arity_problems_for(pin, arguments, location) + end + return ap unless ap.empty? + return [] if !rules.validate_calls? || base.links.first.is_a?(Solargraph::Source::Chain::ZSuper) + + params = first_param_hash(pins) + + all_errors = [] + pin.signatures.sort { |sig| sig.parameters.length }.each do |sig| + signature_errors = signature_argument_problems_for location, locals, closure_pin, params, arguments, sig, pin + if signature_errors.empty? + # we found a signature that works - meaning errors from + # other signatures don't matter. + return [] + end + all_errors.concat signature_errors + end + result.concat all_errors + end + result + end + + # @param location [Location] + # @param locals [Array] + # @param closure_pin [Pin::Closure] + # @param params [Hash{String => Hash{Symbol => String, Solargraph::ComplexType}}] + # @param arguments [Array] + # @param sig [Pin::Signature] + # @param pin [Pin::Method] + # + # @return [Array] + def signature_argument_problems_for location, locals, closure_pin, params, arguments, sig, pin + errors = [] + # @todo add logic mapping up restarg parameters with + # arguments (including restarg arguments). Use tuples + # when possible, and when not, ensure provably + # incorrect situations are detected. + sig.parameters.each_with_index do |par, idx| + return errors if par.decl == :restarg # bail out and assume the rest is valid pending better arg processing + argchain = arguments[idx] + if argchain.nil? + if par.decl == :arg + final_arg = arguments.last + if final_arg && final_arg.node.type == :splat + argchain = final_arg + return errors else - base.base.infer(api_map, block_pin, locals).namespace + errors.push Problem.new(location, "Not enough arguments to #{pin.path}") end - init = api_map.get_method_stack(fqns, 'initialize').first - init ? arity_problems_for(init, arguments, location) : [] else - arity_problems_for(pin, arguments, location) + final_arg = arguments.last + argchain = final_arg if final_arg && [:kwsplat, :hash].include?(final_arg.node.type) end - unless ap.empty? - result.concat ap - break - end - break if !rules.validate_calls? || base.links.first.is_a?(Solargraph::Source::Chain::ZSuper) - - params = first_param_hash(pins) - - all_errors = [] - pin.signatures.sort { |sig| sig.parameters.length }.each do |sig| - errors = [] - sig.parameters.each_with_index do |par, idx| - # @todo add logic mapping up restarg parameters with - # arguments (including restarg arguments). Use tuples - # when possible, and when not, ensure provably - # incorrect situations are detected. - break if par.decl == :restarg # bail out pending better arg processing - argchain = arguments[idx] - if argchain.nil? - if par.decl == :arg - final_arg = arguments.last - if final_arg && final_arg.node.type == :splat - argchain = final_arg - next # don't try to apply the type of the splat - unlikely to be specific enough - else - errors.push Problem.new(location, "Not enough arguments to #{pin.path}") - next - end - else - final_arg = arguments.last - argchain = final_arg if final_arg && [:kwsplat, :hash].include?(final_arg.node.type) - end - end - if argchain - if par.decl != :arg - errors.concat kwarg_problems_for sig, argchain, api_map, block_pin, locals, location, pin, params, idx - next - else - if argchain.node.type == :splat && argchain == arguments.last - final_arg = argchain - end - if (final_arg && final_arg.node.type == :splat) - # The final argument given has been seen and was a - # splat, which doesn't give us useful types or - # arities against positional parameters, so let's - # continue on in case there are any required - # kwargs we should warn about - next - end - - if argchain.node.type == :splat && par != sig.parameters.last - # we have been given a splat and there are more - # arguments to come. - - # @todo Improve this so that we can skip past the - # rest of the positional parameters here but still - # process the kwargs - break - end - ptype = params.key?(par.name) ? params[par.name][:qualified] : ComplexType::UNDEFINED - ptype = ptype.self_to_type(par.context) - if ptype.nil? - # @todo Some level (strong, I guess) should require the param here - else - argtype = argchain.infer(api_map, block_pin, locals) - if argtype.defined? && ptype.defined? && !any_types_match?(api_map, ptype, argtype) - errors.push Problem.new(location, "Wrong argument type for #{pin.path}: #{par.name} expected #{ptype}, received #{argtype}") - next - end - end - end - elsif par.decl == :kwarg - errors.push Problem.new(location, "Call to #{pin.path} is missing keyword argument #{par.name}") - next - end + end + if argchain + if par.decl != :arg + errors.concat kwarg_problems_for sig, argchain, api_map, closure_pin, locals, location, pin, params, idx + next + else + if argchain.node.type == :splat && argchain == arguments.last + final_arg = argchain end - if errors.empty? - all_errors.clear - break + if (final_arg && final_arg.node.type == :splat) + # The final argument given has been seen and was a + # splat, which doesn't give us useful types or + # arities against positional parameters, so let's + # continue on in case there are any required + # kwargs we should warn about + next + end + if argchain.node.type == :splat && par != sig.parameters.last + # we have been given a splat and there are more + # arguments to come. + + # @todo Improve this so that we can skip past the + # rest of the positional parameters here but still + # process the kwargs + return errors + end + ptype = params.key?(par.name) ? params[par.name][:qualified] : ComplexType::UNDEFINED + ptype = ptype.self_to_type(par.context) + if ptype.nil? + # @todo Some level (strong, I guess) should require the param here + else + argtype = argchain.infer(api_map, closure_pin, locals) + if argtype.defined? && ptype.defined? && !any_types_match?(api_map, ptype, argtype) + errors.push Problem.new(location, "Wrong argument type for #{pin.path}: #{par.name} expected #{ptype}, received #{argtype}") + return errors + end end - all_errors.concat errors end - result.concat all_errors + elsif par.decl == :kwarg + errors.push Problem.new(location, "Call to #{pin.path} is missing keyword argument #{par.name}") + next end - base = base.base end - result + errors end # @param sig [Pin::Signature] diff --git a/spec/parser/node_methods_spec.rb b/spec/parser/node_methods_spec.rb index cfd5c614a..f9504b584 100644 --- a/spec/parser/node_methods_spec.rb +++ b/spec/parser/node_methods_spec.rb @@ -464,5 +464,21 @@ def super_with_block calls = Solargraph::Parser::NodeMethods.call_nodes_from(source.node) expect(calls.length).to eq(3) end + + it 'does not over-report calls' do + source = Solargraph::Source.load_string(%( + class Foo + def something + end + end + class Bar < Foo + def something + super(1) + 2 + end + end + )) + calls = Solargraph::Parser::NodeMethods.call_nodes_from(source.node) + expect(calls.length).to eq(2) + end end end From c5e6ad13cccb2651864f1669db429eb48c225e04 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 17 Jul 2025 16:02:16 -0400 Subject: [PATCH 223/561] Factor out and improve Workspace::Gemspecs + Workspace::RequirePaths --- lib/solargraph/doc_map.rb | 10 +- lib/solargraph/workspace.rb | 248 +++------------------- lib/solargraph/workspace/gemspecs.rb | 247 +++++++++++++++++++++ lib/solargraph/workspace/require_paths.rb | 106 +++++++++ spec/doc_map_spec.rb | 2 +- 5 files changed, 386 insertions(+), 227 deletions(-) create mode 100644 lib/solargraph/workspace/gemspecs.rb create mode 100644 lib/solargraph/workspace/require_paths.rb diff --git a/lib/solargraph/doc_map.rb b/lib/solargraph/doc_map.rb index b1f565039..084b75e54 100644 --- a/lib/solargraph/doc_map.rb +++ b/lib/solargraph/doc_map.rb @@ -4,9 +4,10 @@ require 'benchmark' module Solargraph - # A collection of pins generated from required gems. Multiple can - # be created per workspace, to represent the pins available in - # different files based on their 'require' lines. + # A collection of pins generated from specific 'require' statements + # in code. Multiple can be created per workspace, to represent the + # pins available in different files based on their particular + # 'require' lines. # class DocMap include Logging @@ -125,6 +126,7 @@ def load_serialized_gem_pins(out: $stderr) # this will load from disk if needed; no need to manage # uncached_gemspecs to trigger that later stdlib_name_guess = path.split('/').first + # TODO: this results in pins being generated in real time, not in advance with solargrpah gems rbs_pins = pin_cache.cache_stdlib_rbs_map stdlib_name_guess if stdlib_name_guess @pins.concat rbs_pins if rbs_pins end @@ -153,7 +155,7 @@ def load_serialized_gem_pins(out: $stderr) # @return [Hash{String => Array}] def required_gems_map - @required_gems_map ||= requires.to_h { |path| [path, workspace.resolve_path_to_gemspecs(path)] } + @required_gems_map ||= requires.to_h { |require| [require, workspace.resolve_require(require)] } end def inspect diff --git a/lib/solargraph/workspace.rb b/lib/solargraph/workspace.rb index a4832c6f6..9a6ec8c4d 100644 --- a/lib/solargraph/workspace.rb +++ b/lib/solargraph/workspace.rb @@ -13,21 +13,12 @@ class Workspace include Logging autoload :Config, 'solargraph/workspace/config' + autoload :Gemspecs, 'solargraph/workspace/gemspecs' + autoload :RequirePaths, 'solargraph/workspace/require_paths' # @return [String] attr_reader :directory - attr_reader :preferences - - # The require paths associated with the workspace. - # - # @return [Array] - attr_reader :require_paths - - # @return [Array] - attr_reader :gemnames - alias source_gems gemnames - # @param directory [String] # @param config [Config, nil] # @param server [Hash] @@ -36,11 +27,14 @@ def initialize directory = '', config = nil, server = {} @config = config @server = server load_sources - @gemnames = [] - @require_paths = generate_require_paths require_plugins - # @todo implement preferences - @preferences = [] + end + + # The require paths associated with the workspace. + # + # @return [Array] + def require_paths + @require_paths ||= RequirePaths.new(directory, config).generate end # @return [Solargraph::Workspace::Config] @@ -48,11 +42,22 @@ def config @config ||= Solargraph::Workspace::Config.new(directory) end + # @param gemspec [Gem::Specification] + # @return [Array] + def fetch_dependencies gemspec + gemspecs.fetch_dependencies(gemspec) + end + # @return [Solargraph::PinCache] def pin_cache @pin_cache ||= fresh_pincache end + # @return [Array] + def resolve_require require + gemspecs.resolve_require(require) + end + # @return [Environ] def global_environ # empty docmap, since the result needs to work in any possible @@ -90,63 +95,6 @@ def yard_plugins @yard_plugins ||= global_environ.yard_plugins.sort.uniq end - # @param path [String] - # @return [::Array, nil] - def resolve_path_to_gemspecs path - return nil if path.empty? - # TODO: there should be a distinction between everything and the non-require: false stuff - return gemspecs_required_from_bundler if path == 'bundler/require' - - gemspecs = gemspecs_required_from_bundler - # @type [Gem::Specification, nil] - gemspec = gemspecs.find { |gemspec| gemspec.name == path } - if gemspec.nil? - gem_name_guess = path.split('/').first - begin - # this can happen when the gem is included via a local path in - # a Gemfile; Gem doesn't try to index the paths in that case. - # - # See if we can make a good guess: - potential_gemspec = gemspecs.find { |gemspec| gemspec.name == gem_name_guess } - - return nil if potential_gemspec.nil? - - file = "lib/#{path}.rb" - # @sg-ignore Unresolved call to files - gemspec = potential_gemspec if potential_gemspec&.files&.any? { |gemspec_file| file == gemspec_file } - rescue Gem::MissingSpecError - logger.debug { "Require path #{path} could not be resolved to a gem via find_by_path or guess of #{gem_name_guess}" } - [] - end - end - return nil if gemspec.nil? - [gemspec_or_preference(gemspec)] - end - - # @param gemspec [Gem::Specification] - # @return [Array] - def fetch_dependencies gemspec - gemspecs = gemspecs_required_from_bundler - - # @param spec [Gem::Dependency] - only_runtime_dependencies(gemspec).each_with_object(Set.new) do |spec, deps| - Solargraph.logger.info "Adding #{spec.name} dependency for #{gemspec.name}" - # @type [Gem::Specification, nil] - dep = gemspecs.find { |dep| dep.name == spec.name } - # @todo is next line necessary? - dep ||= Gem::Specification.find_by_name(spec.name, spec.requirement) - deps.merge fetch_dependencies(dep) if deps.add?(dep) - rescue Gem::MissingSpecError - Solargraph.logger.warn "Gem dependency #{spec.name} #{spec.requirement} for #{gemspec.name} not found in RubyGems." - end.to_a - end - - # @param gemspec [Gem::Specification] - # @return [Array] - def only_runtime_dependencies gemspec - gemspec.dependencies - gemspec.development_dependencies - end - # Merge the source. A merge will update the existing source for the file # or add it to the sources if the workspace is configured to include it. # The source is ignored if the configuration excludes it. @@ -217,23 +165,6 @@ def would_require? path false end - # True if the workspace contains at least one gemspec file. - # - # @return [Boolean] - def gemspec? - !gemspecs.empty? - end - - # Get an array of all gemspec files in the workspace. - # - # @return [Array] - def gemspecs - return [] if directory.empty? || directory == '*' - @gemspecs ||= Dir[File.join(directory, '**/*.gemspec')].select do |gs| - config.allow? gs - end - end - # @return [String, nil] def rbs_collection_path @gem_rbs_collection ||= read_rbs_collection_path @@ -256,8 +187,10 @@ def rbs_collection_config_path # @return [void] def cache_all_for_workspace! out, rebuild: false PinCache.cache_core(out: $stdout) unless PinCache.has_core? + # TODO: This should bring in dependencies as well + # # @type [Array] - specs = gemspecs_required_from_bundler + specs = immediate_gemspecs_from_bundler specs.each do |spec| unless pin_cache.cached?(spec) pin_cache.cache_gem(gemspec: spec, rebuild: rebuild, out: out) @@ -281,95 +214,9 @@ def command_path private - # True if the workspace has a root Gemfile. - # - # @todo Handle projects with custom Bundler/Gemfile setups (see DocMap#gemspecs_required_from_bundler) - # - def gemfile? - directory && File.file?(File.join(directory, 'Gemfile')) - end - - # @return [Array] - def gemspecs_required_from_bundler - @gemspecs_required_from_bundler ||= - begin - if directory && Bundler.definition&.lockfile&.to_s&.start_with?(directory) # rubocop:disable Style/SafeNavigationChainLength - # Find only the gems bundler is now using - Bundler.definition.locked_gems.specs.flat_map do |lazy_spec| - logger.info "Handling #{lazy_spec.name}:#{lazy_spec.version}" - [Gem::Specification.find_by_name(lazy_spec.name, lazy_spec.version)] - rescue Gem::MissingSpecError => e - logger.info("Could not find #{lazy_spec.name}:#{lazy_spec.version} with find_by_name, falling back to guess") - # can happen in local filesystem references - specs = resolve_path_to_gemspecs lazy_spec.name - logger.warn "Gem #{lazy_spec.name} #{lazy_spec.version} from bundle not found: #{e}" if specs.nil? - next specs - end.compact - else - logger.info 'Fetching gemspecs required from Bundler (bundler/require)' - gemspecs_required_from_external_bundle - end - end - end - - # @return [Array] - def gemspecs_required_from_external_bundle - return [] unless directory - - @gemspecs_required_from_external_bundle ||= - begin - logger.info 'Fetching gemspecs required from external bundle' - - Solargraph.with_clean_env do - cmd = [ - 'ruby', '-e', - "require 'bundler'; require 'json'; Dir.chdir('#{directory}') { puts Bundler.definition.locked_gems.specs.map { |spec| [spec.name, spec.version] }.to_h.to_json }" - ] - # @sg-ignore Unresolved call to capture3 - o, e, s = Open3.capture3(*cmd) - if s.success? - Solargraph.logger.debug "External bundle: #{o}" - hash = o && !o.empty? ? JSON.parse(o.split("\n").last) : {} - hash.flat_map do |name, version| - Gem::Specification.find_by_name(name, version) - rescue Gem::MissingSpecError => e - logger.info("Could not find #{name}:#{version} with find_by_name, falling back to guess") - # can happen in local filesystem references - specs = Gem::Specification.find_by_path(name) - specs ||= Gem::Specification.find_by_name(name) - logger.warn "Gem #{name} #{version} from bundle not found: #{e}" if specs.nil? - next specs - end.compact - else - Solargraph.logger.warn e - raise BundleNotFoundError, "Failed to load gems from bundle at #{directory}" - end - end - end - end - - # @return [Hash{String => Gem::Specification}] - def preference_map - @preference_map ||= preferences.to_h { |gemspec| [gemspec.name, gemspec] } - end - - # @param gemspec [Gem::Specification] - # @return [Gem::Specification] - def gemspec_or_preference gemspec - return gemspec unless preference_map.key?(gemspec.name) - return gemspec if gemspec.version == preference_map[gemspec.name].version - - change_gemspec_version gemspec, preference_map[by_path.name].version - end - - # @param gemspec [Gem::Specification] - # @param version [Gem::Version] - # @return [Gem::Specification] - def change_gemspec_version gemspec, version - Gem::Specification.find_by_name(gemspec.name, "= #{version}") - rescue Gem::MissingSpecError - Solargraph.logger.info "Gem #{gemspec.name} version #{version} not found. Using #{gemspec.version} instead" - gemspec + # @return [Solargraph::Workspace::Gemspecs] + def gemspecs + @gemspecs ||= Solargraph::Workspace::Gemspecs.new(directory) end # The language server configuration (or an empty hash if the workspace was @@ -399,49 +246,6 @@ def load_sources end end - # Generate require paths from gemspecs if they exist or assume the default - # lib directory. - # - # @return [Array] - def generate_require_paths - return configured_require_paths unless gemspec? - result = [] - gemspecs.each do |file| - base = File.dirname(file) - # HACK: Evaluating gemspec files violates the goal of not running - # workspace code, but this is how Gem::Specification.load does it - # anyway. - cmd = ['ruby', '-e', "require 'rubygems'; require 'json'; spec = eval(File.read('#{file}'), TOPLEVEL_BINDING, '#{file}'); return unless Gem::Specification === spec; puts({name: spec.name, paths: spec.require_paths}.to_json)"] - # @sg-ignore Unresolved call to capture3 - o, e, s = Open3.capture3(*cmd) - if s.success? - begin - hash = o && !o.empty? ? JSON.parse(o.split("\n").last) : {} - next if hash.empty? - @gemnames.push hash['name'] - result.concat(hash['paths'].map { |path| File.join(base, path) }) - rescue StandardError => e - Solargraph.logger.warn "Error reading #{file}: [#{e.class}] #{e.message}" - end - else - Solargraph.logger.warn "Error reading #{file}" - Solargraph.logger.warn e - end - end - result.concat(config.require_paths.map { |p| File.join(directory, p) }) - result.push File.join(directory, 'lib') if result.empty? - result - end - - # Get additional require paths defined in the configuration. - # - # @return [Array] - def configured_require_paths - return ['lib'] if directory.empty? - return [File.join(directory, 'lib')] if config.require_paths.empty? - config.require_paths.map { |p| File.join(directory, p) } - end - # @return [void] def require_plugins config.plugins.each do |plugin| diff --git a/lib/solargraph/workspace/gemspecs.rb b/lib/solargraph/workspace/gemspecs.rb new file mode 100644 index 000000000..a7676cca5 --- /dev/null +++ b/lib/solargraph/workspace/gemspecs.rb @@ -0,0 +1,247 @@ +# frozen_string_literal: true + +require 'bundler' + +module Solargraph + class Workspace + # Manages determining which gemspecs are available in a workspace + class Gemspecs + include Logging + + attr_reader :directory, :preferences + + # @param directory [String] + def initialize directory + @directory = directory + # @todo implement preferences + @preferences = [] + end + + # Take the path given to a 'require' statement in a source file + # and return the Gem::Specifications which will be brought into + # scope with it, so we can load pins for them. + # + # @param require [String] The string sent to 'require' in the code to resolve, e.g. 'rails', 'bundler/require' + # @return [::Array, nil] + def resolve_require require + return nil if require.empty? + return auto_required_gemspecs_from_bundler if require == 'bundler/require' + + gemspecs = all_gemspecs_from_bundler + # @type [Gem::Specification, nil] + gemspec = gemspecs.find { |gemspec| gemspec.name == require } + if gemspec.nil? + # TODO: this seems hinky + gem_name_guess = require.split('/').first + begin + # this can happen when the gem is included via a local path in + # a Gemfile; Gem doesn't try to index the paths in that case. + # + # See if we can make a good guess: + potential_gemspec = Gem::Specification.find_by_name(gem_name_guess) + + return nil if potential_gemspec.nil? + + file = "lib/#{require}.rb" + # @sg-ignore Unresolved call to files + gemspec = potential_gemspec if potential_gemspec&.files&.any? { |gemspec_file| file == gemspec_file } + rescue Gem::MissingSpecError + logger.debug do + "Require path #{require} could not be resolved to a gem via find_by_path or guess of #{gem_name_guess}" + end + [] + end + end + return nil if gemspec.nil? + [gemspec_or_preference(gemspec)] + end + + # @param gemspec [Gem::Specification] + # @return [Array] + def fetch_dependencies gemspec + gemspecs = all_gemspecs_from_bundler + + # @param spec [Gem::Dependency] + only_runtime_dependencies(gemspec).each_with_object(Set.new) do |spec, deps| + Solargraph.logger.info "Adding #{spec.name} dependency for #{gemspec.name}" + # @type [Gem::Specification, nil] + dep = gemspecs.find { |dep| dep.name == spec.name } + # TODO: is next line necessary? + dep ||= Gem::Specification.find_by_name(spec.name, spec.requirement) + deps.merge fetch_dependencies(dep) if deps.add?(dep) + rescue Gem::MissingSpecError + Solargraph.logger.warn("Gem dependency #{spec.name} #{spec.requirement} " \ + "for #{gemspec.name} not found in RubyGems.") + end.to_a + end + + private + + # @param command [String] The expression to evaluate in the external bundle + # @sg-ignore Need a JSON type + # @yield [undefined] + def query_external_bundle command, &block + # TODO: probably combine with logic in require_paths.rb + Solargraph.with_clean_env do + cmd = [ + 'ruby', '-e', + "require 'bundler'; require 'json'; Dir.chdir('#{directory}') { puts #{command}.to_json }" + ] + # @sg-ignore Unresolved call to capture3 + o, e, s = Open3.capture3(*cmd) + if s.success? + Solargraph.logger.debug "External bundle: #{o}" + data = o && !o.empty? ? JSON.parse(o.split("\n").last) : {} + block.yield data + else + Solargraph.logger.warn e + raise BundleNotFoundError, "Failed to load gems from bundle at #{directory}" + end + end + end + + # True if the workspace has a root Gemfile. + # + # @todo Handle projects with custom Bundler/Gemfile setups (see DocMap#gemspecs_required_from_bundler) + # + def gemfile? + directory && File.file?(File.join(directory, 'Gemfile')) + end + + def in_this_bundle? + directory && Bundler.definition&.lockfile&.to_s&.start_with?(directory) # rubocop:disable Style/SafeNavigationChainLength + end + + # Returns all gemspecs directly depended on by this workspace's + # bundle (does not include transitive dependencies). + # + # @return [Array] + def all_gemspecs_from_bundler + @all_gemspecs_from_bundler ||= + if in_this_bundle? + all_gemspecs_from_this_bundle + else + all_gemspecs_from_external_bundle + end + end + + # @return [Array] + def all_gemspecs_from_this_bundle + # Find only the gems bundler is now using + Bundler.definition.locked_gems.specs.map(&:materialize_for_installation) + end + + # @return [Array] + def auto_required_gemspecs_from_bundler + logger.info 'Fetching gemspecs autorequired from Bundler (bundler/require)' + @auto_required_gemspecs_from_bundler ||= + if in_this_bundle? + auto_required_gemspecs_from_this_bundle + else + auto_required_gemspecs_from_external_bundle + end + end + + # TODO: "Astute readers will notice that the correct way to + # require the rack-cache gem is require 'rack/cache', not + # require 'rack-cache'. To tell bundler to use require + # 'rack/cache', update your Gemfile:" + # + # gem 'rack-cache', require: 'rack/cache' + + # @return [Array] + def auto_required_gemspecs_from_this_bundle + dependencies = Bundler.definition.dependencies + + all_gemspecs_from_bundler.select do |gemspec| + dependencies.key?(gemspec.name) && + dependencies[gemspec.name].autorequire != [] + end + end + + # @return [Array] + def auto_required_gemspecs_from_external_bundle + @auto_required_gemspecs_from_external_bundle ||= + begin + logger.info 'Fetching auto-required gemspecs from Bundler (bundler/require)' + command = + 'dependencies = Bundler.definition.dependencies; ' \ + 'all_specs = Bundler.definition.locked_gems.specs; ' \ + 'autorequired_specs = all_specs.' \ + 'select { |gemspec| dependencies.key?(gemspec.name) && dependencies[gemspec.name].autorequire != [] }; ' \ + 'autorequired_specs.map { |spec| [spec.name, spec.version] }' + query_external_bundle command do |dependencies| + dependencies.map do |name, requirement| + resolve_gem_ignoring_local_bundle name, requirement + end.compact + end + end + end + + # @param gemspec [Gem::Specification] + # @return [Array] + def only_runtime_dependencies gemspec + gemspec.dependencies - gemspec.development_dependencies + end + + # @todo Should this be using Gem::SpecFetcher and pull them automatically? + # + # @param name [String] + # @param version [String] + # @return [Gem::Specification, nil] + def resolve_gem_ignoring_local_bundle name, version + Gem::Specification.find_by_name(name, version) + rescue Gem::MissingSpecError + begin + Gem::Specification.find_by_name(name) + rescue Gem::MissingSpecError + logger.warn "Please install the gem #{name}:#{version} in Solargraph's Ruby environment" + nil + end + end + + # @return [Array] + def all_gemspecs_from_external_bundle + return [] unless directory + + @all_gemspecs_from_external_bundle ||= + begin + logger.info 'Fetching gemspecs required from external bundle' + + command = 'Bundler.definition.locked_gems.specs.map { |spec| [spec.name, spec.version] }.to_h' + + query_external_bundle command do |names_and_versions| + names_and_versions.map do |name, version| + resolve_gem_ignoring_local_bundle(name, version) + end.compact + end + end + end + + # @return [Hash{String => Gem::Specification}] + def preference_map + @preference_map ||= preferences.to_h { |gemspec| [gemspec.name, gemspec] } + end + + # @param gemspec [Gem::Specification] + # + # @return [Gem::Specification] + def gemspec_or_preference gemspec + return gemspec unless preference_map.key?(gemspec.name) + return gemspec if gemspec.version == preference_map[gemspec.name].version + + change_gemspec_version gemspec, preference_map[gemspec.name].version + end + + # @param gemspec [Gem::Specification] + # @param version [String] + # @return [Gem::Specification] + def change_gemspec_version gemspec, version + Gem::Specification.find_by_name(gemspec.name, "= #{version}") + rescue Gem::MissingSpecError + Solargraph.logger.info "Gem #{gemspec.name} version #{version} not found. Using #{gemspec.version} instead" + gemspec + end + end + end +end diff --git a/lib/solargraph/workspace/require_paths.rb b/lib/solargraph/workspace/require_paths.rb new file mode 100644 index 000000000..e427c4439 --- /dev/null +++ b/lib/solargraph/workspace/require_paths.rb @@ -0,0 +1,106 @@ +# frozen_string_literal: true + +module Solargraph + # A workspace consists of the files in a project's directory and the + # project's configuration. It provides a Source for each file to be used + # in an associated Library or ApiMap. + # + class Workspace + # Manages determining which gemspecs are available in a workspace + class RequirePaths + attr_reader :directory, :config + + # @param directory [String] + # @param config [Config, nil] + def initialize directory, config + @directory = directory + @config = config + end + + # Generate require paths from gemspecs if they exist or assume the default + # lib directory. + # + # @return [Array] + def generate + result = require_paths_from_gemspec_files + return configured_require_paths if result.empty? + result.concat(config.require_paths.map { |p| File.join(directory, p) }) if config + result.push File.join(directory, 'lib') if result.empty? + result + end + + private + + # @return [Array] + def require_paths_from_gemspec_files + results = [] + gemspec_file_paths.each do |gemspec_file_path| + results.concat require_path_from_gemspec_file(gemspec_file_path) + end + results + end + + # Get an array of all gemspec files in the workspace. + # + # @return [Array] + def gemspec_file_paths + # TODO: Document what '*' means and how a user should use it + return [] if directory.empty? || directory == '*' + @gemspec_file_paths ||= Dir[File.join(directory, '**/*.gemspec')].select do |gs| + config.nil? || config.allow?(gs) + end + end + + # Get additional require paths defined in the configuration. + # + # @return [Array] + def configured_require_paths + return ['lib'] if directory.empty? + return [File.join(directory, 'lib')] if !config || config.require_paths.empty? + config.require_paths.map { |p| File.join(directory, p) } + end + + # True if the workspace contains at least one gemspec file. + # + # @return [Boolean] + def gemspec? + !gemspec_file_paths.empty? + end + + # Generate require paths from gemspecs if they exist or assume the default + # lib directory. + # + # @param gemspec_file_path [String] + # @return [Array] + def require_path_from_gemspec_file gemspec_file_path + # TODO: this needs to work with external bundles too + base = File.dirname(gemspec_file_path) + # HACK: Evaluating gemspec files violates the goal of not running + # workspace code, but this is how Gem::Specification.load does it + # anyway. + cmd = ['ruby', '-e', + "require 'rubygems'; " \ + "require 'json'; " \ + "spec = eval(File.read('#{gemspec_file_path}'), TOPLEVEL_BINDING, '#{gemspec_file_path}'); " \ + 'return unless Gem::Specification === spec; ' \ + 'puts({name: spec.name, paths: spec.require_paths}.to_json)'] + # @sg-ignore Unresolved call to capture3 + o, e, s = Open3.capture3(*cmd) + if s.success? + begin + hash = o && !o.empty? ? JSON.parse(o.split("\n").last) : {} + return [] if hash.empty? + hash['paths'].map { |path| File.join(base, path) } + rescue StandardError => e + Solargraph.logger.warn "Error reading #{gemspec_file_path}: [#{e.class}] #{e.message}" + [] + end + else + Solargraph.logger.warn "Error reading #{gemspec_file_path}" + Solargraph.logger.warn e + [] + end + end + end + end +end diff --git a/spec/doc_map_spec.rb b/spec/doc_map_spec.rb index 0f056e07c..84b875f10 100644 --- a/spec/doc_map_spec.rb +++ b/spec/doc_map_spec.rb @@ -51,7 +51,7 @@ before do pincache = instance_double(Solargraph::PinCache) - allow(workspace).to receive(:resolve_path_to_gemspecs).with('uncached_gem').and_return([uncached_lazy_gemspec]) + allow(workspace).to receive(:resolve_require).with('uncached_gem').and_return([uncached_lazy_gemspec]) allow(workspace).to receive(:fetch_dependencies).with(uncached_lazy_gemspec).and_return([]) allow(workspace).to receive(:fresh_pincache).and_return(pincache) allow(pincache).to receive(:deserialize_combined_pin_cache).with(uncached_lazy_gemspec).and_return(nil) From f35b8c39a5f87ee3224d5ef4fb52fb93e2debf57 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 17 Jul 2025 16:06:08 -0400 Subject: [PATCH 224/561] Align trickier rules with current code quality for outside PRs --- .rubocop.yml | 4 +++ .rubocop_todo.yml | 80 ++--------------------------------------------- 2 files changed, 6 insertions(+), 78 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 2332648fd..b8079965e 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -46,6 +46,10 @@ Metrics/MethodLength: Max: 60 Metrics/ClassLength: Max: 500 +Metrics/CyclomaticComplexity: + Max: 23 +Metrics/PerceivedComplexity: + Max: 29 plugins: - rubocop-rspec diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 5ca1b33ce..eeeaed98a 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -846,60 +846,19 @@ Metrics/ClassLength: - 'lib/solargraph/rbs_map/conversions.rb' - 'lib/solargraph/type_checker.rb' -# Offense count: 113 +# Offense count: 11 # Configuration parameters: AllowedMethods, AllowedPatterns, Max. Metrics/CyclomaticComplexity: Exclude: - - 'lib/solargraph/api_map.rb' - - 'lib/solargraph/api_map/index.rb' - 'lib/solargraph/api_map/source_to_yard.rb' - - 'lib/solargraph/api_map/store.rb' - 'lib/solargraph/complex_type.rb' - - 'lib/solargraph/complex_type/type_methods.rb' - - 'lib/solargraph/complex_type/unique_type.rb' - - 'lib/solargraph/convention/data_definition/data_definition_node.rb' - - 'lib/solargraph/convention/struct_definition.rb' - - 'lib/solargraph/convention/struct_definition/struct_definition_node.rb' - - 'lib/solargraph/diagnostics/require_not_found.rb' - - 'lib/solargraph/doc_map.rb' - - 'lib/solargraph/gem_pins.rb' - - 'lib/solargraph/language_server/host.rb' - - 'lib/solargraph/language_server/message/completion_item/resolve.rb' - - 'lib/solargraph/language_server/message/extended/check_gem_version.rb' - - 'lib/solargraph/language_server/message/initialize.rb' - - 'lib/solargraph/language_server/message/text_document/hover.rb' - - 'lib/solargraph/language_server/message/workspace/did_change_configuration.rb' - - 'lib/solargraph/library.rb' - - 'lib/solargraph/parser/flow_sensitive_typing.rb' - 'lib/solargraph/parser/parser_gem/node_chainer.rb' - - 'lib/solargraph/parser/parser_gem/node_methods.rb' - - 'lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb' - - 'lib/solargraph/parser/parser_gem/node_processors/send_node.rb' - - 'lib/solargraph/pin/base.rb' - - 'lib/solargraph/pin/base_variable.rb' - - 'lib/solargraph/pin/block.rb' - - 'lib/solargraph/pin/callable.rb' - 'lib/solargraph/pin/method.rb' - - 'lib/solargraph/pin/parameter.rb' - - 'lib/solargraph/pin/signature.rb' - - 'lib/solargraph/rbs_map.rb' - 'lib/solargraph/rbs_map/conversions.rb' - - 'lib/solargraph/shell.rb' - - 'lib/solargraph/source.rb' - - 'lib/solargraph/source/chain.rb' - - 'lib/solargraph/source/chain/array.rb' - 'lib/solargraph/source/chain/call.rb' - - 'lib/solargraph/source/chain/constant.rb' - - 'lib/solargraph/source/change.rb' - 'lib/solargraph/source/source_chainer.rb' - - 'lib/solargraph/source_map.rb' - - 'lib/solargraph/source_map/clip.rb' - 'lib/solargraph/source_map/mapper.rb' - 'lib/solargraph/type_checker.rb' - - 'lib/solargraph/type_checker/checks.rb' - - 'lib/solargraph/workspace.rb' - - 'lib/solargraph/yard_map/mapper.rb' - - 'lib/solargraph/yard_map/mapper/to_method.rb' # Offense count: 7 # Configuration parameters: CountComments, Max, CountAsOne, AllowedMethods, AllowedPatterns. @@ -931,52 +890,17 @@ Metrics/ParameterLists: - 'lib/solargraph/yard_map/mapper/to_method.rb' - 'lib/solargraph/yard_map/to_method.rb' -# Offense count: 89 +# Offense count: 9 # Configuration parameters: AllowedMethods, AllowedPatterns, Max. Metrics/PerceivedComplexity: Exclude: - - 'lib/solargraph/api_map.rb' - - 'lib/solargraph/api_map/index.rb' - 'lib/solargraph/api_map/source_to_yard.rb' - 'lib/solargraph/complex_type.rb' - - 'lib/solargraph/complex_type/type_methods.rb' - - 'lib/solargraph/complex_type/unique_type.rb' - - 'lib/solargraph/convention/struct_definition.rb' - - 'lib/solargraph/doc_map.rb' - - 'lib/solargraph/language_server/host.rb' - - 'lib/solargraph/language_server/message/extended/check_gem_version.rb' - - 'lib/solargraph/language_server/message/initialize.rb' - - 'lib/solargraph/language_server/message/text_document/hover.rb' - - 'lib/solargraph/language_server/message/workspace/did_change_configuration.rb' - - 'lib/solargraph/library.rb' - - 'lib/solargraph/parser/flow_sensitive_typing.rb' - 'lib/solargraph/parser/parser_gem/node_chainer.rb' - - 'lib/solargraph/parser/parser_gem/node_methods.rb' - - 'lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb' - - 'lib/solargraph/parser/parser_gem/node_processors/send_node.rb' - - 'lib/solargraph/pin/base.rb' - - 'lib/solargraph/pin/base_variable.rb' - - 'lib/solargraph/pin/block.rb' - - 'lib/solargraph/pin/method.rb' - - 'lib/solargraph/pin/parameter.rb' - - 'lib/solargraph/rbs_map.rb' - - 'lib/solargraph/rbs_map/conversions.rb' - - 'lib/solargraph/shell.rb' - - 'lib/solargraph/source.rb' - - 'lib/solargraph/source/chain.rb' - - 'lib/solargraph/source/chain/array.rb' - 'lib/solargraph/source/chain/call.rb' - - 'lib/solargraph/source/chain/constant.rb' - - 'lib/solargraph/source/change.rb' - 'lib/solargraph/source/source_chainer.rb' - - 'lib/solargraph/source_map.rb' - - 'lib/solargraph/source_map/clip.rb' - 'lib/solargraph/source_map/mapper.rb' - 'lib/solargraph/type_checker.rb' - - 'lib/solargraph/type_checker/checks.rb' - - 'lib/solargraph/workspace.rb' - - 'lib/solargraph/yard_map/mapper.rb' - - 'lib/solargraph/yard_map/mapper/to_method.rb' # Offense count: 5 Naming/AccessorMethodName: From 584274f885bb1e17fef6c6dc41fd431e434ddbc4 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 17 Jul 2025 16:44:14 -0400 Subject: [PATCH 225/561] Fixes from testing --- lib/solargraph/pin_cache.rb | 6 ++++- lib/solargraph/workspace.rb | 3 ++- lib/solargraph/workspace/gemspecs.rb | 36 +++++++++++++--------------- spec/workspace_spec.rb | 4 ++-- 4 files changed, 26 insertions(+), 23 deletions(-) diff --git a/lib/solargraph/pin_cache.rb b/lib/solargraph/pin_cache.rb index 0e5760e76..02ccfcd24 100644 --- a/lib/solargraph/pin_cache.rb +++ b/lib/solargraph/pin_cache.rb @@ -197,7 +197,11 @@ def log_cache_info(gemspec, type << rbs_source_desc if build_rbs_collection && !rbs_source_desc.nil? # we'll build it anyway, but it won't take long to build with # only a single source - type << 'combined' if build_combined && !rbs_source_desc.nil? + + # 'combining' is awkward terminology in this case + just_yard = build_yard && rbs_source_desc.nil? + + type << 'combined' if build_combined && !just_yard out.puts("Caching #{type.join(' and ')} pins for gem #{gemspec.name}:#{gemspec.version}") if out end diff --git a/lib/solargraph/workspace.rb b/lib/solargraph/workspace.rb index 9a6ec8c4d..cc8cc816d 100644 --- a/lib/solargraph/workspace.rb +++ b/lib/solargraph/workspace.rb @@ -53,6 +53,7 @@ def pin_cache @pin_cache ||= fresh_pincache end + # @param require [String] The string sent to 'require' in the code to resolve, e.g. 'rails', 'bundler/require' # @return [Array] def resolve_require require gemspecs.resolve_require(require) @@ -190,7 +191,7 @@ def cache_all_for_workspace! out, rebuild: false # TODO: This should bring in dependencies as well # # @type [Array] - specs = immediate_gemspecs_from_bundler + specs = gemspecs.all_gemspecs_from_bundle specs.each do |spec| unless pin_cache.cached?(spec) pin_cache.cache_gem(gemspec: spec, rebuild: rebuild, out: out) diff --git a/lib/solargraph/workspace/gemspecs.rb b/lib/solargraph/workspace/gemspecs.rb index a7676cca5..b5a02da08 100644 --- a/lib/solargraph/workspace/gemspecs.rb +++ b/lib/solargraph/workspace/gemspecs.rb @@ -27,7 +27,7 @@ def resolve_require require return nil if require.empty? return auto_required_gemspecs_from_bundler if require == 'bundler/require' - gemspecs = all_gemspecs_from_bundler + gemspecs = all_gemspecs_from_bundle # @type [Gem::Specification, nil] gemspec = gemspecs.find { |gemspec| gemspec.name == require } if gemspec.nil? @@ -56,10 +56,23 @@ def resolve_require require [gemspec_or_preference(gemspec)] end + # Returns all gemspecs directly depended on by this workspace's + # bundle (does not include transitive dependencies). + # + # @return [Array] + def all_gemspecs_from_bundle + @all_gemspecs_from_bundle ||= + if in_this_bundle? + all_gemspecs_from_this_bundle + else + all_gemspecs_from_external_bundle + end + end + # @param gemspec [Gem::Specification] # @return [Array] def fetch_dependencies gemspec - gemspecs = all_gemspecs_from_bundler + gemspecs = all_gemspecs_from_bundle # @param spec [Gem::Dependency] only_runtime_dependencies(gemspec).each_with_object(Set.new) do |spec, deps| @@ -75,8 +88,6 @@ def fetch_dependencies gemspec end.to_a end - private - # @param command [String] The expression to evaluate in the external bundle # @sg-ignore Need a JSON type # @yield [undefined] @@ -112,19 +123,6 @@ def in_this_bundle? directory && Bundler.definition&.lockfile&.to_s&.start_with?(directory) # rubocop:disable Style/SafeNavigationChainLength end - # Returns all gemspecs directly depended on by this workspace's - # bundle (does not include transitive dependencies). - # - # @return [Array] - def all_gemspecs_from_bundler - @all_gemspecs_from_bundler ||= - if in_this_bundle? - all_gemspecs_from_this_bundle - else - all_gemspecs_from_external_bundle - end - end - # @return [Array] def all_gemspecs_from_this_bundle # Find only the gems bundler is now using @@ -151,9 +149,9 @@ def auto_required_gemspecs_from_bundler # @return [Array] def auto_required_gemspecs_from_this_bundle - dependencies = Bundler.definition.dependencies + dependencies = Bundler.definition.locked_gems.dependencies - all_gemspecs_from_bundler.select do |gemspec| + all_gemspecs_from_bundle.select do |gemspec| dependencies.key?(gemspec.name) && dependencies[gemspec.name].autorequire != [] end diff --git a/spec/workspace_spec.rb b/spec/workspace_spec.rb index 572c3e131..3d4e763a4 100644 --- a/spec/workspace_spec.rb +++ b/spec/workspace_spec.rb @@ -68,7 +68,7 @@ }.not_to raise_error end - it "detects gemspecs in workspaces" do + xit "detects gemspecs in workspaces" do gemspec_file = File.join(dir_path, 'test.gemspec') File.write(gemspec_file, '') expect(workspace.gemspec?).to be(true) @@ -127,7 +127,7 @@ expect(workspace.require_paths).to eq(['spec/fixtures/workspace/lib', 'spec/fixtures/workspace/ext']) end - it 'ignores gemspecs in excluded directories' do + xit 'ignores gemspecs in excluded directories' do # vendor/**/* is excluded by default workspace = Solargraph::Workspace.new('spec/fixtures/vendored') expect(workspace.gemspecs).to be_empty From c1f7cd269914aef39d073db7ccb1a6e6d9db501c Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 17 Jul 2025 16:49:08 -0400 Subject: [PATCH 226/561] Ensure test stability --- spec/doc_map_spec.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/doc_map_spec.rb b/spec/doc_map_spec.rb index 84b875f10..49cc60a42 100644 --- a/spec/doc_map_spec.rb +++ b/spec/doc_map_spec.rb @@ -65,6 +65,7 @@ context 'with require as bundle/require' do it 'imports all gems when bundler/require used' do doc_map_with_bundler_require = Solargraph::DocMap.new(['bundler/require'], workspace) + doc_map_with_bundler_require.cache_doc_map_gems!($stderr) expect(doc_map_with_bundler_require.pins.length - plain_doc_map.pins.length).to be_positive end end From 716b8e837127ad847dcc04cc172e84c6cf0bf913 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 17 Jul 2025 17:07:06 -0400 Subject: [PATCH 227/561] Debug CI failure --- lib/solargraph/workspace.rb | 1 + lib/solargraph/workspace/gemspecs.rb | 31 +++++++++++++++------------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/lib/solargraph/workspace.rb b/lib/solargraph/workspace.rb index cc8cc816d..382dd068e 100644 --- a/lib/solargraph/workspace.rb +++ b/lib/solargraph/workspace.rb @@ -45,6 +45,7 @@ def config # @param gemspec [Gem::Specification] # @return [Array] def fetch_dependencies gemspec + raise ArgumentError, 'gemspec must be a Gem::Specification' unless gemspec.is_a?(Gem::Specification) gemspecs.fetch_dependencies(gemspec) end diff --git a/lib/solargraph/workspace/gemspecs.rb b/lib/solargraph/workspace/gemspecs.rb index b5a02da08..0ff0f510e 100644 --- a/lib/solargraph/workspace/gemspecs.rb +++ b/lib/solargraph/workspace/gemspecs.rb @@ -43,7 +43,6 @@ def resolve_require require return nil if potential_gemspec.nil? file = "lib/#{require}.rb" - # @sg-ignore Unresolved call to files gemspec = potential_gemspec if potential_gemspec&.files&.any? { |gemspec_file| file == gemspec_file } rescue Gem::MissingSpecError logger.debug do @@ -72,20 +71,22 @@ def all_gemspecs_from_bundle # @param gemspec [Gem::Specification] # @return [Array] def fetch_dependencies gemspec + raise ArgumentError, 'gemspec must be a Gem::Specification' unless gemspec.is_a?(Gem::Specification) + gemspecs = all_gemspecs_from_bundle - # @param spec [Gem::Dependency] - only_runtime_dependencies(gemspec).each_with_object(Set.new) do |spec, deps| - Solargraph.logger.info "Adding #{spec.name} dependency for #{gemspec.name}" - # @type [Gem::Specification, nil] - dep = gemspecs.find { |dep| dep.name == spec.name } - # TODO: is next line necessary? - dep ||= Gem::Specification.find_by_name(spec.name, spec.requirement) + # @param runtime_dep [Gem::Dependency] + # @param deps [Set] + only_runtime_dependencies(gemspec).each_with_object(Set.new) do |runtime_dep, deps| + Solargraph.logger.info "Adding #{runtime_dep.name} dependency for #{gemspec.name}" + dep = gemspecs.find { |dep| dep.name == runtime_dep.name } + dep ||= Gem::Specification.find_by_name(runtime_dep.name, runtime_dep.requirement) deps.merge fetch_dependencies(dep) if deps.add?(dep) rescue Gem::MissingSpecError - Solargraph.logger.warn("Gem dependency #{spec.name} #{spec.requirement} " \ - "for #{gemspec.name} not found in RubyGems.") - end.to_a + Solargraph.logger.warn("Gem dependency #{runtime_dep.name} #{runtime_dep.requirement} " \ + "for #{gemspec.name} not found in bundle.") + nil + end.to_a.compact end # @param command [String] The expression to evaluate in the external bundle @@ -149,11 +150,11 @@ def auto_required_gemspecs_from_bundler # @return [Array] def auto_required_gemspecs_from_this_bundle - dependencies = Bundler.definition.locked_gems.dependencies + deps = Bundler.definition.locked_gems.dependencies all_gemspecs_from_bundle.select do |gemspec| - dependencies.key?(gemspec.name) && - dependencies[gemspec.name].autorequire != [] + deps.key?(gemspec.name) && + deps[gemspec.name].autorequire != [] end end @@ -179,6 +180,8 @@ def auto_required_gemspecs_from_external_bundle # @param gemspec [Gem::Specification] # @return [Array] def only_runtime_dependencies gemspec + raise ArgumentError, 'gemspec must be a Gem::Specification' unless gemspec.is_a?(Gem::Specification) + gemspec.dependencies - gemspec.development_dependencies end From b16ab6156a9170ee291be21a1ec7b74454d14a18 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 17 Jul 2025 17:53:44 -0400 Subject: [PATCH 228/561] Handle Bundler::StubSpecification in materialize_for_installation --- lib/solargraph/workspace/gemspecs.rb | 8 +++++++- spec/doc_map_spec.rb | 12 ++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/lib/solargraph/workspace/gemspecs.rb b/lib/solargraph/workspace/gemspecs.rb index 0ff0f510e..9cc789fb8 100644 --- a/lib/solargraph/workspace/gemspecs.rb +++ b/lib/solargraph/workspace/gemspecs.rb @@ -127,7 +127,13 @@ def in_this_bundle? # @return [Array] def all_gemspecs_from_this_bundle # Find only the gems bundler is now using - Bundler.definition.locked_gems.specs.map(&:materialize_for_installation) + Bundler.definition.locked_gems.specs.map(&:materialize_for_installation).map do |spec| + if spec.is_a?(Gem::Specification) + spec + elsif spec.is_a?(Bundler::StubSpecification) + spec.stub.spec + end + end.flatten end # @return [Array] diff --git a/spec/doc_map_spec.rb b/spec/doc_map_spec.rb index 49cc60a42..c9481d354 100644 --- a/spec/doc_map_spec.rb +++ b/spec/doc_map_spec.rb @@ -42,8 +42,8 @@ end context 'with an uncached but valid gemspec' do - let(:uncached_lazy_gemspec) do - Bundler::LazySpecification.new('uncached_gem', '1.0.0', 'ruby') + let(:uncached_gemspec) do + Gem::Specification.new('uncached_gem', '1.0.0') end let(:requires) { ['uncached_gem'] } let(:pre_cache) { false } @@ -51,14 +51,14 @@ before do pincache = instance_double(Solargraph::PinCache) - allow(workspace).to receive(:resolve_require).with('uncached_gem').and_return([uncached_lazy_gemspec]) - allow(workspace).to receive(:fetch_dependencies).with(uncached_lazy_gemspec).and_return([]) + allow(workspace).to receive(:resolve_require).with('uncached_gem').and_return([uncached_gemspec]) + allow(workspace).to receive(:fetch_dependencies).with(uncached_gemspec).and_return([]) allow(workspace).to receive(:fresh_pincache).and_return(pincache) - allow(pincache).to receive(:deserialize_combined_pin_cache).with(uncached_lazy_gemspec).and_return(nil) + allow(pincache).to receive(:deserialize_combined_pin_cache).with(uncached_gemspec).and_return(nil) end it 'tracks uncached_gemspecs' do - expect(doc_map.uncached_gemspecs).to eq([uncached_lazy_gemspec]) + expect(doc_map.uncached_gemspecs).to eq([uncached_gemspec]) end end From f43988fd1199ddf19c68536f1929e62ffa267687 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 17 Jul 2025 18:07:14 -0400 Subject: [PATCH 229/561] Debug --- lib/solargraph/workspace/gemspecs.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/solargraph/workspace/gemspecs.rb b/lib/solargraph/workspace/gemspecs.rb index 9cc789fb8..09f284449 100644 --- a/lib/solargraph/workspace/gemspecs.rb +++ b/lib/solargraph/workspace/gemspecs.rb @@ -132,8 +132,10 @@ def all_gemspecs_from_this_bundle spec elsif spec.is_a?(Bundler::StubSpecification) spec.stub.spec + else + raise "Unexpected type: #{spec.class}" end - end.flatten + end end # @return [Array] From 04d9698088b1da8ba695b7a8aabefcd0680a8ec2 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 17 Jul 2025 18:14:34 -0400 Subject: [PATCH 230/561] Handle Bundler::LazySpecification case --- lib/solargraph/workspace/gemspecs.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/solargraph/workspace/gemspecs.rb b/lib/solargraph/workspace/gemspecs.rb index 09f284449..156d0ed41 100644 --- a/lib/solargraph/workspace/gemspecs.rb +++ b/lib/solargraph/workspace/gemspecs.rb @@ -130,6 +130,10 @@ def all_gemspecs_from_this_bundle Bundler.definition.locked_gems.specs.map(&:materialize_for_installation).map do |spec| if spec.is_a?(Gem::Specification) spec + elsif spec.is_a?(Bundler::LazySpecification) + # materializing didn't work. Let's do one last attept at + # asking the local installed gems + resolve_gem_ignoring_local_bundle spec.name, spec.version elsif spec.is_a?(Bundler::StubSpecification) spec.stub.spec else From 7f63868b17fcf6a27e319bb70408c8906799af31 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 17 Jul 2025 18:28:46 -0400 Subject: [PATCH 231/561] Handle Bundler::LazySpecification case --- lib/solargraph/workspace/gemspecs.rb | 31 +++++++++++++++++++--------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/lib/solargraph/workspace/gemspecs.rb b/lib/solargraph/workspace/gemspecs.rb index 156d0ed41..bc0f80a5f 100644 --- a/lib/solargraph/workspace/gemspecs.rb +++ b/lib/solargraph/workspace/gemspecs.rb @@ -127,17 +127,28 @@ def in_this_bundle? # @return [Array] def all_gemspecs_from_this_bundle # Find only the gems bundler is now using - Bundler.definition.locked_gems.specs.map(&:materialize_for_installation).map do |spec| - if spec.is_a?(Gem::Specification) - spec - elsif spec.is_a?(Bundler::LazySpecification) - # materializing didn't work. Let's do one last attept at - # asking the local installed gems - resolve_gem_ignoring_local_bundle spec.name, spec.version - elsif spec.is_a?(Bundler::StubSpecification) - spec.stub.spec + specish_objects = Bundler.definition.locked_gems.specs + if specish_objects.first.respond_to?(:materialize_for_installation) + specish_objects = specish_objects.map(&:materialize_for_installation) + end + specish_objects.map do |specish| + if specish.is_a?(Gem::Specification) + # yay! + specish + elsif specish.is_a?(Bundler::LazySpecification) + # materializing didn't work. Let's look in the local + # rubygems without bundler's help + resolve_gem_ignoring_local_bundle specish.name, specish.version + elsif specish.is_a?(Bundler::StubSpecification) + # turns a Bundler::StubSpecification into a + # Gem::StubSpecification into a Gem::Specification + specish.stub.spec else - raise "Unexpected type: #{spec.class}" + @@warned_on_gem_type ||= false + unless @@warned_on_gem_type + logger.warn "Unexpected type while resolving gem: #{specish.class}" + @@warned_on_gem_type = true + end end end end From 4a18999bc0df13da39a63b349f781728ba99dd1c Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 17 Jul 2025 18:29:40 -0400 Subject: [PATCH 232/561] Fix spacing --- lib/solargraph/workspace/gemspecs.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/solargraph/workspace/gemspecs.rb b/lib/solargraph/workspace/gemspecs.rb index bc0f80a5f..495e6bd6e 100644 --- a/lib/solargraph/workspace/gemspecs.rb +++ b/lib/solargraph/workspace/gemspecs.rb @@ -145,7 +145,7 @@ def all_gemspecs_from_this_bundle specish.stub.spec else @@warned_on_gem_type ||= false - unless @@warned_on_gem_type + unless @@warned_on_gem_type logger.warn "Unexpected type while resolving gem: #{specish.class}" @@warned_on_gem_type = true end From cbebd98e3fde230cfa963f5612294ace50fc8928 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 17 Jul 2025 18:44:52 -0400 Subject: [PATCH 233/561] Handle another version of bundler --- lib/solargraph/workspace/gemspecs.rb | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/lib/solargraph/workspace/gemspecs.rb b/lib/solargraph/workspace/gemspecs.rb index 495e6bd6e..935d0442c 100644 --- a/lib/solargraph/workspace/gemspecs.rb +++ b/lib/solargraph/workspace/gemspecs.rb @@ -132,17 +132,23 @@ def all_gemspecs_from_this_bundle specish_objects = specish_objects.map(&:materialize_for_installation) end specish_objects.map do |specish| - if specish.is_a?(Gem::Specification) + case specish + when Gem::Specification # yay! specish - elsif specish.is_a?(Bundler::LazySpecification) + when Bundler::LazySpecification # materializing didn't work. Let's look in the local # rubygems without bundler's help resolve_gem_ignoring_local_bundle specish.name, specish.version - elsif specish.is_a?(Bundler::StubSpecification) + when Bundler::StubSpecification # turns a Bundler::StubSpecification into a # Gem::StubSpecification into a Gem::Specification - specish.stub.spec + specish = specish.stub + if specish.respond_to?(&:spec) + specish.spec + else + resolve_gem_ignoring_local_bundle specish.name, specish.version + end else @@warned_on_gem_type ||= false unless @@warned_on_gem_type From 32ad0141dd921dd0f2872b90f74825890aec5a19 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 17 Jul 2025 18:47:21 -0400 Subject: [PATCH 234/561] Handle another version of bundler --- lib/solargraph/workspace/gemspecs.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/solargraph/workspace/gemspecs.rb b/lib/solargraph/workspace/gemspecs.rb index 935d0442c..ec8f8b7ae 100644 --- a/lib/solargraph/workspace/gemspecs.rb +++ b/lib/solargraph/workspace/gemspecs.rb @@ -144,7 +144,7 @@ def all_gemspecs_from_this_bundle # turns a Bundler::StubSpecification into a # Gem::StubSpecification into a Gem::Specification specish = specish.stub - if specish.respond_to?(&:spec) + if specish.respond_to?(:spec) specish.spec else resolve_gem_ignoring_local_bundle specish.name, specish.version From a3b92b18b591f3597bfb6eb0ea7ade7997ef3743 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 18 Jul 2025 08:12:30 -0400 Subject: [PATCH 235/561] OK a RuboCop warning --- lib/solargraph/workspace/gemspecs.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/solargraph/workspace/gemspecs.rb b/lib/solargraph/workspace/gemspecs.rb index ec8f8b7ae..d33e6ba5d 100644 --- a/lib/solargraph/workspace/gemspecs.rb +++ b/lib/solargraph/workspace/gemspecs.rb @@ -150,10 +150,10 @@ def all_gemspecs_from_this_bundle resolve_gem_ignoring_local_bundle specish.name, specish.version end else - @@warned_on_gem_type ||= false + @@warned_on_gem_type ||= false # rubocop:disable Style/ClassVars unless @@warned_on_gem_type logger.warn "Unexpected type while resolving gem: #{specish.class}" - @@warned_on_gem_type = true + @@warned_on_gem_type = true # rubocop:disable Style/ClassVars end end end From 489899c978d20c2b05c218f5a51a88e04dc3e1a0 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 18 Jul 2025 08:18:14 -0400 Subject: [PATCH 236/561] Add TODOs --- lib/solargraph/api_map.rb | 1 + lib/solargraph/doc_map.rb | 1 + lib/solargraph/library.rb | 1 + lib/solargraph/pin_cache.rb | 1 + 4 files changed, 4 insertions(+) diff --git a/lib/solargraph/api_map.rb b/lib/solargraph/api_map.rb index 9894cd19b..b7823db1a 100755 --- a/lib/solargraph/api_map.rb +++ b/lib/solargraph/api_map.rb @@ -22,6 +22,7 @@ class ApiMap # @return [Array] attr_reader :missing_docs + # TODO: Is this needed? # @return [Solargraph::PinCache] attr_reader :pin_cache diff --git a/lib/solargraph/doc_map.rb b/lib/solargraph/doc_map.rb index 084b75e54..c688c9ba8 100644 --- a/lib/solargraph/doc_map.rb +++ b/lib/solargraph/doc_map.rb @@ -39,6 +39,7 @@ def initialize(requires, workspace) pins.concat global_environ.pins end + # TODO: Does this need to be public? # @return [Solargraph::PinCache] def pin_cache @pin_cache ||= workspace.fresh_pincache diff --git a/lib/solargraph/library.rb b/lib/solargraph/library.rb index b194725d7..889f6d529 100644 --- a/lib/solargraph/library.rb +++ b/lib/solargraph/library.rb @@ -602,6 +602,7 @@ def cache_next_gemspec pending = api_map.uncached_gemspecs.length - cache_errors.length - 1 + # TODO: Make this 'cache_processing?' to api_map and make api_map.pin_cache private if pin_cache.yardoc_processing?(spec) # @sg-ignore Unresolved call to name logger.info "Enqueuing cache of #{spec.name} #{spec.version} (already being processed)" diff --git a/lib/solargraph/pin_cache.rb b/lib/solargraph/pin_cache.rb index 02ccfcd24..322717619 100644 --- a/lib/solargraph/pin_cache.rb +++ b/lib/solargraph/pin_cache.rb @@ -21,6 +21,7 @@ def initialize(rbs_collection_path:, rbs_collection_config_path:, @yard_plugins = yard_plugins end + # TODO: Get Solargraph to tell me that the one below is unneeded # @sg-ignore # @param gemspec [Gem::Specification, Bundler::LazySpecification] def cached?(gemspec) From acd4dbe1a955dbadd92120c5c57ddbcf4858a111 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 18 Jul 2025 09:19:09 -0400 Subject: [PATCH 237/561] Fix merge issues --- lib/solargraph/doc_map.rb | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib/solargraph/doc_map.rb b/lib/solargraph/doc_map.rb index d656b5b1f..6f299887f 100644 --- a/lib/solargraph/doc_map.rb +++ b/lib/solargraph/doc_map.rb @@ -89,11 +89,6 @@ def gemspecs @gemspecs ||= required_gems_map.values.compact.flatten end - # @return [Array] - def yard_plugins - @environ.yard_plugins - end - # @return [Set] def dependencies @dependencies ||= (gemspecs.flat_map { |spec| workspace.fetch_dependencies(spec) } - gemspecs).to_set From 6c3b8fd987aad825aa8302bf82753df5ad0b5ffa Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 18 Jul 2025 10:06:07 -0400 Subject: [PATCH 238/561] Complain in strong type-checking if an @sg-ignore line is not needed --- lib/solargraph/api_map.rb | 3 - lib/solargraph/complex_type.rb | 1 - lib/solargraph/complex_type/unique_type.rb | 4 -- lib/solargraph/parser/comment_ripper.rb | 2 +- .../parser/flow_sensitive_typing.rb | 2 - .../parser/parser_gem/class_methods.rb | 2 +- lib/solargraph/pin/base.rb | 2 - lib/solargraph/pin/base_variable.rb | 1 - lib/solargraph/pin/method.rb | 1 - lib/solargraph/source.rb | 3 +- lib/solargraph/source/cursor.rb | 1 - lib/solargraph/type_checker.rb | 61 +++++++++++++++---- lib/solargraph/type_checker/rules.rb | 8 +++ lib/solargraph/workspace/config.rb | 2 - spec/type_checker/levels/strong_spec.rb | 10 +++ 15 files changed, 71 insertions(+), 32 deletions(-) diff --git a/lib/solargraph/api_map.rb b/lib/solargraph/api_map.rb index 6a0edc5a4..e00c0d182 100755 --- a/lib/solargraph/api_map.rb +++ b/lib/solargraph/api_map.rb @@ -202,9 +202,6 @@ class << self # any missing gems. # # - # @todo IO::NULL is incorrectly inferred to be a String. - # @sg-ignore - # # @param directory [String] # @param out [IO] The output stream for messages # @return [ApiMap] diff --git a/lib/solargraph/complex_type.rb b/lib/solargraph/complex_type.rb index 9e23eb502..ac9599329 100644 --- a/lib/solargraph/complex_type.rb +++ b/lib/solargraph/complex_type.rb @@ -299,7 +299,6 @@ class << self # # @todo Need ability to use a literal true as a type below # # @param partial [Boolean] True if the string is part of a another type # # @return [Array] - # @sg-ignore # @todo To be able to select the right signature above, # Chain::Call needs to know the decl type (:arg, :optarg, # :kwarg, etc) of the arguments given, instead of just having diff --git a/lib/solargraph/complex_type/unique_type.rb b/lib/solargraph/complex_type/unique_type.rb index 0f4ec430d..63a6ae15b 100644 --- a/lib/solargraph/complex_type/unique_type.rb +++ b/lib/solargraph/complex_type/unique_type.rb @@ -49,11 +49,7 @@ def self.parse name, substring = '', make_rooted: nil parameters_type = PARAMETERS_TYPE_BY_STARTING_TAG.fetch(substring[0]) if parameters_type == :hash raise ComplexTypeError, "Bad hash type: name=#{name}, substring=#{substring}" unless !subs.is_a?(ComplexType) and subs.length == 2 and !subs[0].is_a?(UniqueType) and !subs[1].is_a?(UniqueType) - # @todo should be able to resolve map; both types have it - # with same return type - # @sg-ignore key_types.concat(subs[0].map { |u| ComplexType.new([u]) }) - # @sg-ignore subtypes.concat(subs[1].map { |u| ComplexType.new([u]) }) elsif parameters_type == :list && name == 'Hash' # Treat Hash as Hash{A => B} diff --git a/lib/solargraph/parser/comment_ripper.rb b/lib/solargraph/parser/comment_ripper.rb index 62a4dacc5..e74fcb259 100644 --- a/lib/solargraph/parser/comment_ripper.rb +++ b/lib/solargraph/parser/comment_ripper.rb @@ -51,7 +51,7 @@ def on_embdoc_end *args result end - # @return [Hash{Integer => String}] + # @return [Hash{Integer => Solargraph::Parser::Snippet}] def parse @comments = {} super diff --git a/lib/solargraph/parser/flow_sensitive_typing.rb b/lib/solargraph/parser/flow_sensitive_typing.rb index 8fb26d498..54de5f5cb 100644 --- a/lib/solargraph/parser/flow_sensitive_typing.rb +++ b/lib/solargraph/parser/flow_sensitive_typing.rb @@ -212,8 +212,6 @@ def type_name(node) "#{module_type_name}::#{class_node}" end - # @todo "return type could not be inferred" should not trigger here - # @sg-ignore # @param clause_node [Parser::AST::Node] def always_breaks?(clause_node) clause_node&.type == :break diff --git a/lib/solargraph/parser/parser_gem/class_methods.rb b/lib/solargraph/parser/parser_gem/class_methods.rb index 58ca8056b..ddc742bd8 100644 --- a/lib/solargraph/parser/parser_gem/class_methods.rb +++ b/lib/solargraph/parser/parser_gem/class_methods.rb @@ -17,7 +17,7 @@ module ParserGem module ClassMethods # @param code [String] # @param filename [String, nil] - # @return [Array(Parser::AST::Node, Hash{Integer => String})] + # @return [Array(Parser::AST::Node, Hash{Integer => Solargraph::Parser::Snippet})] def parse_with_comments code, filename = nil node = parse(code, filename) comments = CommentRipper.new(code, filename, 0).parse diff --git a/lib/solargraph/pin/base.rb b/lib/solargraph/pin/base.rb index cdd6a5ace..03782270c 100644 --- a/lib/solargraph/pin/base.rb +++ b/lib/solargraph/pin/base.rb @@ -98,7 +98,6 @@ def choose_longer(other, attr) val2 = other.send(attr) return val1 if val1 == val2 return val2 if val1.nil? - # @sg-ignore val1.length > val2.length ? val1 : val2 end @@ -237,7 +236,6 @@ def assert_same_array_content(other, attr, &block) raise "Expected #{attr} on #{other} to be an Enumerable, got #{arr2.class}" unless arr2.is_a?(::Enumerable) # @type arr2 [::Enumerable] - # @sg-ignore # @type [undefined] values1 = arr1.map(&block) # @type [undefined] diff --git a/lib/solargraph/pin/base_variable.rb b/lib/solargraph/pin/base_variable.rb index 15cdd918f..3e2dfc254 100644 --- a/lib/solargraph/pin/base_variable.rb +++ b/lib/solargraph/pin/base_variable.rb @@ -43,7 +43,6 @@ def return_type @return_type ||= generate_complex_type end - # @sg-ignore def nil_assignment? # this will always be false - should it be return_type == # ComplexType::NIL or somesuch? diff --git a/lib/solargraph/pin/method.rb b/lib/solargraph/pin/method.rb index 2f807f444..a5ba1e491 100644 --- a/lib/solargraph/pin/method.rb +++ b/lib/solargraph/pin/method.rb @@ -301,7 +301,6 @@ def typify api_map super end - # @sg-ignore def documentation if @documentation.nil? method_docs ||= super || '' diff --git a/lib/solargraph/source.rb b/lib/solargraph/source.rb index 11ab215ed..d2b24cc61 100644 --- a/lib/solargraph/source.rb +++ b/lib/solargraph/source.rb @@ -30,7 +30,7 @@ def node @node end - # @return [Hash{Integer => Array}] + # @return [Hash{Integer => Solargraph::Parser::Snippet}] def comments finalize @comments @@ -235,6 +235,7 @@ def synchronized? # @return [Hash{Integer => String}] def associated_comments @associated_comments ||= begin + # @type [Hash{Integer => String}] result = {} buffer = String.new('') # @type [Integer, nil] diff --git a/lib/solargraph/source/cursor.rb b/lib/solargraph/source/cursor.rb index 0b95bb9bd..70e4fd47a 100644 --- a/lib/solargraph/source/cursor.rb +++ b/lib/solargraph/source/cursor.rb @@ -35,7 +35,6 @@ def word # The part of the word before the current position. Given the text # `foo.bar`, the start_of_word at position(0, 6) is `ba`. # - # @sg-ignore Improve resolution of String#match below # @return [String] def start_of_word @start_of_word ||= begin diff --git a/lib/solargraph/type_checker.rb b/lib/solargraph/type_checker.rb index aa215f97b..e4bb680ef 100644 --- a/lib/solargraph/type_checker.rb +++ b/lib/solargraph/type_checker.rb @@ -38,15 +38,20 @@ def source_map @source_map ||= api_map.source_map(filename) end + # @return [Source] + def source + @source_map.source + end + # @return [Array] def problems @problems ||= begin - without_ignored( - method_tag_problems - .concat variable_type_tag_problems - .concat const_problems - .concat call_problems - ) + all = method_tag_problems + .concat(variable_type_tag_problems) + .concat(const_problems) + .concat(call_problems) + unignored = without_ignored(all) + unignored.concat(unneeded_sgignore_problems) end end @@ -140,7 +145,7 @@ def resolved_constant? pin # @param pin [Pin::Base] def virtual_pin? pin - pin.location && source_map.source.comment_at?(pin.location.range.ending) + pin.location && source.comment_at?(pin.location.range.ending) end # @param pin [Pin::Method] @@ -231,7 +236,7 @@ def all_variables def const_problems return [] unless rules.validate_consts? result = [] - Solargraph::Parser::NodeMethods.const_nodes_from(source_map.source.node).each do |const| + Solargraph::Parser::NodeMethods.const_nodes_from(source.node).each do |const| rng = Solargraph::Range.from_node(const) chain = Solargraph::Parser.chain(const, filename) block_pin = source_map.locate_block_pin(rng.start.line, rng.start.column) @@ -249,7 +254,7 @@ def const_problems # @return [Array] def call_problems result = [] - Solargraph::Parser::NodeMethods.call_nodes_from(source_map.source.node).each do |call| + Solargraph::Parser::NodeMethods.call_nodes_from(source.node).each do |call| rng = Solargraph::Range.from_node(call) next if @marked_ranges.any? { |d| d.contain?(rng.start) } chain = Solargraph::Parser.chain(call, filename) @@ -646,7 +651,6 @@ def parameterized_arity_problems_for(pin, parameters, arguments, location) # @param parameters [Enumerable] # @todo need to use generic types in method to choose correct # signature and generate Integer as return type - # @sg-ignore # @return [Integer] def required_param_count(parameters) parameters.sum { |param| %i[arg kwarg].include?(param.decl) ? 1 : 0 } @@ -687,12 +691,45 @@ def fake_args_for(pin) args end + # @return [Set] + def sg_ignore_lines_processed + @sg_ignore_lines_processed ||= Set.new + end + + # @return [Set] + def all_sg_ignore_lines + source.associated_comments.select do |_line, text| + text.include?('@sg-ignore') + end.keys.to_set + end + + # @return [Array] + def unprocessed_sg_ignore_lines + (all_sg_ignore_lines - sg_ignore_lines_processed).to_a.sort + end + + def unneeded_sgignore_problems + return unless rules.validate_sg_ignores? + + unprocessed_sg_ignore_lines.map do |line| + Problem.new( + Location.new(filename, Range.from_to(line, 0, line, 0)), + "Unneeded @sg-ignore comment" + ) + end + end + # @param problems [Array] # @return [Array] def without_ignored problems problems.reject do |problem| - node = source_map.source.node_at(problem.location.range.start.line, problem.location.range.start.column) - node && source_map.source.comments_for(node)&.include?('@sg-ignore') + node = source.node_at(problem.location.range.start.line, problem.location.range.start.column) + ignored = node && source.comments_for(node)&.include?('@sg-ignore') + unless !ignored || all_sg_ignore_lines.include?(problem.location.range.start.line) + Solargraph.assert_or_log(:sg_ignore) { "@sg-ignore accounting issue - node is #{node}" } + end + sg_ignore_lines_processed.add problem.location.range.start.line if ignored + ignored end end end diff --git a/lib/solargraph/type_checker/rules.rb b/lib/solargraph/type_checker/rules.rb index 0aad5ed8a..8f15037d5 100644 --- a/lib/solargraph/type_checker/rules.rb +++ b/lib/solargraph/type_checker/rules.rb @@ -57,6 +57,14 @@ def validate_tags? def require_all_return_types_match_inferred? rank >= LEVELS[:alpha] end + + # We keep this at strong because if you added an @sg-ignore to + # address a strong-level issue, then ran at a lower level, you'd + # get a false positive - we don't run stronger level checks than + # requested for performance reasons + def validate_sg_ignores? + rank >= LEVELS[:strong] + end end end end diff --git a/lib/solargraph/workspace/config.rb b/lib/solargraph/workspace/config.rb index ce45e5b11..0b2d84a01 100644 --- a/lib/solargraph/workspace/config.rb +++ b/lib/solargraph/workspace/config.rb @@ -90,7 +90,6 @@ def reporters # A hash of options supported by the formatter # - # @sg-ignore pending https://github.com/castwide/solargraph/pull/905 # @return [Hash] def formatter raw_data['formatter'] @@ -105,7 +104,6 @@ def plugins # The maximum number of files to parse from the workspace. # - # @sg-ignore pending https://github.com/castwide/solargraph/pull/905 # @return [Integer] def max_files raw_data['max_files'] diff --git a/spec/type_checker/levels/strong_spec.rb b/spec/type_checker/levels/strong_spec.rb index 12db1e442..e21bea179 100644 --- a/spec/type_checker/levels/strong_spec.rb +++ b/spec/type_checker/levels/strong_spec.rb @@ -4,6 +4,16 @@ def type_checker(code) Solargraph::TypeChecker.load_string(code, 'test.rb', :strong) end + it 'reports unneeded @sg-ignore tags' do + checker = type_checker(%( + class Foo + # @sg-ignore + def bar; end + end + )) + expect(checker.problems.map(&:message)).to eq(['Unneeded @sg-ignore comment']) + end + it 'reports missing return tags' do checker = type_checker(%( class Foo From 1fd40564bba7d28a4fd47349806b501b78eb7dfb Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 18 Jul 2025 10:26:00 -0400 Subject: [PATCH 239/561] Fix return type --- lib/solargraph/type_checker.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/solargraph/type_checker.rb b/lib/solargraph/type_checker.rb index e4bb680ef..bf9f5864d 100644 --- a/lib/solargraph/type_checker.rb +++ b/lib/solargraph/type_checker.rb @@ -708,8 +708,9 @@ def unprocessed_sg_ignore_lines (all_sg_ignore_lines - sg_ignore_lines_processed).to_a.sort end + # @return [Array] def unneeded_sgignore_problems - return unless rules.validate_sg_ignores? + return [] unless rules.validate_sg_ignores? unprocessed_sg_ignore_lines.map do |line| Problem.new( From 418ed2267d07ef538a527a026a1374b107cae146 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 18 Jul 2025 10:32:47 -0400 Subject: [PATCH 240/561] Fix spec --- spec/type_checker/levels/strong_spec.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/type_checker/levels/strong_spec.rb b/spec/type_checker/levels/strong_spec.rb index e21bea179..6fdf84e30 100644 --- a/spec/type_checker/levels/strong_spec.rb +++ b/spec/type_checker/levels/strong_spec.rb @@ -8,6 +8,7 @@ def type_checker(code) checker = type_checker(%( class Foo # @sg-ignore + # @return [void] def bar; end end )) From 90e93c67f9d7de6ceb135b6827cc2b07bc72eff5 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 18 Jul 2025 18:31:46 -0400 Subject: [PATCH 241/561] Improve typechecking of generics --- lib/solargraph/complex_type.rb | 57 +++++- lib/solargraph/complex_type/type_methods.rb | 9 + lib/solargraph/complex_type/unique_type.rb | 173 ++++++++++++++++-- lib/solargraph/gem_pins.rb | 3 +- lib/solargraph/parser/node_methods.rb | 2 +- .../parser/parser_gem/node_methods.rb | 2 +- lib/solargraph/pin/parameter.rb | 12 +- lib/solargraph/range.rb | 3 +- lib/solargraph/source.rb | 2 +- lib/solargraph/type_checker.rb | 40 +++- lib/solargraph/type_checker/checks.rb | 124 ------------- lib/solargraph/type_checker/rules.rb | 6 +- spec/complex_type_spec.rb | 28 +++ spec/type_checker/levels/strong_spec.rb | 16 ++ spec/type_checker/levels/typed_spec.rb | 49 +++++ 15 files changed, 373 insertions(+), 153 deletions(-) delete mode 100644 lib/solargraph/type_checker/checks.rb diff --git a/lib/solargraph/complex_type.rb b/lib/solargraph/complex_type.rb index 9e23eb502..53c28ed6e 100644 --- a/lib/solargraph/complex_type.rb +++ b/lib/solargraph/complex_type.rb @@ -102,7 +102,7 @@ def each_unique_type &block # @param atype [ComplexType] type which may be assigned to this type # @param api_map [ApiMap] The ApiMap that performs qualification def can_assign?(api_map, atype) - any? { |ut| ut.can_assign?(api_map, atype) } + atype.conforms_to?(api_map, self, :assignment) end # @return [Integer] @@ -176,6 +176,61 @@ def desc rooted_tags end + # @param api_map [ApiMap] + # @param expected [ComplexType, ComplexType::UniqueType] + # @param situation [:method_call, :return_type, :assignment] + # @param allow_subtype_skew [Boolean] if false, check if any + # subtypes of the expected type match the inferred type + # @param allow_reverse_match [Boolean] if true, check if any subtypes + # of the expected type match the inferred type + # @param allow_empty_params [Boolean] if true, allow a general + # inferred type without parameters to allow a more specific + # expcted type + # @param allow_any_match [Boolean] if true, any unique type + # matched in the expected qualifies as a match + # @return [Boolean] + def conforms_to? api_map, expected, + situation, + variance: erased_variance(situation), + allow_subtype_skew: false, + allow_empty_params: false, + allow_reverse_match: false, + allow_any_match: false #, +# allow_undefined_in_expected: false + expected = expected.downcast_to_literal_if_possible + inferred = downcast_to_literal_if_possible + + return duck_types_match?(api_map, expected, inferred) if expected.duck_type? + + if allow_any_match + inferred.any? { |inf| inf.conforms_to?(api_map, expected, situation, + allow_subtype_skew: allow_subtype_skew, + allow_empty_params: allow_empty_params, + allow_reverse_match: allow_reverse_match, + allow_any_match: allow_any_match) } + else + inferred.all? { |inf| inf.conforms_to?(api_map, expected, situation, + allow_subtype_skew: allow_subtype_skew, + allow_empty_params: allow_empty_params, + allow_reverse_match: allow_reverse_match, + allow_any_match: allow_any_match) } + end + end + + # @param api_map [ApiMap] + # @param expected [ComplexType] + # @param inferred [ComplexType] + # @return [Boolean] + def duck_types_match? api_map, expected, inferred + raise ArgumentError, 'Expected type must be duck type' unless expected.duck_type? + expected.each do |exp| + next unless exp.duck_type? + quack = exp.to_s[1..-1] + return false if api_map.get_method_stack(inferred.namespace, quack, scope: inferred.scope).empty? + end + true + end + def rooted_tags map(&:rooted_tag).join(', ') end diff --git a/lib/solargraph/complex_type/type_methods.rb b/lib/solargraph/complex_type/type_methods.rb index e6d596244..4fcaadb7f 100644 --- a/lib/solargraph/complex_type/type_methods.rb +++ b/lib/solargraph/complex_type/type_methods.rb @@ -69,6 +69,15 @@ def undefined? name == 'undefined' end + # Variance of the type ignoring any type parameters + def erased_variance situation = :method_call + if [:method_call, :return_type, :assignment].include?(situation) + :covariant + else + raise "Unknown situation: #{situation.inspect}" + end + end + # @param generics_to_erase [Enumerable] # @return [self] def erase_generics(generics_to_erase) diff --git a/lib/solargraph/complex_type/unique_type.rb b/lib/solargraph/complex_type/unique_type.rb index 0f4ec430d..1023d080e 100644 --- a/lib/solargraph/complex_type/unique_type.rb +++ b/lib/solargraph/complex_type/unique_type.rb @@ -151,10 +151,167 @@ def ==(other) eql?(other) end + # https://www.playfulpython.com/type-hinting-covariance-contra-variance/ + + # "[Expected] type variables that are COVARIANT can be substituted with + # a more specific [inferred] type without causing errors" + # + # "[Expected] type variables that are CONTRAVARIANT can be substituted + # with a more general [inferred] type without causing errors" + # + # "[Expected] types where neither is possible are INVARIANT" + # + # @param situation [:method_call] + # @param default [Symbol] The default variance to return if the type is not one of the special cases + # + # @return [:invariant, :covariant, :contravariant] + def parameter_variance situation, default = :covariant + # @todo RBS can specify variance - maybe we can use that info + # and also let folks specify? + # + # Array/Set: ideally invariant, since we don't know if user is + # going to add new stuff into it or read it. But we don't + # have a way to specify, so we use covariant + # Enumerable: covariant: can't be changed, so we can pass + # in more specific subtypes + # Hash: read-only would be covariant, read-write would be + # invariant if we could distinguish that - should default to + # covariant + # contravariant?: Proc - can be changed, so we can pass + # in less specific super types + if ['Hash', 'Tuple', 'Array', 'Set', 'Enumerable'].include?(name) && fixed_parameters? + :covariant + else + default + end + end + + # @param api_map [ApiMap] + # @param expected [ComplexType, ComplexType::UniqueType] + # @param situation [:method_call, :return_type] + # @param allow_subtype_skew [Boolean] if false, check if any + # subtypes of the expected type match the inferred type + # @param allow_empty_params [Boolean] if true, allow a general + # inferred type without parameters to allow a more specific + # expcted type + # @param allow_reverse_match [Boolean] if true, check if any subtypes + # of the expected type match the inferred type + # @param allow_any_match [Boolean] if true, any unique type + # matched in the expected qualifies as a match + def conforms_to_unique_type?(api_map, expected, situation = :method_call, + variance: erased_variance(situation), + allow_subtype_skew: allow_subtype_skew, + allow_empty_params: allow_empty_params, + allow_reverse_match: allow_reverse_match, + allow_any_match: allow_any_match) + expected = expected.downcast_to_literal_if_possible + inferred = downcast_to_literal_if_possible + + if allow_subtype_skew + # parameters are not considered in this case + expected = expected.erase_parameters + end + + if !expected.parameters? && inferred.parameters? + inferred = inferred.erase_parameters + end + + return true if inferred == expected + + if variance == :invariant + return false unless inferred.name == expected.name + elsif erased_variance == :covariant + # covariant: we can pass in a more specific type + + # we contain the expected mix-in, or we have a more specific type + return false unless api_map.type_include?(inferred.name, expected.name) || + api_map.super_and_sub?(expected.name, inferred.name) || + inferred.name == expected.name + + elsif erased_variance == :contravariant + # contravariant: we can pass in a more general type + + # we contain the expected mix-in, or we have a more general type + return false unless api_map.type_include?(inferred.name, expected.name) || + map.super_and_sub?(inferred.name, expected.name) || + inferred.name == expected.name + else + raise "Unknown erased variance: #{erased_variance.inspect}" + end + + return true if inferred.all_params.empty? && allow_empty_params + + # at this point we know the erased type is fine - time to look at parameters + + # there's an implicit 'any' on the expectation parameters + # if there are none specified + return true if expected.all_params.empty? + + unless expected.key_types.empty? + return false if inferred.key_types.empty? + + return false unless ComplexType.new(inferred.key_types).conforms_to?(api_map, + ComplexType.new(expected.key_types), + situation, + variance: parameter_variance(situation), + allow_subtype_skew: allow_subtype_skew, + allow_empty_params: allow_empty_params, + allow_reverse_match: allow_reverse_match, + allow_any_match: allow_any_match) + end + + return true if expected.subtypes.empty? + + return false if inferred.subtypes.empty? + + ComplexType.new(inferred.subtypes).conforms_to?(api_map, ComplexType.new(expected.subtypes), situation, + variance: parameter_variance(situation), + allow_subtype_skew: allow_subtype_skew, + allow_empty_params: allow_empty_params, + allow_reverse_match: allow_reverse_match, + allow_any_match: allow_any_match) + end + + # @param api_map [ApiMap] + # @param expected [ComplexType::UniqueType] + # @param situation [:method_call, :assignment, :return] + # @param allow_subtype_skew [Boolean] if false, check if any + # subtypes of the expected type match the inferred type + # @param allow_empty_params [Boolean] if true, allow a general + # inferred type without parameters to allow a more specific + # expcted type + # @param allow_reverse_match [Boolean] if true, check if any subtypes + # of the expected type match the inferred type + # @param allow_any_match [Boolean] if true, any unique type + # matched in the expected qualifies as a match + def conforms_to?(api_map, expected, + situation = :method_call, + allow_subtype_skew:, + allow_empty_params:, + allow_reverse_match:, + allow_any_match:) + # @todo teach this to validate duck types as inferred type + return true if duck_type? + + # complex types as expectations are unions - we only need to + # match one of their unique types + expected.any? do |expected_unique_type| + conforms_to_unique_type?(api_map, expected_unique_type, situation, + allow_subtype_skew: allow_subtype_skew, + allow_empty_params: allow_empty_params, + allow_reverse_match: allow_reverse_match, + allow_any_match: allow_any_match) + end + end + def hash [self.class, @name, @key_types, @sub_types, @rooted, @all_params, @parameters_type].hash end + def erase_parameters + UniqueType.new(name, rooted: rooted?, parameters_type: parameters_type) + end + # @return [Array] def items [self] @@ -236,18 +393,6 @@ def generic? name == GENERIC_TAG_NAME || all_params.any?(&:generic?) end - # @param api_map [ApiMap] The ApiMap that performs qualification - # @param atype [ComplexType] type which may be assigned to this type - def can_assign?(api_map, atype) - logger.debug { "UniqueType#can_assign?(self=#{rooted_tags.inspect}, atype=#{atype.rooted_tags.inspect})" } - downcasted_atype = atype.downcast_to_literal_if_possible - out = downcasted_atype.all? do |autype| - autype.name == name || api_map.super_and_sub?(name, autype.name) - end - logger.debug { "UniqueType#can_assign?(self=#{rooted_tags.inspect}, atype=#{atype.rooted_tags.inspect}) => #{out}" } - out - end - # @return [UniqueType] def downcast_to_literal_if_possible SINGLE_SUBTYPE.fetch(rooted_tag, self) @@ -437,6 +582,10 @@ def self_to_type dst end end + def any? &block + block.yield self + end + def all_rooted? return true if name == GENERIC_TAG_NAME rooted? && all_params.all?(&:rooted?) diff --git a/lib/solargraph/gem_pins.rb b/lib/solargraph/gem_pins.rb index b92cbd6af..f1dd25a9f 100644 --- a/lib/solargraph/gem_pins.rb +++ b/lib/solargraph/gem_pins.rb @@ -19,7 +19,8 @@ def self.build_yard_pins(gemspec) YardMap::Mapper.new(yardoc, gemspec).map end - # @param pins [Array] + # @param pins [Array] + # @return [Array] def self.combine_method_pins_by_path(pins) # bad_pins = pins.select { |pin| pin.is_a?(Pin::Method) && pin.path == 'StringIO.open' && pin.source == :rbs }; raise "wtf: #{bad_pins}" if bad_pins.length > 1 method_pins, alias_pins = pins.partition { |pin| pin.class == Pin::Method } diff --git a/lib/solargraph/parser/node_methods.rb b/lib/solargraph/parser/node_methods.rb index 12e974c16..2712f2867 100644 --- a/lib/solargraph/parser/node_methods.rb +++ b/lib/solargraph/parser/node_methods.rb @@ -74,7 +74,7 @@ def process node # @abstract # @param node [Parser::AST::Node] - # @return [Hash{Parser::AST::Node => Source::Chain}] + # @return [Hash{Parser::AST::Node, Symbol => Source::Chain}] def convert_hash node raise NotImplementedError end diff --git a/lib/solargraph/parser/parser_gem/node_methods.rb b/lib/solargraph/parser/parser_gem/node_methods.rb index b716b352d..bc0c37eb6 100644 --- a/lib/solargraph/parser/parser_gem/node_methods.rb +++ b/lib/solargraph/parser/parser_gem/node_methods.rb @@ -120,7 +120,7 @@ def drill_signature node, signature end # @param node [Parser::AST::Node] - # @return [Hash{Parser::AST::Node => Chain}] + # @return [Hash{Parser::AST::Node, Symbol => Chain}] def convert_hash node return {} unless Parser.is_ast_node?(node) return convert_hash(node.children[0]) if node.type == :kwsplat diff --git a/lib/solargraph/pin/parameter.rb b/lib/solargraph/pin/parameter.rb index bc802b748..b4fc3d9b2 100644 --- a/lib/solargraph/pin/parameter.rb +++ b/lib/solargraph/pin/parameter.rb @@ -166,7 +166,17 @@ def compatible_arg?(atype, api_map) # make sure we get types from up the method # inheritance chain if we don't have them on this pin ptype = typify api_map - ptype.undefined? || ptype.can_assign?(api_map, atype) || ptype.generic? + return true if ptype.undefined? + + return true if atype.conforms_to?(api_map, + ptype, + :method_call, + allow_subtype_skew: false, + allow_reverse_match: false, + allow_empty_params: true, + allow_any_match: false) + + ptype.generic? end def documentation diff --git a/lib/solargraph/range.rb b/lib/solargraph/range.rb index 615f180af..c508e48fa 100644 --- a/lib/solargraph/range.rb +++ b/lib/solargraph/range.rb @@ -24,6 +24,7 @@ def initialize start, ending [start, ending] end + # @param other [Object] def <=>(other) return nil unless other.is_a?(Range) if start == other.start @@ -78,7 +79,7 @@ def self.from_to l1, c1, l2, c2 # Get a range from a node. # - # @param node [Parser::AST::Node] + # @param node [AST::Node] # @return [Range, nil] def self.from_node node if node&.loc && node.loc.expression diff --git a/lib/solargraph/source.rb b/lib/solargraph/source.rb index 11ab215ed..d4e0c3994 100644 --- a/lib/solargraph/source.rb +++ b/lib/solargraph/source.rb @@ -187,7 +187,7 @@ def code_for(node) frag.strip.gsub(/,$/, '') end - # @param node [Parser::AST::Node] + # @param node [AST::Node] # @return [String, nil] def comments_for node rng = Range.from_node(node) diff --git a/lib/solargraph/type_checker.rb b/lib/solargraph/type_checker.rb index aa215f97b..ab87d5863 100644 --- a/lib/solargraph/type_checker.rb +++ b/lib/solargraph/type_checker.rb @@ -7,9 +7,7 @@ class TypeChecker autoload :Problem, 'solargraph/type_checker/problem' autoload :ParamDef, 'solargraph/type_checker/param_def' autoload :Rules, 'solargraph/type_checker/rules' - autoload :Checks, 'solargraph/type_checker/checks' - include Checks include Parser::NodeMethods # @return [String] @@ -113,7 +111,11 @@ def method_return_type_problems_for pin result.push Problem.new(pin.location, "#{pin.path} return type could not be inferred", pin: pin) end else - unless (rules.require_all_return_types_match_inferred? ? all_types_match?(api_map, inferred, declared) : any_types_match?(api_map, declared, inferred)) + unless inferred.conforms_to?(api_map, declared, :return_type, + allow_subtype_skew: false, + allow_empty_params: !rules.require_inferred_type_params, + allow_reverse_match: false, + allow_any_match: !rules.require_all_unique_types_match_declared?) result.push Problem.new(pin.location, "Declared return type #{declared.rooted_tags} does not match inferred type #{inferred.rooted_tags} for #{pin.path}", pin: pin) end end @@ -202,7 +204,11 @@ def variable_type_tag_problems result.push Problem.new(pin.location, "Variable type could not be inferred for #{pin.name}", pin: pin) end else - unless any_types_match?(api_map, declared, inferred) + unless inferred.conforms_to?(api_map, declared, :assignment, + allow_subtype_skew: false, + allow_empty_params: !rules.require_inferred_type_params, + allow_reverse_match: false, + allow_any_match: !rules.require_all_unique_types_match_declared?) result.push Problem.new(pin.location, "Declared type #{declared} does not match inferred type #{inferred} for variable #{pin.name}", pin: pin) end end @@ -284,8 +290,8 @@ def call_problems # @param chain [Solargraph::Source::Chain] # @param api_map [Solargraph::ApiMap] - # @param block_pin [Solargraph::Pin::Base] - # @param locals [Array] + # @param closure_pin [Solargraph::Pin::Closure] + # @param locals [Array] # @param location [Solargraph::Location] # @return [Array] def argument_problems_for chain, api_map, block_pin, locals, location @@ -383,7 +389,11 @@ def argument_problems_for chain, api_map, block_pin, locals, location # @todo Some level (strong, I guess) should require the param here else argtype = argchain.infer(api_map, block_pin, locals) - if argtype.defined? && ptype.defined? && !any_types_match?(api_map, ptype, argtype) + if argtype.defined? && ptype.defined? && !argtype.conforms_to?(api_map, ptype, :method_call, + allow_subtype_skew: false, + allow_empty_params: !rules.require_inferred_type_params, + allow_reverse_match: false, + allow_any_match: !rules.require_all_unique_types_match_declared?) errors.push Problem.new(location, "Wrong argument type for #{pin.path}: #{par.name} expected #{ptype}, received #{argtype}") next end @@ -433,8 +443,13 @@ def kwarg_problems_for sig, argchain, api_map, block_pin, locals, location, pin, else ptype = data[:qualified] unless ptype.undefined? + argtype = argchain.infer(api_map, block_pin, locals) - if argtype.defined? && ptype && !any_types_match?(api_map, ptype, argtype) + if argtype.defined? && ptype && !argtype.conforms_to?(api_map, ptype, :method_call, + allow_subtype_skew: false, + allow_empty_params: !rules.require_inferred_type_params, + allow_reverse_match: false, + allow_any_match: !rules.require_all_unique_types_match_declared?) result.push Problem.new(location, "Wrong argument type for #{pin.path}: #{par.name} expected #{ptype}, received #{argtype}") end end @@ -460,7 +475,12 @@ def kwrestarg_problems_for(api_map, block_pin, locals, location, pin, params, kw next unless params.key?(pname.to_s) ptype = params[pname.to_s][:qualified] argtype = argchain.infer(api_map, block_pin, locals) - if argtype.defined? && ptype && !any_types_match?(api_map, ptype, argtype) + if argtype.defined? && ptype && !argtype.conforms_to?(api_map, ptype, :method_call, + allow_subtype_skew: false, + allow_empty_params: !rules.require_inferred_type_params, + allow_reverse_match: false, + allow_any_match: !rules.require_all_unique_types_match_declared?) + result.push Problem.new(location, "Wrong argument type for #{pin.path}: #{pname} expected #{ptype}, received #{argtype}") end end @@ -495,6 +515,8 @@ def param_hash(pin) end # @param pins [Array] + # @param method_pin_stack [Array] + # # @return [Hash{String => Hash{Symbol => String, ComplexType}}] def first_param_hash(pins) return {} if pins.empty? diff --git a/lib/solargraph/type_checker/checks.rb b/lib/solargraph/type_checker/checks.rb deleted file mode 100644 index de402978b..000000000 --- a/lib/solargraph/type_checker/checks.rb +++ /dev/null @@ -1,124 +0,0 @@ -# frozen_string_literal: true - -module Solargraph - class TypeChecker - # Helper methods for performing type checks - # - module Checks - module_function - - # Compare an expected type with an inferred type. Common usage is to - # check if the type declared in a method's @return tag matches the type - # inferred from static analysis of the code. - # - # @param api_map [ApiMap] - # @param expected [ComplexType] - # @param inferred [ComplexType] - # @return [Boolean] - def types_match? api_map, expected, inferred - return true if expected.to_s == inferred.to_s - matches = [] - expected.each do |exp| - found = false - inferred.each do |inf| - # if api_map.super_and_sub?(fuzz(inf), fuzz(exp)) - if either_way?(api_map, inf, exp) - found = true - matches.push inf - break - end - end - return false unless found - end - inferred.each do |inf| - next if matches.include?(inf) - found = false - expected.each do |exp| - # if api_map.super_and_sub?(fuzz(inf), fuzz(exp)) - if either_way?(api_map, inf, exp) - found = true - break - end - end - return false unless found - end - true - end - - # @param api_map [ApiMap] - # @param expected [ComplexType] - # @param inferred [ComplexType] - # @return [Boolean] - def any_types_match? api_map, expected, inferred - expected = expected.downcast_to_literal_if_possible - inferred = inferred.downcast_to_literal_if_possible - return duck_types_match?(api_map, expected, inferred) if expected.duck_type? - # walk through the union expected type and see if any members - # of the union match the inferred type - expected.each do |exp| - next if exp.duck_type? - # @todo: there should be a level of typechecking where all - # unique types in the inferred must match one of the - # expected unique types - inferred.each do |inf| - # return true if exp == inf || api_map.super_and_sub?(fuzz(inf), fuzz(exp)) - return true if exp == inf || either_way?(api_map, inf, exp) - end - end - false - end - - # @param api_map [ApiMap] - # @param inferred [ComplexType] - # @param expected [ComplexType] - # @return [Boolean] - def all_types_match? api_map, inferred, expected - expected = expected.downcast_to_literal_if_possible - inferred = inferred.downcast_to_literal_if_possible - return duck_types_match?(api_map, expected, inferred) if expected.duck_type? - inferred.each do |inf| - next if inf.duck_type? - return false unless expected.any? { |exp| exp == inf || either_way?(api_map, inf, exp) } - end - true - end - - # @param api_map [ApiMap] - # @param expected [ComplexType] - # @param inferred [ComplexType] - # @return [Boolean] - def duck_types_match? api_map, expected, inferred - raise ArgumentError, 'Expected type must be duck type' unless expected.duck_type? - expected.each do |exp| - next unless exp.duck_type? - quack = exp.to_s[1..-1] - return false if api_map.get_method_stack(inferred.namespace, quack, scope: inferred.scope).empty? - end - true - end - - # @param type [ComplexType::UniqueType] - # @return [String] - def fuzz type - if type.parameters? - type.name - else - type.tag - end - end - - # @param api_map [ApiMap] - # @param cls1 [ComplexType::UniqueType] - # @param cls2 [ComplexType::UniqueType] - # @return [Boolean] - def either_way?(api_map, cls1, cls2) - # @todo there should be a level of typechecking which uses the - # full tag with parameters to determine compatibility - f1 = cls1.name - f2 = cls2.name - api_map.type_include?(f1, f2) || api_map.super_and_sub?(f1, f2) || api_map.super_and_sub?(f2, f1) - # api_map.type_include?(f1, f2) || api_map.super_and_sub?(f1, f2) || api_map.super_and_sub?(f2, f1) - end - end - end -end diff --git a/lib/solargraph/type_checker/rules.rb b/lib/solargraph/type_checker/rules.rb index 0aad5ed8a..8f2027d30 100644 --- a/lib/solargraph/type_checker/rules.rb +++ b/lib/solargraph/type_checker/rules.rb @@ -54,7 +54,11 @@ def validate_tags? rank > LEVELS[:normal] end - def require_all_return_types_match_inferred? + def require_inferred_type_params + rank >= LEVELS[:alpha] + end + + def require_all_unique_types_match_declared? rank >= LEVELS[:alpha] end end diff --git a/spec/complex_type_spec.rb b/spec/complex_type_spec.rb index f876d642f..2c060ceed 100644 --- a/spec/complex_type_spec.rb +++ b/spec/complex_type_spec.rb @@ -733,5 +733,33 @@ def make_bar expect(type.to_rbs).to eq('[Symbol, String, [Integer, Integer]]') expect(type.to_s).to eq('Array(Symbol, String, Array(Integer, Integer))') end + + it 'recognizes String conforms with itself' do + api_map = Solargraph::ApiMap.new + ptype = Solargraph::ComplexType.parse('String') + atype = Solargraph::ComplexType.parse('String') + expect(ptype.conforms_to?(api_map, atype, :method_call)).to be(true) + end + + it 'recognizes an erased container type conforms with itself' do + api_map = Solargraph::ApiMap.new + ptype = Solargraph::ComplexType.parse('Hash') + atype = Solargraph::ComplexType.parse('Hash') + expect(ptype.conforms_to?(api_map, atype, :method_call)).to be(true) + end + + it 'recognizes an unerased container type conforms with itself' do + api_map = Solargraph::ApiMap.new + ptype = Solargraph::ComplexType.parse('Array') + atype = Solargraph::ComplexType.parse('Array') + expect(ptype.conforms_to?(api_map, atype, :method_call)).to be(true) + end + + it 'recognizes a literal conforms with its type' do + api_map = Solargraph::ApiMap.new + ptype = Solargraph::ComplexType.parse('Symbol') + atype = Solargraph::ComplexType.parse(':foo') + expect(ptype.conforms_to?(api_map, atype, :method_call)).to be(true) + end end end diff --git a/spec/type_checker/levels/strong_spec.rb b/spec/type_checker/levels/strong_spec.rb index 12db1e442..054a09efa 100644 --- a/spec/type_checker/levels/strong_spec.rb +++ b/spec/type_checker/levels/strong_spec.rb @@ -14,6 +14,22 @@ def bar; end expect(checker.problems.first.message).to include('Missing @return tag') end + + it 'ignores nilable type issues' do + checker = type_checker(%( + # @param a [String] + # @return [void] + def foo(a); end + + # @param b [String, nil] + # @return [void] + def bar(b) + foo(b) + end + )) + expect(checker.problems.map(&:message)).to eq([]) + end + it 'reports missing param tags' do checker = type_checker(%( class Foo diff --git a/spec/type_checker/levels/typed_spec.rb b/spec/type_checker/levels/typed_spec.rb index b10bbd42c..659ccee39 100644 --- a/spec/type_checker/levels/typed_spec.rb +++ b/spec/type_checker/levels/typed_spec.rb @@ -38,6 +38,19 @@ def bar expect(checker.problems.first.message).to include('does not match') end + it 'reports mismatched key and subtypes ' do + checker = type_checker(%( + # @return [Hash{String => String}] + def foo + # @type h [Hash{Integer => String}] + h = {} + h + end + )) + expect(checker.problems).to be_one + expect(checker.problems.first.message).to include('does not match') + end + it 'reports mismatched inherited return tags' do checker = type_checker(%( class Sup @@ -189,6 +202,42 @@ def foo expect(checker.problems).to be_empty end + it 'validates parameters in function calls' do + checker = type_checker(%( + # @param bar [String] + def foo(bar); end + + def baz + foo(123) + end + )) + expect(checker.problems.map(&:message)).to eq(['123']) + end + + it 'validates default values of parameters' do + checker = type_checker(%( + # @param bar [String] + def foo(bar = 123); end + )) + expect(checker.problems.map(&:message)).to eq(['Declared type String does not match inferred type 123 for variable bar']) + end + + it 'validates string default values of parameters' do + checker = type_checker(%( + # @param bar [String] + def foo(bar = 'foo'); end + )) + expect(checker.problems.map(&:message)).to be_empty + end + + it 'validates symbol default values of parameters' do + checker = type_checker(%( + # @param bar [Symbol] + def foo(bar = :baz); end + )) + expect(checker.problems.map(&:message)).to eq([]) + end + it 'validates subclass arguments of param types' do checker = type_checker(%( class Sup From 8e9cb6f19710bb9bdaefa09dbcb350b4a90685e8 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 18 Jul 2025 18:37:35 -0400 Subject: [PATCH 242/561] Add alpha typechecking spec --- spec/type_checker/levels/alpha_spec.rb | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 spec/type_checker/levels/alpha_spec.rb diff --git a/spec/type_checker/levels/alpha_spec.rb b/spec/type_checker/levels/alpha_spec.rb new file mode 100644 index 000000000..d700ea3b7 --- /dev/null +++ b/spec/type_checker/levels/alpha_spec.rb @@ -0,0 +1,22 @@ +describe Solargraph::TypeChecker do + context 'alpha level' do + def type_checker(code) + Solargraph::TypeChecker.load_string(code, 'test.rb', :alpha) + end + + it 'reports nilable type issues' do + checker = type_checker(%( + # @param a [String] + # @return [void] + def foo(a); end + + # @param b [String, nil] + # @return [void] + def bar(b) + foo(b) + end + )) + expect(checker.problems.map(&:message)).to eq(["Wrong argument type for #foo: a expected String, received String, nil"]) + end + end +end From 673969bc94a0a2fc1aad563770157536eb7f2813 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 19 Jul 2025 08:49:58 -0400 Subject: [PATCH 243/561] Fixes --- lib/solargraph/api_map.rb | 15 +++++--- lib/solargraph/doc_map.rb | 4 +-- lib/solargraph/workspace.rb | 4 +-- lib/solargraph/workspace/gemspecs.rb | 38 ++++++++++++++------ spec/shell_spec.rb | 52 ++++++++++++++++++++++++++++ 5 files changed, 94 insertions(+), 19 deletions(-) create mode 100644 spec/shell_spec.rb diff --git a/lib/solargraph/api_map.rb b/lib/solargraph/api_map.rb index b7823db1a..592d942d1 100755 --- a/lib/solargraph/api_map.rb +++ b/lib/solargraph/api_map.rb @@ -22,9 +22,8 @@ class ApiMap # @return [Array] attr_reader :missing_docs - # TODO: Is this needed? - # @return [Solargraph::PinCache] - attr_reader :pin_cache + # @return [Solargraph::Workspace::Gemspecs] + attr_reader :gemspecs # @param pins [Array] def initialize pins: [] @@ -106,7 +105,7 @@ def catalog bench if recreate_docmap @doc_map = DocMap.new(unresolved_requires, bench.workspace) # @todo Implement gem preferences - @pin_cache = @doc_map.pin_cache + @gemspecs = @doc_map.workspace.gemspecs @unresolved_requires = @doc_map.unresolved_requires end @cache.clear if store.update(@@core_map.pins, @doc_map.pins, implicit.pins, iced_pins, live_pins) @@ -201,6 +200,14 @@ def workspace @doc_map&.workspace end + # @param name [String] + # @param version [String, nil] + # + # @return [Gem::Specification, nil] + def find_gem(name, version = nil) + gemspecs.find_gem(name, version) + end + # @param gemspec [Gem::Specification] # @param rebuild [Boolean] # @param only_if_used [Boolean] diff --git a/lib/solargraph/doc_map.rb b/lib/solargraph/doc_map.rb index c688c9ba8..e222752f9 100644 --- a/lib/solargraph/doc_map.rb +++ b/lib/solargraph/doc_map.rb @@ -93,8 +93,6 @@ def dependencies @dependencies ||= (gemspecs.flat_map { |spec| workspace.fetch_dependencies(spec) } - gemspecs).to_set end - private - # Cache gem documentation if needed for this doc_map # # @param gemspec [Gem::Specification] @@ -111,6 +109,8 @@ def cache(gemspec, rebuild: false, only_if_used: false, out: nil) out: out) end + private + # @param out [IO, nil] # @return [void] def load_serialized_gem_pins(out: $stderr) diff --git a/lib/solargraph/workspace.rb b/lib/solargraph/workspace.rb index 382dd068e..ab78253b5 100644 --- a/lib/solargraph/workspace.rb +++ b/lib/solargraph/workspace.rb @@ -214,13 +214,13 @@ def command_path server['commandPath'] || 'solargraph' end - private - # @return [Solargraph::Workspace::Gemspecs] def gemspecs @gemspecs ||= Solargraph::Workspace::Gemspecs.new(directory) end + private + # The language server configuration (or an empty hash if the workspace was # not initialized from a server). # diff --git a/lib/solargraph/workspace/gemspecs.rb b/lib/solargraph/workspace/gemspecs.rb index d33e6ba5d..40975e7c6 100644 --- a/lib/solargraph/workspace/gemspecs.rb +++ b/lib/solargraph/workspace/gemspecs.rb @@ -55,17 +55,18 @@ def resolve_require require [gemspec_or_preference(gemspec)] end - # Returns all gemspecs directly depended on by this workspace's - # bundle (does not include transitive dependencies). + # @param name [String] + # @param version [String, nil] # - # @return [Array] - def all_gemspecs_from_bundle - @all_gemspecs_from_bundle ||= - if in_this_bundle? - all_gemspecs_from_this_bundle - else - all_gemspecs_from_external_bundle - end + # @return [Gem::Specification, nil] + def find_gem name, version + gemspec = all_gemspecs_from_bundle.find { |gemspec| gemspec.name == name && gemspec.version == version } + return gemspec if gemspec + + Gem::Specification.find_by_name(name, version) + rescue Gem::MissingSpecError + logger.warn "Please install the gem #{name}:#{version} in Solargraph's Ruby environment" + nil end # @param gemspec [Gem::Specification] @@ -89,6 +90,21 @@ def fetch_dependencies gemspec end.to_a.compact end + # Returns all gemspecs directly depended on by this workspace's + # bundle (does not include transitive dependencies). + # + # @return [Array] + def all_gemspecs_from_bundle + @all_gemspecs_from_bundle ||= + if in_this_bundle? + all_gemspecs_from_this_bundle + else + all_gemspecs_from_external_bundle + end + end + + private + # @param command [String] The expression to evaluate in the external bundle # @sg-ignore Need a JSON type # @yield [undefined] @@ -238,7 +254,7 @@ def all_gemspecs_from_external_bundle begin logger.info 'Fetching gemspecs required from external bundle' - command = 'Bundler.definition.locked_gems.specs.map { |spec| [spec.name, spec.version] }.to_h' + command = 'Bundler.definition.locked_gems&.specs&.map { |spec| [spec.name, spec.version] }.to_h' query_external_bundle command do |names_and_versions| names_and_versions.map do |name, version| diff --git a/spec/shell_spec.rb b/spec/shell_spec.rb new file mode 100644 index 000000000..248cf2396 --- /dev/null +++ b/spec/shell_spec.rb @@ -0,0 +1,52 @@ +require 'tmpdir' +require 'open3' + +describe Solargraph::Shell do + let(:temp_dir) { Dir.mktmpdir } + + before do + File.open(File.join(temp_dir, 'Gemfile'), 'w') do |file| + file.puts "source 'https://rubygems.org'" + file.puts "gem 'solargraph', path: '#{File.expand_path('..', __dir__)}'" + end + output, status = Open3.capture2e('bundle install', chdir: temp_dir) + raise "Failure installing bundle: #{output}" unless status.success? + end + + def bundle_exec(*cmd) + # run the command in the temporary directory with bundle exec + output, status = Open3.capture2e("bundle exec #{cmd.join(' ')}", chdir: temp_dir) + expect(status.success?).to be(true), "Command failed: #{output}" + output + end + + after do + # remove the temporary directory after the tests + FileUtils.rm_rf(temp_dir) + end + + describe '--version' do + it 'returns a version when run' do + output = bundle_exec('solargraph', '--version') + + expect(output).not_to be_empty + expect(output).to eq("#{Solargraph::VERSION}\n") + end + end + + describe 'uncache' do + it 'uncaches without erroring out' do + output = bundle_exec('solargraph', 'uncache', 'solargraph') + + expect(output).to include('Clearing pin cache in') + end + end + + describe 'cache' do + it 'caches without erroring out' do + output = bundle_exec('solargraph', 'gem', 'solargraph') + + expect(output).to include('Caching these gems') + end + end +end From 9282223bd03cdfc983b99217c807eec33ab178ee Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 19 Jul 2025 09:05:35 -0400 Subject: [PATCH 244/561] Address RuboCop issues --- .rubocop.yml | 4 ++++ spec/shell_spec.rb | 9 +++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index b8079965e..8ffc4d31c 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -34,6 +34,10 @@ Metrics/ParameterLists: Max: 7 CountKeywordArgs: false +# we don't use the spec/solargraph directory, instead keeping things +# directly under spec. +RSpec/SpecFilePathFormat: + Enabled: false # # Set a relaxed standard on harder-to-address item for ease of diff --git a/spec/shell_spec.rb b/spec/shell_spec.rb index 248cf2396..dc85a7413 100644 --- a/spec/shell_spec.rb +++ b/spec/shell_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'tmpdir' require 'open3' @@ -26,10 +28,13 @@ def bundle_exec(*cmd) end describe '--version' do - it 'returns a version when run' do - output = bundle_exec('solargraph', '--version') + let(:output) { bundle_exec('solargraph', '--version') } + it 'returns output' do expect(output).not_to be_empty + end + + it 'returns a version when run' do expect(output).to eq("#{Solargraph::VERSION}\n") end end From fe87a7900ba1cd3fb21fa1545c15037cdae3538a Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 19 Jul 2025 09:31:55 -0400 Subject: [PATCH 245/561] Drop outdated tests --- lib/solargraph/workspace/gemspecs.rb | 8 -------- spec/workspace_spec.rb | 13 ------------- 2 files changed, 21 deletions(-) diff --git a/lib/solargraph/workspace/gemspecs.rb b/lib/solargraph/workspace/gemspecs.rb index 40975e7c6..1456a6a2c 100644 --- a/lib/solargraph/workspace/gemspecs.rb +++ b/lib/solargraph/workspace/gemspecs.rb @@ -128,14 +128,6 @@ def query_external_bundle command, &block end end - # True if the workspace has a root Gemfile. - # - # @todo Handle projects with custom Bundler/Gemfile setups (see DocMap#gemspecs_required_from_bundler) - # - def gemfile? - directory && File.file?(File.join(directory, 'Gemfile')) - end - def in_this_bundle? directory && Bundler.definition&.lockfile&.to_s&.start_with?(directory) # rubocop:disable Style/SafeNavigationChainLength end diff --git a/spec/workspace_spec.rb b/spec/workspace_spec.rb index 3d4e763a4..d308e3be2 100644 --- a/spec/workspace_spec.rb +++ b/spec/workspace_spec.rb @@ -68,13 +68,6 @@ }.not_to raise_error end - xit "detects gemspecs in workspaces" do - gemspec_file = File.join(dir_path, 'test.gemspec') - File.write(gemspec_file, '') - expect(workspace.gemspec?).to be(true) - expect(workspace.gemspecs).to eq([gemspec_file]) - end - it "generates default require path" do expect(workspace.require_paths).to eq([File.join(dir_path, 'lib')]) end @@ -127,12 +120,6 @@ expect(workspace.require_paths).to eq(['spec/fixtures/workspace/lib', 'spec/fixtures/workspace/ext']) end - xit 'ignores gemspecs in excluded directories' do - # vendor/**/* is excluded by default - workspace = Solargraph::Workspace.new('spec/fixtures/vendored') - expect(workspace.gemspecs).to be_empty - end - it 'rescues errors loading files into sources' do config = double(:Config, directory: './path', calculated: ['./path/does_not_exist.rb'], max_files: 5000, require_paths: [], plugins: []) expect { From 033a4a5ae741b1c34ad77b1b4d6f1cb4de996c90 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 19 Jul 2025 09:35:44 -0400 Subject: [PATCH 246/561] Address TODOs --- lib/solargraph/doc_map.rb | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/lib/solargraph/doc_map.rb b/lib/solargraph/doc_map.rb index e222752f9..a97a88ef7 100644 --- a/lib/solargraph/doc_map.rb +++ b/lib/solargraph/doc_map.rb @@ -39,12 +39,6 @@ def initialize(requires, workspace) pins.concat global_environ.pins end - # TODO: Does this need to be public? - # @return [Solargraph::PinCache] - def pin_cache - @pin_cache ||= workspace.fresh_pincache - end - # @return [Array] def yard_plugins global_environ.yard_plugins @@ -111,6 +105,11 @@ def cache(gemspec, rebuild: false, only_if_used: false, out: nil) private + # @return [Solargraph::PinCache] + def pin_cache + @pin_cache ||= workspace.fresh_pincache + end + # @param out [IO, nil] # @return [void] def load_serialized_gem_pins(out: $stderr) @@ -127,7 +126,7 @@ def load_serialized_gem_pins(out: $stderr) # this will load from disk if needed; no need to manage # uncached_gemspecs to trigger that later stdlib_name_guess = path.split('/').first - # TODO: this results in pins being generated in real time, not in advance with solargrpah gems + # @todo this results in pins being generated in real time, not in advance with solargrpah gems rbs_pins = pin_cache.cache_stdlib_rbs_map stdlib_name_guess if stdlib_name_guess @pins.concat rbs_pins if rbs_pins end From 5494372fff81a3e8bd63743ad70a433b6e836e95 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 19 Jul 2025 10:48:24 -0400 Subject: [PATCH 247/561] Fix linting --- lib/solargraph/api_map.rb | 32 ++++++++++++------------- lib/solargraph/doc_map.rb | 26 ++++++++++---------- lib/solargraph/library.rb | 3 +-- lib/solargraph/pin_cache.rb | 29 +++++++++++----------- lib/solargraph/workspace.rb | 4 +--- spec/type_checker/levels/alpha_spec.rb | 25 +++++++++++++++++++ spec/type_checker/levels/normal_spec.rb | 2 +- spec/type_checker/levels/strict_spec.rb | 2 +- spec/type_checker/levels/strong_spec.rb | 2 +- spec/type_checker/levels/typed_spec.rb | 4 ++-- spec/workspace/gemspecs_spec.rb | 19 +++++++++++++++ 11 files changed, 94 insertions(+), 54 deletions(-) create mode 100644 spec/type_checker/levels/alpha_spec.rb create mode 100644 spec/workspace/gemspecs_spec.rb diff --git a/lib/solargraph/api_map.rb b/lib/solargraph/api_map.rb index 592d942d1..d3f4f6827 100755 --- a/lib/solargraph/api_map.rb +++ b/lib/solargraph/api_map.rb @@ -40,13 +40,13 @@ def initialize pins: [] # # @param other [Object] - def eql?(other) + def eql? other self.class == other.class && equality_fields == other.equality_fields end # @param other [Object] - def ==(other) + def == other self.eql?(other) end @@ -184,14 +184,14 @@ def self.load directory # @param out [IO, nil] # @return [void] - def cache_all_for_doc_map!(out) + def cache_all_for_doc_map! out @doc_map&.cache_doc_map_gems!(out) end # @param out [IO, nil] # @param rebuild [Boolean] # @return [void] - def cache_all_for_workspace!(out, rebuild: false) + def cache_all_for_workspace! out, rebuild: false workspace.cache_all_for_workspace!(out, rebuild: rebuild) end @@ -204,7 +204,7 @@ def workspace # @param version [String, nil] # # @return [Gem::Specification, nil] - def find_gem(name, version = nil) + def find_gem name, version = nil gemspecs.find_gem(name, version) end @@ -213,7 +213,7 @@ def find_gem(name, version = nil) # @param only_if_used [Boolean] # @param out [IO, nil] # @return [void] - def cache_gem(gemspec, rebuild: false, only_if_used: false, out: nil) + def cache_gem gemspec, rebuild: false, only_if_used: false, out: nil @doc_map&.cache(gemspec, rebuild: rebuild, out: out, only_if_used: only_if_used) end @@ -344,7 +344,7 @@ def qualify tag, context_tag = '' # @param context_namespace [String] The context namespace in which the # tag was referenced; start from here to resolve the name # @return [String, nil] fully qualified namespace - def qualify_namespace(namespace, context_namespace = '') + def qualify_namespace namespace, context_namespace = '' cached = cache.get_qualified_namespace(namespace, context_namespace) return cached.clone unless cached.nil? result = if namespace.start_with?('::') @@ -362,7 +362,7 @@ def qualify_namespace(namespace, context_namespace = '') # @param namespace [String] A fully qualified namespace # @param scope [Symbol] :instance or :class # @return [Array] - def get_instance_variable_pins(namespace, scope = :instance) + def get_instance_variable_pins namespace, scope = :instance result = [] used = [namespace] result.concat store.get_instance_variables(namespace, scope) @@ -387,7 +387,7 @@ def visible_pins(*args, **kwargs, &blk) # # @param namespace [String] A fully qualified namespace # @return [Enumerable] - def get_class_variable_pins(namespace) + def get_class_variable_pins namespace prefer_non_nil_variables(store.get_class_variables(namespace)) end @@ -657,7 +657,7 @@ def bundled? filename # @param sup [String] The superclass # @param sub [String] The subclass # @return [Boolean] - def super_and_sub?(sup, sub) + def super_and_sub? sup, sub fqsup = qualify(sup) cls = qualify(sub) tested = [] @@ -676,7 +676,7 @@ def super_and_sub?(sup, sub) # @param module_ns [String] The module namespace (no type parameters) # # @return [Boolean] - def type_include?(host_ns, module_ns) + def type_include? host_ns, module_ns store.get_includes(host_ns).map { |inc_tag| ComplexType.parse(inc_tag).name }.include?(module_ns) end @@ -781,7 +781,7 @@ def inner_get_methods rooted_tag, scope, visibility, deep, skip, no_core = false # @param skip [Set] # @param no_core [Boolean] Skip core classes if true # @return [Array] - def inner_get_methods_from_reference(fq_reference_tag, namespace_pin, type, scope, visibility, deep, skip, no_core) + def inner_get_methods_from_reference fq_reference_tag, namespace_pin, type, scope, visibility, deep, skip, no_core # logger.debug { "ApiMap#add_methods_from_reference(type=#{type}) starting" } # Ensure the types returned by the methods in the referenced @@ -973,7 +973,7 @@ def resolve_method_alias pin # @param rooted_type [ComplexType] # @param pins [Enumerable] # @return [Array] - def erase_generics(namespace_pin, rooted_type, pins) + def erase_generics namespace_pin, rooted_type, pins return pins unless should_erase_generics_when_done?(namespace_pin, rooted_type) logger.debug("Erasing generics on namespace_pin=#{namespace_pin} / rooted_type=#{rooted_type}") @@ -984,18 +984,18 @@ def erase_generics(namespace_pin, rooted_type, pins) # @param namespace_pin [Pin::Namespace] # @param rooted_type [ComplexType] - def should_erase_generics_when_done?(namespace_pin, rooted_type) + def should_erase_generics_when_done? namespace_pin, rooted_type has_generics?(namespace_pin) && !can_resolve_generics?(namespace_pin, rooted_type) end # @param namespace_pin [Pin::Namespace] - def has_generics?(namespace_pin) + def has_generics? namespace_pin namespace_pin && !namespace_pin.generics.empty? end # @param namespace_pin [Pin::Namespace] # @param rooted_type [ComplexType] - def can_resolve_generics?(namespace_pin, rooted_type) + def can_resolve_generics? namespace_pin, rooted_type has_generics?(namespace_pin) && !rooted_type.all_params.empty? end end diff --git a/lib/solargraph/doc_map.rb b/lib/solargraph/doc_map.rb index a97a88ef7..3d23ef2b3 100644 --- a/lib/solargraph/doc_map.rb +++ b/lib/solargraph/doc_map.rb @@ -31,7 +31,7 @@ def uncached_gemspecs # @param requires [Array] # @param workspace [Workspace] - def initialize(requires, workspace) + def initialize requires, workspace @requires = requires.compact @workspace = workspace @global_environ = Convention.for_global(self) @@ -39,6 +39,11 @@ def initialize(requires, workspace) pins.concat global_environ.pins end + # @return [Solargraph::PinCache] + def pin_cache + @pin_cache ||= workspace.fresh_pincache + end + # @return [Array] def yard_plugins global_environ.yard_plugins @@ -51,14 +56,14 @@ def any_uncached? # Cache all pins needed for the sources in this doc_map # @param out [IO, nil] output stream for logging # @return [void] - def cache_doc_map_gems!(out) + def cache_doc_map_gems! out # if we log at debug level: if logger.info? gem_desc = uncached_gemspecs.map { |gemspec| "#{gemspec.name}:#{gemspec.version}" }.join(', ') logger.info "Caching pins for gems: #{gem_desc}" unless uncached_gemspecs.empty? end logger.debug { "Caching: #{uncached_gemspecs.map(&:name)}" } - PinCache.cache_core unless PinCache.has_core? + PinCache.cache_core unless PinCache.core? load_serialized_gem_pins time = Benchmark.measure do uncached_gemspecs.each do |gemspec| @@ -77,11 +82,6 @@ def unresolved_requires @unresolved_requires ||= required_gems_map.select { |_, gemspecs| gemspecs.nil? }.keys end - # @return [Array] - def gemspecs - @gemspecs ||= required_gems_map.values.compact.flatten - end - # @return [Set] def dependencies @dependencies ||= (gemspecs.flat_map { |spec| workspace.fetch_dependencies(spec) } - gemspecs).to_set @@ -95,7 +95,7 @@ def dependencies # @param out [IO, nil] output stream for logging # # @return [void] - def cache(gemspec, rebuild: false, only_if_used: false, out: nil) + def cache gemspec, rebuild: false, only_if_used: false, out: nil return if only_if_used && !uncached_gemspecs.include?(gemspec) pin_cache.cache_gem(gemspec: gemspec, @@ -105,14 +105,14 @@ def cache(gemspec, rebuild: false, only_if_used: false, out: nil) private - # @return [Solargraph::PinCache] - def pin_cache - @pin_cache ||= workspace.fresh_pincache + # @return [Array] + def gemspecs + @gemspecs ||= required_gems_map.values.compact.flatten end # @param out [IO, nil] # @return [void] - def load_serialized_gem_pins(out: $stderr) + def load_serialized_gem_pins out: $stderr @pins = [] with_gemspecs, without_gemspecs = required_gems_map.partition { |_, v| v } # @sg-ignore Need support for RBS duck interfaces like _ToHash diff --git a/lib/solargraph/library.rb b/lib/solargraph/library.rb index 889f6d529..85a74f4e5 100644 --- a/lib/solargraph/library.rb +++ b/lib/solargraph/library.rb @@ -514,7 +514,7 @@ def external_requires # @return [PinCache] def pin_cache - api_map.pin_cache + workspace.pin_cache end # @return [Hash{String => Set}] @@ -602,7 +602,6 @@ def cache_next_gemspec pending = api_map.uncached_gemspecs.length - cache_errors.length - 1 - # TODO: Make this 'cache_processing?' to api_map and make api_map.pin_cache private if pin_cache.yardoc_processing?(spec) # @sg-ignore Unresolved call to name logger.info "Enqueuing cache of #{spec.name} #{spec.version} (already being processed)" diff --git a/lib/solargraph/pin_cache.rb b/lib/solargraph/pin_cache.rb index 322717619..14da097e4 100644 --- a/lib/solargraph/pin_cache.rb +++ b/lib/solargraph/pin_cache.rb @@ -21,12 +21,11 @@ def initialize(rbs_collection_path:, rbs_collection_config_path:, @yard_plugins = yard_plugins end - # TODO: Get Solargraph to tell me that the one below is unneeded # @sg-ignore # @param gemspec [Gem::Specification, Bundler::LazySpecification] def cached?(gemspec) rbs_version_cache_key = lookup_rbs_version_cache_key(gemspec) - has_combined_gem?(gemspec, rbs_version_cache_key) + combined_gem?(gemspec, rbs_version_cache_key) end # @param gemspec [Gem::Specification, Bundler::LazySpecification] @@ -141,9 +140,9 @@ def calculate_build_needs(gemspec, rebuild:, rbs_version_cache_key:) build_rbs_collection = true build_combined = true else - build_yard = !has_yard_gem?(gemspec) - build_rbs_collection = !has_rbs_collection_pins?(gemspec, rbs_version_cache_key) - build_combined = !has_combined_gem?(gemspec, rbs_version_cache_key) || build_yard || build_rbs_collection + build_yard = !yard_gem?(gemspec) + build_rbs_collection = !rbs_collection_pins?(gemspec, rbs_version_cache_key) + build_combined = !combined_gem?(gemspec, rbs_version_cache_key) || build_yard || build_rbs_collection end build_yard = false if suppress_yard_cache?(gemspec, rbs_version_cache_key) @@ -203,7 +202,7 @@ def log_cache_info(gemspec, just_yard = build_yard && rbs_source_desc.nil? type << 'combined' if build_combined && !just_yard - out.puts("Caching #{type.join(' and ')} pins for gem #{gemspec.name}:#{gemspec.version}") if out + out&.puts("Caching #{type.join(' and ')} pins for gem #{gemspec.name}:#{gemspec.version}") end # @param gemspec [Gem::Specification, Bundler::LazySpecification] @@ -325,13 +324,13 @@ def serialize_yard_gem(gemspec, pins) # @param gemspec [Gem::Specification] # @return [Boolean] - def has_yard_gem?(gemspec) + def yard_gem?(gemspec) exist?(yard_gem_path(gemspec)) end # @param gemspec [Gem::Specification] # @return [Boolean] - def has_yardoc?(gemspec) + def yardoc?(gemspec) exist?(yardoc_path(gemspec)) end @@ -387,7 +386,7 @@ def serialize_combined_gem(gemspec, hash, pins) # @param gemspec [Gem::Specification, Bundler::LazySpecification] # @param hash [String] - def has_combined_gem?(gemspec, hash) + def combined_gem?(gemspec, hash) exist?(combined_path(gemspec, hash)) end @@ -400,7 +399,7 @@ def load_combined_gem gemspec, hash # @param gemspec [Gem::Specification] # @param hash [String] - def has_rbs_collection_pins?(gemspec, hash) + def rbs_collection_pins?(gemspec, hash) exist?(rbs_collection_pins_path(gemspec, hash)) end @@ -416,11 +415,11 @@ def exist? *path def uncache_by_prefix *path_segments, out: nil path = File.join(*path_segments) glob = "#{path}*" - out.puts "Clearing pin cache in #{glob}" unless out.nil? + out&.puts "Clearing pin cache in #{glob}" Dir.glob(glob).each do |file| next unless File.file?(file) FileUtils.rm_rf file, secure: true - out.puts "Clearing pin cache in #{file}" unless out.nil? + out&.puts "Clearing pin cache in #{file}" end end @@ -464,9 +463,9 @@ def uncache *path_segments, out: nil path = File.join(*path_segments) if File.exist?(path) FileUtils.rm_rf path, secure: true - out.puts "Clearing pin cache in #{path}" unless out.nil? + out&.puts "Clearing pin cache in #{path}" else - out.puts "Pin cache file #{path} does not exist" unless out.nil? + out&.puts "Pin cache file #{path} does not exist" end end @@ -523,7 +522,7 @@ def save file, pins logger.debug { "Cache#save: Saved #{pins.length} pins to #{file}" } end - def has_core? + def core? File.file?(core_path) end diff --git a/lib/solargraph/workspace.rb b/lib/solargraph/workspace.rb index ab78253b5..070f0f6d5 100644 --- a/lib/solargraph/workspace.rb +++ b/lib/solargraph/workspace.rb @@ -188,9 +188,7 @@ def rbs_collection_config_path # @param rebuild [Boolean] whether to rebuild the pins even if they are cached # @return [void] def cache_all_for_workspace! out, rebuild: false - PinCache.cache_core(out: $stdout) unless PinCache.has_core? - # TODO: This should bring in dependencies as well - # + PinCache.cache_core(out: $stdout) unless PinCache.core? # @type [Array] specs = gemspecs.all_gemspecs_from_bundle specs.each do |spec| diff --git a/spec/type_checker/levels/alpha_spec.rb b/spec/type_checker/levels/alpha_spec.rb new file mode 100644 index 000000000..ca2ae8a3d --- /dev/null +++ b/spec/type_checker/levels/alpha_spec.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +describe Solargraph::TypeChecker do + context 'when checking at alpha level' do + def type_checker code + Solargraph::TypeChecker.load_string(code, 'test.rb', :alpha) + end + + it 'reports nilable type issues' do + checker = type_checker(%( + # @param a [String] + # @return [void] + def foo(a); end + + # @param b [String, nil] + # @return [void] + def bar(b) + foo(b) + end + )) + expect(checker.problems.map(&:message)) + .to eq(['Wrong argument type for #foo: a expected String, received String, nil']) + end + end +end diff --git a/spec/type_checker/levels/normal_spec.rb b/spec/type_checker/levels/normal_spec.rb index 0f9607a75..0b3024f62 100644 --- a/spec/type_checker/levels/normal_spec.rb +++ b/spec/type_checker/levels/normal_spec.rb @@ -1,5 +1,5 @@ describe Solargraph::TypeChecker do - context 'normal level' do + context 'when checking at normal level' do def type_checker(code) Solargraph::TypeChecker.load_string(code, 'test.rb', :normal) end diff --git a/spec/type_checker/levels/strict_spec.rb b/spec/type_checker/levels/strict_spec.rb index b198cec89..70fb0797c 100644 --- a/spec/type_checker/levels/strict_spec.rb +++ b/spec/type_checker/levels/strict_spec.rb @@ -1,5 +1,5 @@ describe Solargraph::TypeChecker do - context 'strict level' do + context 'when checking at strict level' do # @return [Solargraph::TypeChecker] def type_checker(code) Solargraph::TypeChecker.load_string(code, 'test.rb', :strict) diff --git a/spec/type_checker/levels/strong_spec.rb b/spec/type_checker/levels/strong_spec.rb index 12db1e442..c1c78dfe2 100644 --- a/spec/type_checker/levels/strong_spec.rb +++ b/spec/type_checker/levels/strong_spec.rb @@ -1,5 +1,5 @@ describe Solargraph::TypeChecker do - context 'strong level' do + context 'when tracking at strong level' do def type_checker(code) Solargraph::TypeChecker.load_string(code, 'test.rb', :strong) end diff --git a/spec/type_checker/levels/typed_spec.rb b/spec/type_checker/levels/typed_spec.rb index b10bbd42c..3f84867da 100644 --- a/spec/type_checker/levels/typed_spec.rb +++ b/spec/type_checker/levels/typed_spec.rb @@ -1,6 +1,6 @@ describe Solargraph::TypeChecker do - context 'typed level' do - def type_checker(code) + context 'when checking at typed level' do + def type_checker code Solargraph::TypeChecker.load_string(code, 'test.rb', :typed) end diff --git a/spec/workspace/gemspecs_spec.rb b/spec/workspace/gemspecs_spec.rb new file mode 100644 index 000000000..00825686c --- /dev/null +++ b/spec/workspace/gemspecs_spec.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require 'fileutils' +require 'tmpdir' + +describe Solargraph::Workspace::Gemspecs do + let(:workspace) { Solargraph::Workspace.new(dir_path) } + let(:dir_path) { File.realpath(Dir.mktmpdir) } + let(:file_path) { File.join(dir_path, 'file.rb') } + + before { File.write(file_path, 'exit') } + after { FileUtils.remove_entry(dir_path) } + + it 'ignores gemspecs in excluded directories' do + # vendor/**/* is excluded by default + workspace = Solargraph::Workspace.new('spec/fixtures/vendored') + expect(workspace.gemspecs).to be_empty + end +end From 83d1174413d264915daeb38e67867fd523def880 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 19 Jul 2025 11:09:14 -0400 Subject: [PATCH 248/561] Alter RSpec/ExampleLength config as well --- .rubocop.yml | 2 ++ .rubocop_todo.yml | 48 +---------------------------------------------- 2 files changed, 3 insertions(+), 47 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index b8079965e..e51b2f834 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -50,6 +50,8 @@ Metrics/CyclomaticComplexity: Max: 23 Metrics/PerceivedComplexity: Max: 29 +RSpec/ExampleLength: + Max: 17 plugins: - rubocop-rspec diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index eeeaed98a..32b259f2f 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1145,82 +1145,36 @@ RSpec/EmptyLineAfterFinalLet: Exclude: - 'spec/workspace/config_spec.rb' -# Offense count: 941 +# Offense count: 89 # Configuration parameters: Max, CountAsOne. RSpec/ExampleLength: Exclude: - - 'spec/api_map/config_spec.rb' - - 'spec/api_map/source_to_yard_spec.rb' - - 'spec/api_map/store_spec.rb' - 'spec/api_map_spec.rb' - 'spec/complex_type_spec.rb' - - 'spec/convention/struct_definition_spec.rb' - - 'spec/diagnostics/rubocop_spec.rb' - - 'spec/diagnostics/type_check_spec.rb' - - 'spec/diagnostics/update_errors_spec.rb' - - 'spec/doc_map_spec.rb' - - 'spec/gem_pins_spec.rb' - - 'spec/language_server/host/dispatch_spec.rb' - - 'spec/language_server/host/message_worker_spec.rb' - 'spec/language_server/host_spec.rb' - - 'spec/language_server/message/completion_item/resolve_spec.rb' - - 'spec/language_server/message/extended/check_gem_version_spec.rb' - 'spec/language_server/message/initialize_spec.rb' - 'spec/language_server/message/text_document/definition_spec.rb' - - 'spec/language_server/message/text_document/formatting_spec.rb' - 'spec/language_server/message/text_document/hover_spec.rb' - 'spec/language_server/message/text_document/rename_spec.rb' - 'spec/language_server/message/text_document/type_definition_spec.rb' - - 'spec/language_server/message/workspace/did_change_configuration_spec.rb' - 'spec/language_server/message/workspace/did_change_watched_files_spec.rb' - 'spec/language_server/protocol_spec.rb' - - 'spec/language_server/transport/adapter_spec.rb' - - 'spec/language_server/transport/data_reader_spec.rb' - 'spec/library_spec.rb' - - 'spec/logging_spec.rb' - 'spec/parser/flow_sensitive_typing_spec.rb' - - 'spec/parser/node_chainer_spec.rb' - 'spec/parser/node_methods_spec.rb' - 'spec/parser/node_processor_spec.rb' - - 'spec/pin/base_spec.rb' - 'spec/pin/base_variable_spec.rb' - - 'spec/pin/constant_spec.rb' - 'spec/pin/delegated_method_spec.rb' - 'spec/pin/local_variable_spec.rb' - - 'spec/pin/method_spec.rb' - - 'spec/pin/namespace_spec.rb' - 'spec/pin/parameter_spec.rb' - - 'spec/pin/search_spec.rb' - - 'spec/rbs_map/core_map_spec.rb' - - 'spec/rbs_map/stdlib_map_spec.rb' - - 'spec/rbs_map_spec.rb' - 'spec/source/chain/call_spec.rb' - - 'spec/source/chain/class_variable_spec.rb' - - 'spec/source/chain/constant_spec.rb' - - 'spec/source/chain/global_variable_spec.rb' - - 'spec/source/chain/head_spec.rb' - - 'spec/source/chain/instance_variable_spec.rb' - - 'spec/source/chain/z_super_spec.rb' - 'spec/source/chain_spec.rb' - - 'spec/source/change_spec.rb' - - 'spec/source/cursor_spec.rb' - - 'spec/source/source_chainer_spec.rb' - - 'spec/source/updater_spec.rb' - 'spec/source_map/clip_spec.rb' - 'spec/source_map/mapper_spec.rb' - - 'spec/source_map/node_processor_spec.rb' - 'spec/source_map_spec.rb' - 'spec/source_spec.rb' - - 'spec/type_checker/checks_spec.rb' - - 'spec/type_checker/levels/normal_spec.rb' - 'spec/type_checker/levels/strict_spec.rb' - - 'spec/type_checker/levels/strong_spec.rb' - - 'spec/type_checker/levels/typed_spec.rb' - - 'spec/type_checker/rules_spec.rb' - 'spec/type_checker_spec.rb' - - 'spec/workspace_spec.rb' - - 'spec/yard_map/mapper/to_method_spec.rb' - - 'spec/yard_map/mapper_spec.rb' # Offense count: 10 # This cop supports safe autocorrection (--autocorrect). From f9370a0b71527777f180eeeafd8a6fea9c3d9515 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 19 Jul 2025 11:23:33 -0400 Subject: [PATCH 249/561] Fix overcommit config --- .overcommit.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.overcommit.yml b/.overcommit.yml index 40747ce51..c15ded82f 100644 --- a/.overcommit.yml +++ b/.overcommit.yml @@ -22,6 +22,8 @@ PreCommit: Solargraph: enabled: true + exclude: + - 'spec/**/*' FixMe: enabled: true @@ -44,7 +46,6 @@ PreCommit: problem_on_unmodified_line: fail on_warn: fail exclude: - - 'spec/**/*' - lib/solargraph/rails/annotations/**/* - vendor/**/* - ".bundle/**/*" @@ -54,6 +55,8 @@ PrePush: RSpec: enabled: true +verify_signatures: false + # # TrailingWhitespace: # enabled: true From cc63985319a2f2ae21c5d848b7db3e0dab9548c1 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 19 Jul 2025 11:40:09 -0400 Subject: [PATCH 250/561] Ratchet RuboCop todo file --- .rubocop_todo.yml | 188 ++++++---------------------------------------- 1 file changed, 23 insertions(+), 165 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 32b259f2f..b4f40f056 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -115,23 +115,21 @@ Layout/ElseAlignment: - 'lib/solargraph/type_checker/rules.rb' - 'lib/solargraph/yard_map/mapper.rb' -# Offense count: 3 +# Offense count: 2 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EmptyLineBetweenMethodDefs, EmptyLineBetweenClassDefs, EmptyLineBetweenModuleDefs, DefLikeMacros, AllowAdjacentOneLineDefs, NumberOfEmptyLines. Layout/EmptyLineBetweenDefs: Exclude: - - 'lib/solargraph/doc_map.rb' - 'lib/solargraph/language_server/message/initialize.rb' - 'lib/solargraph/pin/delegated_method.rb' -# Offense count: 13 +# Offense count: 12 # This cop supports safe autocorrection (--autocorrect). Layout/EmptyLines: Exclude: - 'lib/solargraph/bench.rb' - 'lib/solargraph/complex_type/unique_type.rb' - 'lib/solargraph/convention.rb' - - 'lib/solargraph/doc_map.rb' - 'lib/solargraph/language_server/message/extended/check_gem_version.rb' - 'lib/solargraph/language_server/message/initialize.rb' - 'lib/solargraph/pin/delegated_method.rb' @@ -332,13 +330,12 @@ Layout/MultilineMethodCallIndentation: - 'lib/solargraph/pin/search.rb' - 'lib/solargraph/type_checker.rb' -# Offense count: 6 +# Offense count: 3 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, IndentationWidth. # SupportedStyles: aligned, indented Layout/MultilineOperationIndentation: Exclude: - - 'lib/solargraph/api_map.rb' - 'lib/solargraph/language_server/host/dispatch.rb' - 'lib/solargraph/source.rb' @@ -367,7 +364,7 @@ Layout/SpaceAroundKeyword: Exclude: - 'spec/rbs_map/conversions_spec.rb' -# Offense count: 28 +# Offense count: 27 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowForAlignment, EnforcedStyleForExponentOperator, EnforcedStyleForRationalLiterals. # SupportedStylesForExponentOperator: space, no_space @@ -384,7 +381,6 @@ Layout/SpaceAroundOperators: - 'lib/solargraph/source_map/clip.rb' - 'lib/solargraph/workspace/config.rb' - 'spec/library_spec.rb' - - 'spec/yard_map/mapper_spec.rb' # Offense count: 105 # This cop supports safe autocorrection (--autocorrect). @@ -576,7 +572,7 @@ Lint/DuplicateBranch: - 'lib/solargraph/pin/base.rb' - 'lib/solargraph/rbs_map/conversions.rb' -# Offense count: 9 +# Offense count: 7 Lint/DuplicateMethods: Exclude: - 'lib/solargraph/complex_type.rb' @@ -584,8 +580,6 @@ Lint/DuplicateMethods: - 'lib/solargraph/pin/base.rb' - 'lib/solargraph/pin/common.rb' - 'lib/solargraph/pin/signature.rb' - - 'lib/solargraph/rbs_map.rb' - - 'lib/solargraph/rbs_map/core_map.rb' - 'lib/solargraph/source/chain/link.rb' # Offense count: 1 @@ -717,7 +711,7 @@ Lint/UnusedBlockArgument: - 'lib/solargraph/logging.rb' - 'spec/language_server/transport/data_reader_spec.rb' -# Offense count: 38 +# Offense count: 36 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AutoCorrect, AllowUnusedKeywordArguments, IgnoreEmptyMethods, IgnoreNotImplementedMethods, NotImplementedExceptions. # NotImplementedExceptions: NotImplementedError @@ -728,7 +722,6 @@ Lint/UnusedMethodArgument: - 'lib/solargraph/convention/base.rb' - 'lib/solargraph/diagnostics/base.rb' - 'lib/solargraph/diagnostics/update_errors.rb' - - 'lib/solargraph/doc_map.rb' - 'lib/solargraph/pin/namespace.rb' - 'lib/solargraph/rbs_map/conversions.rb' - 'lib/solargraph/source.rb' @@ -795,14 +788,13 @@ Lint/UselessMethodDefinition: Exclude: - 'lib/solargraph/pin/signature.rb' -# Offense count: 22 +# Offense count: 21 # Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes, Max. Metrics/AbcSize: Exclude: - 'lib/solargraph/api_map.rb' - 'lib/solargraph/api_map/source_to_yard.rb' - 'lib/solargraph/complex_type.rb' - - 'lib/solargraph/doc_map.rb' - 'lib/solargraph/language_server/host.rb' - 'lib/solargraph/language_server/message/initialize.rb' - 'lib/solargraph/library.rb' @@ -872,13 +864,12 @@ Metrics/MethodLength: - 'lib/solargraph/source_map/mapper.rb' - 'lib/solargraph/type_checker.rb' -# Offense count: 4 +# Offense count: 3 # Configuration parameters: CountComments, Max, CountAsOne. Metrics/ModuleLength: Exclude: - 'lib/solargraph/complex_type/type_methods.rb' - 'lib/solargraph/parser/parser_gem/node_methods.rb' - - 'lib/solargraph/pin_cache.rb' # Offense count: 6 # Configuration parameters: Max, CountKeywordArgs, MaxOptionalParameters. @@ -922,7 +913,7 @@ Naming/HeredocDelimiterNaming: Exclude: - 'spec/yard_map/mapper/to_method_spec.rb' -# Offense count: 9 +# Offense count: 7 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: EnforcedStyleForLeadingUnderscores. # SupportedStylesForLeadingUnderscores: disallowed, required, optional @@ -932,7 +923,6 @@ Naming/MemoizedInstanceVariableName: - 'lib/solargraph/convention/gemfile.rb' - 'lib/solargraph/convention/gemspec.rb' - 'lib/solargraph/convention/rakefile.rb' - - 'lib/solargraph/doc_map.rb' - 'lib/solargraph/rbs_map.rb' - 'lib/solargraph/workspace.rb' @@ -948,7 +938,7 @@ Naming/MethodParameterName: - 'lib/solargraph/yard_map/mapper/to_method.rb' - 'lib/solargraph/yard_map/to_method.rb' -# Offense count: 14 +# Offense count: 13 # Configuration parameters: Mode, AllowedMethods, AllowedPatterns, AllowBangMethods, WaywardPredicates. # AllowedMethods: call # WaywardPredicates: nonzero? @@ -957,7 +947,6 @@ Naming/PredicateMethod: - 'lib/solargraph/api_map/store.rb' - 'lib/solargraph/convention/data_definition.rb' - 'lib/solargraph/convention/struct_definition.rb' - - 'lib/solargraph/doc_map.rb' - 'lib/solargraph/language_server/progress.rb' - 'lib/solargraph/library.rb' - 'lib/solargraph/parser/node_processor/base.rb' @@ -966,7 +955,7 @@ Naming/PredicateMethod: - 'lib/solargraph/pin/local_variable.rb' - 'lib/solargraph/workspace.rb' -# Offense count: 6 +# Offense count: 4 # Configuration parameters: NamePrefix, ForbiddenPrefixes, AllowedMethods, MethodDefinitionMacros, UseSorbetSigs. # NamePrefix: is_, has_, have_, does_ # ForbiddenPrefixes: is_, has_, have_, does_ @@ -978,7 +967,6 @@ Naming/PredicatePrefix: - 'lib/solargraph/api_map.rb' - 'lib/solargraph/language_server/host.rb' - 'lib/solargraph/parser/parser_gem/class_methods.rb' - - 'lib/solargraph/pin_cache.rb' - 'lib/solargraph/workspace.rb' # Offense count: 1 @@ -1017,18 +1005,17 @@ RSpec/BeNil: - 'spec/api_map/source_to_yard_spec.rb' - 'spec/language_server/host_spec.rb' -# Offense count: 5 +# Offense count: 4 RSpec/BeforeAfterAll: Exclude: - '**/spec/spec_helper.rb' - '**/spec/rails_helper.rb' - '**/spec/support/**/*.rb' - 'spec/api_map_spec.rb' - - 'spec/doc_map_spec.rb' - 'spec/language_server/host/dispatch_spec.rb' - 'spec/language_server/protocol_spec.rb' -# Offense count: 25 +# Offense count: 21 # Configuration parameters: Prefixes, AllowedPatterns. # Prefixes: when, with, without RSpec/ContextWording: @@ -1038,10 +1025,6 @@ RSpec/ContextWording: - 'spec/pin/method_spec.rb' - 'spec/pin/parameter_spec.rb' - 'spec/pin/symbol_spec.rb' - - 'spec/type_checker/levels/normal_spec.rb' - - 'spec/type_checker/levels/strict_spec.rb' - - 'spec/type_checker/levels/strong_spec.rb' - - 'spec/type_checker/levels/typed_spec.rb' # Offense count: 2 # Configuration parameters: IgnoredMetadata. @@ -1055,7 +1038,7 @@ RSpec/DescribeClass: - 'spec/complex_type_spec.rb' - 'spec/source_map/node_processor_spec.rb' -# Offense count: 412 +# Offense count: 396 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: SkipBlocks, EnforcedStyle, OnlyStaticConstants. # SupportedStyles: described_class, explicit @@ -1073,7 +1056,6 @@ RSpec/DescribedClass: - 'spec/diagnostics/update_errors_spec.rb' - 'spec/diagnostics_spec.rb' - 'spec/doc_map_spec.rb' - - 'spec/gem_pins_spec.rb' - 'spec/language_server/host/diagnoser_spec.rb' - 'spec/language_server/host/dispatch_spec.rb' - 'spec/language_server/host/message_worker_spec.rb' @@ -1263,7 +1245,7 @@ RSpec/MissingExampleGroupArgument: Exclude: - 'spec/diagnostics/rubocop_helpers_spec.rb' -# Offense count: 481 +# Offense count: 480 # Configuration parameters: Max. RSpec/MultipleExpectations: Exclude: @@ -1278,7 +1260,6 @@ RSpec/MultipleExpectations: - 'spec/diagnostics/type_check_spec.rb' - 'spec/diagnostics/update_errors_spec.rb' - 'spec/diagnostics_spec.rb' - - 'spec/doc_map_spec.rb' - 'spec/gem_pins_spec.rb' - 'spec/language_server/host/message_worker_spec.rb' - 'spec/language_server/host_spec.rb' @@ -1422,102 +1403,6 @@ RSpec/ScatteredLet: Exclude: - 'spec/complex_type_spec.rb' -# Offense count: 89 -# Configuration parameters: Include, CustomTransform, IgnoreMethods, IgnoreMetadata. -# Include: **/*_spec.rb -RSpec/SpecFilePathFormat: - Exclude: - - '**/spec/routing/**/*' - - 'spec/api_map/cache_spec.rb' - - 'spec/api_map/config_spec.rb' - - 'spec/api_map/source_to_yard_spec.rb' - - 'spec/api_map/store_spec.rb' - - 'spec/api_map_spec.rb' - - 'spec/convention/struct_definition_spec.rb' - - 'spec/convention_spec.rb' - - 'spec/diagnostics/base_spec.rb' - - 'spec/diagnostics/require_not_found_spec.rb' - - 'spec/diagnostics/rubocop_helpers_spec.rb' - - 'spec/diagnostics/rubocop_spec.rb' - - 'spec/diagnostics/type_check_spec.rb' - - 'spec/diagnostics/update_errors_spec.rb' - - 'spec/diagnostics_spec.rb' - - 'spec/doc_map_spec.rb' - - 'spec/gem_pins_spec.rb' - - 'spec/language_server/host/diagnoser_spec.rb' - - 'spec/language_server/host/dispatch_spec.rb' - - 'spec/language_server/host/message_worker_spec.rb' - - 'spec/language_server/host_spec.rb' - - 'spec/language_server/message/completion_item/resolve_spec.rb' - - 'spec/language_server/message/extended/check_gem_version_spec.rb' - - 'spec/language_server/message/initialize_spec.rb' - - 'spec/language_server/message/text_document/definition_spec.rb' - - 'spec/language_server/message/text_document/formatting_spec.rb' - - 'spec/language_server/message/text_document/hover_spec.rb' - - 'spec/language_server/message/text_document/rename_spec.rb' - - 'spec/language_server/message/text_document/type_definition_spec.rb' - - 'spec/language_server/message/workspace/did_change_configuration_spec.rb' - - 'spec/language_server/message/workspace/did_change_watched_files_spec.rb' - - 'spec/language_server/message_spec.rb' - - 'spec/language_server/transport/adapter_spec.rb' - - 'spec/language_server/transport/data_reader_spec.rb' - - 'spec/language_server/uri_helpers_spec.rb' - - 'spec/library_spec.rb' - - 'spec/logging_spec.rb' - - 'spec/parser/flow_sensitive_typing_spec.rb' - - 'spec/parser/node_methods_spec.rb' - - 'spec/parser/node_processor_spec.rb' - - 'spec/parser_spec.rb' - - 'spec/pin/base_spec.rb' - - 'spec/pin/base_variable_spec.rb' - - 'spec/pin/block_spec.rb' - - 'spec/pin/constant_spec.rb' - - 'spec/pin/delegated_method_spec.rb' - - 'spec/pin/documenting_spec.rb' - - 'spec/pin/instance_variable_spec.rb' - - 'spec/pin/keyword_spec.rb' - - 'spec/pin/local_variable_spec.rb' - - 'spec/pin/method_spec.rb' - - 'spec/pin/namespace_spec.rb' - - 'spec/pin/parameter_spec.rb' - - 'spec/pin/search_spec.rb' - - 'spec/pin/symbol_spec.rb' - - 'spec/position_spec.rb' - - 'spec/rbs_map/conversions_spec.rb' - - 'spec/rbs_map/core_map_spec.rb' - - 'spec/rbs_map/stdlib_map_spec.rb' - - 'spec/rbs_map_spec.rb' - - 'spec/source/chain/array_spec.rb' - - 'spec/source/chain/call_spec.rb' - - 'spec/source/chain/class_variable_spec.rb' - - 'spec/source/chain/constant_spec.rb' - - 'spec/source/chain/global_variable_spec.rb' - - 'spec/source/chain/head_spec.rb' - - 'spec/source/chain/instance_variable_spec.rb' - - 'spec/source/chain/link_spec.rb' - - 'spec/source/chain/literal_spec.rb' - - 'spec/source/chain/z_super_spec.rb' - - 'spec/source/chain_spec.rb' - - 'spec/source/change_spec.rb' - - 'spec/source/cursor_spec.rb' - - 'spec/source/source_chainer_spec.rb' - - 'spec/source/updater_spec.rb' - - 'spec/source_map/clip_spec.rb' - - 'spec/source_map/mapper_spec.rb' - - 'spec/source_map_spec.rb' - - 'spec/source_spec.rb' - - 'spec/type_checker/checks_spec.rb' - - 'spec/type_checker/levels/normal_spec.rb' - - 'spec/type_checker/levels/strict_spec.rb' - - 'spec/type_checker/levels/strong_spec.rb' - - 'spec/type_checker/levels/typed_spec.rb' - - 'spec/type_checker/rules_spec.rb' - - 'spec/type_checker_spec.rb' - - 'spec/workspace/config_spec.rb' - - 'spec/workspace_spec.rb' - - 'spec/yard_map/mapper/to_method_spec.rb' - - 'spec/yard_map/mapper_spec.rb' - # Offense count: 1 RSpec/StubbedMock: Exclude: @@ -1566,13 +1451,12 @@ Style/AccessModifierDeclarations: - 'lib/solargraph/source/chain/link.rb' - 'lib/solargraph/source/chain/literal.rb' -# Offense count: 13 +# Offense count: 11 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: separated, grouped Style/AccessorGrouping: Exclude: - - 'lib/solargraph/doc_map.rb' - 'lib/solargraph/parser/flow_sensitive_typing.rb' - 'lib/solargraph/pin/base.rb' - 'lib/solargraph/pin/method.rb' @@ -2129,7 +2013,7 @@ Style/IfInsideElse: - 'lib/solargraph/source_map/clip.rb' - 'lib/solargraph/type_checker.rb' -# Offense count: 63 +# Offense count: 62 # This cop supports safe autocorrection (--autocorrect). Style/IfUnlessModifier: Exclude: @@ -2137,7 +2021,6 @@ Style/IfUnlessModifier: - 'lib/solargraph/api_map/index.rb' - 'lib/solargraph/complex_type.rb' - 'lib/solargraph/complex_type/unique_type.rb' - - 'lib/solargraph/doc_map.rb' - 'lib/solargraph/language_server/message/completion_item/resolve.rb' - 'lib/solargraph/language_server/message/initialize.rb' - 'lib/solargraph/language_server/message/text_document/completion.rb' @@ -2198,14 +2081,13 @@ Style/MapToSet: - 'lib/solargraph/library.rb' - 'spec/source_map/clip_spec.rb' -# Offense count: 203 +# Offense count: 202 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: require_parentheses, require_no_parentheses, require_no_parentheses_except_multiline Style/MethodDefParentheses: Exclude: - 'lib/solargraph.rb' - - 'lib/solargraph/api_map.rb' - 'lib/solargraph/api_map/index.rb' - 'lib/solargraph/api_map/store.rb' - 'lib/solargraph/complex_type.rb' @@ -2219,7 +2101,6 @@ Style/MethodDefParentheses: - 'lib/solargraph/convention/struct_definition/struct_assignment_node.rb' - 'lib/solargraph/convention/struct_definition/struct_definition_node.rb' - 'lib/solargraph/diagnostics/rubocop_helpers.rb' - - 'lib/solargraph/doc_map.rb' - 'lib/solargraph/equality.rb' - 'lib/solargraph/gem_pins.rb' - 'lib/solargraph/language_server/host/message_worker.rb' @@ -2263,7 +2144,6 @@ Style/MethodDefParentheses: - 'spec/type_checker/levels/normal_spec.rb' - 'spec/type_checker/levels/strict_spec.rb' - 'spec/type_checker/levels/strong_spec.rb' - - 'spec/type_checker/levels/typed_spec.rb' # Offense count: 1 Style/MultilineBlockChain: @@ -2459,13 +2339,6 @@ Style/RedundantFreeze: - 'lib/solargraph/complex_type.rb' - 'lib/solargraph/source_map/mapper.rb' -# Offense count: 1 -# This cop supports unsafe autocorrection (--autocorrect-all). -# Configuration parameters: AutoCorrect, AllowComments. -Style/RedundantInitialize: - Exclude: - - 'lib/solargraph/rbs_map/core_map.rb' - # Offense count: 3 # This cop supports unsafe autocorrection (--autocorrect-all). Style/RedundantInterpolation: @@ -2506,14 +2379,13 @@ Style/RedundantRegexpEscape: - 'lib/solargraph/source_map/clip.rb' - 'lib/solargraph/source_map/mapper.rb' -# Offense count: 9 +# Offense count: 6 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowMultipleReturnValues. Style/RedundantReturn: Exclude: - 'lib/solargraph/api_map.rb' - 'lib/solargraph/complex_type/type_methods.rb' - - 'lib/solargraph/doc_map.rb' - 'lib/solargraph/parser/parser_gem/node_methods.rb' - 'lib/solargraph/source/chain/z_super.rb' @@ -2547,14 +2419,13 @@ Style/RescueStandardError: Exclude: - 'lib/solargraph/pin/base.rb' -# Offense count: 17 +# Offense count: 13 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: ConvertCodeThatCanStartToReturnNil, AllowedMethods, MaxChainLength. # AllowedMethods: present?, blank?, presence, try, try! Style/SafeNavigation: Exclude: - 'lib/solargraph/api_map/index.rb' - - 'lib/solargraph/doc_map.rb' - 'lib/solargraph/language_server/message/completion_item/resolve.rb' - 'lib/solargraph/language_server/request.rb' - 'lib/solargraph/language_server/transport/data_reader.rb' @@ -2562,16 +2433,9 @@ Style/SafeNavigation: - 'lib/solargraph/pin/base.rb' - 'lib/solargraph/pin/conversions.rb' - 'lib/solargraph/pin/method.rb' - - 'lib/solargraph/pin_cache.rb' - 'lib/solargraph/range.rb' - 'lib/solargraph/type_checker.rb' -# Offense count: 1 -# Configuration parameters: Max. -Style/SafeNavigationChainLength: - Exclude: - - 'lib/solargraph/doc_map.rb' - # Offense count: 40 # This cop supports unsafe autocorrection (--autocorrect-all). Style/SlicingWithRange: @@ -2630,7 +2494,7 @@ Style/StringConcatenation: - 'lib/solargraph/pin/namespace.rb' - 'solargraph.gemspec' -# Offense count: 613 +# Offense count: 612 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline. # SupportedStyles: single_quotes, double_quotes @@ -2882,19 +2746,16 @@ YARD/CollectionType: Exclude: - 'lib/solargraph/range.rb' -# Offense count: 60 +# Offense count: 49 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStylePrototypeName. # SupportedStylesPrototypeName: before, after YARD/MismatchName: Exclude: - - 'lib/solargraph/api_map.rb' - 'lib/solargraph/complex_type.rb' - 'lib/solargraph/complex_type/unique_type.rb' - 'lib/solargraph/convention.rb' - 'lib/solargraph/convention/data_definition.rb' - - 'lib/solargraph/doc_map.rb' - - 'lib/solargraph/gem_pins.rb' - 'lib/solargraph/language_server/host.rb' - 'lib/solargraph/language_server/host/dispatch.rb' - 'lib/solargraph/language_server/message/text_document/formatting.rb' @@ -2917,8 +2778,6 @@ YARD/MismatchName: - 'lib/solargraph/pin/until.rb' - 'lib/solargraph/pin/while.rb' - 'lib/solargraph/pin_cache.rb' - - 'lib/solargraph/rbs_map.rb' - - 'lib/solargraph/shell.rb' - 'lib/solargraph/source/chain.rb' - 'lib/solargraph/source/chain/call.rb' - 'lib/solargraph/source/chain/z_super.rb' @@ -2931,7 +2790,7 @@ YARD/TagTypeSyntax: - 'lib/solargraph/parser/comment_ripper.rb' - 'lib/solargraph/type_checker.rb' -# Offense count: 195 +# Offense count: 186 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: Max, AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, IgnoreCopDirectives, AllowedPatterns, SplitStrings. # URISchemes: http, https @@ -2943,7 +2802,6 @@ Layout/LineLength: - 'lib/solargraph/complex_type.rb' - 'lib/solargraph/complex_type/unique_type.rb' - 'lib/solargraph/convention/data_definition.rb' - - 'lib/solargraph/doc_map.rb' - 'lib/solargraph/gem_pins.rb' - 'lib/solargraph/language_server/host.rb' - 'lib/solargraph/language_server/message/extended/check_gem_version.rb' From b7eaa99ccf922ab255a7a0c6b5857305ef66162d Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 19 Jul 2025 11:48:56 -0400 Subject: [PATCH 251/561] Drop accidentally added file --- spec/type_checker/levels/alpha_spec.rb | 25 ------------------------- 1 file changed, 25 deletions(-) delete mode 100644 spec/type_checker/levels/alpha_spec.rb diff --git a/spec/type_checker/levels/alpha_spec.rb b/spec/type_checker/levels/alpha_spec.rb deleted file mode 100644 index ca2ae8a3d..000000000 --- a/spec/type_checker/levels/alpha_spec.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true - -describe Solargraph::TypeChecker do - context 'when checking at alpha level' do - def type_checker code - Solargraph::TypeChecker.load_string(code, 'test.rb', :alpha) - end - - it 'reports nilable type issues' do - checker = type_checker(%( - # @param a [String] - # @return [void] - def foo(a); end - - # @param b [String, nil] - # @return [void] - def bar(b) - foo(b) - end - )) - expect(checker.problems.map(&:message)) - .to eq(['Wrong argument type for #foo: a expected String, received String, nil']) - end - end -end From f4835e5caf52153126dfbbd11b4807dba7cb1f51 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 19 Jul 2025 11:53:06 -0400 Subject: [PATCH 252/561] Drop unfinished spec --- spec/workspace/gemspecs_spec.rb | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 spec/workspace/gemspecs_spec.rb diff --git a/spec/workspace/gemspecs_spec.rb b/spec/workspace/gemspecs_spec.rb deleted file mode 100644 index 00825686c..000000000 --- a/spec/workspace/gemspecs_spec.rb +++ /dev/null @@ -1,19 +0,0 @@ -# frozen_string_literal: true - -require 'fileutils' -require 'tmpdir' - -describe Solargraph::Workspace::Gemspecs do - let(:workspace) { Solargraph::Workspace.new(dir_path) } - let(:dir_path) { File.realpath(Dir.mktmpdir) } - let(:file_path) { File.join(dir_path, 'file.rb') } - - before { File.write(file_path, 'exit') } - after { FileUtils.remove_entry(dir_path) } - - it 'ignores gemspecs in excluded directories' do - # vendor/**/* is excluded by default - workspace = Solargraph::Workspace.new('spec/fixtures/vendored') - expect(workspace.gemspecs).to be_empty - end -end From 660edc4a2d862a7f14fa7ed6dc002e79aa77d0b5 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 19 Jul 2025 12:13:50 -0400 Subject: [PATCH 253/561] Move to full Bundler fill --- rbs/fills/bundler/bundler.rbs | 4254 +++++++++++++++++++++++++++++++++ rbs/fills/bundler/dsl.rbs | 200 -- 2 files changed, 4254 insertions(+), 200 deletions(-) create mode 100644 rbs/fills/bundler/bundler.rbs delete mode 100644 rbs/fills/bundler/dsl.rbs diff --git a/rbs/fills/bundler/bundler.rbs b/rbs/fills/bundler/bundler.rbs new file mode 100644 index 000000000..f60fe3837 --- /dev/null +++ b/rbs/fills/bundler/bundler.rbs @@ -0,0 +1,4254 @@ +# [`Bundler`](https://docs.ruby-lang.org/en/2.7.0/Bundler.html) provides a +# consistent environment for Ruby projects by tracking and installing the exact +# gems and versions that are needed. +# +# Since Ruby 2.6, [`Bundler`](https://docs.ruby-lang.org/en/2.7.0/Bundler.html) +# is a part of Ruby's standard library. +# +# Bunder is used by creating *gemfiles* listing all the project dependencies and +# (optionally) their versions and then using +# +# ```ruby +# require 'bundler/setup' +# ``` +# +# or +# [`Bundler.setup`](https://docs.ruby-lang.org/en/2.7.0/Bundler.html#method-c-setup) +# to setup environment where only specified gems and their specified versions +# could be used. +# +# See [Bundler website](https://bundler.io/docs.html) for extensive +# documentation on gemfiles creation and +# [`Bundler`](https://docs.ruby-lang.org/en/2.7.0/Bundler.html) usage. +# +# As a standard library inside project, +# [`Bundler`](https://docs.ruby-lang.org/en/2.7.0/Bundler.html) could be used +# for introspection of loaded and required modules. +module Bundler + def self.app_cache: (?untyped custom_path) -> untyped + + def self.app_config_path: () -> untyped + + # Returns absolute location of where binstubs are installed to. + def self.bin_path: () -> untyped + + # Returns absolute path of where gems are installed on the filesystem. + def self.bundle_path: () -> untyped + + def self.bundler_major_version: () -> untyped + + # @deprecated Use `unbundled\_env` instead + def self.clean_env: () -> untyped + + def self.clean_exec: (*untyped args) -> untyped + + def self.clean_system: (*untyped args) -> untyped + + def self.clear_gemspec_cache: () -> untyped + + def self.configure: () -> untyped + + def self.configured_bundle_path: () -> untyped + + # Returns current version of Ruby + # + # @return [CurrentRuby] Current version of Ruby + def self.current_ruby: () -> untyped + + def self.default_bundle_dir: () -> untyped + + def self.default_gemfile: () -> untyped + + def self.default_lockfile: () -> untyped + + def self.definition: (?(::Hash[String, Boolean | nil] | Boolean | nil) unlock) -> Bundler::Definition + + def self.environment: () -> untyped + + def self.feature_flag: () -> untyped + + def self.frozen_bundle?: () -> untyped + + def self.git_present?: () -> untyped + + def self.home: () -> untyped + + def self.install_path: () -> untyped + + def self.load: () -> untyped + + def self.load_gemspec: (untyped file, ?untyped validate) -> untyped + + def self.load_gemspec_uncached: (untyped file, ?untyped validate) -> untyped + + def self.load_marshal: (untyped data) -> untyped + + def self.local_platform: () -> untyped + + def self.locked_gems: () -> untyped + + def self.mkdir_p: (untyped path, ?untyped options) -> untyped + + # @return [Hash] Environment present before + # [`Bundler`](https://docs.ruby-lang.org/en/2.7.0/Bundler.html) was activated + def self.original_env: () -> untyped + + def self.read_file: (untyped file) -> untyped + + def self.require: (*untyped groups) -> untyped + + def self.require_thor_actions: () -> untyped + + def self.requires_sudo?: () -> untyped + + def self.reset!: () -> untyped + + def self.reset_paths!: () -> untyped + + def self.reset_rubygems!: () -> untyped + + def self.rm_rf: (untyped path) -> untyped + + def self.root: () -> untyped + + def self.ruby_scope: () -> untyped + + def self.rubygems: () -> untyped + + def self.settings: () -> untyped + + def self.setup: (*untyped groups) -> untyped + + def self.specs_path: () -> untyped + + def self.sudo: (untyped str) -> untyped + + def self.system_bindir: () -> untyped + + def self.tmp: (?untyped name) -> untyped + + def self.tmp_home_path: (untyped login, untyped warning) -> untyped + + def self.ui: () -> untyped + + def self.ui=: (untyped ui) -> untyped + + def self.use_system_gems?: () -> untyped + + def self.user_bundle_path: (?untyped dir) -> untyped + + def self.user_cache: () -> untyped + + def self.user_home: () -> untyped + + def self.which: (untyped executable) -> untyped + + # @deprecated Use `with\_unbundled\_env` instead + def self.with_clean_env: () { () -> untyped } -> untyped + + def self.with_unbundled_env: () { () -> untyped } -> untyped + + # Run block with environment present before + # [`Bundler`](https://docs.ruby-lang.org/en/2.7.0/Bundler.html) was activated + def self.with_original_env: () { () -> untyped } -> untyped +end + +Bundler::FREEBSD: untyped + +Bundler::NULL: untyped + +Bundler::ORIGINAL_ENV: untyped + +Bundler::SUDO_MUTEX: untyped + +Bundler::VERSION: untyped + +Bundler::WINDOWS: untyped + +class Bundler::APIResponseMismatchError < Bundler::BundlerError + def status_code: () -> untyped +end + +# Represents metadata from when the +# [`Bundler`](https://docs.ruby-lang.org/en/2.7.0/Bundler.html) gem was built. +module Bundler::BuildMetadata + # A string representing the date the bundler gem was built. + def self.built_at: () -> untyped + + # The SHA for the git commit the bundler gem was built from. + def self.git_commit_sha: () -> untyped + + # Whether this is an official release build of + # [`Bundler`](https://docs.ruby-lang.org/en/2.7.0/Bundler.html). + def self.release?: () -> untyped + + # A hash representation of the build metadata. + def self.to_h: () -> untyped +end + +class Bundler::BundlerError < StandardError + def self.all_errors: () -> untyped + + def self.status_code: (untyped code) -> untyped +end + +class Bundler::CurrentRuby + def jruby?: () -> untyped + + def jruby_18?: () -> untyped + + def jruby_19?: () -> untyped + + def jruby_1?: () -> untyped + + def jruby_20?: () -> untyped + + def jruby_21?: () -> untyped + + def jruby_22?: () -> untyped + + def jruby_23?: () -> untyped + + def jruby_24?: () -> untyped + + def jruby_25?: () -> untyped + + def jruby_26?: () -> untyped + + def jruby_27?: () -> untyped + + def jruby_2?: () -> untyped + + def maglev?: () -> untyped + + def maglev_18?: () -> untyped + + def maglev_19?: () -> untyped + + def maglev_1?: () -> untyped + + def maglev_20?: () -> untyped + + def maglev_21?: () -> untyped + + def maglev_22?: () -> untyped + + def maglev_23?: () -> untyped + + def maglev_24?: () -> untyped + + def maglev_25?: () -> untyped + + def maglev_26?: () -> untyped + + def maglev_27?: () -> untyped + + def maglev_2?: () -> untyped + + def mingw?: () -> untyped + + def mingw_18?: () -> untyped + + def mingw_19?: () -> untyped + + def mingw_1?: () -> untyped + + def mingw_20?: () -> untyped + + def mingw_21?: () -> untyped + + def mingw_22?: () -> untyped + + def mingw_23?: () -> untyped + + def mingw_24?: () -> untyped + + def mingw_25?: () -> untyped + + def mingw_26?: () -> untyped + + def mingw_27?: () -> untyped + + def mingw_2?: () -> untyped + + def mri?: () -> untyped + + def mri_18?: () -> untyped + + def mri_19?: () -> untyped + + def mri_1?: () -> untyped + + def mri_20?: () -> untyped + + def mri_21?: () -> untyped + + def mri_22?: () -> untyped + + def mri_23?: () -> untyped + + def mri_24?: () -> untyped + + def mri_25?: () -> untyped + + def mri_26?: () -> untyped + + def mri_27?: () -> untyped + + def mri_2?: () -> untyped + + def mswin64?: () -> untyped + + def mswin64_18?: () -> untyped + + def mswin64_19?: () -> untyped + + def mswin64_1?: () -> untyped + + def mswin64_20?: () -> untyped + + def mswin64_21?: () -> untyped + + def mswin64_22?: () -> untyped + + def mswin64_23?: () -> untyped + + def mswin64_24?: () -> untyped + + def mswin64_25?: () -> untyped + + def mswin64_26?: () -> untyped + + def mswin64_27?: () -> untyped + + def mswin64_2?: () -> untyped + + def mswin?: () -> untyped + + def mswin_18?: () -> untyped + + def mswin_19?: () -> untyped + + def mswin_1?: () -> untyped + + def mswin_20?: () -> untyped + + def mswin_21?: () -> untyped + + def mswin_22?: () -> untyped + + def mswin_23?: () -> untyped + + def mswin_24?: () -> untyped + + def mswin_25?: () -> untyped + + def mswin_26?: () -> untyped + + def mswin_27?: () -> untyped + + def mswin_2?: () -> untyped + + def on_18?: () -> untyped + + def on_19?: () -> untyped + + def on_1?: () -> untyped + + def on_20?: () -> untyped + + def on_21?: () -> untyped + + def on_22?: () -> untyped + + def on_23?: () -> untyped + + def on_24?: () -> untyped + + def on_25?: () -> untyped + + def on_26?: () -> untyped + + def on_27?: () -> untyped + + def on_2?: () -> untyped + + def rbx?: () -> untyped + + def rbx_18?: () -> untyped + + def rbx_19?: () -> untyped + + def rbx_1?: () -> untyped + + def rbx_20?: () -> untyped + + def rbx_21?: () -> untyped + + def rbx_22?: () -> untyped + + def rbx_23?: () -> untyped + + def rbx_24?: () -> untyped + + def rbx_25?: () -> untyped + + def rbx_26?: () -> untyped + + def rbx_27?: () -> untyped + + def rbx_2?: () -> untyped + + def ruby?: () -> untyped + + def ruby_18?: () -> untyped + + def ruby_19?: () -> untyped + + def ruby_1?: () -> untyped + + def ruby_20?: () -> untyped + + def ruby_21?: () -> untyped + + def ruby_22?: () -> untyped + + def ruby_23?: () -> untyped + + def ruby_24?: () -> untyped + + def ruby_25?: () -> untyped + + def ruby_26?: () -> untyped + + def ruby_27?: () -> untyped + + def ruby_2?: () -> untyped + + def truffleruby?: () -> untyped + + def truffleruby_18?: () -> untyped + + def truffleruby_19?: () -> untyped + + def truffleruby_1?: () -> untyped + + def truffleruby_20?: () -> untyped + + def truffleruby_21?: () -> untyped + + def truffleruby_22?: () -> untyped + + def truffleruby_23?: () -> untyped + + def truffleruby_24?: () -> untyped + + def truffleruby_25?: () -> untyped + + def truffleruby_26?: () -> untyped + + def truffleruby_27?: () -> untyped + + def truffleruby_2?: () -> untyped + + def x64_mingw?: () -> untyped + + def x64_mingw_18?: () -> untyped + + def x64_mingw_19?: () -> untyped + + def x64_mingw_1?: () -> untyped + + def x64_mingw_20?: () -> untyped + + def x64_mingw_21?: () -> untyped + + def x64_mingw_22?: () -> untyped + + def x64_mingw_23?: () -> untyped + + def x64_mingw_24?: () -> untyped + + def x64_mingw_25?: () -> untyped + + def x64_mingw_26?: () -> untyped + + def x64_mingw_27?: () -> untyped + + def x64_mingw_2?: () -> untyped +end + +Bundler::CurrentRuby::KNOWN_MAJOR_VERSIONS: untyped + +Bundler::CurrentRuby::KNOWN_MINOR_VERSIONS: untyped + +Bundler::CurrentRuby::KNOWN_PLATFORMS: untyped + +class Bundler::CyclicDependencyError < Bundler::BundlerError + def status_code: () -> untyped +end + +class Bundler::Definition + include ::Bundler::GemHelpers + + def add_current_platform: () -> untyped + + def add_platform: (untyped platform) -> untyped + + def current_dependencies: () -> untyped + + def dependencies: () -> Array[::Bundler::Dependency] + + def ensure_equivalent_gemfile_and_lockfile: (?untyped explicit_flag) -> untyped + + def find_indexed_specs: (untyped current_spec) -> untyped + + def find_resolved_spec: (untyped current_spec) -> untyped + + def gem_version_promoter: () -> untyped + + def gemfiles: () -> untyped + + def groups: () -> untyped + + def has_local_dependencies?: () -> untyped + + def has_rubygems_remotes?: () -> untyped + + def index: () -> untyped + + def initialize: (untyped lockfile, untyped dependencies, untyped sources, untyped unlock, ?untyped ruby_version, ?untyped optional_groups, ?untyped gemfiles) -> void + + def lock: (untyped file, ?untyped preserve_unknown_sections) -> untyped + + def locked_bundler_version: () -> untyped + + def locked_deps: () -> untyped + + def locked_gems: () -> Bundler::LockfileParser + + def locked_ruby_version: () -> untyped + + def locked_ruby_version_object: () -> untyped + + def lockfile: () -> Pathname + + def missing_specs: () -> untyped + + def missing_specs?: () -> untyped + + def new_platform?: () -> untyped + + def new_specs: () -> untyped + + def nothing_changed?: () -> untyped + + def platforms: () -> untyped + + def remove_platform: (untyped platform) -> untyped + + def removed_specs: () -> untyped + + def requested_specs: () -> untyped + + def requires: () -> untyped + + # Resolve all the dependencies specified in Gemfile. It ensures that + # dependencies that have been already resolved via locked file and are fresh + # are reused when resolving dependencies + # + # @return [SpecSet] resolved dependencies + def resolve: () -> untyped + + def resolve_remotely!: () -> untyped + + def resolve_with_cache!: () -> untyped + + def ruby_version: () -> untyped + + def spec_git_paths: () -> untyped + + # For given dependency list returns a SpecSet with Gemspec of all the required + # dependencies. + # + # ``` + # 1. The method first resolves the dependencies specified in Gemfile + # 2. After that it tries and fetches gemspec of resolved dependencies + # ``` + # + # @return [Bundler::SpecSet] + def specs: () -> untyped + + def specs_for: (untyped groups) -> untyped + + def to_lock: () -> untyped + + def unlocking?: () -> untyped + + def validate_platforms!: () -> untyped + + def validate_ruby!: () -> untyped + + def validate_runtime!: () -> untyped + + def self.build: (untyped gemfile, untyped lockfile, untyped unlock) -> untyped +end + +class Bundler::DepProxy + def ==: (untyped other) -> untyped + + def __platform: () -> untyped + + def dep: () -> untyped + + def eql?: (untyped other) -> untyped + + def hash: () -> untyped + + def initialize: (untyped dep, untyped platform) -> void + + def name: () -> untyped + + def requirement: () -> untyped + + def to_s: () -> untyped + + def type: () -> untyped +end + +class Bundler::Dependency < Gem::Dependency + def autorequire: () -> untyped + + def current_env?: () -> untyped + + def current_platform?: () -> untyped + + def gem_platforms: (untyped valid_platforms) -> untyped + + def gemfile: () -> untyped + + def groups: () -> untyped + + def initialize: (untyped name, untyped version, ?untyped options) { () -> untyped } -> void + + def platforms: () -> untyped + + def should_include?: () -> untyped + + def specific?: () -> untyped + + def to_lock: () -> untyped +end + +Bundler::Dependency::PLATFORM_MAP: untyped + +Bundler::Dependency::REVERSE_PLATFORM_MAP: untyped + +class Bundler::DeprecatedError < Bundler::BundlerError + def status_code: () -> untyped +end + +class Bundler::Dsl + @source: untyped + + @sources: untyped + + @git_sources: untyped + + @dependencies: untyped + + @groups: untyped + + @install_conditionals: untyped + + @optional_groups: untyped + + @platforms: untyped + + @env: untyped + + @ruby_version: untyped + + @gemspecs: untyped + + @gemfile: untyped + + @gemfiles: untyped + + @valid_keys: untyped + + include RubyDsl + + def self.evaluate: (untyped gemfile, untyped lockfile, untyped unlock) -> untyped + + VALID_PLATFORMS: untyped + + VALID_KEYS: ::Array["group" | "groups" | "git" | "path" | "glob" | "name" | "branch" | "ref" | "tag" | "require" | "submodules" | "platform" | "platforms" | "source" | "install_if" | "force_ruby_platform"] + + GITHUB_PULL_REQUEST_URL: ::Regexp + + GITLAB_MERGE_REQUEST_URL: ::Regexp + + attr_reader gemspecs: untyped + + attr_reader gemfile: untyped + + attr_accessor dependencies: untyped + + def initialize: () -> void + + def eval_gemfile: (untyped gemfile, ?untyped? contents) -> untyped + + def gemspec: (?path: ::String, ?glob: ::String, ?name: ::String, ?development_group: ::Symbol) -> void + + def gem: (untyped name, *untyped args) -> void + + def source: (::String source, ?type: ::Symbol) ?{ (?) -> untyped } -> void + + def git_source: (untyped name) ?{ (?) -> untyped } -> untyped + + def path: (untyped path, ?::Hash[untyped, untyped] options) ?{ (?) -> untyped } -> untyped + + def git: (untyped uri, ?::Hash[untyped, untyped] options) ?{ (?) -> untyped } -> untyped + + def github: (untyped repo, ?::Hash[untyped, untyped] options) ?{ () -> untyped } -> untyped + + def to_definition: (untyped lockfile, untyped unlock) -> untyped + + def group: (*untyped args) { () -> untyped } -> untyped + + def install_if: (*untyped args) { () -> untyped } -> untyped + + def platforms: (*untyped platforms) { () -> untyped } -> untyped + + alias platform platforms + + def env: (untyped name) { () -> untyped } -> untyped + + def plugin: (*untyped args) -> nil + + def method_missing: (untyped name, *untyped args) -> untyped + + def check_primary_source_safety: () -> untyped + + private + + def add_dependency: (untyped name, ?untyped? version, ?::Hash[untyped, untyped] options) -> (nil | untyped) + + def with_gemfile: (untyped gemfile) { (untyped) -> untyped } -> untyped + + def add_git_sources: () -> untyped + + def with_source: (untyped source) ?{ () -> untyped } -> untyped + + def normalize_hash: (untyped opts) -> untyped + + def valid_keys: () -> untyped + + def normalize_options: (untyped name, untyped version, untyped opts) -> untyped + + def normalize_group_options: (untyped opts, untyped groups) -> untyped + + def validate_keys: (untyped command, untyped opts, untyped valid_keys) -> (true | untyped) + + def normalize_source: (untyped source) -> untyped + + def deprecate_legacy_windows_platforms: (untyped platforms) -> (nil | untyped) + + def check_path_source_safety: () -> (nil | untyped) + + def check_rubygems_source_safety: () -> (untyped | nil) + + def multiple_global_source_warning: () -> untyped + + class DSLError < GemfileError + @status_code: untyped + + @description: untyped + + @dsl_path: untyped + + @backtrace: untyped + + @contents: untyped + + @to_s: untyped + + # @return [::String] the description that should be presented to the user. + # + attr_reader description: ::String + + # @return [::String] the path of the dsl file that raised the exception. + # + attr_reader dsl_path: ::String + + # @return [::Exception] the backtrace of the exception raised by the + # evaluation of the dsl file. + # + attr_reader backtrace: ::Exception + + # @param [::Exception] backtrace @see backtrace + # @param [::String] dsl_path @see dsl_path + # + def initialize: (untyped description, ::String dsl_path, ::Exception backtrace, ?untyped? contents) -> void + + def status_code: () -> untyped + + # @return [::String] the contents of the DSL that cause the exception to + # be raised. + # + def contents: () -> ::String + + # The message of the exception reports the content of podspec for the + # line that generated the original exception. + # + # @example Output + # + # Invalid podspec at `RestKit.podspec` - undefined method + # `exclude_header_search_paths=' for # + # + # from spec-repos/master/RestKit/0.9.3/RestKit.podspec:36 + # ------------------------------------------- + # # because it would break: #import + # > ns.exclude_header_search_paths = 'Code/RestKit.h' + # end + # ------------------------------------------- + # + # @return [::String] the message of the exception. + # + def to_s: () -> ::String + + private + + def parse_line_number_from_description: () -> ::Array[untyped] + end + + def gemfile_root: () -> untyped +end + +# used for Creating Specifications from the Gemcutter Endpoint +class Bundler::EndpointSpecification < Gem::Specification + def __swap__: (untyped spec) -> untyped + + def _local_specification: () -> untyped + + # needed for bundle clean + def bindir: () -> untyped + + def checksum: () -> untyped + + def dependencies: () -> untyped + + def dependencies=: (untyped dependencies) -> untyped + + # needed for binstubs + def executables: () -> untyped + + # needed for "with native extensions" during install + def extensions: () -> untyped + + def fetch_platform: () -> untyped + + def initialize: (untyped name, untyped version, untyped platform, untyped dependencies, ?untyped metadata) -> void + + # needed for inline + def load_paths: () -> untyped + + def name: () -> untyped + + def platform: () -> untyped + + # needed for post\_install\_messages during install + def post_install_message: () -> untyped + + def remote: () -> untyped + + def remote=: (untyped remote) -> untyped + + # needed for standalone, load required\_paths from local gemspec after the gem + # is installed + def require_paths: () -> untyped + + def required_ruby_version: () -> untyped + + def required_rubygems_version: () -> untyped + + def source: () -> untyped + + def source=: (untyped source) -> untyped + + def version: () -> untyped +end + +Bundler::EndpointSpecification::Elem: untyped + +Bundler::EndpointSpecification::ILLFORMED_MESSAGE: untyped + +class Bundler::EnvironmentPreserver + # @return [Hash] + def backup: () -> untyped + + def initialize: (untyped env, untyped keys) -> void + + # @return [Hash] + def restore: () -> untyped +end + +Bundler::EnvironmentPreserver::BUNDLER_KEYS: untyped + +Bundler::EnvironmentPreserver::BUNDLER_PREFIX: untyped + +Bundler::EnvironmentPreserver::INTENTIONALLY_NIL: untyped + +class Bundler::FeatureFlag + def allow_bundler_dependency_conflicts?: () -> untyped + + def allow_offline_install?: () -> untyped + + def auto_clean_without_path?: () -> untyped + + def auto_config_jobs?: () -> untyped + + def bundler_10_mode?: () -> untyped + + def bundler_1_mode?: () -> untyped + + def bundler_2_mode?: () -> untyped + + def bundler_3_mode?: () -> untyped + + def bundler_4_mode?: () -> untyped + + def bundler_5_mode?: () -> untyped + + def bundler_6_mode?: () -> untyped + + def bundler_7_mode?: () -> untyped + + def bundler_8_mode?: () -> untyped + + def bundler_9_mode?: () -> untyped + + def cache_all?: () -> untyped + + def cache_command_is_package?: () -> untyped + + def console_command?: () -> untyped + + def default_cli_command: () -> untyped + + def default_install_uses_path?: () -> untyped + + def deployment_means_frozen?: () -> untyped + + def disable_multisource?: () -> untyped + + def error_on_stderr?: () -> untyped + + def forget_cli_options?: () -> untyped + + def global_gem_cache?: () -> untyped + + def init_gems_rb?: () -> untyped + + def initialize: (untyped bundler_version) -> void + + def list_command?: () -> untyped + + def lockfile_uses_separate_rubygems_sources?: () -> untyped + + def only_update_to_newer_versions?: () -> untyped + + def path_relative_to_cwd?: () -> untyped + + def plugins?: () -> untyped + + def prefer_gems_rb?: () -> untyped + + def print_only_version_number?: () -> untyped + + def setup_makes_kernel_gem_public?: () -> untyped + + def skip_default_git_sources?: () -> untyped + + def specific_platform?: () -> untyped + + def suppress_install_using_messages?: () -> untyped + + def unlock_source_unlocks_spec?: () -> untyped + + def update_requires_all_flag?: () -> untyped + + def use_gem_version_promoter_for_major_updates?: () -> untyped + + def viz_command?: () -> untyped +end + +# # fileutils.rb +# +# Copyright (c) 2000-2007 Minero Aoki +# +# This program is free software. You can distribute/modify this program under +# the same terms of ruby. +# +# ## module [`Bundler::FileUtils`](https://docs.ruby-lang.org/en/2.7.0/Bundler/FileUtils.html) +# +# Namespace for several file utility methods for copying, moving, removing, etc. +# +# ### [`Module`](https://docs.ruby-lang.org/en/2.7.0/Module.html) Functions +# +# ```ruby +# require 'bundler/vendor/fileutils/lib/fileutils' +# +# Bundler::FileUtils.cd(dir, **options) +# Bundler::FileUtils.cd(dir, **options) {|dir| block } +# Bundler::FileUtils.pwd() +# Bundler::FileUtils.mkdir(dir, **options) +# Bundler::FileUtils.mkdir(list, **options) +# Bundler::FileUtils.mkdir_p(dir, **options) +# Bundler::FileUtils.mkdir_p(list, **options) +# Bundler::FileUtils.rmdir(dir, **options) +# Bundler::FileUtils.rmdir(list, **options) +# Bundler::FileUtils.ln(target, link, **options) +# Bundler::FileUtils.ln(targets, dir, **options) +# Bundler::FileUtils.ln_s(target, link, **options) +# Bundler::FileUtils.ln_s(targets, dir, **options) +# Bundler::FileUtils.ln_sf(target, link, **options) +# Bundler::FileUtils.cp(src, dest, **options) +# Bundler::FileUtils.cp(list, dir, **options) +# Bundler::FileUtils.cp_r(src, dest, **options) +# Bundler::FileUtils.cp_r(list, dir, **options) +# Bundler::FileUtils.mv(src, dest, **options) +# Bundler::FileUtils.mv(list, dir, **options) +# Bundler::FileUtils.rm(list, **options) +# Bundler::FileUtils.rm_r(list, **options) +# Bundler::FileUtils.rm_rf(list, **options) +# Bundler::FileUtils.install(src, dest, **options) +# Bundler::FileUtils.chmod(mode, list, **options) +# Bundler::FileUtils.chmod_R(mode, list, **options) +# Bundler::FileUtils.chown(user, group, list, **options) +# Bundler::FileUtils.chown_R(user, group, list, **options) +# Bundler::FileUtils.touch(list, **options) +# ``` +# +# Possible `options` are: +# +# `:force` +# : forced operation (rewrite files if exist, remove directories if not empty, +# etc.); +# `:verbose` +# : print command to be run, in bash syntax, before performing it; +# `:preserve` +# : preserve object's group, user and modification time on copying; +# `:noop` +# : no changes are made (usable in combination with `:verbose` which will +# print the command to run) +# +# +# Each method documents the options that it honours. See also +# [`::commands`](https://docs.ruby-lang.org/en/2.7.0/FileUtils.html#method-c-commands), +# [`::options`](https://docs.ruby-lang.org/en/2.7.0/FileUtils.html#method-c-options) +# and +# [`::options_of`](https://docs.ruby-lang.org/en/2.7.0/FileUtils.html#method-c-options_of) +# methods to introspect which command have which options. +# +# All methods that have the concept of a "source" file or directory can take +# either one file or a list of files in that argument. See the method +# documentation for examples. +# +# There are some 'low level' methods, which do not accept keyword arguments: +# +# ```ruby +# Bundler::FileUtils.copy_entry(src, dest, preserve = false, dereference_root = false, remove_destination = false) +# Bundler::FileUtils.copy_file(src, dest, preserve = false, dereference = true) +# Bundler::FileUtils.copy_stream(srcstream, deststream) +# Bundler::FileUtils.remove_entry(path, force = false) +# Bundler::FileUtils.remove_entry_secure(path, force = false) +# Bundler::FileUtils.remove_file(path, force = false) +# Bundler::FileUtils.compare_file(path_a, path_b) +# Bundler::FileUtils.compare_stream(stream_a, stream_b) +# Bundler::FileUtils.uptodate?(file, cmp_list) +# ``` +# +# ## module [`Bundler::FileUtils::Verbose`](https://docs.ruby-lang.org/en/2.7.0/Bundler/FileUtils/Verbose.html) +# +# This module has all methods of +# [`Bundler::FileUtils`](https://docs.ruby-lang.org/en/2.7.0/Bundler/FileUtils.html) +# module, but it outputs messages before acting. This equates to passing the +# `:verbose` flag to methods in +# [`Bundler::FileUtils`](https://docs.ruby-lang.org/en/2.7.0/Bundler/FileUtils.html). +# +# ## module [`Bundler::FileUtils::NoWrite`](https://docs.ruby-lang.org/en/2.7.0/Bundler/FileUtils/NoWrite.html) +# +# This module has all methods of +# [`Bundler::FileUtils`](https://docs.ruby-lang.org/en/2.7.0/Bundler/FileUtils.html) +# module, but never changes files/directories. This equates to passing the +# `:noop` flag to methods in +# [`Bundler::FileUtils`](https://docs.ruby-lang.org/en/2.7.0/Bundler/FileUtils.html). +# +# ## module [`Bundler::FileUtils::DryRun`](https://docs.ruby-lang.org/en/2.7.0/Bundler/FileUtils/DryRun.html) +# +# This module has all methods of +# [`Bundler::FileUtils`](https://docs.ruby-lang.org/en/2.7.0/Bundler/FileUtils.html) +# module, but never changes files/directories. This equates to passing the +# `:noop` and `:verbose` flags to methods in +# [`Bundler::FileUtils`](https://docs.ruby-lang.org/en/2.7.0/Bundler/FileUtils.html). +module Bundler::FileUtils + include ::Bundler::FileUtils::StreamUtils_ + + extend ::Bundler::FileUtils::StreamUtils_ + + def self.cd: (untyped dir, ?verbose: untyped verbose) { () -> untyped } -> untyped + + def self.chdir: (untyped dir, ?verbose: untyped verbose) { () -> untyped } -> untyped + + def self.chmod: (untyped mode, untyped list, ?noop: untyped noop, ?verbose: untyped verbose) -> untyped + + def self.chmod_R: (untyped mode, untyped list, ?noop: untyped noop, ?verbose: untyped verbose, ?force: untyped force) -> untyped + + def self.chown: (untyped user, untyped group, untyped list, ?noop: untyped noop, ?verbose: untyped verbose) -> untyped + + def self.chown_R: (untyped user, untyped group, untyped list, ?noop: untyped noop, ?verbose: untyped verbose, ?force: untyped force) -> untyped + + def self.cmp: (untyped a, untyped b) -> untyped + + def self.collect_method: (untyped opt) -> untyped + + # Returns an [`Array`](https://docs.ruby-lang.org/en/2.7.0/Array.html) of + # names of high-level methods that accept any keyword arguments. + # + # ```ruby + # p Bundler::FileUtils.commands #=> ["chmod", "cp", "cp_r", "install", ...] + # ``` + def self.commands: () -> untyped + + def self.compare_file: (untyped a, untyped b) -> untyped + + def self.compare_stream: (untyped a, untyped b) -> untyped + + def self.copy: (untyped src, untyped dest, ?preserve: untyped preserve, ?noop: untyped noop, ?verbose: untyped verbose) -> untyped + + def self.copy_entry: (untyped src, untyped dest, ?untyped preserve, ?untyped dereference_root, ?untyped remove_destination) -> untyped + + def self.copy_file: (untyped src, untyped dest, ?untyped preserve, ?untyped dereference) -> untyped + + def self.copy_stream: (untyped src, untyped dest) -> untyped + + def self.cp: (untyped src, untyped dest, ?preserve: untyped preserve, ?noop: untyped noop, ?verbose: untyped verbose) -> untyped + + def self.cp_r: (untyped src, untyped dest, ?preserve: untyped preserve, ?noop: untyped noop, ?verbose: untyped verbose, ?dereference_root: untyped dereference_root, ?remove_destination: untyped remove_destination) -> untyped + + # Alias for: + # [`pwd`](https://docs.ruby-lang.org/en/2.7.0/FileUtils.html#method-i-pwd) + def self.getwd: () -> untyped + + def self.have_option?: (untyped mid, untyped opt) -> untyped + + def self.identical?: (untyped a, untyped b) -> untyped + + def self.install: (untyped src, untyped dest, ?mode: untyped mode, ?owner: untyped owner, ?group: untyped group, ?preserve: untyped preserve, ?noop: untyped noop, ?verbose: untyped verbose) -> untyped + + def self.link: (untyped src, untyped dest, ?force: untyped force, ?noop: untyped noop, ?verbose: untyped verbose) -> untyped + + def self.ln: (untyped src, untyped dest, ?force: untyped force, ?noop: untyped noop, ?verbose: untyped verbose) -> untyped + + def self.ln_s: (untyped src, untyped dest, ?force: untyped force, ?noop: untyped noop, ?verbose: untyped verbose) -> untyped + + def self.ln_sf: (untyped src, untyped dest, ?noop: untyped noop, ?verbose: untyped verbose) -> untyped + + def self.makedirs: (untyped list, ?mode: untyped mode, ?noop: untyped noop, ?verbose: untyped verbose) -> untyped + + def self.mkdir: (untyped list, ?mode: untyped mode, ?noop: untyped noop, ?verbose: untyped verbose) -> untyped + + def self.mkdir_p: (untyped list, ?mode: untyped mode, ?noop: untyped noop, ?verbose: untyped verbose) -> untyped + + def self.mkpath: (untyped list, ?mode: untyped mode, ?noop: untyped noop, ?verbose: untyped verbose) -> untyped + + def self.move: (untyped src, untyped dest, ?force: untyped force, ?noop: untyped noop, ?verbose: untyped verbose, ?secure: untyped secure) -> untyped + + def self.mv: (untyped src, untyped dest, ?force: untyped force, ?noop: untyped noop, ?verbose: untyped verbose, ?secure: untyped secure) -> untyped + + # Returns an [`Array`](https://docs.ruby-lang.org/en/2.7.0/Array.html) of + # option names. + # + # ```ruby + # p Bundler::FileUtils.options #=> ["noop", "force", "verbose", "preserve", "mode"] + # ``` + def self.options: () -> untyped + + def self.options_of: (untyped mid) -> untyped + + def self.private_module_function: (untyped name) -> untyped + + # Returns the name of the current directory. + # + # Also aliased as: + # [`getwd`](https://docs.ruby-lang.org/en/2.7.0/FileUtils.html#method-c-getwd) + def self.pwd: () -> untyped + + def self.remove: (untyped list, ?force: untyped force, ?noop: untyped noop, ?verbose: untyped verbose) -> untyped + + def self.remove_dir: (untyped path, ?untyped force) -> untyped + + def self.remove_entry: (untyped path, ?untyped force) -> untyped + + def self.remove_entry_secure: (untyped path, ?untyped force) -> untyped + + def self.remove_file: (untyped path, ?untyped force) -> untyped + + def self.rm: (untyped list, ?force: untyped force, ?noop: untyped noop, ?verbose: untyped verbose) -> untyped + + def self.rm_f: (untyped list, ?noop: untyped noop, ?verbose: untyped verbose) -> untyped + + def self.rm_r: (untyped list, ?force: untyped force, ?noop: untyped noop, ?verbose: untyped verbose, ?secure: untyped secure) -> untyped + + def self.rm_rf: (untyped list, ?noop: untyped noop, ?verbose: untyped verbose, ?secure: untyped secure) -> untyped + + def self.rmdir: (untyped list, ?parents: untyped parents, ?noop: untyped noop, ?verbose: untyped verbose) -> untyped + + def self.rmtree: (untyped list, ?noop: untyped noop, ?verbose: untyped verbose, ?secure: untyped secure) -> untyped + + def self.safe_unlink: (untyped list, ?noop: untyped noop, ?verbose: untyped verbose) -> untyped + + def self.symlink: (untyped src, untyped dest, ?force: untyped force, ?noop: untyped noop, ?verbose: untyped verbose) -> untyped + + def self.touch: (untyped list, ?noop: untyped noop, ?verbose: untyped verbose, ?mtime: untyped mtime, ?nocreate: untyped nocreate) -> untyped + + def self.uptodate?: (untyped new, untyped old_list) -> untyped +end + +Bundler::FileUtils::LOW_METHODS: untyped + +Bundler::FileUtils::METHODS: untyped + +Bundler::FileUtils::OPT_TABLE: untyped + +# This module has all methods of +# [`Bundler::FileUtils`](https://docs.ruby-lang.org/en/2.7.0/Bundler/FileUtils.html) +# module, but never changes files/directories, with printing message before +# acting. This equates to passing the `:noop` and `:verbose` flag to methods in +# [`Bundler::FileUtils`](https://docs.ruby-lang.org/en/2.7.0/Bundler/FileUtils.html). +module Bundler::FileUtils::DryRun + include ::Bundler::FileUtils::LowMethods + + include ::Bundler::FileUtils + + include ::Bundler::FileUtils::StreamUtils_ + + extend ::Bundler::FileUtils::DryRun + + extend ::Bundler::FileUtils::LowMethods + + extend ::Bundler::FileUtils + + extend ::Bundler::FileUtils::StreamUtils_ + + def self.cd: (*untyped _) -> untyped + + def self.chdir: (*untyped _) -> untyped + + def self.chmod: (*untyped args, **untyped options) -> untyped + + def self.chmod_R: (*untyped args, **untyped options) -> untyped + + def self.chown: (*untyped args, **untyped options) -> untyped + + def self.chown_R: (*untyped args, **untyped options) -> untyped + + def self.cmp: (*untyped _) -> untyped + + def self.compare_file: (*untyped _) -> untyped + + def self.compare_stream: (*untyped _) -> untyped + + def self.copy: (*untyped args, **untyped options) -> untyped + + def self.copy_entry: (*untyped _) -> untyped + + def self.copy_file: (*untyped _) -> untyped + + def self.copy_stream: (*untyped _) -> untyped + + def self.cp: (*untyped args, **untyped options) -> untyped + + def self.cp_r: (*untyped args, **untyped options) -> untyped + + def self.getwd: (*untyped _) -> untyped + + def self.identical?: (*untyped _) -> untyped + + def self.install: (*untyped args, **untyped options) -> untyped + + def self.link: (*untyped args, **untyped options) -> untyped + + def self.ln: (*untyped args, **untyped options) -> untyped + + def self.ln_s: (*untyped args, **untyped options) -> untyped + + def self.ln_sf: (*untyped args, **untyped options) -> untyped + + def self.makedirs: (*untyped args, **untyped options) -> untyped + + def self.mkdir: (*untyped args, **untyped options) -> untyped + + def self.mkdir_p: (*untyped args, **untyped options) -> untyped + + def self.mkpath: (*untyped args, **untyped options) -> untyped + + def self.move: (*untyped args, **untyped options) -> untyped + + def self.mv: (*untyped args, **untyped options) -> untyped + + def self.pwd: (*untyped _) -> untyped + + def self.remove: (*untyped args, **untyped options) -> untyped + + def self.remove_dir: (*untyped _) -> untyped + + def self.remove_entry: (*untyped _) -> untyped + + def self.remove_entry_secure: (*untyped _) -> untyped + + def self.remove_file: (*untyped _) -> untyped + + def self.rm: (*untyped args, **untyped options) -> untyped + + def self.rm_f: (*untyped args, **untyped options) -> untyped + + def self.rm_r: (*untyped args, **untyped options) -> untyped + + def self.rm_rf: (*untyped args, **untyped options) -> untyped + + def self.rmdir: (*untyped args, **untyped options) -> untyped + + def self.rmtree: (*untyped args, **untyped options) -> untyped + + def self.safe_unlink: (*untyped args, **untyped options) -> untyped + + def self.symlink: (*untyped args, **untyped options) -> untyped + + def self.touch: (*untyped args, **untyped options) -> untyped + + def self.uptodate?: (*untyped _) -> untyped +end + +class Bundler::FileUtils::Entry_ + include ::Bundler::FileUtils::StreamUtils_ + + def blockdev?: () -> untyped + + def chardev?: () -> untyped + + def chmod: (untyped mode) -> untyped + + def chown: (untyped uid, untyped gid) -> untyped + + def copy: (untyped dest) -> untyped + + def copy_file: (untyped dest) -> untyped + + def copy_metadata: (untyped path) -> untyped + + def dereference?: () -> untyped + + def directory?: () -> untyped + + def door?: () -> untyped + + def entries: () -> untyped + + def exist?: () -> untyped + + def file?: () -> untyped + + def initialize: (untyped a, ?untyped b, ?untyped deref) -> void + + def inspect: () -> untyped + + def lstat: () -> untyped + + def lstat!: () -> untyped + + def path: () -> untyped + + def pipe?: () -> untyped + + def platform_support: () -> untyped + + def postorder_traverse: () -> untyped + + def prefix: () -> untyped + + def preorder_traverse: () -> untyped + + def rel: () -> untyped + + def remove: () -> untyped + + def remove_dir1: () -> untyped + + def remove_file: () -> untyped + + def socket?: () -> untyped + + def stat: () -> untyped + + def stat!: () -> untyped + + def symlink?: () -> untyped + + def traverse: () -> untyped + + def wrap_traverse: (untyped pre, untyped post) -> untyped +end + +Bundler::FileUtils::Entry_::DIRECTORY_TERM: untyped + +Bundler::FileUtils::Entry_::SYSCASE: untyped + +Bundler::FileUtils::Entry_::S_IF_DOOR: untyped + +module Bundler::FileUtils::LowMethods +end + +# This module has all methods of +# [`Bundler::FileUtils`](https://docs.ruby-lang.org/en/2.7.0/Bundler/FileUtils.html) +# module, but never changes files/directories. This equates to passing the +# `:noop` flag to methods in +# [`Bundler::FileUtils`](https://docs.ruby-lang.org/en/2.7.0/Bundler/FileUtils.html). +module Bundler::FileUtils::NoWrite + include ::Bundler::FileUtils::LowMethods + + include ::Bundler::FileUtils + + include ::Bundler::FileUtils::StreamUtils_ + + extend ::Bundler::FileUtils::NoWrite + + extend ::Bundler::FileUtils::LowMethods + + extend ::Bundler::FileUtils + + extend ::Bundler::FileUtils::StreamUtils_ + + def self.cd: (*untyped _) -> untyped + + def self.chdir: (*untyped _) -> untyped + + def self.chmod: (*untyped args, **untyped options) -> untyped + + def self.chmod_R: (*untyped args, **untyped options) -> untyped + + def self.chown: (*untyped args, **untyped options) -> untyped + + def self.chown_R: (*untyped args, **untyped options) -> untyped + + def self.cmp: (*untyped _) -> untyped + + def self.compare_file: (*untyped _) -> untyped + + def self.compare_stream: (*untyped _) -> untyped + + def self.copy: (*untyped args, **untyped options) -> untyped + + def self.copy_entry: (*untyped _) -> untyped + + def self.copy_file: (*untyped _) -> untyped + + def self.copy_stream: (*untyped _) -> untyped + + def self.cp: (*untyped args, **untyped options) -> untyped + + def self.cp_r: (*untyped args, **untyped options) -> untyped + + def self.getwd: (*untyped _) -> untyped + + def self.identical?: (*untyped _) -> untyped + + def self.install: (*untyped args, **untyped options) -> untyped + + def self.link: (*untyped args, **untyped options) -> untyped + + def self.ln: (*untyped args, **untyped options) -> untyped + + def self.ln_s: (*untyped args, **untyped options) -> untyped + + def self.ln_sf: (*untyped args, **untyped options) -> untyped + + def self.makedirs: (*untyped args, **untyped options) -> untyped + + def self.mkdir: (*untyped args, **untyped options) -> untyped + + def self.mkdir_p: (*untyped args, **untyped options) -> untyped + + def self.mkpath: (*untyped args, **untyped options) -> untyped + + def self.move: (*untyped args, **untyped options) -> untyped + + def self.mv: (*untyped args, **untyped options) -> untyped + + def self.pwd: (*untyped _) -> untyped + + def self.remove: (*untyped args, **untyped options) -> untyped + + def self.remove_dir: (*untyped _) -> untyped + + def self.remove_entry: (*untyped _) -> untyped + + def self.remove_entry_secure: (*untyped _) -> untyped + + def self.remove_file: (*untyped _) -> untyped + + def self.rm: (*untyped args, **untyped options) -> untyped + + def self.rm_f: (*untyped args, **untyped options) -> untyped + + def self.rm_r: (*untyped args, **untyped options) -> untyped + + def self.rm_rf: (*untyped args, **untyped options) -> untyped + + def self.rmdir: (*untyped args, **untyped options) -> untyped + + def self.rmtree: (*untyped args, **untyped options) -> untyped + + def self.safe_unlink: (*untyped args, **untyped options) -> untyped + + def self.symlink: (*untyped args, **untyped options) -> untyped + + def self.touch: (*untyped args, **untyped options) -> untyped + + def self.uptodate?: (*untyped _) -> untyped +end + +module Bundler::FileUtils::StreamUtils_ +end + +# This module has all methods of +# [`Bundler::FileUtils`](https://docs.ruby-lang.org/en/2.7.0/Bundler/FileUtils.html) +# module, but it outputs messages before acting. This equates to passing the +# `:verbose` flag to methods in +# [`Bundler::FileUtils`](https://docs.ruby-lang.org/en/2.7.0/Bundler/FileUtils.html). +module Bundler::FileUtils::Verbose + include ::Bundler::FileUtils + + include ::Bundler::FileUtils::StreamUtils_ + + extend ::Bundler::FileUtils::Verbose + + extend ::Bundler::FileUtils + + extend ::Bundler::FileUtils::StreamUtils_ + + def self.cd: (*untyped args, **untyped options) -> untyped + + def self.chdir: (*untyped args, **untyped options) -> untyped + + def self.chmod: (*untyped args, **untyped options) -> untyped + + def self.chmod_R: (*untyped args, **untyped options) -> untyped + + def self.chown: (*untyped args, **untyped options) -> untyped + + def self.chown_R: (*untyped args, **untyped options) -> untyped + + def self.cmp: (untyped a, untyped b) -> untyped + + def self.compare_file: (untyped a, untyped b) -> untyped + + def self.compare_stream: (untyped a, untyped b) -> untyped + + def self.copy: (*untyped args, **untyped options) -> untyped + + def self.copy_entry: (untyped src, untyped dest, ?untyped preserve, ?untyped dereference_root, ?untyped remove_destination) -> untyped + + def self.copy_file: (untyped src, untyped dest, ?untyped preserve, ?untyped dereference) -> untyped + + def self.copy_stream: (untyped src, untyped dest) -> untyped + + def self.cp: (*untyped args, **untyped options) -> untyped + + def self.cp_r: (*untyped args, **untyped options) -> untyped + + def self.getwd: () -> untyped + + def self.identical?: (untyped a, untyped b) -> untyped + + def self.install: (*untyped args, **untyped options) -> untyped + + def self.link: (*untyped args, **untyped options) -> untyped + + def self.ln: (*untyped args, **untyped options) -> untyped + + def self.ln_s: (*untyped args, **untyped options) -> untyped + + def self.ln_sf: (*untyped args, **untyped options) -> untyped + + def self.makedirs: (*untyped args, **untyped options) -> untyped + + def self.mkdir: (*untyped args, **untyped options) -> untyped + + def self.mkdir_p: (*untyped args, **untyped options) -> untyped + + def self.mkpath: (*untyped args, **untyped options) -> untyped + + def self.move: (*untyped args, **untyped options) -> untyped + + def self.mv: (*untyped args, **untyped options) -> untyped + + def self.pwd: () -> untyped + + def self.remove: (*untyped args, **untyped options) -> untyped + + def self.remove_dir: (untyped path, ?untyped force) -> untyped + + def self.remove_entry: (untyped path, ?untyped force) -> untyped + + def self.remove_entry_secure: (untyped path, ?untyped force) -> untyped + + def self.remove_file: (untyped path, ?untyped force) -> untyped + + def self.rm: (*untyped args, **untyped options) -> untyped + + def self.rm_f: (*untyped args, **untyped options) -> untyped + + def self.rm_r: (*untyped args, **untyped options) -> untyped + + def self.rm_rf: (*untyped args, **untyped options) -> untyped + + def self.rmdir: (*untyped args, **untyped options) -> untyped + + def self.rmtree: (*untyped args, **untyped options) -> untyped + + def self.safe_unlink: (*untyped args, **untyped options) -> untyped + + def self.symlink: (*untyped args, **untyped options) -> untyped + + def self.touch: (*untyped args, **untyped options) -> untyped + + def self.uptodate?: (untyped new, untyped old_list) -> untyped +end + +class Bundler::GemHelper + def allowed_push_host: () -> untyped + + def already_tagged?: () -> untyped + + def base: () -> untyped + + def build_checksum: (?untyped built_gem_path) -> untyped + + def build_gem: () -> untyped + + def built_gem_path: () -> untyped + + def clean?: () -> untyped + + def committed?: () -> untyped + + def current_branch: () -> untyped + + def default_remote: () -> untyped + + def gem_command: () -> untyped + + def gem_key: () -> untyped + + def gem_push?: () -> untyped + + def gem_push_host: () -> untyped + + def gemspec: () -> untyped + + def git_push: (?untyped remote) -> untyped + + def guard_clean: () -> untyped + + def initialize: (?untyped base, ?untyped name) -> void + + def install: () -> untyped + + def install_gem: (?untyped built_gem_path, ?untyped local) -> untyped + + def name: () -> untyped + + def rubygem_push: (untyped path) -> untyped + + def sh: (untyped cmd) { () -> untyped } -> untyped + + def sh_with_input: (untyped cmd) -> untyped + + def sh_with_status: (untyped cmd) { () -> untyped } -> untyped + + def spec_path: () -> untyped + + def tag_prefix=: (untyped tag_prefix) -> untyped + + def tag_version: () -> untyped + + def version: () -> untyped + + def version_tag: () -> untyped + + def self.instance: () -> untyped + + def self.instance=: (untyped instance) -> untyped + + def self.install_tasks: (?untyped opts) -> untyped + + def self.tag_prefix=: (untyped tag_prefix) -> untyped + + def self.gemspec: () { () -> untyped } -> untyped +end + +module Bundler::GemHelpers + def self.generic: (untyped p) -> untyped + + def self.generic_local_platform: () -> untyped + + def self.platform_specificity_match: (untyped spec_platform, untyped user_platform) -> untyped + + def self.select_best_platform_match: (untyped specs, untyped platform) -> untyped +end + +Bundler::GemHelpers::GENERICS: untyped + +Bundler::GemHelpers::GENERIC_CACHE: untyped + +class Bundler::GemHelpers::PlatformMatch < Struct + def <=>: (untyped other) -> untyped + + def cpu_match: () -> untyped + + def cpu_match=: (untyped _) -> untyped + + def os_match: () -> untyped + + def os_match=: (untyped _) -> untyped + + def platform_version_match: () -> untyped + + def platform_version_match=: (untyped _) -> untyped + + def self.[]: (*untyped _) -> untyped + + def self.cpu_match: (untyped spec_platform, untyped user_platform) -> untyped + + def self.members: () -> untyped + + def self.new: (*untyped _) -> untyped + + def self.os_match: (untyped spec_platform, untyped user_platform) -> untyped + + def self.platform_version_match: (untyped spec_platform, untyped user_platform) -> untyped +end + +Bundler::GemHelpers::PlatformMatch::Elem: untyped + +Bundler::GemHelpers::PlatformMatch::EXACT_MATCH: untyped + +Bundler::GemHelpers::PlatformMatch::WORST_MATCH: untyped + +class Bundler::GemNotFound < Bundler::BundlerError + def status_code: () -> untyped +end + +class Bundler::GemRequireError < Bundler::BundlerError + def initialize: (untyped orig_exception, untyped msg) -> void + + def orig_exception: () -> untyped + + def status_code: () -> untyped +end + +class Bundler::GemfileError < Bundler::BundlerError + def status_code: () -> untyped +end + +class Bundler::GemfileEvalError < Bundler::GemfileError +end + +class Bundler::GemfileLockNotFound < Bundler::BundlerError + def status_code: () -> untyped +end + +class Bundler::GemfileNotFound < Bundler::BundlerError + def status_code: () -> untyped +end + +class Bundler::GemspecError < Bundler::BundlerError + def status_code: () -> untyped +end + +class Bundler::GenericSystemCallError < Bundler::BundlerError + def initialize: (untyped underlying_error, untyped message) -> void + + def status_code: () -> untyped + + def underlying_error: () -> untyped +end + +class Bundler::GitError < Bundler::BundlerError + def status_code: () -> untyped +end + +class Bundler::HTTPError < Bundler::BundlerError + def filter_uri: (untyped uri) -> untyped + + def status_code: () -> untyped +end + +# Handles all the fetching with the rubygems server +class Bundler::Fetcher +end + +# This error is raised if HTTP authentication is required, but not provided. +class Bundler::Fetcher::AuthenticationRequiredError < Bundler::HTTPError +end + +# This error is raised if HTTP authentication is provided, but incorrect. +class Bundler::Fetcher::BadAuthenticationError < Bundler::HTTPError +end + +# This is the error raised if +# [`OpenSSL`](https://docs.ruby-lang.org/en/2.7.0/OpenSSL.html) fails the cert +# verification +class Bundler::Fetcher::CertificateFailureError < Bundler::HTTPError +end + +# This error is raised if the API returns a 413 (only printed in verbose) +class Bundler::Fetcher::FallbackError < Bundler::HTTPError +end + +# This error is raised when it looks like the network is down +class Bundler::Fetcher::NetworkDownError < Bundler::HTTPError +end + +# This is the error raised when a source is HTTPS and +# [`OpenSSL`](https://docs.ruby-lang.org/en/2.7.0/OpenSSL.html) didn't load +class Bundler::Fetcher::SSLError < Bundler::HTTPError +end + +class Bundler::Index[out Elem] + include ::Enumerable + + def <<: (untyped spec) -> untyped + + def ==: (untyped other) -> untyped + + def []: (untyped query, ?untyped base) -> untyped + + def add_source: (untyped index) -> untyped + + def all_specs: () -> untyped + + def dependencies_eql?: (untyped spec, untyped other_spec) -> untyped + + def dependency_names: () -> untyped + + def each: () { () -> untyped } -> untyped + + def empty?: () -> untyped + + def initialize: () -> void + + def inspect: () -> untyped + + def local_search: (untyped query, ?untyped base) -> untyped + + def search: (untyped query, ?untyped base) -> untyped + + def search_all: (untyped name) -> untyped + + def size: () -> untyped + + def sort_specs: (untyped specs) -> untyped + + def sources: () -> untyped + + def spec_names: () -> untyped + + def specs: () -> untyped + + # returns a list of the dependencies + def unmet_dependency_names: () -> untyped + + def unsorted_search: (untyped query, untyped base) -> untyped + + def use: (untyped other, ?untyped override_dupes) -> untyped + + def self.build: () -> untyped + + def self.sort_specs: (untyped specs) -> untyped +end + +Bundler::Index::EMPTY_SEARCH: untyped + +Bundler::Index::NULL: untyped + +Bundler::Index::RUBY: untyped + +class Bundler::InstallError < Bundler::BundlerError + def status_code: () -> untyped +end + +class Bundler::InstallHookError < Bundler::BundlerError + def status_code: () -> untyped +end + +class Bundler::InvalidOption < Bundler::BundlerError + def status_code: () -> untyped +end + +class Bundler::LazySpecification + include ::Bundler::MatchPlatform + + include ::Bundler::GemHelpers + + def ==: (untyped other) -> untyped + + def __materialize__: () -> untyped + + def dependencies: () -> untyped + + def full_name: () -> untyped + + def git_version: () -> untyped + + def identifier: () -> untyped + + def initialize: (untyped name, untyped version, untyped platform, ?untyped source) -> void + + def name: () -> String + + def platform: () -> untyped + + def remote: () -> untyped + + def remote=: (untyped remote) -> untyped + + def respond_to?: (*untyped args) -> untyped + + def satisfies?: (untyped dependency) -> untyped + + def source: () -> untyped + + def source=: (untyped source) -> untyped + + def to_lock: () -> untyped + + def to_s: () -> untyped + + def version: () -> String +end + +class Bundler::LazySpecification::Identifier < Struct + include ::Comparable + + extend ::T::Generic + + def <=>: (untyped other) -> untyped + + def dependencies: () -> untyped + + def dependencies=: (untyped _) -> untyped + + def name: () -> untyped + + def name=: (untyped _) -> untyped + + def platform: () -> untyped + + def platform=: (untyped _) -> untyped + + def platform_string: () -> untyped + + def source: () -> untyped + + def source=: (untyped _) -> untyped + + def version: () -> untyped + + def version=: (untyped _) -> untyped + + def self.[]: (*untyped _) -> untyped + + def self.members: () -> untyped + + def self.new: (*untyped _) -> untyped +end + +Bundler::LazySpecification::Identifier::Elem: untyped + +class Bundler::LockfileError < Bundler::BundlerError + def status_code: () -> untyped +end + +class Bundler::LockfileParser + def bundler_version: () -> untyped + + def dependencies: () -> Hash[String, Bundler::Dependency] + + def initialize: (untyped lockfile) -> void + + def platforms: () -> untyped + + def ruby_version: () -> untyped + + def sources: () -> untyped + + def specs: () -> ::Array[::Bundler::LazySpecification] + + def warn_for_outdated_bundler_version: () -> untyped + + def self.sections_in_lockfile: (untyped lockfile_contents) -> untyped + + def self.sections_to_ignore: (?untyped base_version) -> untyped + + def self.unknown_sections_in_lockfile: (untyped lockfile_contents) -> untyped +end + +Bundler::LockfileParser::BUNDLED: untyped + +Bundler::LockfileParser::DEPENDENCIES: untyped + +Bundler::LockfileParser::ENVIRONMENT_VERSION_SECTIONS: untyped + +Bundler::LockfileParser::GEM: untyped + +Bundler::LockfileParser::GIT: untyped + +Bundler::LockfileParser::KNOWN_SECTIONS: untyped + +Bundler::LockfileParser::NAME_VERSION: untyped + +Bundler::LockfileParser::OPTIONS: untyped + +Bundler::LockfileParser::PATH: untyped + +Bundler::LockfileParser::PLATFORMS: untyped + +Bundler::LockfileParser::PLUGIN: untyped + +Bundler::LockfileParser::RUBY: untyped + +Bundler::LockfileParser::SECTIONS_BY_VERSION_INTRODUCED: untyped + +Bundler::LockfileParser::SOURCE: untyped + +Bundler::LockfileParser::SPECS: untyped + +Bundler::LockfileParser::TYPES: untyped + +class Bundler::MarshalError < StandardError +end + +module Bundler::MatchPlatform + include ::Bundler::GemHelpers + + def match_platform: (untyped p) -> untyped + + def self.platforms_match?: (untyped gemspec_platform, untyped local_platform) -> untyped +end + +# [`Bundler::Molinillo`](https://docs.ruby-lang.org/en/2.7.0/Bundler/Molinillo.html) +# is a generic dependency resolution algorithm. +module Bundler::Molinillo +end + +Bundler::Molinillo::VERSION: untyped + +# An error caused by attempting to fulfil a dependency that was circular +# +# @note This exception will be thrown iff a {Vertex} is added to a +# +# ``` +# {DependencyGraph} that has a {DependencyGraph::Vertex#path_to?} an +# existing {DependencyGraph::Vertex} +# ``` +class Bundler::Molinillo::CircularDependencyError < Bundler::Molinillo::ResolverError + # [`Set`](https://docs.ruby-lang.org/en/2.7.0/Set.html) + # : the dependencies responsible for causing the error + def dependencies: () -> untyped + + def initialize: (untyped vertices) -> void +end + +# Hacks needed for old Ruby versions. +module Bundler::Molinillo::Compatibility + def self.flat_map: (untyped enum) { () -> untyped } -> untyped +end + +# @!visibility private +module Bundler::Molinillo::Delegates +end + +# [`Delegates`](https://docs.ruby-lang.org/en/2.7.0/Bundler/Molinillo/Delegates.html) +# all {Bundler::Molinillo::ResolutionState} methods to a `#state` property. +module Bundler::Molinillo::Delegates::ResolutionState + # (see Bundler::Molinillo::ResolutionState#activated) + def activated: () -> untyped + + # (see Bundler::Molinillo::ResolutionState#conflicts) + def conflicts: () -> untyped + + # (see Bundler::Molinillo::ResolutionState#depth) + def depth: () -> untyped + + # (see Bundler::Molinillo::ResolutionState#name) + def name: () -> untyped + + # (see Bundler::Molinillo::ResolutionState#possibilities) + def possibilities: () -> untyped + + # (see Bundler::Molinillo::ResolutionState#requirement) + def requirement: () -> untyped + + # (see Bundler::Molinillo::ResolutionState#requirements) + def requirements: () -> untyped + + # (see Bundler::Molinillo::ResolutionState#unused\_unwind\_options) + def unused_unwind_options: () -> untyped +end + +# [`Delegates`](https://docs.ruby-lang.org/en/2.7.0/Bundler/Molinillo/Delegates.html) +# all {Bundler::Molinillo::SpecificationProvider} methods to a +# `#specification\_provider` property. +module Bundler::Molinillo::Delegates::SpecificationProvider + def allow_missing?: (untyped dependency) -> untyped + + def dependencies_for: (untyped specification) -> untyped + + def name_for: (untyped dependency) -> untyped + + # (see + # [`Bundler::Molinillo::SpecificationProvider#name_for_explicit_dependency_source`](https://docs.ruby-lang.org/en/2.7.0/Bundler/Molinillo/SpecificationProvider.html#method-i-name_for_explicit_dependency_source)) + def name_for_explicit_dependency_source: () -> untyped + + # (see + # [`Bundler::Molinillo::SpecificationProvider#name_for_locking_dependency_source`](https://docs.ruby-lang.org/en/2.7.0/Bundler/Molinillo/SpecificationProvider.html#method-i-name_for_locking_dependency_source)) + def name_for_locking_dependency_source: () -> untyped + + def requirement_satisfied_by?: (untyped requirement, untyped activated, untyped spec) -> untyped + + def search_for: (untyped dependency) -> untyped + + def sort_dependencies: (untyped dependencies, untyped activated, untyped conflicts) -> untyped +end + +# A directed acyclic graph that is tuned to hold named dependencies +class Bundler::Molinillo::DependencyGraph[out Elem] + include ::TSort + + include ::Enumerable + + def ==: (untyped other) -> untyped + + def add_child_vertex: (untyped name, untyped payload, untyped parent_names, untyped requirement) -> untyped + + def add_edge: (untyped origin, untyped destination, untyped requirement) -> untyped + + def add_vertex: (untyped name, untyped payload, ?untyped root) -> untyped + + def delete_edge: (untyped edge) -> untyped + + def detach_vertex_named: (untyped name) -> untyped + + def each: () { () -> untyped } -> untyped + + def initialize: () -> void + + # @return [String] a string suitable for debugging + def inspect: () -> untyped + + # @return [Log] the op log for this graph + def log: () -> untyped + + def rewind_to: (untyped tag) -> untyped + + def root_vertex_named: (untyped name) -> untyped + + def set_payload: (untyped name, untyped payload) -> untyped + + def tag: (untyped tag) -> untyped + + def to_dot: (?untyped options) -> untyped + + def tsort_each_child: (untyped vertex) { () -> untyped } -> untyped + + # @!visibility private + # + # Alias for: + # [`each`](https://docs.ruby-lang.org/en/2.7.0/Bundler/Molinillo/DependencyGraph.html#method-i-each) + def tsort_each_node: () -> untyped + + def vertex_named: (untyped name) -> untyped + + # @return [{String => Vertex}] the vertices of the dependency graph, keyed + # + # ``` + # by {Vertex#name} + # ``` + def vertices: () -> untyped + + def self.tsort: (untyped vertices) -> untyped +end + +# An action that modifies a {DependencyGraph} that is reversible. @abstract +class Bundler::Molinillo::DependencyGraph::Action + def down: (untyped graph) -> untyped + + # @return [Action,Nil] The next action + def next: () -> untyped + + def next=: (untyped _) -> untyped + + # @return [Action,Nil] The previous action + def previous: () -> untyped + + def previous=: (untyped previous) -> untyped + + def up: (untyped graph) -> untyped + + # @return [Symbol] The name of the action. + def self.action_name: () -> untyped +end + +# @!visibility private (see +# [`DependencyGraph#add_edge_no_circular`](https://docs.ruby-lang.org/en/2.7.0/Bundler/Molinillo/DependencyGraph.html#method-i-add_edge_no_circular)) +class Bundler::Molinillo::DependencyGraph::AddEdgeNoCircular < Bundler::Molinillo::DependencyGraph::Action + # @return [String] the name of the destination of the edge + def destination: () -> untyped + + def down: (untyped graph) -> untyped + + def initialize: (untyped origin, untyped destination, untyped requirement) -> void + + def make_edge: (untyped graph) -> untyped + + # @return [String] the name of the origin of the edge + def origin: () -> untyped + + # @return [Object] the requirement that the edge represents + def requirement: () -> untyped + + def up: (untyped graph) -> untyped + + # (see Action.action\_name) + def self.action_name: () -> untyped +end + +class Bundler::Molinillo::DependencyGraph::AddVertex < Bundler::Molinillo::DependencyGraph::Action + def down: (untyped graph) -> untyped + + def initialize: (untyped name, untyped payload, untyped root) -> void + + def name: () -> untyped + + def payload: () -> untyped + + def root: () -> untyped + + def up: (untyped graph) -> untyped + + def self.action_name: () -> untyped +end + +# @!visibility private (see +# [`DependencyGraph#delete_edge`](https://docs.ruby-lang.org/en/2.7.0/Bundler/Molinillo/DependencyGraph.html#method-i-delete_edge)) +class Bundler::Molinillo::DependencyGraph::DeleteEdge < Bundler::Molinillo::DependencyGraph::Action + # @return [String] the name of the destination of the edge + def destination_name: () -> untyped + + def down: (untyped graph) -> untyped + + def initialize: (untyped origin_name, untyped destination_name, untyped requirement) -> void + + def make_edge: (untyped graph) -> untyped + + # @return [String] the name of the origin of the edge + def origin_name: () -> untyped + + # @return [Object] the requirement that the edge represents + def requirement: () -> untyped + + def up: (untyped graph) -> untyped + + # (see Action.action\_name) + def self.action_name: () -> untyped +end + +# @!visibility private @see +# [`DependencyGraph#detach_vertex_named`](https://docs.ruby-lang.org/en/2.7.0/Bundler/Molinillo/DependencyGraph.html#method-i-detach_vertex_named) +class Bundler::Molinillo::DependencyGraph::DetachVertexNamed < Bundler::Molinillo::DependencyGraph::Action + def down: (untyped graph) -> untyped + + def initialize: (untyped name) -> void + + # @return [String] the name of the vertex to detach + def name: () -> untyped + + def up: (untyped graph) -> untyped + + # (see Action#name) + def self.action_name: () -> untyped +end + +# A directed edge of a {DependencyGraph} @attr [Vertex] origin The origin of the +# directed edge @attr [Vertex] destination The destination of the directed edge +# @attr [Object] requirement The requirement the directed edge represents +class Bundler::Molinillo::DependencyGraph::Edge < Struct + def destination: () -> untyped + + def destination=: (untyped _) -> untyped + + def origin: () -> untyped + + def origin=: (untyped _) -> untyped + + def requirement: () -> untyped + + def requirement=: (untyped _) -> untyped + + def self.[]: (*untyped _) -> untyped + + def self.members: () -> untyped + + def self.new: (*untyped _) -> untyped +end + +Bundler::Molinillo::DependencyGraph::Edge::Elem: untyped + +# A log for dependency graph actions +class Bundler::Molinillo::DependencyGraph::Log + extend ::Enumerable + + def add_edge_no_circular: (untyped graph, untyped origin, untyped destination, untyped requirement) -> untyped + + def add_vertex: (untyped graph, untyped name, untyped payload, untyped root) -> untyped + + def delete_edge: (untyped graph, untyped origin_name, untyped destination_name, untyped requirement) -> untyped + + def detach_vertex_named: (untyped graph, untyped name) -> untyped + + def each: () { () -> untyped } -> untyped + + def initialize: () -> void + + def pop!: (untyped graph) -> untyped + + # @!visibility private Enumerates each action in the log in reverse order + # @yield [Action] + def reverse_each: () -> untyped + + def rewind_to: (untyped graph, untyped tag) -> untyped + + def set_payload: (untyped graph, untyped name, untyped payload) -> untyped + + def tag: (untyped graph, untyped tag) -> untyped +end + +Bundler::Molinillo::DependencyGraph::Log::Elem: untyped + +class Bundler::Molinillo::DependencyGraph::SetPayload < Bundler::Molinillo::DependencyGraph::Action + def down: (untyped graph) -> untyped + + def initialize: (untyped name, untyped payload) -> void + + def name: () -> untyped + + def payload: () -> untyped + + def up: (untyped graph) -> untyped + + def self.action_name: () -> untyped +end + +# @!visibility private @see +# [`DependencyGraph#tag`](https://docs.ruby-lang.org/en/2.7.0/Bundler/Molinillo/DependencyGraph.html#method-i-tag) +class Bundler::Molinillo::DependencyGraph::Tag < Bundler::Molinillo::DependencyGraph::Action + def down: (untyped _graph) -> untyped + + def initialize: (untyped tag) -> void + + # @return [Object] An opaque tag + def tag: () -> untyped + + def up: (untyped _graph) -> untyped + + # (see Action.action\_name) + def self.action_name: () -> untyped +end + +# A vertex in a {DependencyGraph} that encapsulates a {#name} and a {#payload} +class Bundler::Molinillo::DependencyGraph::Vertex + def ==: (untyped other) -> untyped + + def _path_to?: (untyped other, ?untyped visited) -> untyped + + def ancestor?: (untyped other) -> untyped + + def descendent?: (untyped other) -> untyped + + def eql?: (untyped other) -> untyped + + # @return [Array] the explicit requirements that required + # + # ```ruby + # this vertex + # ``` + def explicit_requirements: () -> untyped + + # @return [Fixnum] a hash for the vertex based upon its {#name} + def hash: () -> untyped + + # @return [Array] the edges of {#graph} that have `self` as their + # + # ``` + # {Edge#destination} + # ``` + def incoming_edges: () -> untyped + + def incoming_edges=: (untyped incoming_edges) -> untyped + + def initialize: (untyped name, untyped payload) -> void + + # @return [String] a string suitable for debugging + def inspect: () -> untyped + + def is_reachable_from?: (untyped other) -> untyped + + # @return [String] the name of the vertex + def name: () -> untyped + + def name=: (untyped name) -> untyped + + # @return [Array] the edges of {#graph} that have `self` as their + # + # ``` + # {Edge#origin} + # ``` + def outgoing_edges: () -> untyped + + def outgoing_edges=: (untyped outgoing_edges) -> untyped + + def path_to?: (untyped other) -> untyped + + # @return [Object] the payload the vertex holds + def payload: () -> untyped + + def payload=: (untyped payload) -> untyped + + # @return [Array] the vertices of {#graph} that have an edge with + # + # ``` + # `self` as their {Edge#destination} + # ``` + def predecessors: () -> untyped + + # @return [Set] the vertices of {#graph} where `self` is a + # + # ``` + # {#descendent?} + # ``` + def recursive_predecessors: () -> untyped + + # @return [Set] the vertices of {#graph} where `self` is an + # + # ``` + # {#ancestor?} + # ``` + def recursive_successors: () -> untyped + + # @return [Array] all of the requirements that required + # + # ```ruby + # this vertex + # ``` + def requirements: () -> untyped + + # @return [Boolean] whether the vertex is considered a root vertex + def root: () -> untyped + + def root=: (untyped root) -> untyped + + # @return [Boolean] whether the vertex is considered a root vertex + def root?: () -> untyped + + def shallow_eql?: (untyped other) -> untyped + + # @return [Array] the vertices of {#graph} that have an edge with + # + # ``` + # `self` as their {Edge#origin} + # ``` + def successors: () -> untyped +end + +# A state that encapsulates a set of {#requirements} with an {Array} of +# possibilities +class Bundler::Molinillo::DependencyState < Bundler::Molinillo::ResolutionState + # Removes a possibility from `self` @return [PossibilityState] a state with a + # single possibility, + # + # ```ruby + # the possibility that was removed from `self` + # ``` + def pop_possibility_state: () -> untyped +end + +Bundler::Molinillo::DependencyState::Elem: untyped + +# An error caused by searching for a dependency that is completely unknown, i.e. +# has no versions available whatsoever. +class Bundler::Molinillo::NoSuchDependencyError < Bundler::Molinillo::ResolverError + # @return [Object] the dependency that could not be found + def dependency: () -> untyped + + def dependency=: (untyped dependency) -> untyped + + def initialize: (untyped dependency, ?untyped required_by) -> void + + # The error message for the missing dependency, including the specifications + # that had this dependency. + def message: () -> untyped + + # @return [Array] the specifications that depended upon {#dependency} + def required_by: () -> untyped + + def required_by=: (untyped required_by) -> untyped +end + +# A state that encapsulates a single possibility to fulfill the given +# {#requirement} +class Bundler::Molinillo::PossibilityState < Bundler::Molinillo::ResolutionState +end + +Bundler::Molinillo::PossibilityState::Elem: untyped + +class Bundler::Molinillo::ResolutionState < Struct + def activated: () -> untyped + + def activated=: (untyped _) -> untyped + + def conflicts: () -> untyped + + def conflicts=: (untyped _) -> untyped + + def depth: () -> untyped + + def depth=: (untyped _) -> untyped + + def name: () -> untyped + + def name=: (untyped _) -> untyped + + def possibilities: () -> untyped + + def possibilities=: (untyped _) -> untyped + + def requirement: () -> untyped + + def requirement=: (untyped _) -> untyped + + def requirements: () -> untyped + + def requirements=: (untyped _) -> untyped + + def unused_unwind_options: () -> untyped + + def unused_unwind_options=: (untyped _) -> untyped + + def self.[]: (*untyped _) -> untyped + + # Returns an empty resolution state @return [ResolutionState] an empty state + def self.empty: () -> untyped + + def self.members: () -> untyped + + def self.new: (*untyped _) -> untyped +end + +Bundler::Molinillo::ResolutionState::Elem: untyped + +# This class encapsulates a dependency resolver. The resolver is responsible for +# determining which set of dependencies to activate, with feedback from the +# {#specification\_provider} +class Bundler::Molinillo::Resolver + def initialize: (untyped specification_provider, untyped resolver_ui) -> void + + def resolve: (untyped requested, ?untyped base) -> untyped + + # @return [UI] the UI module used to communicate back to the user + # + # ```ruby + # during the resolution process + # ``` + def resolver_ui: () -> untyped + + # @return [SpecificationProvider] the specification provider used + # + # ``` + # in the resolution process + # ``` + def specification_provider: () -> untyped +end + +# A specific resolution from a given {Resolver} +class Bundler::Molinillo::Resolver::Resolution + include ::Bundler::Molinillo::Delegates::SpecificationProvider + + include ::Bundler::Molinillo::Delegates::ResolutionState + + # @return [DependencyGraph] the base dependency graph to which + # + # ```ruby + # dependencies should be 'locked' + # ``` + def base: () -> untyped + + def initialize: (untyped specification_provider, untyped resolver_ui, untyped requested, untyped base) -> void + + def iteration_rate=: (untyped iteration_rate) -> untyped + + # @return [Array] the dependencies that were explicitly required + def original_requested: () -> untyped + + # Resolves the {#original\_requested} dependencies into a full dependency + # + # ```ruby + # graph + # ``` + # + # @raise [ResolverError] if successful resolution is impossible @return + # [DependencyGraph] the dependency graph of successfully resolved + # + # ```ruby + # dependencies + # ``` + def resolve: () -> untyped + + # @return [UI] the UI that knows how to communicate feedback about the + # + # ```ruby + # resolution process back to the user + # ``` + def resolver_ui: () -> untyped + + # @return [SpecificationProvider] the provider that knows about + # + # ``` + # dependencies, requirements, specifications, versions, etc. + # ``` + def specification_provider: () -> untyped + + def started_at=: (untyped started_at) -> untyped + + def states=: (untyped states) -> untyped +end + +class Bundler::Molinillo::Resolver::Resolution::Conflict < Struct + def activated_by_name: () -> untyped + + def activated_by_name=: (untyped _) -> untyped + + def existing: () -> untyped + + def existing=: (untyped _) -> untyped + + def locked_requirement: () -> untyped + + def locked_requirement=: (untyped _) -> untyped + + # @return [Object] a spec that was unable to be activated due to a conflict + def possibility: () -> untyped + + def possibility_set: () -> untyped + + def possibility_set=: (untyped _) -> untyped + + def requirement: () -> untyped + + def requirement=: (untyped _) -> untyped + + def requirement_trees: () -> untyped + + def requirement_trees=: (untyped _) -> untyped + + def requirements: () -> untyped + + def requirements=: (untyped _) -> untyped + + def underlying_error: () -> untyped + + def underlying_error=: (untyped _) -> untyped + + def self.[]: (*untyped _) -> untyped + + def self.members: () -> untyped + + def self.new: (*untyped _) -> untyped +end + +Bundler::Molinillo::Resolver::Resolution::Conflict::Elem: untyped + +class Bundler::Molinillo::Resolver::Resolution::PossibilitySet < Struct + def dependencies: () -> untyped + + def dependencies=: (untyped _) -> untyped + + # @return [Object] most up-to-date dependency in the possibility set + def latest_version: () -> untyped + + def possibilities: () -> untyped + + def possibilities=: (untyped _) -> untyped + + # [`String`](https://docs.ruby-lang.org/en/2.7.0/String.html) representation + # of the possibility set, for debugging + def to_s: () -> untyped + + def self.[]: (*untyped _) -> untyped + + def self.members: () -> untyped + + def self.new: (*untyped _) -> untyped +end + +Bundler::Molinillo::Resolver::Resolution::PossibilitySet::Elem: untyped + +class Bundler::Molinillo::Resolver::Resolution::UnwindDetails < Struct + include ::Comparable + + def <=>: (untyped other) -> untyped + + # @return [Array] array of all the requirements that led to the need for + # + # ```ruby + # this unwind + # ``` + def all_requirements: () -> untyped + + def conflicting_requirements: () -> untyped + + def conflicting_requirements=: (untyped _) -> untyped + + def requirement_tree: () -> untyped + + def requirement_tree=: (untyped _) -> untyped + + def requirement_trees: () -> untyped + + def requirement_trees=: (untyped _) -> untyped + + def requirements_unwound_to_instead: () -> untyped + + def requirements_unwound_to_instead=: (untyped _) -> untyped + + # @return [Integer] index of state requirement in reversed requirement tree + # + # ```ruby + # (the conflicting requirement itself will be at position 0) + # ``` + def reversed_requirement_tree_index: () -> untyped + + def state_index: () -> untyped + + def state_index=: (untyped _) -> untyped + + def state_requirement: () -> untyped + + def state_requirement=: (untyped _) -> untyped + + # @return [Array] array of sub-dependencies to avoid when choosing a + # + # ``` + # new possibility for the state we've unwound to. Only relevant for + # non-primary unwinds + # ``` + def sub_dependencies_to_avoid: () -> untyped + + # @return [Boolean] where the requirement of the state we're unwinding + # + # ``` + # to directly caused the conflict. Note: in this case, it is + # impossible for the state we're unwinding to to be a parent of + # any of the other conflicting requirements (or we would have + # circularity) + # ``` + def unwinding_to_primary_requirement?: () -> untyped + + def self.[]: (*untyped _) -> untyped + + def self.members: () -> untyped + + def self.new: (*untyped _) -> untyped +end + +Bundler::Molinillo::Resolver::Resolution::UnwindDetails::Elem: untyped + +# An error that occurred during the resolution process +class Bundler::Molinillo::ResolverError < StandardError +end + +# Provides information about specifications and dependencies to the resolver, +# allowing the {Resolver} class to remain generic while still providing power +# and flexibility. +# +# This module contains the methods that users of +# [`Bundler::Molinillo`](https://docs.ruby-lang.org/en/2.7.0/Bundler/Molinillo.html) +# must to implement, using knowledge of their own model classes. +module Bundler::Molinillo::SpecificationProvider + def allow_missing?: (untyped dependency) -> untyped + + def dependencies_for: (untyped specification) -> untyped + + def name_for: (untyped dependency) -> untyped + + # @return [String] the name of the source of explicit dependencies, i.e. + # + # ``` + # those passed to {Resolver#resolve} directly. + # ``` + def name_for_explicit_dependency_source: () -> untyped + + # @return [String] the name of the source of 'locked' dependencies, i.e. + # + # ``` + # those passed to {Resolver#resolve} directly as the `base` + # ``` + def name_for_locking_dependency_source: () -> untyped + + def requirement_satisfied_by?: (untyped requirement, untyped activated, untyped spec) -> untyped + + def search_for: (untyped dependency) -> untyped + + def sort_dependencies: (untyped dependencies, untyped activated, untyped conflicts) -> untyped +end + +# Conveys information about the resolution process to a user. +module Bundler::Molinillo::UI + # Called after resolution ends (either successfully or with an error). By + # default, prints a newline. + # + # @return [void] + def after_resolution: () -> untyped + + # Called before resolution begins. + # + # @return [void] + def before_resolution: () -> untyped + + def debug: (?untyped depth) -> untyped + + # Whether or not debug messages should be printed. By default, whether or not + # the `MOLINILLO\_DEBUG` environment variable is set. + # + # @return [Boolean] + def debug?: () -> untyped + + # Called roughly every {#progress\_rate}, this method should convey progress + # to the user. + # + # @return [void] + def indicate_progress: () -> untyped + + # The {IO} object that should be used to print output. `STDOUT`, by default. + # + # @return [IO] + def output: () -> untyped + + # How often progress should be conveyed to the user via {#indicate\_progress}, + # in seconds. A third of a second, by default. + # + # @return [Float] + def progress_rate: () -> untyped +end + +# An error caused by conflicts in version +class Bundler::Molinillo::VersionConflict < Bundler::Molinillo::ResolverError + include ::Bundler::Molinillo::Delegates::SpecificationProvider + + # @return [{String => Resolution::Conflict}] the conflicts that caused + # + # ```ruby + # resolution to fail + # ``` + def conflicts: () -> untyped + + def initialize: (untyped conflicts, untyped specification_provider) -> void + + def message_with_trees: (?untyped opts) -> untyped + + # @return [SpecificationProvider] the specification provider used during + # + # ```ruby + # resolution + # ``` + def specification_provider: () -> untyped +end + +class Bundler::NoSpaceOnDeviceError < Bundler::PermissionError + def message: () -> untyped + + def status_code: () -> untyped +end + +class Bundler::OperationNotSupportedError < Bundler::PermissionError + def message: () -> untyped + + def status_code: () -> untyped +end + +class Bundler::PathError < Bundler::BundlerError + def status_code: () -> untyped +end + +class Bundler::PermissionError < Bundler::BundlerError + def action: () -> untyped + + def initialize: (untyped path, ?untyped permission_type) -> void + + def message: () -> untyped + + def status_code: () -> untyped +end + +# This is the interfacing class represents the API that we intend to provide the +# plugins to use. +# +# For plugins to be independent of the +# [`Bundler`](https://docs.ruby-lang.org/en/2.7.0/Bundler.html) internals they +# shall limit their interactions to methods of this class only. This will save +# them from breaking when some internal change. +# +# Currently we are delegating the methods defined in +# [`Bundler`](https://docs.ruby-lang.org/en/2.7.0/Bundler.html) class to itself. +# So, this class acts as a buffer. +# +# If there is some change in the +# [`Bundler`](https://docs.ruby-lang.org/en/2.7.0/Bundler.html) class that is +# incompatible to its previous behavior or if otherwise desired, we can +# reimplement(or implement) the method to preserve compatibility. +# +# To use this, either the class can inherit this class or use it directly. For +# example of both types of use, refer the file `spec/plugins/command.rb` +# +# To use it without inheriting, you will have to create an object of this to use +# the functions (except for declaration functions like command, source, and +# hooks). +# Manages which plugins are installed and their sources. This also is supposed +# to map which plugin does what (currently the features are not implemented so +# this class is now a stub class). +# Handles the installation of plugin in appropriate directories. +# +# This class is supposed to be wrapper over the existing gem installation infra +# but currently it itself handles everything as the Source's subclasses (e.g. +# Source::RubyGems) are heavily dependent on the Gemfile. +# SourceList object to be used while parsing the Gemfile, setting the +# approptiate options to be used with Source classes for plugin installation +module Bundler::Plugin + def self.add_command: (untyped command, untyped cls) -> untyped + + def self.add_hook: (untyped event) { () -> untyped } -> untyped + + def self.add_source: (untyped source, untyped cls) -> untyped + + def self.cache: () -> untyped + + def self.command?: (untyped command) -> untyped + + def self.exec_command: (untyped command, untyped args) -> untyped + + def self.gemfile_install: (?untyped gemfile) { () -> untyped } -> untyped + + def self.global_root: () -> untyped + + def self.hook: (untyped event, *untyped args) { () -> untyped } -> untyped + + def self.index: () -> untyped + + def self.install: (untyped names, untyped options) -> untyped + + def self.installed?: (untyped plugin) -> untyped + + def self.local_root: () -> untyped + + def self.reset!: () -> untyped + + def self.root: () -> untyped + + def self.source: (untyped name) -> untyped + + def self.source?: (untyped name) -> untyped + + def self.source_from_lock: (untyped locked_opts) -> untyped +end + +Bundler::Plugin::PLUGIN_FILE_NAME: untyped + +class Bundler::Plugin::API + # The cache dir to be used by the plugins for storage + # + # @return [Pathname] path of the cache dir + def cache_dir: () -> untyped + + def method_missing: (untyped name, *untyped args) { () -> untyped } -> untyped + + def tmp: (*untyped names) -> untyped + + def self.command: (untyped command, ?untyped cls) -> untyped + + def self.hook: (untyped event) { () -> untyped } -> untyped + + def self.source: (untyped source, ?untyped cls) -> untyped +end + +# Dsl to parse the Gemfile looking for plugins to install +class Bundler::Plugin::DSL < Bundler::Dsl + def _gem: (untyped name, *untyped args) -> untyped + + # This lists the plugins that was added automatically and not specified by the + # user. + # + # When we encounter :type attribute with a source block, we add a plugin by + # name bundler-source- to list of plugins to be installed. + # + # These plugins are optional and are not installed when there is conflict with + # any other plugin. + def inferred_plugins: () -> untyped + + def plugin: (untyped name, *untyped args) -> untyped +end + +module Bundler::Plugin::Events +end + +class Bundler::Plugin::Index +end + +class Bundler::Plugin::MalformattedPlugin < Bundler::PluginError +end + +class Bundler::Plugin::UndefinedCommandError < Bundler::PluginError +end + +class Bundler::Plugin::UnknownSourceError < Bundler::PluginError +end + +class Bundler::Plugin::DSL::PluginGemfileError < Bundler::PluginError +end + +class Bundler::PluginError < Bundler::BundlerError + def status_code: () -> untyped +end + +class Bundler::ProductionError < Bundler::BundlerError + def status_code: () -> untyped +end + +# Represents a lazily loaded gem specification, where the full specification is +# on the source server in rubygems' "quick" index. The proxy object is to be +# seeded with what we're given from the source's abbreviated index - the full +# specification will only be fetched when necessary. +class Bundler::RemoteSpecification + include ::Comparable + + include ::Bundler::MatchPlatform + + include ::Bundler::GemHelpers + + def <=>: (untyped other) -> untyped + + def __swap__: (untyped spec) -> untyped + + def dependencies: () -> untyped + + def dependencies=: (untyped dependencies) -> untyped + + # Needed before installs, since the arch matters then and quick specs don't + # bother to include the arch in the platform string + def fetch_platform: () -> untyped + + def full_name: () -> untyped + + def git_version: () -> untyped + + def initialize: (untyped name, untyped version, untyped platform, untyped spec_fetcher) -> void + + def name: () -> untyped + + def platform: () -> untyped + + def remote: () -> untyped + + def remote=: (untyped remote) -> untyped + + def respond_to?: (untyped method, ?untyped include_all) -> untyped + + # Create a delegate used for sorting. This strategy is copied from RubyGems + # 2.23 and ensures that Bundler's specifications can be compared and sorted + # with RubyGems' own specifications. + # + # @see #<=> @see + # [`Gem::Specification#sort_obj`](https://docs.ruby-lang.org/en/2.7.0/Gem/Specification.html#method-i-sort_obj) + # + # @return [Array] an object you can use to compare and sort this + # + # ```ruby + # specification against other specifications + # ``` + def sort_obj: () -> untyped + + def source: () -> untyped + + def source=: (untyped source) -> untyped + + def to_s: () -> untyped + + def version: () -> untyped +end + +class Bundler::Resolver + include ::Bundler::Molinillo::SpecificationProvider + + include ::Bundler::Molinillo::UI + + def after_resolution: () -> untyped + + def before_resolution: () -> untyped + + def debug: (?untyped depth) -> untyped + + def debug?: () -> untyped + + def dependencies_for: (untyped specification) -> untyped + + def index_for: (untyped dependency) -> untyped + + def indicate_progress: () -> untyped + + def initialize: (untyped index, untyped source_requirements, untyped base, untyped gem_version_promoter, untyped additional_base_requirements, untyped platforms) -> void + + def name_for: (untyped dependency) -> untyped + + def name_for_explicit_dependency_source: () -> untyped + + def name_for_locking_dependency_source: () -> untyped + + def relevant_sources_for_vertex: (untyped vertex) -> untyped + + def requirement_satisfied_by?: (untyped requirement, untyped activated, untyped spec) -> untyped + + def search_for: (untyped dependency) -> untyped + + def sort_dependencies: (untyped dependencies, untyped activated, untyped conflicts) -> untyped + + def start: (untyped requirements) -> untyped + + def self.platform_sort_key: (untyped platform) -> untyped + + def self.resolve: (untyped requirements, untyped index, ?untyped source_requirements, ?untyped base, ?untyped gem_version_promoter, ?untyped additional_base_requirements, ?untyped platforms) -> untyped + + def self.sort_platforms: (untyped platforms) -> untyped +end + +class Bundler::Resolver::SpecGroup + include ::Bundler::GemHelpers + + def ==: (untyped other) -> untyped + + def activate_platform!: (untyped platform) -> untyped + + def dependencies_for_activated_platforms: () -> untyped + + def eql?: (untyped other) -> untyped + + def for?: (untyped platform) -> untyped + + def hash: () -> untyped + + def ignores_bundler_dependencies: () -> untyped + + def ignores_bundler_dependencies=: (untyped ignores_bundler_dependencies) -> untyped + + def initialize: (untyped all_specs) -> void + + def name: () -> untyped + + def name=: (untyped name) -> untyped + + def source: () -> untyped + + def source=: (untyped source) -> untyped + + def to_s: () -> untyped + + def to_specs: () -> untyped + + def version: () -> untyped + + def version=: (untyped version) -> untyped +end + +module Bundler::RubyDsl + def ruby: (*untyped ruby_version) -> untyped +end + +class Bundler::RubyVersion + def ==: (untyped other) -> untyped + + def diff: (untyped other) -> untyped + + def engine: () -> untyped + + def engine_gem_version: () -> untyped + + def engine_versions: () -> untyped + + def exact?: () -> untyped + + def gem_version: () -> untyped + + def host: () -> untyped + + def initialize: (untyped versions, untyped patchlevel, untyped engine, untyped engine_version) -> void + + def patchlevel: () -> untyped + + def single_version_string: () -> untyped + + def to_gem_version_with_patchlevel: () -> untyped + + def to_s: (?untyped versions) -> untyped + + def versions: () -> untyped + + def versions_string: (untyped versions) -> untyped + + def self.from_string: (untyped string) -> untyped + + def self.system: () -> untyped +end + +Bundler::RubyVersion::PATTERN: untyped + +class Bundler::RubyVersionMismatch < Bundler::BundlerError + def status_code: () -> untyped +end + +class Bundler::RubygemsIntegration + # This backports base\_dir which replaces installation path RubyGems 1.8+ + def backport_base_dir: () -> untyped + + def backport_cache_file: () -> untyped + + # This backports the correct segment generation code from RubyGems 1.4+ by + # monkeypatching it into the method in RubyGems 1.3.6 and 1.3.7. + def backport_segment_generation: () -> untyped + + def backport_spec_file: () -> untyped + + # This backport fixes the marshaling of @segments. + def backport_yaml_initialize: () -> untyped + + def bin_path: (untyped gem, untyped bin, untyped ver) -> untyped + + def binstubs_call_gem?: () -> untyped + + def build: (untyped spec, ?untyped skip_validation) -> untyped + + def build_args: () -> untyped + + def build_args=: (untyped args) -> untyped + + def build_gem: (untyped gem_dir, untyped spec) -> untyped + + def clear_paths: () -> untyped + + def config_map: () -> untyped + + def configuration: () -> untyped + + def download_gem: (untyped spec, untyped uri, untyped path) -> untyped + + def ext_lock: () -> untyped + + def fetch_all_remote_specs: (untyped remote) -> untyped + + def fetch_prerelease_specs: () -> untyped + + def fetch_specs: (untyped all, untyped pre) { () -> untyped } -> untyped + + def gem_bindir: () -> untyped + + def gem_cache: () -> untyped + + def gem_dir: () -> untyped + + def gem_from_path: (untyped path, ?untyped policy) -> untyped + + def gem_path: () -> untyped + + def inflate: (untyped obj) -> untyped + + def initialize: () -> void + + def install_with_build_args: (untyped args) -> untyped + + def load_path_insert_index: () -> untyped + + def load_plugin_files: (untyped files) -> untyped + + def load_plugins: () -> untyped + + def loaded_gem_paths: () -> untyped + + def loaded_specs: (untyped name) -> untyped + + def mark_loaded: (untyped spec) -> untyped + + def marshal_spec_dir: () -> untyped + + def method_visibility: (untyped klass, untyped method) -> untyped + + def path: (untyped obj) -> untyped + + def path_separator: () -> untyped + + def platforms: () -> untyped + + def post_reset_hooks: () -> untyped + + def preserve_paths: () -> untyped + + def provides?: (untyped req_str) -> untyped + + def read_binary: (untyped path) -> untyped + + def redefine_method: (untyped klass, untyped method, ?untyped unbound_method) { () -> untyped } -> untyped + + def replace_bin_path: (untyped specs, untyped specs_by_name) -> untyped + + def replace_entrypoints: (untyped specs) -> untyped + + def replace_gem: (untyped specs, untyped specs_by_name) -> untyped + + # Because [`Bundler`](https://docs.ruby-lang.org/en/2.6.0/Bundler.html) has a + # static view of what specs are available, we don't refresh, so stub it out. + def replace_refresh: () -> untyped + + def repository_subdirectories: () -> untyped + + def reset: () -> untyped + + def reverse_rubygems_kernel_mixin: () -> untyped + + def ruby_engine: () -> untyped + + def security_policies: () -> untyped + + def security_policy_keys: () -> untyped + + def set_installed_by_version: (untyped spec, ?untyped installed_by_version) -> untyped + + def sources: () -> untyped + + def sources=: (untyped val) -> untyped + + def spec_cache_dirs: () -> untyped + + def spec_default_gem?: (untyped spec) -> untyped + + def spec_extension_dir: (untyped spec) -> untyped + + def spec_from_gem: (untyped path, ?untyped policy) -> untyped + + def spec_matches_for_glob: (untyped spec, untyped glob) -> untyped + + def spec_missing_extensions?: (untyped spec, ?untyped default) -> untyped + + def stub_set_spec: (untyped stub, untyped spec) -> untyped + + def stub_source_index: (untyped specs) -> untyped + + def stubs_provide_full_functionality?: () -> untyped + + def suffix_pattern: () -> untyped + + def ui=: (untyped obj) -> untyped + + def undo_replacements: () -> untyped + + def user_home: () -> untyped + + def validate: (untyped spec) -> untyped + + def version: () -> untyped + + def with_build_args: (untyped args) -> untyped + + def self.provides?: (untyped req_str) -> untyped + + def self.version: () -> untyped +end + +Bundler::RubygemsIntegration::EXT_LOCK: untyped + +# RubyGems 1.8.0 to 1.8.4 +class Bundler::RubygemsIntegration::AlmostModern < Bundler::RubygemsIntegration::Modern + # RubyGems [>= 1.8.0, < 1.8.5] has a bug that changes + # [`Gem.dir`](https://docs.ruby-lang.org/en/2.6.0/Gem.html#method-c-dir) + # whenever you call + # [`Gem::Installer#install`](https://docs.ruby-lang.org/en/2.6.0/Installer.html#method-i-install) + # with an :install\_dir set. We have to change it back for our sudo mode to + # work. + def preserve_paths: () -> untyped +end + +# RubyGems versions 1.3.6 and 1.3.7 +class Bundler::RubygemsIntegration::Ancient < Bundler::RubygemsIntegration::Legacy + def initialize: () -> void +end + +# RubyGems 2.0 +class Bundler::RubygemsIntegration::Future < Bundler::RubygemsIntegration + def all_specs: () -> untyped + + def build: (untyped spec, ?untyped skip_validation) -> untyped + + def download_gem: (untyped spec, untyped uri, untyped path) -> untyped + + def fetch_all_remote_specs: (untyped remote) -> untyped + + def fetch_specs: (untyped source, untyped remote, untyped name) -> untyped + + def find_name: (untyped name) -> untyped + + def gem_from_path: (untyped path, ?untyped policy) -> untyped + + def gem_remote_fetcher: () -> untyped + + def install_with_build_args: (untyped args) -> untyped + + def path_separator: () -> untyped + + def repository_subdirectories: () -> untyped + + def stub_rubygems: (untyped specs) -> untyped +end + +# RubyGems 1.4 through 1.6 +class Bundler::RubygemsIntegration::Legacy < Bundler::RubygemsIntegration + def all_specs: () -> untyped + + def find_name: (untyped name) -> untyped + + def initialize: () -> void + + def post_reset_hooks: () -> untyped + + def reset: () -> untyped + + def stub_rubygems: (untyped specs) -> untyped + + def validate: (untyped spec) -> untyped +end + +# RubyGems 1.8.5-1.8.19 +class Bundler::RubygemsIntegration::Modern < Bundler::RubygemsIntegration + def all_specs: () -> untyped + + def find_name: (untyped name) -> untyped + + def stub_rubygems: (untyped specs) -> untyped +end + +# RubyGems 2.1.0 +class Bundler::RubygemsIntegration::MoreFuture < Bundler::RubygemsIntegration::Future + def all_specs: () -> untyped + + # RubyGems-generated binstubs call + # [`Kernel#gem`](https://docs.ruby-lang.org/en/2.6.0/Kernel.html#method-i-gem) + def binstubs_call_gem?: () -> untyped + + def find_name: (untyped name) -> untyped + + def initialize: () -> void + + # only 2.5.2+ has all of the stub methods we want to use, and since this is a + # performance optimization *only*, we'll restrict ourselves to the most recent + # RG versions instead of all versions that have stubs + def stubs_provide_full_functionality?: () -> untyped + + def use_gemdeps: (untyped gemfile) -> untyped +end + +# RubyGems 1.8.20+ +class Bundler::RubygemsIntegration::MoreModern < Bundler::RubygemsIntegration::Modern + def build: (untyped spec, ?untyped skip_validation) -> untyped +end + +# RubyGems 1.7 +class Bundler::RubygemsIntegration::Transitional < Bundler::RubygemsIntegration::Legacy + def stub_rubygems: (untyped specs) -> untyped + + def validate: (untyped spec) -> untyped +end + +class Bundler::Runtime + include ::Bundler::SharedHelpers + + def cache: (?untyped custom_path) -> untyped + + def clean: (?untyped dry_run) -> untyped + + def current_dependencies: () -> untyped + + def dependencies: () -> untyped + + def gems: () -> untyped + + def initialize: (untyped root, untyped definition) -> void + + def lock: (?untyped opts) -> untyped + + def prune_cache: (untyped cache_path) -> untyped + + def requested_specs: () -> untyped + + def require: (*untyped groups) -> untyped + + def requires: () -> untyped + + def setup: (*untyped groups) -> untyped + + def specs: () -> untyped +end + +Bundler::Runtime::REQUIRE_ERRORS: untyped + +class Bundler::SecurityError < Bundler::BundlerError + def status_code: () -> untyped +end + +class Bundler::Settings + def []: (untyped name) -> untyped + + def all: () -> untyped + + def allow_sudo?: () -> untyped + + def app_cache_path: () -> untyped + + def credentials_for: (untyped uri) -> untyped + + def gem_mirrors: () -> untyped + + def ignore_config?: () -> untyped + + def initialize: (?untyped root) -> void + + def key_for: (untyped key) -> untyped + + def local_overrides: () -> untyped + + def locations: (untyped key) -> untyped + + def mirror_for: (untyped uri) -> untyped + + # for legacy reasons, in + # [`Bundler`](https://docs.ruby-lang.org/en/2.7.0/Bundler.html) 2, we do not + # respect :disable\_shared\_gems + def path: () -> untyped + + def pretty_values_for: (untyped exposed_key) -> untyped + + def set_command_option: (untyped key, untyped value) -> untyped + + def set_command_option_if_given: (untyped key, untyped value) -> untyped + + def set_global: (untyped key, untyped value) -> untyped + + def set_local: (untyped key, untyped value) -> untyped + + def temporary: (untyped update) -> untyped + + def validate!: () -> untyped + + def self.normalize_uri: (untyped uri) -> untyped +end + +Bundler::Settings::ARRAY_KEYS: untyped + +Bundler::Settings::BOOL_KEYS: untyped + +Bundler::Settings::CONFIG_REGEX: untyped + +Bundler::Settings::DEFAULT_CONFIG: untyped + +Bundler::Settings::NORMALIZE_URI_OPTIONS_PATTERN: untyped + +Bundler::Settings::NUMBER_KEYS: untyped + +Bundler::Settings::PER_URI_OPTIONS: untyped + +class Bundler::Settings::Path < Struct + def append_ruby_scope: () -> untyped + + def append_ruby_scope=: (untyped _) -> untyped + + def base_path: () -> untyped + + def base_path_relative_to_pwd: () -> untyped + + def default_install_uses_path: () -> untyped + + def default_install_uses_path=: (untyped _) -> untyped + + def explicit_path: () -> untyped + + def explicit_path=: (untyped _) -> untyped + + def path: () -> untyped + + def system_path: () -> untyped + + def system_path=: (untyped _) -> untyped + + def use_system_gems?: () -> untyped + + def validate!: () -> untyped + + def self.[]: (*untyped _) -> untyped + + def self.members: () -> untyped + + def self.new: (*untyped _) -> untyped +end + +Bundler::Settings::Path::Elem: untyped + +module Bundler::SharedHelpers + extend ::Bundler::SharedHelpers + + def chdir: (untyped dir) { () -> untyped } -> untyped + + def const_get_safely: (untyped constant_name, untyped namespace) -> untyped + + def default_bundle_dir: () -> untyped + + def default_gemfile: () -> untyped + + def default_lockfile: () -> untyped + + def digest: (untyped name) -> untyped + + def ensure_same_dependencies: (untyped spec, untyped old_deps, untyped new_deps) -> untyped + + def filesystem_access: (untyped path, ?untyped action) { () -> untyped } -> untyped + + def in_bundle?: () -> untyped + + def major_deprecation: (untyped major_version, untyped message) -> untyped + + def md5_available?: () -> untyped + + def pretty_dependency: (untyped dep, ?untyped print_source) -> untyped + + def print_major_deprecations!: () -> untyped + + def pwd: () -> untyped + + def root: () -> untyped + + def set_bundle_environment: () -> untyped + + def set_env: (untyped key, untyped value) -> untyped + + def trap: (untyped signal, ?untyped override) { () -> untyped } -> untyped + + def with_clean_git_env: () { () -> untyped } -> untyped + + def write_to_gemfile: (untyped gemfile_path, untyped contents) -> untyped +end + +class Bundler::Source + def can_lock?: (untyped spec) -> untyped + + def dependency_names: () -> untyped + + def dependency_names=: (untyped dependency_names) -> untyped + + def dependency_names_to_double_check: () -> untyped + + def double_check_for: (*untyped _) -> untyped + + def extension_cache_path: (untyped spec) -> untyped + + def include?: (untyped other) -> untyped + + def inspect: () -> untyped + + def path?: () -> untyped + + def unmet_deps: () -> untyped + + def version_message: (untyped spec) -> untyped +end + +class Bundler::Source::Gemspec < Bundler::Source::Path + def as_path_source: () -> untyped + + def gemspec: () -> untyped + + def initialize: (untyped options) -> void +end + +class Bundler::Source::Git < Bundler::Source::Path + def ==: (untyped other) -> untyped + + def allow_git_ops?: () -> untyped + + def app_cache_dirname: () -> untyped + + def branch: () -> untyped + + def cache: (untyped spec, ?untyped custom_path) -> untyped + + # This is the path which is going to contain a cache of the git repository. + # When using the same git repository across different projects, this cache + # will be shared. When using local git repos, this is set to the local repo. + def cache_path: () -> untyped + + def eql?: (untyped other) -> untyped + + def extension_dir_name: () -> untyped + + def hash: () -> untyped + + def initialize: (untyped options) -> void + + def install: (untyped spec, ?untyped options) -> untyped + + # This is the path which is going to contain a specific checkout of the git + # repository. When using local git repos, this is set to the local repo. + # + # Also aliased as: + # [`path`](https://docs.ruby-lang.org/en/2.7.0/Bundler/Source/Git.html#method-i-path) + def install_path: () -> untyped + + def load_spec_files: () -> untyped + + def local_override!: (untyped path) -> untyped + + def name: () -> untyped + + def options: () -> untyped + + # Alias for: + # [`install_path`](https://docs.ruby-lang.org/en/2.7.0/Bundler/Source/Git.html#method-i-install_path) + def path: () -> untyped + + def ref: () -> untyped + + def revision: () -> untyped + + def specs: (*untyped _) -> untyped + + def submodules: () -> untyped + + def to_lock: () -> untyped + + def to_s: () -> untyped + + def unlock!: () -> untyped + + def uri: () -> untyped + + def self.from_lock: (untyped options) -> untyped +end + +class Bundler::Source::Git::GitCommandError < Bundler::GitError + def initialize: (untyped command, ?untyped path, ?untyped extra_info) -> void +end + +class Bundler::Source::Git::GitNotAllowedError < Bundler::GitError + def initialize: (untyped command) -> void +end + +class Bundler::Source::Git::GitNotInstalledError < Bundler::GitError + def initialize: () -> void +end + +# The +# [`GitProxy`](https://docs.ruby-lang.org/en/2.7.0/Bundler/Source/Git/GitProxy.html) +# is responsible to interact with git repositories. All actions required by the +# [`Git`](https://docs.ruby-lang.org/en/2.7.0/Bundler/Source/Git.html) source is +# encapsulated in this object. +class Bundler::Source::Git::GitProxy + def branch: () -> untyped + + def checkout: () -> untyped + + def contains?: (untyped commit) -> untyped + + def copy_to: (untyped destination, ?untyped submodules) -> untyped + + def full_version: () -> untyped + + def initialize: (untyped path, untyped uri, untyped ref, ?untyped revision, ?untyped git) -> void + + def path: () -> untyped + + def path=: (untyped path) -> untyped + + def ref: () -> untyped + + def ref=: (untyped ref) -> untyped + + def revision: () -> untyped + + def revision=: (untyped revision) -> untyped + + def uri: () -> untyped + + def uri=: (untyped uri) -> untyped + + def version: () -> untyped +end + +class Bundler::Source::Git::MissingGitRevisionError < Bundler::GitError + def initialize: (untyped ref, untyped repo) -> void +end + +class Bundler::Source::Metadata < Bundler::Source + def ==: (untyped other) -> untyped + + def cached!: () -> untyped + + def eql?: (untyped other) -> untyped + + def hash: () -> untyped + + def install: (untyped spec, ?untyped _opts) -> untyped + + def options: () -> untyped + + def remote!: () -> untyped + + def specs: () -> untyped + + def to_s: () -> untyped + + def version_message: (untyped spec) -> untyped +end + +class Bundler::Source::Path < Bundler::Source + def ==: (untyped other) -> untyped + + def app_cache_dirname: () -> untyped + + def cache: (untyped spec, ?untyped custom_path) -> untyped + + def cached!: () -> untyped + + def eql?: (untyped other) -> untyped + + def expanded_original_path: () -> untyped + + def hash: () -> untyped + + def initialize: (untyped options) -> void + + def install: (untyped spec, ?untyped options) -> untyped + + def local_specs: (*untyped _) -> untyped + + def name: () -> untyped + + def name=: (untyped name) -> untyped + + def options: () -> untyped + + def original_path: () -> untyped + + def path: () -> untyped + + def remote!: () -> untyped + + def root: () -> untyped + + def root_path: () -> untyped + + def specs: () -> untyped + + def to_lock: () -> untyped + + def to_s: () -> untyped + + def version: () -> untyped + + def version=: (untyped version) -> untyped + + def self.from_lock: (untyped options) -> untyped +end + +Bundler::Source::Path::DEFAULT_GLOB: untyped + +class Bundler::Source::Rubygems < Bundler::Source + def ==: (untyped other) -> untyped + + def add_remote: (untyped source) -> untyped + + def api_fetchers: () -> untyped + + def builtin_gem?: (untyped spec) -> untyped + + def cache: (untyped spec, ?untyped custom_path) -> untyped + + def cache_path: () -> untyped + + def cached!: () -> untyped + + def cached_built_in_gem: (untyped spec) -> untyped + + def cached_gem: (untyped spec) -> untyped + + def cached_path: (untyped spec) -> untyped + + def cached_specs: () -> untyped + + def caches: () -> untyped + + def can_lock?: (untyped spec) -> untyped + + def credless_remotes: () -> untyped + + def dependency_names_to_double_check: () -> untyped + + def double_check_for: (untyped unmet_dependency_names) -> untyped + + def eql?: (untyped other) -> untyped + + def equivalent_remotes?: (untyped other_remotes) -> untyped + + def fetch_gem: (untyped spec) -> untyped + + def fetch_names: (untyped fetchers, untyped dependency_names, untyped index, untyped override_dupes) -> untyped + + def fetchers: () -> untyped + + def hash: () -> untyped + + def include?: (untyped o) -> untyped + + def initialize: (?untyped options) -> void + + def install: (untyped spec, ?untyped opts) -> untyped + + def installed?: (untyped spec) -> untyped + + def installed_specs: () -> untyped + + def loaded_from: (untyped spec) -> untyped + + # Alias for: + # [`to_s`](https://docs.ruby-lang.org/en/2.7.0/Bundler/Source/Rubygems.html#method-i-to_s) + def name: () -> untyped + + def normalize_uri: (untyped uri) -> untyped + + def options: () -> untyped + + def remote!: () -> untyped + + def remote_specs: () -> untyped + + def remotes: () -> untyped + + def remotes_for_spec: (untyped spec) -> untyped + + def remove_auth: (untyped remote) -> untyped + + def replace_remotes: (untyped other_remotes, ?untyped allow_equivalent) -> untyped + + def requires_sudo?: () -> untyped + + def rubygems_dir: () -> untyped + + def specs: () -> untyped + + def suppress_configured_credentials: (untyped remote) -> untyped + + def to_lock: () -> untyped + + # Also aliased as: + # [`name`](https://docs.ruby-lang.org/en/2.7.0/Bundler/Source/Rubygems.html#method-i-name) + def to_s: () -> untyped + + def unmet_deps: () -> untyped + + def self.from_lock: (untyped options) -> untyped +end + +Bundler::Source::Rubygems::API_REQUEST_LIMIT: untyped + +Bundler::Source::Rubygems::API_REQUEST_SIZE: untyped + +class Bundler::SourceList + def add_git_source: (?untyped options) -> untyped + + def add_path_source: (?untyped options) -> untyped + + def add_plugin_source: (untyped source, ?untyped options) -> untyped + + def add_rubygems_remote: (untyped uri) -> untyped + + def add_rubygems_source: (?untyped options) -> untyped + + def all_sources: () -> untyped + + def cached!: () -> untyped + + def default_source: () -> untyped + + def get: (untyped source) -> untyped + + def git_sources: () -> untyped + + def global_rubygems_source: () -> untyped + + def global_rubygems_source=: (untyped uri) -> untyped + + def initialize: () -> void + + def lock_sources: () -> untyped + + def metadata_source: () -> untyped + + def path_sources: () -> untyped + + def plugin_sources: () -> untyped + + def remote!: () -> untyped + + def replace_sources!: (untyped replacement_sources) -> untyped + + def rubygems_primary_remotes: () -> untyped + + def rubygems_remotes: () -> untyped + + def rubygems_sources: () -> untyped +end + +class Bundler::SpecSet[out Elem] + include ::TSort + + include ::Enumerable + + def <<: (*untyped args) { () -> untyped } -> untyped + + def []: (untyped key) -> untyped + + def []=: (untyped key, untyped value) -> untyped + + def add: (*untyped args) { () -> untyped } -> untyped + + def each: (*untyped args) { () -> untyped } -> untyped + + def empty?: (*untyped args) { () -> untyped } -> untyped + + def find_by_name_and_platform: (untyped name, untyped platform) -> untyped + + def for: (untyped dependencies, ?untyped skip, ?untyped check, ?untyped match_current_platform, ?untyped raise_on_missing) -> untyped + + def initialize: (untyped specs) -> void + + def length: (*untyped args) { () -> untyped } -> untyped + + def materialize: (untyped deps, ?untyped missing_specs) -> untyped + + # Materialize for all the specs in the spec set, regardless of what platform + # they're for This is in contrast to how for does platform filtering (and + # specifically different from how `materialize` calls `for` only for the + # current platform) @return [Array] + def materialized_for_all_platforms: () -> untyped + + def merge: (untyped set) -> untyped + + def remove: (*untyped args) { () -> untyped } -> untyped + + def size: (*untyped args) { () -> untyped } -> untyped + + def sort!: () -> untyped + + def to_a: () -> untyped + + def to_hash: () -> untyped + + def valid_for?: (untyped deps) -> untyped + + def what_required: (untyped spec) -> untyped +end + +class Bundler::StubSpecification < Bundler::RemoteSpecification + def activated: () -> untyped + + def activated=: (untyped activated) -> untyped + + def default_gem: () -> untyped + + def default_gem?: () -> bool + + def full_gem_path: () -> untyped + + def full_require_paths: () -> untyped + + def ignored: () -> untyped + + def ignored=: (untyped ignored) -> untyped + + # This is what we do in bundler/rubygems\_ext + # [`full_require_paths`](https://docs.ruby-lang.org/en/2.6.0/Bundler/StubSpecification.html#method-i-full_require_paths) + # is always implemented in >= 2.2.0 + def load_paths: () -> untyped + + def loaded_from: () -> untyped + + def matches_for_glob: (untyped glob) -> untyped + + # This is defined directly to avoid having to load every installed spec + def missing_extensions?: () -> untyped + + def raw_require_paths: () -> untyped + + def source=: (untyped source) -> untyped + + def stub: () -> untyped + + def stub=: (untyped stub) -> untyped + + def to_yaml: () -> untyped + + def self.from_stub: (untyped stub) -> untyped +end + +class Bundler::SudoNotPermittedError < Bundler::BundlerError + def status_code: () -> untyped +end + +class Bundler::TemporaryResourceError < Bundler::PermissionError + def message: () -> untyped + + def status_code: () -> untyped +end + +class Bundler::ThreadCreationError < Bundler::BundlerError + def status_code: () -> untyped +end + +module Bundler::UI +end + +class Bundler::UI::RGProxy < Gem::SilentUI + def initialize: (untyped ui) -> void + + def say: (untyped message) -> untyped +end + +class Bundler::UI::Silent + def add_color: (untyped string, untyped color) -> untyped + + def ask: (untyped message) -> untyped + + def confirm: (untyped message, ?untyped newline) -> untyped + + def debug: (untyped message, ?untyped newline) -> untyped + + def debug?: () -> untyped + + def error: (untyped message, ?untyped newline) -> untyped + + def info: (untyped message, ?untyped newline) -> untyped + + def initialize: () -> void + + def level: (?untyped name) -> untyped + + def level=: (untyped name) -> untyped + + def no?: () -> untyped + + def quiet?: () -> untyped + + def shell=: (untyped shell) -> untyped + + def silence: () -> untyped + + def trace: (untyped message, ?untyped newline, ?untyped force) -> untyped + + def unprinted_warnings: () -> untyped + + def warn: (untyped message, ?untyped newline) -> untyped + + def yes?: (untyped msg) -> untyped +end + +module Bundler::URICredentialsFilter + def self.credential_filtered_string: (untyped str_to_filter, untyped uri) -> untyped + + def self.credential_filtered_uri: (untyped uri_to_anonymize) -> untyped +end + +# Internal error, should be rescued +class Bundler::VersionConflict < Bundler::BundlerError + def conflicts: () -> untyped + + def initialize: (untyped conflicts, ?untyped msg) -> void + + def status_code: () -> untyped +end + +class Bundler::VirtualProtocolError < Bundler::BundlerError + def message: () -> untyped + + def status_code: () -> untyped +end + +# A stub yaml serializer that can handle only hashes and strings (as of now). +module Bundler::YAMLSerializer + def self.dump: (untyped hash) -> untyped + + def self.load: (untyped str) -> untyped +end + +Bundler::YAMLSerializer::ARRAY_REGEX: untyped + +Bundler::YAMLSerializer::HASH_REGEX: untyped + +class Bundler::YamlSyntaxError < Bundler::BundlerError + def initialize: (untyped orig_exception, untyped msg) -> void + + def orig_exception: () -> untyped + + def status_code: () -> untyped +end + +class Bundler::Installer + def self.ambiguous_gems=: (untyped ambiguous_gems) -> untyped + + def self.ambiguous_gems: () -> untyped + + def post_install_messages: () -> untyped + + def self.install: (untyped root, untyped definition, ?untyped options) -> untyped + + def initialize: (untyped root, untyped definition) -> void + + def run: (untyped options) -> void + + def generate_bundler_executable_stubs: (untyped spec, ?untyped options) -> void + + def generate_standalone_bundler_executable_stubs: (untyped spec, ?untyped options) -> void +end diff --git a/rbs/fills/bundler/dsl.rbs b/rbs/fills/bundler/dsl.rbs deleted file mode 100644 index b63cd736c..000000000 --- a/rbs/fills/bundler/dsl.rbs +++ /dev/null @@ -1,200 +0,0 @@ -# -# Bundler provides a consistent environment for Ruby projects by tracking and -# installing the exact gems and versions that are needed. -# -# Bundler is a part of Ruby's standard library. -# -# Bundler is used by creating *gemfiles* listing all the project dependencies -# and (optionally) their versions and then using -# -# require 'bundler/setup' -# -# or Bundler.setup to setup environment where only specified gems and their -# specified versions could be used. -# -# See [Bundler website](https://bundler.io/docs.html) for extensive -# documentation on gemfiles creation and Bundler usage. -# -# As a standard library inside project, Bundler could be used for introspection -# of loaded and required modules. -# -module Bundler - class Dsl - @source: untyped - - @sources: untyped - - @git_sources: untyped - - @dependencies: untyped - - @groups: untyped - - @install_conditionals: untyped - - @optional_groups: untyped - - @platforms: untyped - - @env: untyped - - @ruby_version: untyped - - @gemspecs: untyped - - @gemfile: untyped - - @gemfiles: untyped - - @valid_keys: untyped - - include RubyDsl - - def self.evaluate: (untyped gemfile, untyped lockfile, untyped unlock) -> untyped - - VALID_PLATFORMS: untyped - - VALID_KEYS: ::Array["group" | "groups" | "git" | "path" | "glob" | "name" | "branch" | "ref" | "tag" | "require" | "submodules" | "platform" | "platforms" | "source" | "install_if" | "force_ruby_platform"] - - GITHUB_PULL_REQUEST_URL: ::Regexp - - GITLAB_MERGE_REQUEST_URL: ::Regexp - - attr_reader gemspecs: untyped - - attr_reader gemfile: untyped - - attr_accessor dependencies: untyped - - def initialize: () -> void - - def eval_gemfile: (untyped gemfile, ?untyped? contents) -> untyped - - def gemspec: (?path: ::String, ?glob: ::String, ?name: ::String, ?development_group: ::Symbol) -> void - - def gem: (untyped name, *untyped args) -> void - - def source: (::String source, ?type: ::Symbol) ?{ (?) -> untyped } -> void - - def git_source: (untyped name) ?{ (?) -> untyped } -> untyped - - def path: (untyped path, ?::Hash[untyped, untyped] options) ?{ (?) -> untyped } -> untyped - - def git: (untyped uri, ?::Hash[untyped, untyped] options) ?{ (?) -> untyped } -> untyped - - def github: (untyped repo, ?::Hash[untyped, untyped] options) ?{ () -> untyped } -> untyped - - def to_definition: (untyped lockfile, untyped unlock) -> untyped - - def group: (*untyped args) { () -> untyped } -> untyped - - def install_if: (*untyped args) { () -> untyped } -> untyped - - def platforms: (*untyped platforms) { () -> untyped } -> untyped - - alias platform platforms - - def env: (untyped name) { () -> untyped } -> untyped - - def plugin: (*untyped args) -> nil - - def method_missing: (untyped name, *untyped args) -> untyped - - def check_primary_source_safety: () -> untyped - - private - - def add_dependency: (untyped name, ?untyped? version, ?::Hash[untyped, untyped] options) -> (nil | untyped) - - def with_gemfile: (untyped gemfile) { (untyped) -> untyped } -> untyped - - def add_git_sources: () -> untyped - - def with_source: (untyped source) ?{ () -> untyped } -> untyped - - def normalize_hash: (untyped opts) -> untyped - - def valid_keys: () -> untyped - - def normalize_options: (untyped name, untyped version, untyped opts) -> untyped - - def normalize_group_options: (untyped opts, untyped groups) -> untyped - - def validate_keys: (untyped command, untyped opts, untyped valid_keys) -> (true | untyped) - - def normalize_source: (untyped source) -> untyped - - def deprecate_legacy_windows_platforms: (untyped platforms) -> (nil | untyped) - - def check_path_source_safety: () -> (nil | untyped) - - def check_rubygems_source_safety: () -> (untyped | nil) - - def multiple_global_source_warning: () -> untyped - - class DSLError < GemfileError - @status_code: untyped - - @description: untyped - - @dsl_path: untyped - - @backtrace: untyped - - @contents: untyped - - @to_s: untyped - - # @return [::String] the description that should be presented to the user. - # - attr_reader description: ::String - - # @return [::String] the path of the dsl file that raised the exception. - # - attr_reader dsl_path: ::String - - # @return [::Exception] the backtrace of the exception raised by the - # evaluation of the dsl file. - # - attr_reader backtrace: ::Exception - - # @param [::Exception] backtrace @see backtrace - # @param [::String] dsl_path @see dsl_path - # - def initialize: (untyped description, ::String dsl_path, ::Exception backtrace, ?untyped? contents) -> void - - def status_code: () -> untyped - - # @return [::String] the contents of the DSL that cause the exception to - # be raised. - # - def contents: () -> ::String - - # The message of the exception reports the content of podspec for the - # line that generated the original exception. - # - # @example Output - # - # Invalid podspec at `RestKit.podspec` - undefined method - # `exclude_header_search_paths=' for # - # - # from spec-repos/master/RestKit/0.9.3/RestKit.podspec:36 - # ------------------------------------------- - # # because it would break: #import - # > ns.exclude_header_search_paths = 'Code/RestKit.h' - # end - # ------------------------------------------- - # - # @return [::String] the message of the exception. - # - def to_s: () -> ::String - - private - - def parse_line_number_from_description: () -> ::Array[untyped] - end - - def gemfile_root: () -> untyped - end -end From 06fdd7631086fdfdae40f1174a5d6a9af6369903 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 19 Jul 2025 12:18:05 -0400 Subject: [PATCH 254/561] Merge in ruby_dsl.rbs --- rbs/fills/bundler/{ => 0}/bundler.rbs | 19 +++++++++++- rbs/fills/bundler/ruby_dsl.rbs | 42 --------------------------- 2 files changed, 18 insertions(+), 43 deletions(-) rename rbs/fills/bundler/{ => 0}/bundler.rbs (99%) delete mode 100644 rbs/fills/bundler/ruby_dsl.rbs diff --git a/rbs/fills/bundler/bundler.rbs b/rbs/fills/bundler/0/bundler.rbs similarity index 99% rename from rbs/fills/bundler/bundler.rbs rename to rbs/fills/bundler/0/bundler.rbs index f60fe3837..4af422af1 100644 --- a/rbs/fills/bundler/bundler.rbs +++ b/rbs/fills/bundler/0/bundler.rbs @@ -3192,7 +3192,24 @@ class Bundler::Resolver::SpecGroup end module Bundler::RubyDsl - def ruby: (*untyped ruby_version) -> untyped + @ruby_version: untyped + + def ruby: (*::String ruby_version) -> void + + # Support the various file formats found in .ruby-version files. + # + # 3.2.2 + # ruby-3.2.2 + # + # Also supports .tool-versions files for asdf. Lines not starting with "ruby" are ignored. + # + # ruby 2.5.1 # comment is ignored + # ruby 2.5.1# close comment and extra spaces doesn't confuse + # + # Intentionally does not support `3.2.1@gemset` since rvm recommends using .ruby-gemset instead + # + # Loads the file relative to the dirname of the Gemfile itself. + def normalize_ruby_file: (::String filename) -> ::String end class Bundler::RubyVersion diff --git a/rbs/fills/bundler/ruby_dsl.rbs b/rbs/fills/bundler/ruby_dsl.rbs deleted file mode 100644 index 35b30c681..000000000 --- a/rbs/fills/bundler/ruby_dsl.rbs +++ /dev/null @@ -1,42 +0,0 @@ -# -# Bundler provides a consistent environment for Ruby projects by tracking and -# installing the exact gems and versions that are needed. -# -# Bundler is a part of Ruby's standard library. -# -# Bundler is used by creating *gemfiles* listing all the project dependencies -# and (optionally) their versions and then using -# -# require 'bundler/setup' -# -# or Bundler.setup to setup environment where only specified gems and their -# specified versions could be used. -# -# See [Bundler website](https://bundler.io/docs.html) for extensive -# documentation on gemfiles creation and Bundler usage. -# -# As a standard library inside project, Bundler could be used for introspection -# of loaded and required modules. -# -module Bundler - module RubyDsl - @ruby_version: untyped - - def ruby: (*::String ruby_version) -> void - - # Support the various file formats found in .ruby-version files. - # - # 3.2.2 - # ruby-3.2.2 - # - # Also supports .tool-versions files for asdf. Lines not starting with "ruby" are ignored. - # - # ruby 2.5.1 # comment is ignored - # ruby 2.5.1# close comment and extra spaces doesn't confuse - # - # Intentionally does not support `3.2.1@gemset` since rvm recommends using .ruby-gemset instead - # - # Loads the file relative to the dirname of the Gemfile itself. - def normalize_ruby_file: (::String filename) -> ::String - end -end From bf0e5d7e17b51d1ff76f1ebbe6ec77804063d22f Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 19 Jul 2025 12:19:49 -0400 Subject: [PATCH 255/561] Add bundler RBS fill --- rbs/fills/bundler/0/bundler.rbs | 4271 +++++++++++++++++++++++++++++++ 1 file changed, 4271 insertions(+) create mode 100644 rbs/fills/bundler/0/bundler.rbs diff --git a/rbs/fills/bundler/0/bundler.rbs b/rbs/fills/bundler/0/bundler.rbs new file mode 100644 index 000000000..4af422af1 --- /dev/null +++ b/rbs/fills/bundler/0/bundler.rbs @@ -0,0 +1,4271 @@ +# [`Bundler`](https://docs.ruby-lang.org/en/2.7.0/Bundler.html) provides a +# consistent environment for Ruby projects by tracking and installing the exact +# gems and versions that are needed. +# +# Since Ruby 2.6, [`Bundler`](https://docs.ruby-lang.org/en/2.7.0/Bundler.html) +# is a part of Ruby's standard library. +# +# Bunder is used by creating *gemfiles* listing all the project dependencies and +# (optionally) their versions and then using +# +# ```ruby +# require 'bundler/setup' +# ``` +# +# or +# [`Bundler.setup`](https://docs.ruby-lang.org/en/2.7.0/Bundler.html#method-c-setup) +# to setup environment where only specified gems and their specified versions +# could be used. +# +# See [Bundler website](https://bundler.io/docs.html) for extensive +# documentation on gemfiles creation and +# [`Bundler`](https://docs.ruby-lang.org/en/2.7.0/Bundler.html) usage. +# +# As a standard library inside project, +# [`Bundler`](https://docs.ruby-lang.org/en/2.7.0/Bundler.html) could be used +# for introspection of loaded and required modules. +module Bundler + def self.app_cache: (?untyped custom_path) -> untyped + + def self.app_config_path: () -> untyped + + # Returns absolute location of where binstubs are installed to. + def self.bin_path: () -> untyped + + # Returns absolute path of where gems are installed on the filesystem. + def self.bundle_path: () -> untyped + + def self.bundler_major_version: () -> untyped + + # @deprecated Use `unbundled\_env` instead + def self.clean_env: () -> untyped + + def self.clean_exec: (*untyped args) -> untyped + + def self.clean_system: (*untyped args) -> untyped + + def self.clear_gemspec_cache: () -> untyped + + def self.configure: () -> untyped + + def self.configured_bundle_path: () -> untyped + + # Returns current version of Ruby + # + # @return [CurrentRuby] Current version of Ruby + def self.current_ruby: () -> untyped + + def self.default_bundle_dir: () -> untyped + + def self.default_gemfile: () -> untyped + + def self.default_lockfile: () -> untyped + + def self.definition: (?(::Hash[String, Boolean | nil] | Boolean | nil) unlock) -> Bundler::Definition + + def self.environment: () -> untyped + + def self.feature_flag: () -> untyped + + def self.frozen_bundle?: () -> untyped + + def self.git_present?: () -> untyped + + def self.home: () -> untyped + + def self.install_path: () -> untyped + + def self.load: () -> untyped + + def self.load_gemspec: (untyped file, ?untyped validate) -> untyped + + def self.load_gemspec_uncached: (untyped file, ?untyped validate) -> untyped + + def self.load_marshal: (untyped data) -> untyped + + def self.local_platform: () -> untyped + + def self.locked_gems: () -> untyped + + def self.mkdir_p: (untyped path, ?untyped options) -> untyped + + # @return [Hash] Environment present before + # [`Bundler`](https://docs.ruby-lang.org/en/2.7.0/Bundler.html) was activated + def self.original_env: () -> untyped + + def self.read_file: (untyped file) -> untyped + + def self.require: (*untyped groups) -> untyped + + def self.require_thor_actions: () -> untyped + + def self.requires_sudo?: () -> untyped + + def self.reset!: () -> untyped + + def self.reset_paths!: () -> untyped + + def self.reset_rubygems!: () -> untyped + + def self.rm_rf: (untyped path) -> untyped + + def self.root: () -> untyped + + def self.ruby_scope: () -> untyped + + def self.rubygems: () -> untyped + + def self.settings: () -> untyped + + def self.setup: (*untyped groups) -> untyped + + def self.specs_path: () -> untyped + + def self.sudo: (untyped str) -> untyped + + def self.system_bindir: () -> untyped + + def self.tmp: (?untyped name) -> untyped + + def self.tmp_home_path: (untyped login, untyped warning) -> untyped + + def self.ui: () -> untyped + + def self.ui=: (untyped ui) -> untyped + + def self.use_system_gems?: () -> untyped + + def self.user_bundle_path: (?untyped dir) -> untyped + + def self.user_cache: () -> untyped + + def self.user_home: () -> untyped + + def self.which: (untyped executable) -> untyped + + # @deprecated Use `with\_unbundled\_env` instead + def self.with_clean_env: () { () -> untyped } -> untyped + + def self.with_unbundled_env: () { () -> untyped } -> untyped + + # Run block with environment present before + # [`Bundler`](https://docs.ruby-lang.org/en/2.7.0/Bundler.html) was activated + def self.with_original_env: () { () -> untyped } -> untyped +end + +Bundler::FREEBSD: untyped + +Bundler::NULL: untyped + +Bundler::ORIGINAL_ENV: untyped + +Bundler::SUDO_MUTEX: untyped + +Bundler::VERSION: untyped + +Bundler::WINDOWS: untyped + +class Bundler::APIResponseMismatchError < Bundler::BundlerError + def status_code: () -> untyped +end + +# Represents metadata from when the +# [`Bundler`](https://docs.ruby-lang.org/en/2.7.0/Bundler.html) gem was built. +module Bundler::BuildMetadata + # A string representing the date the bundler gem was built. + def self.built_at: () -> untyped + + # The SHA for the git commit the bundler gem was built from. + def self.git_commit_sha: () -> untyped + + # Whether this is an official release build of + # [`Bundler`](https://docs.ruby-lang.org/en/2.7.0/Bundler.html). + def self.release?: () -> untyped + + # A hash representation of the build metadata. + def self.to_h: () -> untyped +end + +class Bundler::BundlerError < StandardError + def self.all_errors: () -> untyped + + def self.status_code: (untyped code) -> untyped +end + +class Bundler::CurrentRuby + def jruby?: () -> untyped + + def jruby_18?: () -> untyped + + def jruby_19?: () -> untyped + + def jruby_1?: () -> untyped + + def jruby_20?: () -> untyped + + def jruby_21?: () -> untyped + + def jruby_22?: () -> untyped + + def jruby_23?: () -> untyped + + def jruby_24?: () -> untyped + + def jruby_25?: () -> untyped + + def jruby_26?: () -> untyped + + def jruby_27?: () -> untyped + + def jruby_2?: () -> untyped + + def maglev?: () -> untyped + + def maglev_18?: () -> untyped + + def maglev_19?: () -> untyped + + def maglev_1?: () -> untyped + + def maglev_20?: () -> untyped + + def maglev_21?: () -> untyped + + def maglev_22?: () -> untyped + + def maglev_23?: () -> untyped + + def maglev_24?: () -> untyped + + def maglev_25?: () -> untyped + + def maglev_26?: () -> untyped + + def maglev_27?: () -> untyped + + def maglev_2?: () -> untyped + + def mingw?: () -> untyped + + def mingw_18?: () -> untyped + + def mingw_19?: () -> untyped + + def mingw_1?: () -> untyped + + def mingw_20?: () -> untyped + + def mingw_21?: () -> untyped + + def mingw_22?: () -> untyped + + def mingw_23?: () -> untyped + + def mingw_24?: () -> untyped + + def mingw_25?: () -> untyped + + def mingw_26?: () -> untyped + + def mingw_27?: () -> untyped + + def mingw_2?: () -> untyped + + def mri?: () -> untyped + + def mri_18?: () -> untyped + + def mri_19?: () -> untyped + + def mri_1?: () -> untyped + + def mri_20?: () -> untyped + + def mri_21?: () -> untyped + + def mri_22?: () -> untyped + + def mri_23?: () -> untyped + + def mri_24?: () -> untyped + + def mri_25?: () -> untyped + + def mri_26?: () -> untyped + + def mri_27?: () -> untyped + + def mri_2?: () -> untyped + + def mswin64?: () -> untyped + + def mswin64_18?: () -> untyped + + def mswin64_19?: () -> untyped + + def mswin64_1?: () -> untyped + + def mswin64_20?: () -> untyped + + def mswin64_21?: () -> untyped + + def mswin64_22?: () -> untyped + + def mswin64_23?: () -> untyped + + def mswin64_24?: () -> untyped + + def mswin64_25?: () -> untyped + + def mswin64_26?: () -> untyped + + def mswin64_27?: () -> untyped + + def mswin64_2?: () -> untyped + + def mswin?: () -> untyped + + def mswin_18?: () -> untyped + + def mswin_19?: () -> untyped + + def mswin_1?: () -> untyped + + def mswin_20?: () -> untyped + + def mswin_21?: () -> untyped + + def mswin_22?: () -> untyped + + def mswin_23?: () -> untyped + + def mswin_24?: () -> untyped + + def mswin_25?: () -> untyped + + def mswin_26?: () -> untyped + + def mswin_27?: () -> untyped + + def mswin_2?: () -> untyped + + def on_18?: () -> untyped + + def on_19?: () -> untyped + + def on_1?: () -> untyped + + def on_20?: () -> untyped + + def on_21?: () -> untyped + + def on_22?: () -> untyped + + def on_23?: () -> untyped + + def on_24?: () -> untyped + + def on_25?: () -> untyped + + def on_26?: () -> untyped + + def on_27?: () -> untyped + + def on_2?: () -> untyped + + def rbx?: () -> untyped + + def rbx_18?: () -> untyped + + def rbx_19?: () -> untyped + + def rbx_1?: () -> untyped + + def rbx_20?: () -> untyped + + def rbx_21?: () -> untyped + + def rbx_22?: () -> untyped + + def rbx_23?: () -> untyped + + def rbx_24?: () -> untyped + + def rbx_25?: () -> untyped + + def rbx_26?: () -> untyped + + def rbx_27?: () -> untyped + + def rbx_2?: () -> untyped + + def ruby?: () -> untyped + + def ruby_18?: () -> untyped + + def ruby_19?: () -> untyped + + def ruby_1?: () -> untyped + + def ruby_20?: () -> untyped + + def ruby_21?: () -> untyped + + def ruby_22?: () -> untyped + + def ruby_23?: () -> untyped + + def ruby_24?: () -> untyped + + def ruby_25?: () -> untyped + + def ruby_26?: () -> untyped + + def ruby_27?: () -> untyped + + def ruby_2?: () -> untyped + + def truffleruby?: () -> untyped + + def truffleruby_18?: () -> untyped + + def truffleruby_19?: () -> untyped + + def truffleruby_1?: () -> untyped + + def truffleruby_20?: () -> untyped + + def truffleruby_21?: () -> untyped + + def truffleruby_22?: () -> untyped + + def truffleruby_23?: () -> untyped + + def truffleruby_24?: () -> untyped + + def truffleruby_25?: () -> untyped + + def truffleruby_26?: () -> untyped + + def truffleruby_27?: () -> untyped + + def truffleruby_2?: () -> untyped + + def x64_mingw?: () -> untyped + + def x64_mingw_18?: () -> untyped + + def x64_mingw_19?: () -> untyped + + def x64_mingw_1?: () -> untyped + + def x64_mingw_20?: () -> untyped + + def x64_mingw_21?: () -> untyped + + def x64_mingw_22?: () -> untyped + + def x64_mingw_23?: () -> untyped + + def x64_mingw_24?: () -> untyped + + def x64_mingw_25?: () -> untyped + + def x64_mingw_26?: () -> untyped + + def x64_mingw_27?: () -> untyped + + def x64_mingw_2?: () -> untyped +end + +Bundler::CurrentRuby::KNOWN_MAJOR_VERSIONS: untyped + +Bundler::CurrentRuby::KNOWN_MINOR_VERSIONS: untyped + +Bundler::CurrentRuby::KNOWN_PLATFORMS: untyped + +class Bundler::CyclicDependencyError < Bundler::BundlerError + def status_code: () -> untyped +end + +class Bundler::Definition + include ::Bundler::GemHelpers + + def add_current_platform: () -> untyped + + def add_platform: (untyped platform) -> untyped + + def current_dependencies: () -> untyped + + def dependencies: () -> Array[::Bundler::Dependency] + + def ensure_equivalent_gemfile_and_lockfile: (?untyped explicit_flag) -> untyped + + def find_indexed_specs: (untyped current_spec) -> untyped + + def find_resolved_spec: (untyped current_spec) -> untyped + + def gem_version_promoter: () -> untyped + + def gemfiles: () -> untyped + + def groups: () -> untyped + + def has_local_dependencies?: () -> untyped + + def has_rubygems_remotes?: () -> untyped + + def index: () -> untyped + + def initialize: (untyped lockfile, untyped dependencies, untyped sources, untyped unlock, ?untyped ruby_version, ?untyped optional_groups, ?untyped gemfiles) -> void + + def lock: (untyped file, ?untyped preserve_unknown_sections) -> untyped + + def locked_bundler_version: () -> untyped + + def locked_deps: () -> untyped + + def locked_gems: () -> Bundler::LockfileParser + + def locked_ruby_version: () -> untyped + + def locked_ruby_version_object: () -> untyped + + def lockfile: () -> Pathname + + def missing_specs: () -> untyped + + def missing_specs?: () -> untyped + + def new_platform?: () -> untyped + + def new_specs: () -> untyped + + def nothing_changed?: () -> untyped + + def platforms: () -> untyped + + def remove_platform: (untyped platform) -> untyped + + def removed_specs: () -> untyped + + def requested_specs: () -> untyped + + def requires: () -> untyped + + # Resolve all the dependencies specified in Gemfile. It ensures that + # dependencies that have been already resolved via locked file and are fresh + # are reused when resolving dependencies + # + # @return [SpecSet] resolved dependencies + def resolve: () -> untyped + + def resolve_remotely!: () -> untyped + + def resolve_with_cache!: () -> untyped + + def ruby_version: () -> untyped + + def spec_git_paths: () -> untyped + + # For given dependency list returns a SpecSet with Gemspec of all the required + # dependencies. + # + # ``` + # 1. The method first resolves the dependencies specified in Gemfile + # 2. After that it tries and fetches gemspec of resolved dependencies + # ``` + # + # @return [Bundler::SpecSet] + def specs: () -> untyped + + def specs_for: (untyped groups) -> untyped + + def to_lock: () -> untyped + + def unlocking?: () -> untyped + + def validate_platforms!: () -> untyped + + def validate_ruby!: () -> untyped + + def validate_runtime!: () -> untyped + + def self.build: (untyped gemfile, untyped lockfile, untyped unlock) -> untyped +end + +class Bundler::DepProxy + def ==: (untyped other) -> untyped + + def __platform: () -> untyped + + def dep: () -> untyped + + def eql?: (untyped other) -> untyped + + def hash: () -> untyped + + def initialize: (untyped dep, untyped platform) -> void + + def name: () -> untyped + + def requirement: () -> untyped + + def to_s: () -> untyped + + def type: () -> untyped +end + +class Bundler::Dependency < Gem::Dependency + def autorequire: () -> untyped + + def current_env?: () -> untyped + + def current_platform?: () -> untyped + + def gem_platforms: (untyped valid_platforms) -> untyped + + def gemfile: () -> untyped + + def groups: () -> untyped + + def initialize: (untyped name, untyped version, ?untyped options) { () -> untyped } -> void + + def platforms: () -> untyped + + def should_include?: () -> untyped + + def specific?: () -> untyped + + def to_lock: () -> untyped +end + +Bundler::Dependency::PLATFORM_MAP: untyped + +Bundler::Dependency::REVERSE_PLATFORM_MAP: untyped + +class Bundler::DeprecatedError < Bundler::BundlerError + def status_code: () -> untyped +end + +class Bundler::Dsl + @source: untyped + + @sources: untyped + + @git_sources: untyped + + @dependencies: untyped + + @groups: untyped + + @install_conditionals: untyped + + @optional_groups: untyped + + @platforms: untyped + + @env: untyped + + @ruby_version: untyped + + @gemspecs: untyped + + @gemfile: untyped + + @gemfiles: untyped + + @valid_keys: untyped + + include RubyDsl + + def self.evaluate: (untyped gemfile, untyped lockfile, untyped unlock) -> untyped + + VALID_PLATFORMS: untyped + + VALID_KEYS: ::Array["group" | "groups" | "git" | "path" | "glob" | "name" | "branch" | "ref" | "tag" | "require" | "submodules" | "platform" | "platforms" | "source" | "install_if" | "force_ruby_platform"] + + GITHUB_PULL_REQUEST_URL: ::Regexp + + GITLAB_MERGE_REQUEST_URL: ::Regexp + + attr_reader gemspecs: untyped + + attr_reader gemfile: untyped + + attr_accessor dependencies: untyped + + def initialize: () -> void + + def eval_gemfile: (untyped gemfile, ?untyped? contents) -> untyped + + def gemspec: (?path: ::String, ?glob: ::String, ?name: ::String, ?development_group: ::Symbol) -> void + + def gem: (untyped name, *untyped args) -> void + + def source: (::String source, ?type: ::Symbol) ?{ (?) -> untyped } -> void + + def git_source: (untyped name) ?{ (?) -> untyped } -> untyped + + def path: (untyped path, ?::Hash[untyped, untyped] options) ?{ (?) -> untyped } -> untyped + + def git: (untyped uri, ?::Hash[untyped, untyped] options) ?{ (?) -> untyped } -> untyped + + def github: (untyped repo, ?::Hash[untyped, untyped] options) ?{ () -> untyped } -> untyped + + def to_definition: (untyped lockfile, untyped unlock) -> untyped + + def group: (*untyped args) { () -> untyped } -> untyped + + def install_if: (*untyped args) { () -> untyped } -> untyped + + def platforms: (*untyped platforms) { () -> untyped } -> untyped + + alias platform platforms + + def env: (untyped name) { () -> untyped } -> untyped + + def plugin: (*untyped args) -> nil + + def method_missing: (untyped name, *untyped args) -> untyped + + def check_primary_source_safety: () -> untyped + + private + + def add_dependency: (untyped name, ?untyped? version, ?::Hash[untyped, untyped] options) -> (nil | untyped) + + def with_gemfile: (untyped gemfile) { (untyped) -> untyped } -> untyped + + def add_git_sources: () -> untyped + + def with_source: (untyped source) ?{ () -> untyped } -> untyped + + def normalize_hash: (untyped opts) -> untyped + + def valid_keys: () -> untyped + + def normalize_options: (untyped name, untyped version, untyped opts) -> untyped + + def normalize_group_options: (untyped opts, untyped groups) -> untyped + + def validate_keys: (untyped command, untyped opts, untyped valid_keys) -> (true | untyped) + + def normalize_source: (untyped source) -> untyped + + def deprecate_legacy_windows_platforms: (untyped platforms) -> (nil | untyped) + + def check_path_source_safety: () -> (nil | untyped) + + def check_rubygems_source_safety: () -> (untyped | nil) + + def multiple_global_source_warning: () -> untyped + + class DSLError < GemfileError + @status_code: untyped + + @description: untyped + + @dsl_path: untyped + + @backtrace: untyped + + @contents: untyped + + @to_s: untyped + + # @return [::String] the description that should be presented to the user. + # + attr_reader description: ::String + + # @return [::String] the path of the dsl file that raised the exception. + # + attr_reader dsl_path: ::String + + # @return [::Exception] the backtrace of the exception raised by the + # evaluation of the dsl file. + # + attr_reader backtrace: ::Exception + + # @param [::Exception] backtrace @see backtrace + # @param [::String] dsl_path @see dsl_path + # + def initialize: (untyped description, ::String dsl_path, ::Exception backtrace, ?untyped? contents) -> void + + def status_code: () -> untyped + + # @return [::String] the contents of the DSL that cause the exception to + # be raised. + # + def contents: () -> ::String + + # The message of the exception reports the content of podspec for the + # line that generated the original exception. + # + # @example Output + # + # Invalid podspec at `RestKit.podspec` - undefined method + # `exclude_header_search_paths=' for # + # + # from spec-repos/master/RestKit/0.9.3/RestKit.podspec:36 + # ------------------------------------------- + # # because it would break: #import + # > ns.exclude_header_search_paths = 'Code/RestKit.h' + # end + # ------------------------------------------- + # + # @return [::String] the message of the exception. + # + def to_s: () -> ::String + + private + + def parse_line_number_from_description: () -> ::Array[untyped] + end + + def gemfile_root: () -> untyped +end + +# used for Creating Specifications from the Gemcutter Endpoint +class Bundler::EndpointSpecification < Gem::Specification + def __swap__: (untyped spec) -> untyped + + def _local_specification: () -> untyped + + # needed for bundle clean + def bindir: () -> untyped + + def checksum: () -> untyped + + def dependencies: () -> untyped + + def dependencies=: (untyped dependencies) -> untyped + + # needed for binstubs + def executables: () -> untyped + + # needed for "with native extensions" during install + def extensions: () -> untyped + + def fetch_platform: () -> untyped + + def initialize: (untyped name, untyped version, untyped platform, untyped dependencies, ?untyped metadata) -> void + + # needed for inline + def load_paths: () -> untyped + + def name: () -> untyped + + def platform: () -> untyped + + # needed for post\_install\_messages during install + def post_install_message: () -> untyped + + def remote: () -> untyped + + def remote=: (untyped remote) -> untyped + + # needed for standalone, load required\_paths from local gemspec after the gem + # is installed + def require_paths: () -> untyped + + def required_ruby_version: () -> untyped + + def required_rubygems_version: () -> untyped + + def source: () -> untyped + + def source=: (untyped source) -> untyped + + def version: () -> untyped +end + +Bundler::EndpointSpecification::Elem: untyped + +Bundler::EndpointSpecification::ILLFORMED_MESSAGE: untyped + +class Bundler::EnvironmentPreserver + # @return [Hash] + def backup: () -> untyped + + def initialize: (untyped env, untyped keys) -> void + + # @return [Hash] + def restore: () -> untyped +end + +Bundler::EnvironmentPreserver::BUNDLER_KEYS: untyped + +Bundler::EnvironmentPreserver::BUNDLER_PREFIX: untyped + +Bundler::EnvironmentPreserver::INTENTIONALLY_NIL: untyped + +class Bundler::FeatureFlag + def allow_bundler_dependency_conflicts?: () -> untyped + + def allow_offline_install?: () -> untyped + + def auto_clean_without_path?: () -> untyped + + def auto_config_jobs?: () -> untyped + + def bundler_10_mode?: () -> untyped + + def bundler_1_mode?: () -> untyped + + def bundler_2_mode?: () -> untyped + + def bundler_3_mode?: () -> untyped + + def bundler_4_mode?: () -> untyped + + def bundler_5_mode?: () -> untyped + + def bundler_6_mode?: () -> untyped + + def bundler_7_mode?: () -> untyped + + def bundler_8_mode?: () -> untyped + + def bundler_9_mode?: () -> untyped + + def cache_all?: () -> untyped + + def cache_command_is_package?: () -> untyped + + def console_command?: () -> untyped + + def default_cli_command: () -> untyped + + def default_install_uses_path?: () -> untyped + + def deployment_means_frozen?: () -> untyped + + def disable_multisource?: () -> untyped + + def error_on_stderr?: () -> untyped + + def forget_cli_options?: () -> untyped + + def global_gem_cache?: () -> untyped + + def init_gems_rb?: () -> untyped + + def initialize: (untyped bundler_version) -> void + + def list_command?: () -> untyped + + def lockfile_uses_separate_rubygems_sources?: () -> untyped + + def only_update_to_newer_versions?: () -> untyped + + def path_relative_to_cwd?: () -> untyped + + def plugins?: () -> untyped + + def prefer_gems_rb?: () -> untyped + + def print_only_version_number?: () -> untyped + + def setup_makes_kernel_gem_public?: () -> untyped + + def skip_default_git_sources?: () -> untyped + + def specific_platform?: () -> untyped + + def suppress_install_using_messages?: () -> untyped + + def unlock_source_unlocks_spec?: () -> untyped + + def update_requires_all_flag?: () -> untyped + + def use_gem_version_promoter_for_major_updates?: () -> untyped + + def viz_command?: () -> untyped +end + +# # fileutils.rb +# +# Copyright (c) 2000-2007 Minero Aoki +# +# This program is free software. You can distribute/modify this program under +# the same terms of ruby. +# +# ## module [`Bundler::FileUtils`](https://docs.ruby-lang.org/en/2.7.0/Bundler/FileUtils.html) +# +# Namespace for several file utility methods for copying, moving, removing, etc. +# +# ### [`Module`](https://docs.ruby-lang.org/en/2.7.0/Module.html) Functions +# +# ```ruby +# require 'bundler/vendor/fileutils/lib/fileutils' +# +# Bundler::FileUtils.cd(dir, **options) +# Bundler::FileUtils.cd(dir, **options) {|dir| block } +# Bundler::FileUtils.pwd() +# Bundler::FileUtils.mkdir(dir, **options) +# Bundler::FileUtils.mkdir(list, **options) +# Bundler::FileUtils.mkdir_p(dir, **options) +# Bundler::FileUtils.mkdir_p(list, **options) +# Bundler::FileUtils.rmdir(dir, **options) +# Bundler::FileUtils.rmdir(list, **options) +# Bundler::FileUtils.ln(target, link, **options) +# Bundler::FileUtils.ln(targets, dir, **options) +# Bundler::FileUtils.ln_s(target, link, **options) +# Bundler::FileUtils.ln_s(targets, dir, **options) +# Bundler::FileUtils.ln_sf(target, link, **options) +# Bundler::FileUtils.cp(src, dest, **options) +# Bundler::FileUtils.cp(list, dir, **options) +# Bundler::FileUtils.cp_r(src, dest, **options) +# Bundler::FileUtils.cp_r(list, dir, **options) +# Bundler::FileUtils.mv(src, dest, **options) +# Bundler::FileUtils.mv(list, dir, **options) +# Bundler::FileUtils.rm(list, **options) +# Bundler::FileUtils.rm_r(list, **options) +# Bundler::FileUtils.rm_rf(list, **options) +# Bundler::FileUtils.install(src, dest, **options) +# Bundler::FileUtils.chmod(mode, list, **options) +# Bundler::FileUtils.chmod_R(mode, list, **options) +# Bundler::FileUtils.chown(user, group, list, **options) +# Bundler::FileUtils.chown_R(user, group, list, **options) +# Bundler::FileUtils.touch(list, **options) +# ``` +# +# Possible `options` are: +# +# `:force` +# : forced operation (rewrite files if exist, remove directories if not empty, +# etc.); +# `:verbose` +# : print command to be run, in bash syntax, before performing it; +# `:preserve` +# : preserve object's group, user and modification time on copying; +# `:noop` +# : no changes are made (usable in combination with `:verbose` which will +# print the command to run) +# +# +# Each method documents the options that it honours. See also +# [`::commands`](https://docs.ruby-lang.org/en/2.7.0/FileUtils.html#method-c-commands), +# [`::options`](https://docs.ruby-lang.org/en/2.7.0/FileUtils.html#method-c-options) +# and +# [`::options_of`](https://docs.ruby-lang.org/en/2.7.0/FileUtils.html#method-c-options_of) +# methods to introspect which command have which options. +# +# All methods that have the concept of a "source" file or directory can take +# either one file or a list of files in that argument. See the method +# documentation for examples. +# +# There are some 'low level' methods, which do not accept keyword arguments: +# +# ```ruby +# Bundler::FileUtils.copy_entry(src, dest, preserve = false, dereference_root = false, remove_destination = false) +# Bundler::FileUtils.copy_file(src, dest, preserve = false, dereference = true) +# Bundler::FileUtils.copy_stream(srcstream, deststream) +# Bundler::FileUtils.remove_entry(path, force = false) +# Bundler::FileUtils.remove_entry_secure(path, force = false) +# Bundler::FileUtils.remove_file(path, force = false) +# Bundler::FileUtils.compare_file(path_a, path_b) +# Bundler::FileUtils.compare_stream(stream_a, stream_b) +# Bundler::FileUtils.uptodate?(file, cmp_list) +# ``` +# +# ## module [`Bundler::FileUtils::Verbose`](https://docs.ruby-lang.org/en/2.7.0/Bundler/FileUtils/Verbose.html) +# +# This module has all methods of +# [`Bundler::FileUtils`](https://docs.ruby-lang.org/en/2.7.0/Bundler/FileUtils.html) +# module, but it outputs messages before acting. This equates to passing the +# `:verbose` flag to methods in +# [`Bundler::FileUtils`](https://docs.ruby-lang.org/en/2.7.0/Bundler/FileUtils.html). +# +# ## module [`Bundler::FileUtils::NoWrite`](https://docs.ruby-lang.org/en/2.7.0/Bundler/FileUtils/NoWrite.html) +# +# This module has all methods of +# [`Bundler::FileUtils`](https://docs.ruby-lang.org/en/2.7.0/Bundler/FileUtils.html) +# module, but never changes files/directories. This equates to passing the +# `:noop` flag to methods in +# [`Bundler::FileUtils`](https://docs.ruby-lang.org/en/2.7.0/Bundler/FileUtils.html). +# +# ## module [`Bundler::FileUtils::DryRun`](https://docs.ruby-lang.org/en/2.7.0/Bundler/FileUtils/DryRun.html) +# +# This module has all methods of +# [`Bundler::FileUtils`](https://docs.ruby-lang.org/en/2.7.0/Bundler/FileUtils.html) +# module, but never changes files/directories. This equates to passing the +# `:noop` and `:verbose` flags to methods in +# [`Bundler::FileUtils`](https://docs.ruby-lang.org/en/2.7.0/Bundler/FileUtils.html). +module Bundler::FileUtils + include ::Bundler::FileUtils::StreamUtils_ + + extend ::Bundler::FileUtils::StreamUtils_ + + def self.cd: (untyped dir, ?verbose: untyped verbose) { () -> untyped } -> untyped + + def self.chdir: (untyped dir, ?verbose: untyped verbose) { () -> untyped } -> untyped + + def self.chmod: (untyped mode, untyped list, ?noop: untyped noop, ?verbose: untyped verbose) -> untyped + + def self.chmod_R: (untyped mode, untyped list, ?noop: untyped noop, ?verbose: untyped verbose, ?force: untyped force) -> untyped + + def self.chown: (untyped user, untyped group, untyped list, ?noop: untyped noop, ?verbose: untyped verbose) -> untyped + + def self.chown_R: (untyped user, untyped group, untyped list, ?noop: untyped noop, ?verbose: untyped verbose, ?force: untyped force) -> untyped + + def self.cmp: (untyped a, untyped b) -> untyped + + def self.collect_method: (untyped opt) -> untyped + + # Returns an [`Array`](https://docs.ruby-lang.org/en/2.7.0/Array.html) of + # names of high-level methods that accept any keyword arguments. + # + # ```ruby + # p Bundler::FileUtils.commands #=> ["chmod", "cp", "cp_r", "install", ...] + # ``` + def self.commands: () -> untyped + + def self.compare_file: (untyped a, untyped b) -> untyped + + def self.compare_stream: (untyped a, untyped b) -> untyped + + def self.copy: (untyped src, untyped dest, ?preserve: untyped preserve, ?noop: untyped noop, ?verbose: untyped verbose) -> untyped + + def self.copy_entry: (untyped src, untyped dest, ?untyped preserve, ?untyped dereference_root, ?untyped remove_destination) -> untyped + + def self.copy_file: (untyped src, untyped dest, ?untyped preserve, ?untyped dereference) -> untyped + + def self.copy_stream: (untyped src, untyped dest) -> untyped + + def self.cp: (untyped src, untyped dest, ?preserve: untyped preserve, ?noop: untyped noop, ?verbose: untyped verbose) -> untyped + + def self.cp_r: (untyped src, untyped dest, ?preserve: untyped preserve, ?noop: untyped noop, ?verbose: untyped verbose, ?dereference_root: untyped dereference_root, ?remove_destination: untyped remove_destination) -> untyped + + # Alias for: + # [`pwd`](https://docs.ruby-lang.org/en/2.7.0/FileUtils.html#method-i-pwd) + def self.getwd: () -> untyped + + def self.have_option?: (untyped mid, untyped opt) -> untyped + + def self.identical?: (untyped a, untyped b) -> untyped + + def self.install: (untyped src, untyped dest, ?mode: untyped mode, ?owner: untyped owner, ?group: untyped group, ?preserve: untyped preserve, ?noop: untyped noop, ?verbose: untyped verbose) -> untyped + + def self.link: (untyped src, untyped dest, ?force: untyped force, ?noop: untyped noop, ?verbose: untyped verbose) -> untyped + + def self.ln: (untyped src, untyped dest, ?force: untyped force, ?noop: untyped noop, ?verbose: untyped verbose) -> untyped + + def self.ln_s: (untyped src, untyped dest, ?force: untyped force, ?noop: untyped noop, ?verbose: untyped verbose) -> untyped + + def self.ln_sf: (untyped src, untyped dest, ?noop: untyped noop, ?verbose: untyped verbose) -> untyped + + def self.makedirs: (untyped list, ?mode: untyped mode, ?noop: untyped noop, ?verbose: untyped verbose) -> untyped + + def self.mkdir: (untyped list, ?mode: untyped mode, ?noop: untyped noop, ?verbose: untyped verbose) -> untyped + + def self.mkdir_p: (untyped list, ?mode: untyped mode, ?noop: untyped noop, ?verbose: untyped verbose) -> untyped + + def self.mkpath: (untyped list, ?mode: untyped mode, ?noop: untyped noop, ?verbose: untyped verbose) -> untyped + + def self.move: (untyped src, untyped dest, ?force: untyped force, ?noop: untyped noop, ?verbose: untyped verbose, ?secure: untyped secure) -> untyped + + def self.mv: (untyped src, untyped dest, ?force: untyped force, ?noop: untyped noop, ?verbose: untyped verbose, ?secure: untyped secure) -> untyped + + # Returns an [`Array`](https://docs.ruby-lang.org/en/2.7.0/Array.html) of + # option names. + # + # ```ruby + # p Bundler::FileUtils.options #=> ["noop", "force", "verbose", "preserve", "mode"] + # ``` + def self.options: () -> untyped + + def self.options_of: (untyped mid) -> untyped + + def self.private_module_function: (untyped name) -> untyped + + # Returns the name of the current directory. + # + # Also aliased as: + # [`getwd`](https://docs.ruby-lang.org/en/2.7.0/FileUtils.html#method-c-getwd) + def self.pwd: () -> untyped + + def self.remove: (untyped list, ?force: untyped force, ?noop: untyped noop, ?verbose: untyped verbose) -> untyped + + def self.remove_dir: (untyped path, ?untyped force) -> untyped + + def self.remove_entry: (untyped path, ?untyped force) -> untyped + + def self.remove_entry_secure: (untyped path, ?untyped force) -> untyped + + def self.remove_file: (untyped path, ?untyped force) -> untyped + + def self.rm: (untyped list, ?force: untyped force, ?noop: untyped noop, ?verbose: untyped verbose) -> untyped + + def self.rm_f: (untyped list, ?noop: untyped noop, ?verbose: untyped verbose) -> untyped + + def self.rm_r: (untyped list, ?force: untyped force, ?noop: untyped noop, ?verbose: untyped verbose, ?secure: untyped secure) -> untyped + + def self.rm_rf: (untyped list, ?noop: untyped noop, ?verbose: untyped verbose, ?secure: untyped secure) -> untyped + + def self.rmdir: (untyped list, ?parents: untyped parents, ?noop: untyped noop, ?verbose: untyped verbose) -> untyped + + def self.rmtree: (untyped list, ?noop: untyped noop, ?verbose: untyped verbose, ?secure: untyped secure) -> untyped + + def self.safe_unlink: (untyped list, ?noop: untyped noop, ?verbose: untyped verbose) -> untyped + + def self.symlink: (untyped src, untyped dest, ?force: untyped force, ?noop: untyped noop, ?verbose: untyped verbose) -> untyped + + def self.touch: (untyped list, ?noop: untyped noop, ?verbose: untyped verbose, ?mtime: untyped mtime, ?nocreate: untyped nocreate) -> untyped + + def self.uptodate?: (untyped new, untyped old_list) -> untyped +end + +Bundler::FileUtils::LOW_METHODS: untyped + +Bundler::FileUtils::METHODS: untyped + +Bundler::FileUtils::OPT_TABLE: untyped + +# This module has all methods of +# [`Bundler::FileUtils`](https://docs.ruby-lang.org/en/2.7.0/Bundler/FileUtils.html) +# module, but never changes files/directories, with printing message before +# acting. This equates to passing the `:noop` and `:verbose` flag to methods in +# [`Bundler::FileUtils`](https://docs.ruby-lang.org/en/2.7.0/Bundler/FileUtils.html). +module Bundler::FileUtils::DryRun + include ::Bundler::FileUtils::LowMethods + + include ::Bundler::FileUtils + + include ::Bundler::FileUtils::StreamUtils_ + + extend ::Bundler::FileUtils::DryRun + + extend ::Bundler::FileUtils::LowMethods + + extend ::Bundler::FileUtils + + extend ::Bundler::FileUtils::StreamUtils_ + + def self.cd: (*untyped _) -> untyped + + def self.chdir: (*untyped _) -> untyped + + def self.chmod: (*untyped args, **untyped options) -> untyped + + def self.chmod_R: (*untyped args, **untyped options) -> untyped + + def self.chown: (*untyped args, **untyped options) -> untyped + + def self.chown_R: (*untyped args, **untyped options) -> untyped + + def self.cmp: (*untyped _) -> untyped + + def self.compare_file: (*untyped _) -> untyped + + def self.compare_stream: (*untyped _) -> untyped + + def self.copy: (*untyped args, **untyped options) -> untyped + + def self.copy_entry: (*untyped _) -> untyped + + def self.copy_file: (*untyped _) -> untyped + + def self.copy_stream: (*untyped _) -> untyped + + def self.cp: (*untyped args, **untyped options) -> untyped + + def self.cp_r: (*untyped args, **untyped options) -> untyped + + def self.getwd: (*untyped _) -> untyped + + def self.identical?: (*untyped _) -> untyped + + def self.install: (*untyped args, **untyped options) -> untyped + + def self.link: (*untyped args, **untyped options) -> untyped + + def self.ln: (*untyped args, **untyped options) -> untyped + + def self.ln_s: (*untyped args, **untyped options) -> untyped + + def self.ln_sf: (*untyped args, **untyped options) -> untyped + + def self.makedirs: (*untyped args, **untyped options) -> untyped + + def self.mkdir: (*untyped args, **untyped options) -> untyped + + def self.mkdir_p: (*untyped args, **untyped options) -> untyped + + def self.mkpath: (*untyped args, **untyped options) -> untyped + + def self.move: (*untyped args, **untyped options) -> untyped + + def self.mv: (*untyped args, **untyped options) -> untyped + + def self.pwd: (*untyped _) -> untyped + + def self.remove: (*untyped args, **untyped options) -> untyped + + def self.remove_dir: (*untyped _) -> untyped + + def self.remove_entry: (*untyped _) -> untyped + + def self.remove_entry_secure: (*untyped _) -> untyped + + def self.remove_file: (*untyped _) -> untyped + + def self.rm: (*untyped args, **untyped options) -> untyped + + def self.rm_f: (*untyped args, **untyped options) -> untyped + + def self.rm_r: (*untyped args, **untyped options) -> untyped + + def self.rm_rf: (*untyped args, **untyped options) -> untyped + + def self.rmdir: (*untyped args, **untyped options) -> untyped + + def self.rmtree: (*untyped args, **untyped options) -> untyped + + def self.safe_unlink: (*untyped args, **untyped options) -> untyped + + def self.symlink: (*untyped args, **untyped options) -> untyped + + def self.touch: (*untyped args, **untyped options) -> untyped + + def self.uptodate?: (*untyped _) -> untyped +end + +class Bundler::FileUtils::Entry_ + include ::Bundler::FileUtils::StreamUtils_ + + def blockdev?: () -> untyped + + def chardev?: () -> untyped + + def chmod: (untyped mode) -> untyped + + def chown: (untyped uid, untyped gid) -> untyped + + def copy: (untyped dest) -> untyped + + def copy_file: (untyped dest) -> untyped + + def copy_metadata: (untyped path) -> untyped + + def dereference?: () -> untyped + + def directory?: () -> untyped + + def door?: () -> untyped + + def entries: () -> untyped + + def exist?: () -> untyped + + def file?: () -> untyped + + def initialize: (untyped a, ?untyped b, ?untyped deref) -> void + + def inspect: () -> untyped + + def lstat: () -> untyped + + def lstat!: () -> untyped + + def path: () -> untyped + + def pipe?: () -> untyped + + def platform_support: () -> untyped + + def postorder_traverse: () -> untyped + + def prefix: () -> untyped + + def preorder_traverse: () -> untyped + + def rel: () -> untyped + + def remove: () -> untyped + + def remove_dir1: () -> untyped + + def remove_file: () -> untyped + + def socket?: () -> untyped + + def stat: () -> untyped + + def stat!: () -> untyped + + def symlink?: () -> untyped + + def traverse: () -> untyped + + def wrap_traverse: (untyped pre, untyped post) -> untyped +end + +Bundler::FileUtils::Entry_::DIRECTORY_TERM: untyped + +Bundler::FileUtils::Entry_::SYSCASE: untyped + +Bundler::FileUtils::Entry_::S_IF_DOOR: untyped + +module Bundler::FileUtils::LowMethods +end + +# This module has all methods of +# [`Bundler::FileUtils`](https://docs.ruby-lang.org/en/2.7.0/Bundler/FileUtils.html) +# module, but never changes files/directories. This equates to passing the +# `:noop` flag to methods in +# [`Bundler::FileUtils`](https://docs.ruby-lang.org/en/2.7.0/Bundler/FileUtils.html). +module Bundler::FileUtils::NoWrite + include ::Bundler::FileUtils::LowMethods + + include ::Bundler::FileUtils + + include ::Bundler::FileUtils::StreamUtils_ + + extend ::Bundler::FileUtils::NoWrite + + extend ::Bundler::FileUtils::LowMethods + + extend ::Bundler::FileUtils + + extend ::Bundler::FileUtils::StreamUtils_ + + def self.cd: (*untyped _) -> untyped + + def self.chdir: (*untyped _) -> untyped + + def self.chmod: (*untyped args, **untyped options) -> untyped + + def self.chmod_R: (*untyped args, **untyped options) -> untyped + + def self.chown: (*untyped args, **untyped options) -> untyped + + def self.chown_R: (*untyped args, **untyped options) -> untyped + + def self.cmp: (*untyped _) -> untyped + + def self.compare_file: (*untyped _) -> untyped + + def self.compare_stream: (*untyped _) -> untyped + + def self.copy: (*untyped args, **untyped options) -> untyped + + def self.copy_entry: (*untyped _) -> untyped + + def self.copy_file: (*untyped _) -> untyped + + def self.copy_stream: (*untyped _) -> untyped + + def self.cp: (*untyped args, **untyped options) -> untyped + + def self.cp_r: (*untyped args, **untyped options) -> untyped + + def self.getwd: (*untyped _) -> untyped + + def self.identical?: (*untyped _) -> untyped + + def self.install: (*untyped args, **untyped options) -> untyped + + def self.link: (*untyped args, **untyped options) -> untyped + + def self.ln: (*untyped args, **untyped options) -> untyped + + def self.ln_s: (*untyped args, **untyped options) -> untyped + + def self.ln_sf: (*untyped args, **untyped options) -> untyped + + def self.makedirs: (*untyped args, **untyped options) -> untyped + + def self.mkdir: (*untyped args, **untyped options) -> untyped + + def self.mkdir_p: (*untyped args, **untyped options) -> untyped + + def self.mkpath: (*untyped args, **untyped options) -> untyped + + def self.move: (*untyped args, **untyped options) -> untyped + + def self.mv: (*untyped args, **untyped options) -> untyped + + def self.pwd: (*untyped _) -> untyped + + def self.remove: (*untyped args, **untyped options) -> untyped + + def self.remove_dir: (*untyped _) -> untyped + + def self.remove_entry: (*untyped _) -> untyped + + def self.remove_entry_secure: (*untyped _) -> untyped + + def self.remove_file: (*untyped _) -> untyped + + def self.rm: (*untyped args, **untyped options) -> untyped + + def self.rm_f: (*untyped args, **untyped options) -> untyped + + def self.rm_r: (*untyped args, **untyped options) -> untyped + + def self.rm_rf: (*untyped args, **untyped options) -> untyped + + def self.rmdir: (*untyped args, **untyped options) -> untyped + + def self.rmtree: (*untyped args, **untyped options) -> untyped + + def self.safe_unlink: (*untyped args, **untyped options) -> untyped + + def self.symlink: (*untyped args, **untyped options) -> untyped + + def self.touch: (*untyped args, **untyped options) -> untyped + + def self.uptodate?: (*untyped _) -> untyped +end + +module Bundler::FileUtils::StreamUtils_ +end + +# This module has all methods of +# [`Bundler::FileUtils`](https://docs.ruby-lang.org/en/2.7.0/Bundler/FileUtils.html) +# module, but it outputs messages before acting. This equates to passing the +# `:verbose` flag to methods in +# [`Bundler::FileUtils`](https://docs.ruby-lang.org/en/2.7.0/Bundler/FileUtils.html). +module Bundler::FileUtils::Verbose + include ::Bundler::FileUtils + + include ::Bundler::FileUtils::StreamUtils_ + + extend ::Bundler::FileUtils::Verbose + + extend ::Bundler::FileUtils + + extend ::Bundler::FileUtils::StreamUtils_ + + def self.cd: (*untyped args, **untyped options) -> untyped + + def self.chdir: (*untyped args, **untyped options) -> untyped + + def self.chmod: (*untyped args, **untyped options) -> untyped + + def self.chmod_R: (*untyped args, **untyped options) -> untyped + + def self.chown: (*untyped args, **untyped options) -> untyped + + def self.chown_R: (*untyped args, **untyped options) -> untyped + + def self.cmp: (untyped a, untyped b) -> untyped + + def self.compare_file: (untyped a, untyped b) -> untyped + + def self.compare_stream: (untyped a, untyped b) -> untyped + + def self.copy: (*untyped args, **untyped options) -> untyped + + def self.copy_entry: (untyped src, untyped dest, ?untyped preserve, ?untyped dereference_root, ?untyped remove_destination) -> untyped + + def self.copy_file: (untyped src, untyped dest, ?untyped preserve, ?untyped dereference) -> untyped + + def self.copy_stream: (untyped src, untyped dest) -> untyped + + def self.cp: (*untyped args, **untyped options) -> untyped + + def self.cp_r: (*untyped args, **untyped options) -> untyped + + def self.getwd: () -> untyped + + def self.identical?: (untyped a, untyped b) -> untyped + + def self.install: (*untyped args, **untyped options) -> untyped + + def self.link: (*untyped args, **untyped options) -> untyped + + def self.ln: (*untyped args, **untyped options) -> untyped + + def self.ln_s: (*untyped args, **untyped options) -> untyped + + def self.ln_sf: (*untyped args, **untyped options) -> untyped + + def self.makedirs: (*untyped args, **untyped options) -> untyped + + def self.mkdir: (*untyped args, **untyped options) -> untyped + + def self.mkdir_p: (*untyped args, **untyped options) -> untyped + + def self.mkpath: (*untyped args, **untyped options) -> untyped + + def self.move: (*untyped args, **untyped options) -> untyped + + def self.mv: (*untyped args, **untyped options) -> untyped + + def self.pwd: () -> untyped + + def self.remove: (*untyped args, **untyped options) -> untyped + + def self.remove_dir: (untyped path, ?untyped force) -> untyped + + def self.remove_entry: (untyped path, ?untyped force) -> untyped + + def self.remove_entry_secure: (untyped path, ?untyped force) -> untyped + + def self.remove_file: (untyped path, ?untyped force) -> untyped + + def self.rm: (*untyped args, **untyped options) -> untyped + + def self.rm_f: (*untyped args, **untyped options) -> untyped + + def self.rm_r: (*untyped args, **untyped options) -> untyped + + def self.rm_rf: (*untyped args, **untyped options) -> untyped + + def self.rmdir: (*untyped args, **untyped options) -> untyped + + def self.rmtree: (*untyped args, **untyped options) -> untyped + + def self.safe_unlink: (*untyped args, **untyped options) -> untyped + + def self.symlink: (*untyped args, **untyped options) -> untyped + + def self.touch: (*untyped args, **untyped options) -> untyped + + def self.uptodate?: (untyped new, untyped old_list) -> untyped +end + +class Bundler::GemHelper + def allowed_push_host: () -> untyped + + def already_tagged?: () -> untyped + + def base: () -> untyped + + def build_checksum: (?untyped built_gem_path) -> untyped + + def build_gem: () -> untyped + + def built_gem_path: () -> untyped + + def clean?: () -> untyped + + def committed?: () -> untyped + + def current_branch: () -> untyped + + def default_remote: () -> untyped + + def gem_command: () -> untyped + + def gem_key: () -> untyped + + def gem_push?: () -> untyped + + def gem_push_host: () -> untyped + + def gemspec: () -> untyped + + def git_push: (?untyped remote) -> untyped + + def guard_clean: () -> untyped + + def initialize: (?untyped base, ?untyped name) -> void + + def install: () -> untyped + + def install_gem: (?untyped built_gem_path, ?untyped local) -> untyped + + def name: () -> untyped + + def rubygem_push: (untyped path) -> untyped + + def sh: (untyped cmd) { () -> untyped } -> untyped + + def sh_with_input: (untyped cmd) -> untyped + + def sh_with_status: (untyped cmd) { () -> untyped } -> untyped + + def spec_path: () -> untyped + + def tag_prefix=: (untyped tag_prefix) -> untyped + + def tag_version: () -> untyped + + def version: () -> untyped + + def version_tag: () -> untyped + + def self.instance: () -> untyped + + def self.instance=: (untyped instance) -> untyped + + def self.install_tasks: (?untyped opts) -> untyped + + def self.tag_prefix=: (untyped tag_prefix) -> untyped + + def self.gemspec: () { () -> untyped } -> untyped +end + +module Bundler::GemHelpers + def self.generic: (untyped p) -> untyped + + def self.generic_local_platform: () -> untyped + + def self.platform_specificity_match: (untyped spec_platform, untyped user_platform) -> untyped + + def self.select_best_platform_match: (untyped specs, untyped platform) -> untyped +end + +Bundler::GemHelpers::GENERICS: untyped + +Bundler::GemHelpers::GENERIC_CACHE: untyped + +class Bundler::GemHelpers::PlatformMatch < Struct + def <=>: (untyped other) -> untyped + + def cpu_match: () -> untyped + + def cpu_match=: (untyped _) -> untyped + + def os_match: () -> untyped + + def os_match=: (untyped _) -> untyped + + def platform_version_match: () -> untyped + + def platform_version_match=: (untyped _) -> untyped + + def self.[]: (*untyped _) -> untyped + + def self.cpu_match: (untyped spec_platform, untyped user_platform) -> untyped + + def self.members: () -> untyped + + def self.new: (*untyped _) -> untyped + + def self.os_match: (untyped spec_platform, untyped user_platform) -> untyped + + def self.platform_version_match: (untyped spec_platform, untyped user_platform) -> untyped +end + +Bundler::GemHelpers::PlatformMatch::Elem: untyped + +Bundler::GemHelpers::PlatformMatch::EXACT_MATCH: untyped + +Bundler::GemHelpers::PlatformMatch::WORST_MATCH: untyped + +class Bundler::GemNotFound < Bundler::BundlerError + def status_code: () -> untyped +end + +class Bundler::GemRequireError < Bundler::BundlerError + def initialize: (untyped orig_exception, untyped msg) -> void + + def orig_exception: () -> untyped + + def status_code: () -> untyped +end + +class Bundler::GemfileError < Bundler::BundlerError + def status_code: () -> untyped +end + +class Bundler::GemfileEvalError < Bundler::GemfileError +end + +class Bundler::GemfileLockNotFound < Bundler::BundlerError + def status_code: () -> untyped +end + +class Bundler::GemfileNotFound < Bundler::BundlerError + def status_code: () -> untyped +end + +class Bundler::GemspecError < Bundler::BundlerError + def status_code: () -> untyped +end + +class Bundler::GenericSystemCallError < Bundler::BundlerError + def initialize: (untyped underlying_error, untyped message) -> void + + def status_code: () -> untyped + + def underlying_error: () -> untyped +end + +class Bundler::GitError < Bundler::BundlerError + def status_code: () -> untyped +end + +class Bundler::HTTPError < Bundler::BundlerError + def filter_uri: (untyped uri) -> untyped + + def status_code: () -> untyped +end + +# Handles all the fetching with the rubygems server +class Bundler::Fetcher +end + +# This error is raised if HTTP authentication is required, but not provided. +class Bundler::Fetcher::AuthenticationRequiredError < Bundler::HTTPError +end + +# This error is raised if HTTP authentication is provided, but incorrect. +class Bundler::Fetcher::BadAuthenticationError < Bundler::HTTPError +end + +# This is the error raised if +# [`OpenSSL`](https://docs.ruby-lang.org/en/2.7.0/OpenSSL.html) fails the cert +# verification +class Bundler::Fetcher::CertificateFailureError < Bundler::HTTPError +end + +# This error is raised if the API returns a 413 (only printed in verbose) +class Bundler::Fetcher::FallbackError < Bundler::HTTPError +end + +# This error is raised when it looks like the network is down +class Bundler::Fetcher::NetworkDownError < Bundler::HTTPError +end + +# This is the error raised when a source is HTTPS and +# [`OpenSSL`](https://docs.ruby-lang.org/en/2.7.0/OpenSSL.html) didn't load +class Bundler::Fetcher::SSLError < Bundler::HTTPError +end + +class Bundler::Index[out Elem] + include ::Enumerable + + def <<: (untyped spec) -> untyped + + def ==: (untyped other) -> untyped + + def []: (untyped query, ?untyped base) -> untyped + + def add_source: (untyped index) -> untyped + + def all_specs: () -> untyped + + def dependencies_eql?: (untyped spec, untyped other_spec) -> untyped + + def dependency_names: () -> untyped + + def each: () { () -> untyped } -> untyped + + def empty?: () -> untyped + + def initialize: () -> void + + def inspect: () -> untyped + + def local_search: (untyped query, ?untyped base) -> untyped + + def search: (untyped query, ?untyped base) -> untyped + + def search_all: (untyped name) -> untyped + + def size: () -> untyped + + def sort_specs: (untyped specs) -> untyped + + def sources: () -> untyped + + def spec_names: () -> untyped + + def specs: () -> untyped + + # returns a list of the dependencies + def unmet_dependency_names: () -> untyped + + def unsorted_search: (untyped query, untyped base) -> untyped + + def use: (untyped other, ?untyped override_dupes) -> untyped + + def self.build: () -> untyped + + def self.sort_specs: (untyped specs) -> untyped +end + +Bundler::Index::EMPTY_SEARCH: untyped + +Bundler::Index::NULL: untyped + +Bundler::Index::RUBY: untyped + +class Bundler::InstallError < Bundler::BundlerError + def status_code: () -> untyped +end + +class Bundler::InstallHookError < Bundler::BundlerError + def status_code: () -> untyped +end + +class Bundler::InvalidOption < Bundler::BundlerError + def status_code: () -> untyped +end + +class Bundler::LazySpecification + include ::Bundler::MatchPlatform + + include ::Bundler::GemHelpers + + def ==: (untyped other) -> untyped + + def __materialize__: () -> untyped + + def dependencies: () -> untyped + + def full_name: () -> untyped + + def git_version: () -> untyped + + def identifier: () -> untyped + + def initialize: (untyped name, untyped version, untyped platform, ?untyped source) -> void + + def name: () -> String + + def platform: () -> untyped + + def remote: () -> untyped + + def remote=: (untyped remote) -> untyped + + def respond_to?: (*untyped args) -> untyped + + def satisfies?: (untyped dependency) -> untyped + + def source: () -> untyped + + def source=: (untyped source) -> untyped + + def to_lock: () -> untyped + + def to_s: () -> untyped + + def version: () -> String +end + +class Bundler::LazySpecification::Identifier < Struct + include ::Comparable + + extend ::T::Generic + + def <=>: (untyped other) -> untyped + + def dependencies: () -> untyped + + def dependencies=: (untyped _) -> untyped + + def name: () -> untyped + + def name=: (untyped _) -> untyped + + def platform: () -> untyped + + def platform=: (untyped _) -> untyped + + def platform_string: () -> untyped + + def source: () -> untyped + + def source=: (untyped _) -> untyped + + def version: () -> untyped + + def version=: (untyped _) -> untyped + + def self.[]: (*untyped _) -> untyped + + def self.members: () -> untyped + + def self.new: (*untyped _) -> untyped +end + +Bundler::LazySpecification::Identifier::Elem: untyped + +class Bundler::LockfileError < Bundler::BundlerError + def status_code: () -> untyped +end + +class Bundler::LockfileParser + def bundler_version: () -> untyped + + def dependencies: () -> Hash[String, Bundler::Dependency] + + def initialize: (untyped lockfile) -> void + + def platforms: () -> untyped + + def ruby_version: () -> untyped + + def sources: () -> untyped + + def specs: () -> ::Array[::Bundler::LazySpecification] + + def warn_for_outdated_bundler_version: () -> untyped + + def self.sections_in_lockfile: (untyped lockfile_contents) -> untyped + + def self.sections_to_ignore: (?untyped base_version) -> untyped + + def self.unknown_sections_in_lockfile: (untyped lockfile_contents) -> untyped +end + +Bundler::LockfileParser::BUNDLED: untyped + +Bundler::LockfileParser::DEPENDENCIES: untyped + +Bundler::LockfileParser::ENVIRONMENT_VERSION_SECTIONS: untyped + +Bundler::LockfileParser::GEM: untyped + +Bundler::LockfileParser::GIT: untyped + +Bundler::LockfileParser::KNOWN_SECTIONS: untyped + +Bundler::LockfileParser::NAME_VERSION: untyped + +Bundler::LockfileParser::OPTIONS: untyped + +Bundler::LockfileParser::PATH: untyped + +Bundler::LockfileParser::PLATFORMS: untyped + +Bundler::LockfileParser::PLUGIN: untyped + +Bundler::LockfileParser::RUBY: untyped + +Bundler::LockfileParser::SECTIONS_BY_VERSION_INTRODUCED: untyped + +Bundler::LockfileParser::SOURCE: untyped + +Bundler::LockfileParser::SPECS: untyped + +Bundler::LockfileParser::TYPES: untyped + +class Bundler::MarshalError < StandardError +end + +module Bundler::MatchPlatform + include ::Bundler::GemHelpers + + def match_platform: (untyped p) -> untyped + + def self.platforms_match?: (untyped gemspec_platform, untyped local_platform) -> untyped +end + +# [`Bundler::Molinillo`](https://docs.ruby-lang.org/en/2.7.0/Bundler/Molinillo.html) +# is a generic dependency resolution algorithm. +module Bundler::Molinillo +end + +Bundler::Molinillo::VERSION: untyped + +# An error caused by attempting to fulfil a dependency that was circular +# +# @note This exception will be thrown iff a {Vertex} is added to a +# +# ``` +# {DependencyGraph} that has a {DependencyGraph::Vertex#path_to?} an +# existing {DependencyGraph::Vertex} +# ``` +class Bundler::Molinillo::CircularDependencyError < Bundler::Molinillo::ResolverError + # [`Set`](https://docs.ruby-lang.org/en/2.7.0/Set.html) + # : the dependencies responsible for causing the error + def dependencies: () -> untyped + + def initialize: (untyped vertices) -> void +end + +# Hacks needed for old Ruby versions. +module Bundler::Molinillo::Compatibility + def self.flat_map: (untyped enum) { () -> untyped } -> untyped +end + +# @!visibility private +module Bundler::Molinillo::Delegates +end + +# [`Delegates`](https://docs.ruby-lang.org/en/2.7.0/Bundler/Molinillo/Delegates.html) +# all {Bundler::Molinillo::ResolutionState} methods to a `#state` property. +module Bundler::Molinillo::Delegates::ResolutionState + # (see Bundler::Molinillo::ResolutionState#activated) + def activated: () -> untyped + + # (see Bundler::Molinillo::ResolutionState#conflicts) + def conflicts: () -> untyped + + # (see Bundler::Molinillo::ResolutionState#depth) + def depth: () -> untyped + + # (see Bundler::Molinillo::ResolutionState#name) + def name: () -> untyped + + # (see Bundler::Molinillo::ResolutionState#possibilities) + def possibilities: () -> untyped + + # (see Bundler::Molinillo::ResolutionState#requirement) + def requirement: () -> untyped + + # (see Bundler::Molinillo::ResolutionState#requirements) + def requirements: () -> untyped + + # (see Bundler::Molinillo::ResolutionState#unused\_unwind\_options) + def unused_unwind_options: () -> untyped +end + +# [`Delegates`](https://docs.ruby-lang.org/en/2.7.0/Bundler/Molinillo/Delegates.html) +# all {Bundler::Molinillo::SpecificationProvider} methods to a +# `#specification\_provider` property. +module Bundler::Molinillo::Delegates::SpecificationProvider + def allow_missing?: (untyped dependency) -> untyped + + def dependencies_for: (untyped specification) -> untyped + + def name_for: (untyped dependency) -> untyped + + # (see + # [`Bundler::Molinillo::SpecificationProvider#name_for_explicit_dependency_source`](https://docs.ruby-lang.org/en/2.7.0/Bundler/Molinillo/SpecificationProvider.html#method-i-name_for_explicit_dependency_source)) + def name_for_explicit_dependency_source: () -> untyped + + # (see + # [`Bundler::Molinillo::SpecificationProvider#name_for_locking_dependency_source`](https://docs.ruby-lang.org/en/2.7.0/Bundler/Molinillo/SpecificationProvider.html#method-i-name_for_locking_dependency_source)) + def name_for_locking_dependency_source: () -> untyped + + def requirement_satisfied_by?: (untyped requirement, untyped activated, untyped spec) -> untyped + + def search_for: (untyped dependency) -> untyped + + def sort_dependencies: (untyped dependencies, untyped activated, untyped conflicts) -> untyped +end + +# A directed acyclic graph that is tuned to hold named dependencies +class Bundler::Molinillo::DependencyGraph[out Elem] + include ::TSort + + include ::Enumerable + + def ==: (untyped other) -> untyped + + def add_child_vertex: (untyped name, untyped payload, untyped parent_names, untyped requirement) -> untyped + + def add_edge: (untyped origin, untyped destination, untyped requirement) -> untyped + + def add_vertex: (untyped name, untyped payload, ?untyped root) -> untyped + + def delete_edge: (untyped edge) -> untyped + + def detach_vertex_named: (untyped name) -> untyped + + def each: () { () -> untyped } -> untyped + + def initialize: () -> void + + # @return [String] a string suitable for debugging + def inspect: () -> untyped + + # @return [Log] the op log for this graph + def log: () -> untyped + + def rewind_to: (untyped tag) -> untyped + + def root_vertex_named: (untyped name) -> untyped + + def set_payload: (untyped name, untyped payload) -> untyped + + def tag: (untyped tag) -> untyped + + def to_dot: (?untyped options) -> untyped + + def tsort_each_child: (untyped vertex) { () -> untyped } -> untyped + + # @!visibility private + # + # Alias for: + # [`each`](https://docs.ruby-lang.org/en/2.7.0/Bundler/Molinillo/DependencyGraph.html#method-i-each) + def tsort_each_node: () -> untyped + + def vertex_named: (untyped name) -> untyped + + # @return [{String => Vertex}] the vertices of the dependency graph, keyed + # + # ``` + # by {Vertex#name} + # ``` + def vertices: () -> untyped + + def self.tsort: (untyped vertices) -> untyped +end + +# An action that modifies a {DependencyGraph} that is reversible. @abstract +class Bundler::Molinillo::DependencyGraph::Action + def down: (untyped graph) -> untyped + + # @return [Action,Nil] The next action + def next: () -> untyped + + def next=: (untyped _) -> untyped + + # @return [Action,Nil] The previous action + def previous: () -> untyped + + def previous=: (untyped previous) -> untyped + + def up: (untyped graph) -> untyped + + # @return [Symbol] The name of the action. + def self.action_name: () -> untyped +end + +# @!visibility private (see +# [`DependencyGraph#add_edge_no_circular`](https://docs.ruby-lang.org/en/2.7.0/Bundler/Molinillo/DependencyGraph.html#method-i-add_edge_no_circular)) +class Bundler::Molinillo::DependencyGraph::AddEdgeNoCircular < Bundler::Molinillo::DependencyGraph::Action + # @return [String] the name of the destination of the edge + def destination: () -> untyped + + def down: (untyped graph) -> untyped + + def initialize: (untyped origin, untyped destination, untyped requirement) -> void + + def make_edge: (untyped graph) -> untyped + + # @return [String] the name of the origin of the edge + def origin: () -> untyped + + # @return [Object] the requirement that the edge represents + def requirement: () -> untyped + + def up: (untyped graph) -> untyped + + # (see Action.action\_name) + def self.action_name: () -> untyped +end + +class Bundler::Molinillo::DependencyGraph::AddVertex < Bundler::Molinillo::DependencyGraph::Action + def down: (untyped graph) -> untyped + + def initialize: (untyped name, untyped payload, untyped root) -> void + + def name: () -> untyped + + def payload: () -> untyped + + def root: () -> untyped + + def up: (untyped graph) -> untyped + + def self.action_name: () -> untyped +end + +# @!visibility private (see +# [`DependencyGraph#delete_edge`](https://docs.ruby-lang.org/en/2.7.0/Bundler/Molinillo/DependencyGraph.html#method-i-delete_edge)) +class Bundler::Molinillo::DependencyGraph::DeleteEdge < Bundler::Molinillo::DependencyGraph::Action + # @return [String] the name of the destination of the edge + def destination_name: () -> untyped + + def down: (untyped graph) -> untyped + + def initialize: (untyped origin_name, untyped destination_name, untyped requirement) -> void + + def make_edge: (untyped graph) -> untyped + + # @return [String] the name of the origin of the edge + def origin_name: () -> untyped + + # @return [Object] the requirement that the edge represents + def requirement: () -> untyped + + def up: (untyped graph) -> untyped + + # (see Action.action\_name) + def self.action_name: () -> untyped +end + +# @!visibility private @see +# [`DependencyGraph#detach_vertex_named`](https://docs.ruby-lang.org/en/2.7.0/Bundler/Molinillo/DependencyGraph.html#method-i-detach_vertex_named) +class Bundler::Molinillo::DependencyGraph::DetachVertexNamed < Bundler::Molinillo::DependencyGraph::Action + def down: (untyped graph) -> untyped + + def initialize: (untyped name) -> void + + # @return [String] the name of the vertex to detach + def name: () -> untyped + + def up: (untyped graph) -> untyped + + # (see Action#name) + def self.action_name: () -> untyped +end + +# A directed edge of a {DependencyGraph} @attr [Vertex] origin The origin of the +# directed edge @attr [Vertex] destination The destination of the directed edge +# @attr [Object] requirement The requirement the directed edge represents +class Bundler::Molinillo::DependencyGraph::Edge < Struct + def destination: () -> untyped + + def destination=: (untyped _) -> untyped + + def origin: () -> untyped + + def origin=: (untyped _) -> untyped + + def requirement: () -> untyped + + def requirement=: (untyped _) -> untyped + + def self.[]: (*untyped _) -> untyped + + def self.members: () -> untyped + + def self.new: (*untyped _) -> untyped +end + +Bundler::Molinillo::DependencyGraph::Edge::Elem: untyped + +# A log for dependency graph actions +class Bundler::Molinillo::DependencyGraph::Log + extend ::Enumerable + + def add_edge_no_circular: (untyped graph, untyped origin, untyped destination, untyped requirement) -> untyped + + def add_vertex: (untyped graph, untyped name, untyped payload, untyped root) -> untyped + + def delete_edge: (untyped graph, untyped origin_name, untyped destination_name, untyped requirement) -> untyped + + def detach_vertex_named: (untyped graph, untyped name) -> untyped + + def each: () { () -> untyped } -> untyped + + def initialize: () -> void + + def pop!: (untyped graph) -> untyped + + # @!visibility private Enumerates each action in the log in reverse order + # @yield [Action] + def reverse_each: () -> untyped + + def rewind_to: (untyped graph, untyped tag) -> untyped + + def set_payload: (untyped graph, untyped name, untyped payload) -> untyped + + def tag: (untyped graph, untyped tag) -> untyped +end + +Bundler::Molinillo::DependencyGraph::Log::Elem: untyped + +class Bundler::Molinillo::DependencyGraph::SetPayload < Bundler::Molinillo::DependencyGraph::Action + def down: (untyped graph) -> untyped + + def initialize: (untyped name, untyped payload) -> void + + def name: () -> untyped + + def payload: () -> untyped + + def up: (untyped graph) -> untyped + + def self.action_name: () -> untyped +end + +# @!visibility private @see +# [`DependencyGraph#tag`](https://docs.ruby-lang.org/en/2.7.0/Bundler/Molinillo/DependencyGraph.html#method-i-tag) +class Bundler::Molinillo::DependencyGraph::Tag < Bundler::Molinillo::DependencyGraph::Action + def down: (untyped _graph) -> untyped + + def initialize: (untyped tag) -> void + + # @return [Object] An opaque tag + def tag: () -> untyped + + def up: (untyped _graph) -> untyped + + # (see Action.action\_name) + def self.action_name: () -> untyped +end + +# A vertex in a {DependencyGraph} that encapsulates a {#name} and a {#payload} +class Bundler::Molinillo::DependencyGraph::Vertex + def ==: (untyped other) -> untyped + + def _path_to?: (untyped other, ?untyped visited) -> untyped + + def ancestor?: (untyped other) -> untyped + + def descendent?: (untyped other) -> untyped + + def eql?: (untyped other) -> untyped + + # @return [Array] the explicit requirements that required + # + # ```ruby + # this vertex + # ``` + def explicit_requirements: () -> untyped + + # @return [Fixnum] a hash for the vertex based upon its {#name} + def hash: () -> untyped + + # @return [Array] the edges of {#graph} that have `self` as their + # + # ``` + # {Edge#destination} + # ``` + def incoming_edges: () -> untyped + + def incoming_edges=: (untyped incoming_edges) -> untyped + + def initialize: (untyped name, untyped payload) -> void + + # @return [String] a string suitable for debugging + def inspect: () -> untyped + + def is_reachable_from?: (untyped other) -> untyped + + # @return [String] the name of the vertex + def name: () -> untyped + + def name=: (untyped name) -> untyped + + # @return [Array] the edges of {#graph} that have `self` as their + # + # ``` + # {Edge#origin} + # ``` + def outgoing_edges: () -> untyped + + def outgoing_edges=: (untyped outgoing_edges) -> untyped + + def path_to?: (untyped other) -> untyped + + # @return [Object] the payload the vertex holds + def payload: () -> untyped + + def payload=: (untyped payload) -> untyped + + # @return [Array] the vertices of {#graph} that have an edge with + # + # ``` + # `self` as their {Edge#destination} + # ``` + def predecessors: () -> untyped + + # @return [Set] the vertices of {#graph} where `self` is a + # + # ``` + # {#descendent?} + # ``` + def recursive_predecessors: () -> untyped + + # @return [Set] the vertices of {#graph} where `self` is an + # + # ``` + # {#ancestor?} + # ``` + def recursive_successors: () -> untyped + + # @return [Array] all of the requirements that required + # + # ```ruby + # this vertex + # ``` + def requirements: () -> untyped + + # @return [Boolean] whether the vertex is considered a root vertex + def root: () -> untyped + + def root=: (untyped root) -> untyped + + # @return [Boolean] whether the vertex is considered a root vertex + def root?: () -> untyped + + def shallow_eql?: (untyped other) -> untyped + + # @return [Array] the vertices of {#graph} that have an edge with + # + # ``` + # `self` as their {Edge#origin} + # ``` + def successors: () -> untyped +end + +# A state that encapsulates a set of {#requirements} with an {Array} of +# possibilities +class Bundler::Molinillo::DependencyState < Bundler::Molinillo::ResolutionState + # Removes a possibility from `self` @return [PossibilityState] a state with a + # single possibility, + # + # ```ruby + # the possibility that was removed from `self` + # ``` + def pop_possibility_state: () -> untyped +end + +Bundler::Molinillo::DependencyState::Elem: untyped + +# An error caused by searching for a dependency that is completely unknown, i.e. +# has no versions available whatsoever. +class Bundler::Molinillo::NoSuchDependencyError < Bundler::Molinillo::ResolverError + # @return [Object] the dependency that could not be found + def dependency: () -> untyped + + def dependency=: (untyped dependency) -> untyped + + def initialize: (untyped dependency, ?untyped required_by) -> void + + # The error message for the missing dependency, including the specifications + # that had this dependency. + def message: () -> untyped + + # @return [Array] the specifications that depended upon {#dependency} + def required_by: () -> untyped + + def required_by=: (untyped required_by) -> untyped +end + +# A state that encapsulates a single possibility to fulfill the given +# {#requirement} +class Bundler::Molinillo::PossibilityState < Bundler::Molinillo::ResolutionState +end + +Bundler::Molinillo::PossibilityState::Elem: untyped + +class Bundler::Molinillo::ResolutionState < Struct + def activated: () -> untyped + + def activated=: (untyped _) -> untyped + + def conflicts: () -> untyped + + def conflicts=: (untyped _) -> untyped + + def depth: () -> untyped + + def depth=: (untyped _) -> untyped + + def name: () -> untyped + + def name=: (untyped _) -> untyped + + def possibilities: () -> untyped + + def possibilities=: (untyped _) -> untyped + + def requirement: () -> untyped + + def requirement=: (untyped _) -> untyped + + def requirements: () -> untyped + + def requirements=: (untyped _) -> untyped + + def unused_unwind_options: () -> untyped + + def unused_unwind_options=: (untyped _) -> untyped + + def self.[]: (*untyped _) -> untyped + + # Returns an empty resolution state @return [ResolutionState] an empty state + def self.empty: () -> untyped + + def self.members: () -> untyped + + def self.new: (*untyped _) -> untyped +end + +Bundler::Molinillo::ResolutionState::Elem: untyped + +# This class encapsulates a dependency resolver. The resolver is responsible for +# determining which set of dependencies to activate, with feedback from the +# {#specification\_provider} +class Bundler::Molinillo::Resolver + def initialize: (untyped specification_provider, untyped resolver_ui) -> void + + def resolve: (untyped requested, ?untyped base) -> untyped + + # @return [UI] the UI module used to communicate back to the user + # + # ```ruby + # during the resolution process + # ``` + def resolver_ui: () -> untyped + + # @return [SpecificationProvider] the specification provider used + # + # ``` + # in the resolution process + # ``` + def specification_provider: () -> untyped +end + +# A specific resolution from a given {Resolver} +class Bundler::Molinillo::Resolver::Resolution + include ::Bundler::Molinillo::Delegates::SpecificationProvider + + include ::Bundler::Molinillo::Delegates::ResolutionState + + # @return [DependencyGraph] the base dependency graph to which + # + # ```ruby + # dependencies should be 'locked' + # ``` + def base: () -> untyped + + def initialize: (untyped specification_provider, untyped resolver_ui, untyped requested, untyped base) -> void + + def iteration_rate=: (untyped iteration_rate) -> untyped + + # @return [Array] the dependencies that were explicitly required + def original_requested: () -> untyped + + # Resolves the {#original\_requested} dependencies into a full dependency + # + # ```ruby + # graph + # ``` + # + # @raise [ResolverError] if successful resolution is impossible @return + # [DependencyGraph] the dependency graph of successfully resolved + # + # ```ruby + # dependencies + # ``` + def resolve: () -> untyped + + # @return [UI] the UI that knows how to communicate feedback about the + # + # ```ruby + # resolution process back to the user + # ``` + def resolver_ui: () -> untyped + + # @return [SpecificationProvider] the provider that knows about + # + # ``` + # dependencies, requirements, specifications, versions, etc. + # ``` + def specification_provider: () -> untyped + + def started_at=: (untyped started_at) -> untyped + + def states=: (untyped states) -> untyped +end + +class Bundler::Molinillo::Resolver::Resolution::Conflict < Struct + def activated_by_name: () -> untyped + + def activated_by_name=: (untyped _) -> untyped + + def existing: () -> untyped + + def existing=: (untyped _) -> untyped + + def locked_requirement: () -> untyped + + def locked_requirement=: (untyped _) -> untyped + + # @return [Object] a spec that was unable to be activated due to a conflict + def possibility: () -> untyped + + def possibility_set: () -> untyped + + def possibility_set=: (untyped _) -> untyped + + def requirement: () -> untyped + + def requirement=: (untyped _) -> untyped + + def requirement_trees: () -> untyped + + def requirement_trees=: (untyped _) -> untyped + + def requirements: () -> untyped + + def requirements=: (untyped _) -> untyped + + def underlying_error: () -> untyped + + def underlying_error=: (untyped _) -> untyped + + def self.[]: (*untyped _) -> untyped + + def self.members: () -> untyped + + def self.new: (*untyped _) -> untyped +end + +Bundler::Molinillo::Resolver::Resolution::Conflict::Elem: untyped + +class Bundler::Molinillo::Resolver::Resolution::PossibilitySet < Struct + def dependencies: () -> untyped + + def dependencies=: (untyped _) -> untyped + + # @return [Object] most up-to-date dependency in the possibility set + def latest_version: () -> untyped + + def possibilities: () -> untyped + + def possibilities=: (untyped _) -> untyped + + # [`String`](https://docs.ruby-lang.org/en/2.7.0/String.html) representation + # of the possibility set, for debugging + def to_s: () -> untyped + + def self.[]: (*untyped _) -> untyped + + def self.members: () -> untyped + + def self.new: (*untyped _) -> untyped +end + +Bundler::Molinillo::Resolver::Resolution::PossibilitySet::Elem: untyped + +class Bundler::Molinillo::Resolver::Resolution::UnwindDetails < Struct + include ::Comparable + + def <=>: (untyped other) -> untyped + + # @return [Array] array of all the requirements that led to the need for + # + # ```ruby + # this unwind + # ``` + def all_requirements: () -> untyped + + def conflicting_requirements: () -> untyped + + def conflicting_requirements=: (untyped _) -> untyped + + def requirement_tree: () -> untyped + + def requirement_tree=: (untyped _) -> untyped + + def requirement_trees: () -> untyped + + def requirement_trees=: (untyped _) -> untyped + + def requirements_unwound_to_instead: () -> untyped + + def requirements_unwound_to_instead=: (untyped _) -> untyped + + # @return [Integer] index of state requirement in reversed requirement tree + # + # ```ruby + # (the conflicting requirement itself will be at position 0) + # ``` + def reversed_requirement_tree_index: () -> untyped + + def state_index: () -> untyped + + def state_index=: (untyped _) -> untyped + + def state_requirement: () -> untyped + + def state_requirement=: (untyped _) -> untyped + + # @return [Array] array of sub-dependencies to avoid when choosing a + # + # ``` + # new possibility for the state we've unwound to. Only relevant for + # non-primary unwinds + # ``` + def sub_dependencies_to_avoid: () -> untyped + + # @return [Boolean] where the requirement of the state we're unwinding + # + # ``` + # to directly caused the conflict. Note: in this case, it is + # impossible for the state we're unwinding to to be a parent of + # any of the other conflicting requirements (or we would have + # circularity) + # ``` + def unwinding_to_primary_requirement?: () -> untyped + + def self.[]: (*untyped _) -> untyped + + def self.members: () -> untyped + + def self.new: (*untyped _) -> untyped +end + +Bundler::Molinillo::Resolver::Resolution::UnwindDetails::Elem: untyped + +# An error that occurred during the resolution process +class Bundler::Molinillo::ResolverError < StandardError +end + +# Provides information about specifications and dependencies to the resolver, +# allowing the {Resolver} class to remain generic while still providing power +# and flexibility. +# +# This module contains the methods that users of +# [`Bundler::Molinillo`](https://docs.ruby-lang.org/en/2.7.0/Bundler/Molinillo.html) +# must to implement, using knowledge of their own model classes. +module Bundler::Molinillo::SpecificationProvider + def allow_missing?: (untyped dependency) -> untyped + + def dependencies_for: (untyped specification) -> untyped + + def name_for: (untyped dependency) -> untyped + + # @return [String] the name of the source of explicit dependencies, i.e. + # + # ``` + # those passed to {Resolver#resolve} directly. + # ``` + def name_for_explicit_dependency_source: () -> untyped + + # @return [String] the name of the source of 'locked' dependencies, i.e. + # + # ``` + # those passed to {Resolver#resolve} directly as the `base` + # ``` + def name_for_locking_dependency_source: () -> untyped + + def requirement_satisfied_by?: (untyped requirement, untyped activated, untyped spec) -> untyped + + def search_for: (untyped dependency) -> untyped + + def sort_dependencies: (untyped dependencies, untyped activated, untyped conflicts) -> untyped +end + +# Conveys information about the resolution process to a user. +module Bundler::Molinillo::UI + # Called after resolution ends (either successfully or with an error). By + # default, prints a newline. + # + # @return [void] + def after_resolution: () -> untyped + + # Called before resolution begins. + # + # @return [void] + def before_resolution: () -> untyped + + def debug: (?untyped depth) -> untyped + + # Whether or not debug messages should be printed. By default, whether or not + # the `MOLINILLO\_DEBUG` environment variable is set. + # + # @return [Boolean] + def debug?: () -> untyped + + # Called roughly every {#progress\_rate}, this method should convey progress + # to the user. + # + # @return [void] + def indicate_progress: () -> untyped + + # The {IO} object that should be used to print output. `STDOUT`, by default. + # + # @return [IO] + def output: () -> untyped + + # How often progress should be conveyed to the user via {#indicate\_progress}, + # in seconds. A third of a second, by default. + # + # @return [Float] + def progress_rate: () -> untyped +end + +# An error caused by conflicts in version +class Bundler::Molinillo::VersionConflict < Bundler::Molinillo::ResolverError + include ::Bundler::Molinillo::Delegates::SpecificationProvider + + # @return [{String => Resolution::Conflict}] the conflicts that caused + # + # ```ruby + # resolution to fail + # ``` + def conflicts: () -> untyped + + def initialize: (untyped conflicts, untyped specification_provider) -> void + + def message_with_trees: (?untyped opts) -> untyped + + # @return [SpecificationProvider] the specification provider used during + # + # ```ruby + # resolution + # ``` + def specification_provider: () -> untyped +end + +class Bundler::NoSpaceOnDeviceError < Bundler::PermissionError + def message: () -> untyped + + def status_code: () -> untyped +end + +class Bundler::OperationNotSupportedError < Bundler::PermissionError + def message: () -> untyped + + def status_code: () -> untyped +end + +class Bundler::PathError < Bundler::BundlerError + def status_code: () -> untyped +end + +class Bundler::PermissionError < Bundler::BundlerError + def action: () -> untyped + + def initialize: (untyped path, ?untyped permission_type) -> void + + def message: () -> untyped + + def status_code: () -> untyped +end + +# This is the interfacing class represents the API that we intend to provide the +# plugins to use. +# +# For plugins to be independent of the +# [`Bundler`](https://docs.ruby-lang.org/en/2.7.0/Bundler.html) internals they +# shall limit their interactions to methods of this class only. This will save +# them from breaking when some internal change. +# +# Currently we are delegating the methods defined in +# [`Bundler`](https://docs.ruby-lang.org/en/2.7.0/Bundler.html) class to itself. +# So, this class acts as a buffer. +# +# If there is some change in the +# [`Bundler`](https://docs.ruby-lang.org/en/2.7.0/Bundler.html) class that is +# incompatible to its previous behavior or if otherwise desired, we can +# reimplement(or implement) the method to preserve compatibility. +# +# To use this, either the class can inherit this class or use it directly. For +# example of both types of use, refer the file `spec/plugins/command.rb` +# +# To use it without inheriting, you will have to create an object of this to use +# the functions (except for declaration functions like command, source, and +# hooks). +# Manages which plugins are installed and their sources. This also is supposed +# to map which plugin does what (currently the features are not implemented so +# this class is now a stub class). +# Handles the installation of plugin in appropriate directories. +# +# This class is supposed to be wrapper over the existing gem installation infra +# but currently it itself handles everything as the Source's subclasses (e.g. +# Source::RubyGems) are heavily dependent on the Gemfile. +# SourceList object to be used while parsing the Gemfile, setting the +# approptiate options to be used with Source classes for plugin installation +module Bundler::Plugin + def self.add_command: (untyped command, untyped cls) -> untyped + + def self.add_hook: (untyped event) { () -> untyped } -> untyped + + def self.add_source: (untyped source, untyped cls) -> untyped + + def self.cache: () -> untyped + + def self.command?: (untyped command) -> untyped + + def self.exec_command: (untyped command, untyped args) -> untyped + + def self.gemfile_install: (?untyped gemfile) { () -> untyped } -> untyped + + def self.global_root: () -> untyped + + def self.hook: (untyped event, *untyped args) { () -> untyped } -> untyped + + def self.index: () -> untyped + + def self.install: (untyped names, untyped options) -> untyped + + def self.installed?: (untyped plugin) -> untyped + + def self.local_root: () -> untyped + + def self.reset!: () -> untyped + + def self.root: () -> untyped + + def self.source: (untyped name) -> untyped + + def self.source?: (untyped name) -> untyped + + def self.source_from_lock: (untyped locked_opts) -> untyped +end + +Bundler::Plugin::PLUGIN_FILE_NAME: untyped + +class Bundler::Plugin::API + # The cache dir to be used by the plugins for storage + # + # @return [Pathname] path of the cache dir + def cache_dir: () -> untyped + + def method_missing: (untyped name, *untyped args) { () -> untyped } -> untyped + + def tmp: (*untyped names) -> untyped + + def self.command: (untyped command, ?untyped cls) -> untyped + + def self.hook: (untyped event) { () -> untyped } -> untyped + + def self.source: (untyped source, ?untyped cls) -> untyped +end + +# Dsl to parse the Gemfile looking for plugins to install +class Bundler::Plugin::DSL < Bundler::Dsl + def _gem: (untyped name, *untyped args) -> untyped + + # This lists the plugins that was added automatically and not specified by the + # user. + # + # When we encounter :type attribute with a source block, we add a plugin by + # name bundler-source- to list of plugins to be installed. + # + # These plugins are optional and are not installed when there is conflict with + # any other plugin. + def inferred_plugins: () -> untyped + + def plugin: (untyped name, *untyped args) -> untyped +end + +module Bundler::Plugin::Events +end + +class Bundler::Plugin::Index +end + +class Bundler::Plugin::MalformattedPlugin < Bundler::PluginError +end + +class Bundler::Plugin::UndefinedCommandError < Bundler::PluginError +end + +class Bundler::Plugin::UnknownSourceError < Bundler::PluginError +end + +class Bundler::Plugin::DSL::PluginGemfileError < Bundler::PluginError +end + +class Bundler::PluginError < Bundler::BundlerError + def status_code: () -> untyped +end + +class Bundler::ProductionError < Bundler::BundlerError + def status_code: () -> untyped +end + +# Represents a lazily loaded gem specification, where the full specification is +# on the source server in rubygems' "quick" index. The proxy object is to be +# seeded with what we're given from the source's abbreviated index - the full +# specification will only be fetched when necessary. +class Bundler::RemoteSpecification + include ::Comparable + + include ::Bundler::MatchPlatform + + include ::Bundler::GemHelpers + + def <=>: (untyped other) -> untyped + + def __swap__: (untyped spec) -> untyped + + def dependencies: () -> untyped + + def dependencies=: (untyped dependencies) -> untyped + + # Needed before installs, since the arch matters then and quick specs don't + # bother to include the arch in the platform string + def fetch_platform: () -> untyped + + def full_name: () -> untyped + + def git_version: () -> untyped + + def initialize: (untyped name, untyped version, untyped platform, untyped spec_fetcher) -> void + + def name: () -> untyped + + def platform: () -> untyped + + def remote: () -> untyped + + def remote=: (untyped remote) -> untyped + + def respond_to?: (untyped method, ?untyped include_all) -> untyped + + # Create a delegate used for sorting. This strategy is copied from RubyGems + # 2.23 and ensures that Bundler's specifications can be compared and sorted + # with RubyGems' own specifications. + # + # @see #<=> @see + # [`Gem::Specification#sort_obj`](https://docs.ruby-lang.org/en/2.7.0/Gem/Specification.html#method-i-sort_obj) + # + # @return [Array] an object you can use to compare and sort this + # + # ```ruby + # specification against other specifications + # ``` + def sort_obj: () -> untyped + + def source: () -> untyped + + def source=: (untyped source) -> untyped + + def to_s: () -> untyped + + def version: () -> untyped +end + +class Bundler::Resolver + include ::Bundler::Molinillo::SpecificationProvider + + include ::Bundler::Molinillo::UI + + def after_resolution: () -> untyped + + def before_resolution: () -> untyped + + def debug: (?untyped depth) -> untyped + + def debug?: () -> untyped + + def dependencies_for: (untyped specification) -> untyped + + def index_for: (untyped dependency) -> untyped + + def indicate_progress: () -> untyped + + def initialize: (untyped index, untyped source_requirements, untyped base, untyped gem_version_promoter, untyped additional_base_requirements, untyped platforms) -> void + + def name_for: (untyped dependency) -> untyped + + def name_for_explicit_dependency_source: () -> untyped + + def name_for_locking_dependency_source: () -> untyped + + def relevant_sources_for_vertex: (untyped vertex) -> untyped + + def requirement_satisfied_by?: (untyped requirement, untyped activated, untyped spec) -> untyped + + def search_for: (untyped dependency) -> untyped + + def sort_dependencies: (untyped dependencies, untyped activated, untyped conflicts) -> untyped + + def start: (untyped requirements) -> untyped + + def self.platform_sort_key: (untyped platform) -> untyped + + def self.resolve: (untyped requirements, untyped index, ?untyped source_requirements, ?untyped base, ?untyped gem_version_promoter, ?untyped additional_base_requirements, ?untyped platforms) -> untyped + + def self.sort_platforms: (untyped platforms) -> untyped +end + +class Bundler::Resolver::SpecGroup + include ::Bundler::GemHelpers + + def ==: (untyped other) -> untyped + + def activate_platform!: (untyped platform) -> untyped + + def dependencies_for_activated_platforms: () -> untyped + + def eql?: (untyped other) -> untyped + + def for?: (untyped platform) -> untyped + + def hash: () -> untyped + + def ignores_bundler_dependencies: () -> untyped + + def ignores_bundler_dependencies=: (untyped ignores_bundler_dependencies) -> untyped + + def initialize: (untyped all_specs) -> void + + def name: () -> untyped + + def name=: (untyped name) -> untyped + + def source: () -> untyped + + def source=: (untyped source) -> untyped + + def to_s: () -> untyped + + def to_specs: () -> untyped + + def version: () -> untyped + + def version=: (untyped version) -> untyped +end + +module Bundler::RubyDsl + @ruby_version: untyped + + def ruby: (*::String ruby_version) -> void + + # Support the various file formats found in .ruby-version files. + # + # 3.2.2 + # ruby-3.2.2 + # + # Also supports .tool-versions files for asdf. Lines not starting with "ruby" are ignored. + # + # ruby 2.5.1 # comment is ignored + # ruby 2.5.1# close comment and extra spaces doesn't confuse + # + # Intentionally does not support `3.2.1@gemset` since rvm recommends using .ruby-gemset instead + # + # Loads the file relative to the dirname of the Gemfile itself. + def normalize_ruby_file: (::String filename) -> ::String +end + +class Bundler::RubyVersion + def ==: (untyped other) -> untyped + + def diff: (untyped other) -> untyped + + def engine: () -> untyped + + def engine_gem_version: () -> untyped + + def engine_versions: () -> untyped + + def exact?: () -> untyped + + def gem_version: () -> untyped + + def host: () -> untyped + + def initialize: (untyped versions, untyped patchlevel, untyped engine, untyped engine_version) -> void + + def patchlevel: () -> untyped + + def single_version_string: () -> untyped + + def to_gem_version_with_patchlevel: () -> untyped + + def to_s: (?untyped versions) -> untyped + + def versions: () -> untyped + + def versions_string: (untyped versions) -> untyped + + def self.from_string: (untyped string) -> untyped + + def self.system: () -> untyped +end + +Bundler::RubyVersion::PATTERN: untyped + +class Bundler::RubyVersionMismatch < Bundler::BundlerError + def status_code: () -> untyped +end + +class Bundler::RubygemsIntegration + # This backports base\_dir which replaces installation path RubyGems 1.8+ + def backport_base_dir: () -> untyped + + def backport_cache_file: () -> untyped + + # This backports the correct segment generation code from RubyGems 1.4+ by + # monkeypatching it into the method in RubyGems 1.3.6 and 1.3.7. + def backport_segment_generation: () -> untyped + + def backport_spec_file: () -> untyped + + # This backport fixes the marshaling of @segments. + def backport_yaml_initialize: () -> untyped + + def bin_path: (untyped gem, untyped bin, untyped ver) -> untyped + + def binstubs_call_gem?: () -> untyped + + def build: (untyped spec, ?untyped skip_validation) -> untyped + + def build_args: () -> untyped + + def build_args=: (untyped args) -> untyped + + def build_gem: (untyped gem_dir, untyped spec) -> untyped + + def clear_paths: () -> untyped + + def config_map: () -> untyped + + def configuration: () -> untyped + + def download_gem: (untyped spec, untyped uri, untyped path) -> untyped + + def ext_lock: () -> untyped + + def fetch_all_remote_specs: (untyped remote) -> untyped + + def fetch_prerelease_specs: () -> untyped + + def fetch_specs: (untyped all, untyped pre) { () -> untyped } -> untyped + + def gem_bindir: () -> untyped + + def gem_cache: () -> untyped + + def gem_dir: () -> untyped + + def gem_from_path: (untyped path, ?untyped policy) -> untyped + + def gem_path: () -> untyped + + def inflate: (untyped obj) -> untyped + + def initialize: () -> void + + def install_with_build_args: (untyped args) -> untyped + + def load_path_insert_index: () -> untyped + + def load_plugin_files: (untyped files) -> untyped + + def load_plugins: () -> untyped + + def loaded_gem_paths: () -> untyped + + def loaded_specs: (untyped name) -> untyped + + def mark_loaded: (untyped spec) -> untyped + + def marshal_spec_dir: () -> untyped + + def method_visibility: (untyped klass, untyped method) -> untyped + + def path: (untyped obj) -> untyped + + def path_separator: () -> untyped + + def platforms: () -> untyped + + def post_reset_hooks: () -> untyped + + def preserve_paths: () -> untyped + + def provides?: (untyped req_str) -> untyped + + def read_binary: (untyped path) -> untyped + + def redefine_method: (untyped klass, untyped method, ?untyped unbound_method) { () -> untyped } -> untyped + + def replace_bin_path: (untyped specs, untyped specs_by_name) -> untyped + + def replace_entrypoints: (untyped specs) -> untyped + + def replace_gem: (untyped specs, untyped specs_by_name) -> untyped + + # Because [`Bundler`](https://docs.ruby-lang.org/en/2.6.0/Bundler.html) has a + # static view of what specs are available, we don't refresh, so stub it out. + def replace_refresh: () -> untyped + + def repository_subdirectories: () -> untyped + + def reset: () -> untyped + + def reverse_rubygems_kernel_mixin: () -> untyped + + def ruby_engine: () -> untyped + + def security_policies: () -> untyped + + def security_policy_keys: () -> untyped + + def set_installed_by_version: (untyped spec, ?untyped installed_by_version) -> untyped + + def sources: () -> untyped + + def sources=: (untyped val) -> untyped + + def spec_cache_dirs: () -> untyped + + def spec_default_gem?: (untyped spec) -> untyped + + def spec_extension_dir: (untyped spec) -> untyped + + def spec_from_gem: (untyped path, ?untyped policy) -> untyped + + def spec_matches_for_glob: (untyped spec, untyped glob) -> untyped + + def spec_missing_extensions?: (untyped spec, ?untyped default) -> untyped + + def stub_set_spec: (untyped stub, untyped spec) -> untyped + + def stub_source_index: (untyped specs) -> untyped + + def stubs_provide_full_functionality?: () -> untyped + + def suffix_pattern: () -> untyped + + def ui=: (untyped obj) -> untyped + + def undo_replacements: () -> untyped + + def user_home: () -> untyped + + def validate: (untyped spec) -> untyped + + def version: () -> untyped + + def with_build_args: (untyped args) -> untyped + + def self.provides?: (untyped req_str) -> untyped + + def self.version: () -> untyped +end + +Bundler::RubygemsIntegration::EXT_LOCK: untyped + +# RubyGems 1.8.0 to 1.8.4 +class Bundler::RubygemsIntegration::AlmostModern < Bundler::RubygemsIntegration::Modern + # RubyGems [>= 1.8.0, < 1.8.5] has a bug that changes + # [`Gem.dir`](https://docs.ruby-lang.org/en/2.6.0/Gem.html#method-c-dir) + # whenever you call + # [`Gem::Installer#install`](https://docs.ruby-lang.org/en/2.6.0/Installer.html#method-i-install) + # with an :install\_dir set. We have to change it back for our sudo mode to + # work. + def preserve_paths: () -> untyped +end + +# RubyGems versions 1.3.6 and 1.3.7 +class Bundler::RubygemsIntegration::Ancient < Bundler::RubygemsIntegration::Legacy + def initialize: () -> void +end + +# RubyGems 2.0 +class Bundler::RubygemsIntegration::Future < Bundler::RubygemsIntegration + def all_specs: () -> untyped + + def build: (untyped spec, ?untyped skip_validation) -> untyped + + def download_gem: (untyped spec, untyped uri, untyped path) -> untyped + + def fetch_all_remote_specs: (untyped remote) -> untyped + + def fetch_specs: (untyped source, untyped remote, untyped name) -> untyped + + def find_name: (untyped name) -> untyped + + def gem_from_path: (untyped path, ?untyped policy) -> untyped + + def gem_remote_fetcher: () -> untyped + + def install_with_build_args: (untyped args) -> untyped + + def path_separator: () -> untyped + + def repository_subdirectories: () -> untyped + + def stub_rubygems: (untyped specs) -> untyped +end + +# RubyGems 1.4 through 1.6 +class Bundler::RubygemsIntegration::Legacy < Bundler::RubygemsIntegration + def all_specs: () -> untyped + + def find_name: (untyped name) -> untyped + + def initialize: () -> void + + def post_reset_hooks: () -> untyped + + def reset: () -> untyped + + def stub_rubygems: (untyped specs) -> untyped + + def validate: (untyped spec) -> untyped +end + +# RubyGems 1.8.5-1.8.19 +class Bundler::RubygemsIntegration::Modern < Bundler::RubygemsIntegration + def all_specs: () -> untyped + + def find_name: (untyped name) -> untyped + + def stub_rubygems: (untyped specs) -> untyped +end + +# RubyGems 2.1.0 +class Bundler::RubygemsIntegration::MoreFuture < Bundler::RubygemsIntegration::Future + def all_specs: () -> untyped + + # RubyGems-generated binstubs call + # [`Kernel#gem`](https://docs.ruby-lang.org/en/2.6.0/Kernel.html#method-i-gem) + def binstubs_call_gem?: () -> untyped + + def find_name: (untyped name) -> untyped + + def initialize: () -> void + + # only 2.5.2+ has all of the stub methods we want to use, and since this is a + # performance optimization *only*, we'll restrict ourselves to the most recent + # RG versions instead of all versions that have stubs + def stubs_provide_full_functionality?: () -> untyped + + def use_gemdeps: (untyped gemfile) -> untyped +end + +# RubyGems 1.8.20+ +class Bundler::RubygemsIntegration::MoreModern < Bundler::RubygemsIntegration::Modern + def build: (untyped spec, ?untyped skip_validation) -> untyped +end + +# RubyGems 1.7 +class Bundler::RubygemsIntegration::Transitional < Bundler::RubygemsIntegration::Legacy + def stub_rubygems: (untyped specs) -> untyped + + def validate: (untyped spec) -> untyped +end + +class Bundler::Runtime + include ::Bundler::SharedHelpers + + def cache: (?untyped custom_path) -> untyped + + def clean: (?untyped dry_run) -> untyped + + def current_dependencies: () -> untyped + + def dependencies: () -> untyped + + def gems: () -> untyped + + def initialize: (untyped root, untyped definition) -> void + + def lock: (?untyped opts) -> untyped + + def prune_cache: (untyped cache_path) -> untyped + + def requested_specs: () -> untyped + + def require: (*untyped groups) -> untyped + + def requires: () -> untyped + + def setup: (*untyped groups) -> untyped + + def specs: () -> untyped +end + +Bundler::Runtime::REQUIRE_ERRORS: untyped + +class Bundler::SecurityError < Bundler::BundlerError + def status_code: () -> untyped +end + +class Bundler::Settings + def []: (untyped name) -> untyped + + def all: () -> untyped + + def allow_sudo?: () -> untyped + + def app_cache_path: () -> untyped + + def credentials_for: (untyped uri) -> untyped + + def gem_mirrors: () -> untyped + + def ignore_config?: () -> untyped + + def initialize: (?untyped root) -> void + + def key_for: (untyped key) -> untyped + + def local_overrides: () -> untyped + + def locations: (untyped key) -> untyped + + def mirror_for: (untyped uri) -> untyped + + # for legacy reasons, in + # [`Bundler`](https://docs.ruby-lang.org/en/2.7.0/Bundler.html) 2, we do not + # respect :disable\_shared\_gems + def path: () -> untyped + + def pretty_values_for: (untyped exposed_key) -> untyped + + def set_command_option: (untyped key, untyped value) -> untyped + + def set_command_option_if_given: (untyped key, untyped value) -> untyped + + def set_global: (untyped key, untyped value) -> untyped + + def set_local: (untyped key, untyped value) -> untyped + + def temporary: (untyped update) -> untyped + + def validate!: () -> untyped + + def self.normalize_uri: (untyped uri) -> untyped +end + +Bundler::Settings::ARRAY_KEYS: untyped + +Bundler::Settings::BOOL_KEYS: untyped + +Bundler::Settings::CONFIG_REGEX: untyped + +Bundler::Settings::DEFAULT_CONFIG: untyped + +Bundler::Settings::NORMALIZE_URI_OPTIONS_PATTERN: untyped + +Bundler::Settings::NUMBER_KEYS: untyped + +Bundler::Settings::PER_URI_OPTIONS: untyped + +class Bundler::Settings::Path < Struct + def append_ruby_scope: () -> untyped + + def append_ruby_scope=: (untyped _) -> untyped + + def base_path: () -> untyped + + def base_path_relative_to_pwd: () -> untyped + + def default_install_uses_path: () -> untyped + + def default_install_uses_path=: (untyped _) -> untyped + + def explicit_path: () -> untyped + + def explicit_path=: (untyped _) -> untyped + + def path: () -> untyped + + def system_path: () -> untyped + + def system_path=: (untyped _) -> untyped + + def use_system_gems?: () -> untyped + + def validate!: () -> untyped + + def self.[]: (*untyped _) -> untyped + + def self.members: () -> untyped + + def self.new: (*untyped _) -> untyped +end + +Bundler::Settings::Path::Elem: untyped + +module Bundler::SharedHelpers + extend ::Bundler::SharedHelpers + + def chdir: (untyped dir) { () -> untyped } -> untyped + + def const_get_safely: (untyped constant_name, untyped namespace) -> untyped + + def default_bundle_dir: () -> untyped + + def default_gemfile: () -> untyped + + def default_lockfile: () -> untyped + + def digest: (untyped name) -> untyped + + def ensure_same_dependencies: (untyped spec, untyped old_deps, untyped new_deps) -> untyped + + def filesystem_access: (untyped path, ?untyped action) { () -> untyped } -> untyped + + def in_bundle?: () -> untyped + + def major_deprecation: (untyped major_version, untyped message) -> untyped + + def md5_available?: () -> untyped + + def pretty_dependency: (untyped dep, ?untyped print_source) -> untyped + + def print_major_deprecations!: () -> untyped + + def pwd: () -> untyped + + def root: () -> untyped + + def set_bundle_environment: () -> untyped + + def set_env: (untyped key, untyped value) -> untyped + + def trap: (untyped signal, ?untyped override) { () -> untyped } -> untyped + + def with_clean_git_env: () { () -> untyped } -> untyped + + def write_to_gemfile: (untyped gemfile_path, untyped contents) -> untyped +end + +class Bundler::Source + def can_lock?: (untyped spec) -> untyped + + def dependency_names: () -> untyped + + def dependency_names=: (untyped dependency_names) -> untyped + + def dependency_names_to_double_check: () -> untyped + + def double_check_for: (*untyped _) -> untyped + + def extension_cache_path: (untyped spec) -> untyped + + def include?: (untyped other) -> untyped + + def inspect: () -> untyped + + def path?: () -> untyped + + def unmet_deps: () -> untyped + + def version_message: (untyped spec) -> untyped +end + +class Bundler::Source::Gemspec < Bundler::Source::Path + def as_path_source: () -> untyped + + def gemspec: () -> untyped + + def initialize: (untyped options) -> void +end + +class Bundler::Source::Git < Bundler::Source::Path + def ==: (untyped other) -> untyped + + def allow_git_ops?: () -> untyped + + def app_cache_dirname: () -> untyped + + def branch: () -> untyped + + def cache: (untyped spec, ?untyped custom_path) -> untyped + + # This is the path which is going to contain a cache of the git repository. + # When using the same git repository across different projects, this cache + # will be shared. When using local git repos, this is set to the local repo. + def cache_path: () -> untyped + + def eql?: (untyped other) -> untyped + + def extension_dir_name: () -> untyped + + def hash: () -> untyped + + def initialize: (untyped options) -> void + + def install: (untyped spec, ?untyped options) -> untyped + + # This is the path which is going to contain a specific checkout of the git + # repository. When using local git repos, this is set to the local repo. + # + # Also aliased as: + # [`path`](https://docs.ruby-lang.org/en/2.7.0/Bundler/Source/Git.html#method-i-path) + def install_path: () -> untyped + + def load_spec_files: () -> untyped + + def local_override!: (untyped path) -> untyped + + def name: () -> untyped + + def options: () -> untyped + + # Alias for: + # [`install_path`](https://docs.ruby-lang.org/en/2.7.0/Bundler/Source/Git.html#method-i-install_path) + def path: () -> untyped + + def ref: () -> untyped + + def revision: () -> untyped + + def specs: (*untyped _) -> untyped + + def submodules: () -> untyped + + def to_lock: () -> untyped + + def to_s: () -> untyped + + def unlock!: () -> untyped + + def uri: () -> untyped + + def self.from_lock: (untyped options) -> untyped +end + +class Bundler::Source::Git::GitCommandError < Bundler::GitError + def initialize: (untyped command, ?untyped path, ?untyped extra_info) -> void +end + +class Bundler::Source::Git::GitNotAllowedError < Bundler::GitError + def initialize: (untyped command) -> void +end + +class Bundler::Source::Git::GitNotInstalledError < Bundler::GitError + def initialize: () -> void +end + +# The +# [`GitProxy`](https://docs.ruby-lang.org/en/2.7.0/Bundler/Source/Git/GitProxy.html) +# is responsible to interact with git repositories. All actions required by the +# [`Git`](https://docs.ruby-lang.org/en/2.7.0/Bundler/Source/Git.html) source is +# encapsulated in this object. +class Bundler::Source::Git::GitProxy + def branch: () -> untyped + + def checkout: () -> untyped + + def contains?: (untyped commit) -> untyped + + def copy_to: (untyped destination, ?untyped submodules) -> untyped + + def full_version: () -> untyped + + def initialize: (untyped path, untyped uri, untyped ref, ?untyped revision, ?untyped git) -> void + + def path: () -> untyped + + def path=: (untyped path) -> untyped + + def ref: () -> untyped + + def ref=: (untyped ref) -> untyped + + def revision: () -> untyped + + def revision=: (untyped revision) -> untyped + + def uri: () -> untyped + + def uri=: (untyped uri) -> untyped + + def version: () -> untyped +end + +class Bundler::Source::Git::MissingGitRevisionError < Bundler::GitError + def initialize: (untyped ref, untyped repo) -> void +end + +class Bundler::Source::Metadata < Bundler::Source + def ==: (untyped other) -> untyped + + def cached!: () -> untyped + + def eql?: (untyped other) -> untyped + + def hash: () -> untyped + + def install: (untyped spec, ?untyped _opts) -> untyped + + def options: () -> untyped + + def remote!: () -> untyped + + def specs: () -> untyped + + def to_s: () -> untyped + + def version_message: (untyped spec) -> untyped +end + +class Bundler::Source::Path < Bundler::Source + def ==: (untyped other) -> untyped + + def app_cache_dirname: () -> untyped + + def cache: (untyped spec, ?untyped custom_path) -> untyped + + def cached!: () -> untyped + + def eql?: (untyped other) -> untyped + + def expanded_original_path: () -> untyped + + def hash: () -> untyped + + def initialize: (untyped options) -> void + + def install: (untyped spec, ?untyped options) -> untyped + + def local_specs: (*untyped _) -> untyped + + def name: () -> untyped + + def name=: (untyped name) -> untyped + + def options: () -> untyped + + def original_path: () -> untyped + + def path: () -> untyped + + def remote!: () -> untyped + + def root: () -> untyped + + def root_path: () -> untyped + + def specs: () -> untyped + + def to_lock: () -> untyped + + def to_s: () -> untyped + + def version: () -> untyped + + def version=: (untyped version) -> untyped + + def self.from_lock: (untyped options) -> untyped +end + +Bundler::Source::Path::DEFAULT_GLOB: untyped + +class Bundler::Source::Rubygems < Bundler::Source + def ==: (untyped other) -> untyped + + def add_remote: (untyped source) -> untyped + + def api_fetchers: () -> untyped + + def builtin_gem?: (untyped spec) -> untyped + + def cache: (untyped spec, ?untyped custom_path) -> untyped + + def cache_path: () -> untyped + + def cached!: () -> untyped + + def cached_built_in_gem: (untyped spec) -> untyped + + def cached_gem: (untyped spec) -> untyped + + def cached_path: (untyped spec) -> untyped + + def cached_specs: () -> untyped + + def caches: () -> untyped + + def can_lock?: (untyped spec) -> untyped + + def credless_remotes: () -> untyped + + def dependency_names_to_double_check: () -> untyped + + def double_check_for: (untyped unmet_dependency_names) -> untyped + + def eql?: (untyped other) -> untyped + + def equivalent_remotes?: (untyped other_remotes) -> untyped + + def fetch_gem: (untyped spec) -> untyped + + def fetch_names: (untyped fetchers, untyped dependency_names, untyped index, untyped override_dupes) -> untyped + + def fetchers: () -> untyped + + def hash: () -> untyped + + def include?: (untyped o) -> untyped + + def initialize: (?untyped options) -> void + + def install: (untyped spec, ?untyped opts) -> untyped + + def installed?: (untyped spec) -> untyped + + def installed_specs: () -> untyped + + def loaded_from: (untyped spec) -> untyped + + # Alias for: + # [`to_s`](https://docs.ruby-lang.org/en/2.7.0/Bundler/Source/Rubygems.html#method-i-to_s) + def name: () -> untyped + + def normalize_uri: (untyped uri) -> untyped + + def options: () -> untyped + + def remote!: () -> untyped + + def remote_specs: () -> untyped + + def remotes: () -> untyped + + def remotes_for_spec: (untyped spec) -> untyped + + def remove_auth: (untyped remote) -> untyped + + def replace_remotes: (untyped other_remotes, ?untyped allow_equivalent) -> untyped + + def requires_sudo?: () -> untyped + + def rubygems_dir: () -> untyped + + def specs: () -> untyped + + def suppress_configured_credentials: (untyped remote) -> untyped + + def to_lock: () -> untyped + + # Also aliased as: + # [`name`](https://docs.ruby-lang.org/en/2.7.0/Bundler/Source/Rubygems.html#method-i-name) + def to_s: () -> untyped + + def unmet_deps: () -> untyped + + def self.from_lock: (untyped options) -> untyped +end + +Bundler::Source::Rubygems::API_REQUEST_LIMIT: untyped + +Bundler::Source::Rubygems::API_REQUEST_SIZE: untyped + +class Bundler::SourceList + def add_git_source: (?untyped options) -> untyped + + def add_path_source: (?untyped options) -> untyped + + def add_plugin_source: (untyped source, ?untyped options) -> untyped + + def add_rubygems_remote: (untyped uri) -> untyped + + def add_rubygems_source: (?untyped options) -> untyped + + def all_sources: () -> untyped + + def cached!: () -> untyped + + def default_source: () -> untyped + + def get: (untyped source) -> untyped + + def git_sources: () -> untyped + + def global_rubygems_source: () -> untyped + + def global_rubygems_source=: (untyped uri) -> untyped + + def initialize: () -> void + + def lock_sources: () -> untyped + + def metadata_source: () -> untyped + + def path_sources: () -> untyped + + def plugin_sources: () -> untyped + + def remote!: () -> untyped + + def replace_sources!: (untyped replacement_sources) -> untyped + + def rubygems_primary_remotes: () -> untyped + + def rubygems_remotes: () -> untyped + + def rubygems_sources: () -> untyped +end + +class Bundler::SpecSet[out Elem] + include ::TSort + + include ::Enumerable + + def <<: (*untyped args) { () -> untyped } -> untyped + + def []: (untyped key) -> untyped + + def []=: (untyped key, untyped value) -> untyped + + def add: (*untyped args) { () -> untyped } -> untyped + + def each: (*untyped args) { () -> untyped } -> untyped + + def empty?: (*untyped args) { () -> untyped } -> untyped + + def find_by_name_and_platform: (untyped name, untyped platform) -> untyped + + def for: (untyped dependencies, ?untyped skip, ?untyped check, ?untyped match_current_platform, ?untyped raise_on_missing) -> untyped + + def initialize: (untyped specs) -> void + + def length: (*untyped args) { () -> untyped } -> untyped + + def materialize: (untyped deps, ?untyped missing_specs) -> untyped + + # Materialize for all the specs in the spec set, regardless of what platform + # they're for This is in contrast to how for does platform filtering (and + # specifically different from how `materialize` calls `for` only for the + # current platform) @return [Array] + def materialized_for_all_platforms: () -> untyped + + def merge: (untyped set) -> untyped + + def remove: (*untyped args) { () -> untyped } -> untyped + + def size: (*untyped args) { () -> untyped } -> untyped + + def sort!: () -> untyped + + def to_a: () -> untyped + + def to_hash: () -> untyped + + def valid_for?: (untyped deps) -> untyped + + def what_required: (untyped spec) -> untyped +end + +class Bundler::StubSpecification < Bundler::RemoteSpecification + def activated: () -> untyped + + def activated=: (untyped activated) -> untyped + + def default_gem: () -> untyped + + def default_gem?: () -> bool + + def full_gem_path: () -> untyped + + def full_require_paths: () -> untyped + + def ignored: () -> untyped + + def ignored=: (untyped ignored) -> untyped + + # This is what we do in bundler/rubygems\_ext + # [`full_require_paths`](https://docs.ruby-lang.org/en/2.6.0/Bundler/StubSpecification.html#method-i-full_require_paths) + # is always implemented in >= 2.2.0 + def load_paths: () -> untyped + + def loaded_from: () -> untyped + + def matches_for_glob: (untyped glob) -> untyped + + # This is defined directly to avoid having to load every installed spec + def missing_extensions?: () -> untyped + + def raw_require_paths: () -> untyped + + def source=: (untyped source) -> untyped + + def stub: () -> untyped + + def stub=: (untyped stub) -> untyped + + def to_yaml: () -> untyped + + def self.from_stub: (untyped stub) -> untyped +end + +class Bundler::SudoNotPermittedError < Bundler::BundlerError + def status_code: () -> untyped +end + +class Bundler::TemporaryResourceError < Bundler::PermissionError + def message: () -> untyped + + def status_code: () -> untyped +end + +class Bundler::ThreadCreationError < Bundler::BundlerError + def status_code: () -> untyped +end + +module Bundler::UI +end + +class Bundler::UI::RGProxy < Gem::SilentUI + def initialize: (untyped ui) -> void + + def say: (untyped message) -> untyped +end + +class Bundler::UI::Silent + def add_color: (untyped string, untyped color) -> untyped + + def ask: (untyped message) -> untyped + + def confirm: (untyped message, ?untyped newline) -> untyped + + def debug: (untyped message, ?untyped newline) -> untyped + + def debug?: () -> untyped + + def error: (untyped message, ?untyped newline) -> untyped + + def info: (untyped message, ?untyped newline) -> untyped + + def initialize: () -> void + + def level: (?untyped name) -> untyped + + def level=: (untyped name) -> untyped + + def no?: () -> untyped + + def quiet?: () -> untyped + + def shell=: (untyped shell) -> untyped + + def silence: () -> untyped + + def trace: (untyped message, ?untyped newline, ?untyped force) -> untyped + + def unprinted_warnings: () -> untyped + + def warn: (untyped message, ?untyped newline) -> untyped + + def yes?: (untyped msg) -> untyped +end + +module Bundler::URICredentialsFilter + def self.credential_filtered_string: (untyped str_to_filter, untyped uri) -> untyped + + def self.credential_filtered_uri: (untyped uri_to_anonymize) -> untyped +end + +# Internal error, should be rescued +class Bundler::VersionConflict < Bundler::BundlerError + def conflicts: () -> untyped + + def initialize: (untyped conflicts, ?untyped msg) -> void + + def status_code: () -> untyped +end + +class Bundler::VirtualProtocolError < Bundler::BundlerError + def message: () -> untyped + + def status_code: () -> untyped +end + +# A stub yaml serializer that can handle only hashes and strings (as of now). +module Bundler::YAMLSerializer + def self.dump: (untyped hash) -> untyped + + def self.load: (untyped str) -> untyped +end + +Bundler::YAMLSerializer::ARRAY_REGEX: untyped + +Bundler::YAMLSerializer::HASH_REGEX: untyped + +class Bundler::YamlSyntaxError < Bundler::BundlerError + def initialize: (untyped orig_exception, untyped msg) -> void + + def orig_exception: () -> untyped + + def status_code: () -> untyped +end + +class Bundler::Installer + def self.ambiguous_gems=: (untyped ambiguous_gems) -> untyped + + def self.ambiguous_gems: () -> untyped + + def post_install_messages: () -> untyped + + def self.install: (untyped root, untyped definition, ?untyped options) -> untyped + + def initialize: (untyped root, untyped definition) -> void + + def run: (untyped options) -> void + + def generate_bundler_executable_stubs: (untyped spec, ?untyped options) -> void + + def generate_standalone_bundler_executable_stubs: (untyped spec, ?untyped options) -> void +end From 07fc5cba531972778390b997fcec20a4a119bcc2 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 19 Jul 2025 12:30:25 -0400 Subject: [PATCH 256/561] Add @sg-ignore --- lib/solargraph/workspace/gemspecs.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/solargraph/workspace/gemspecs.rb b/lib/solargraph/workspace/gemspecs.rb index 1456a6a2c..9ecb439c4 100644 --- a/lib/solargraph/workspace/gemspecs.rb +++ b/lib/solargraph/workspace/gemspecs.rb @@ -195,6 +195,9 @@ def auto_required_gemspecs_from_this_bundle end end + # @sg-ignore + # Solargraph::Workspace::Gemspecs#auto_required_gemspecs_from_external_bundle + # return type could not be inferred # @return [Array] def auto_required_gemspecs_from_external_bundle @auto_required_gemspecs_from_external_bundle ||= From 3919e624b319290b7bc99c9e10a7bccce443c868 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 19 Jul 2025 14:11:25 -0400 Subject: [PATCH 257/561] Finalize some TODOs --- lib/solargraph/rbs_map/stdlib_map.rb | 6 -- lib/solargraph/shell.rb | 2 +- lib/solargraph/workspace.rb | 2 +- lib/solargraph/workspace/gemspecs.rb | 81 +++++++++++++---------- lib/solargraph/workspace/require_paths.rb | 3 +- spec/shell_spec.rb | 10 ++- 6 files changed, 58 insertions(+), 46 deletions(-) diff --git a/lib/solargraph/rbs_map/stdlib_map.rb b/lib/solargraph/rbs_map/stdlib_map.rb index 4aeaaf402..4ab83eb5f 100644 --- a/lib/solargraph/rbs_map/stdlib_map.rb +++ b/lib/solargraph/rbs_map/stdlib_map.rb @@ -9,12 +9,6 @@ class RbsMap class StdlibMap < RbsMap include Logging - # @return [Symbol] - def log_level - # TODO: track down remaining unfound requires - :info - end - # @type [Hash{String => RbsMap}] @stdlib_maps_hash = {} diff --git a/lib/solargraph/shell.rb b/lib/solargraph/shell.rb index 4d617dbfd..bb93446f6 100755 --- a/lib/solargraph/shell.rb +++ b/lib/solargraph/shell.rb @@ -106,7 +106,7 @@ def clear # @param gem [String] # @param version [String, nil] def cache gem, version = nil - gems(gem + version ? "=#{version}" : '') + gems(gem + (version ? "=#{version}" : '')) # " end diff --git a/lib/solargraph/workspace.rb b/lib/solargraph/workspace.rb index 070f0f6d5..cb6663f83 100644 --- a/lib/solargraph/workspace.rb +++ b/lib/solargraph/workspace.rb @@ -23,7 +23,7 @@ class Workspace # @param config [Config, nil] # @param server [Hash] def initialize directory = '', config = nil, server = {} - @directory = directory + @directory = File.absolute_path(directory) @config = config @server = server load_sources diff --git a/lib/solargraph/workspace/gemspecs.rb b/lib/solargraph/workspace/gemspecs.rb index 9ecb439c4..8bc2b4ba3 100644 --- a/lib/solargraph/workspace/gemspecs.rb +++ b/lib/solargraph/workspace/gemspecs.rb @@ -12,7 +12,7 @@ class Gemspecs # @param directory [String] def initialize directory - @directory = directory + @directory = File.absolute_path(directory) # @todo implement preferences @preferences = [] end @@ -25,34 +25,52 @@ def initialize directory # @return [::Array, nil] def resolve_require require return nil if require.empty? + + # This is added in the parser when it sees 'Bundler.require' - + # see https://bundler.io/guides/bundler_setup.html ' + # + # @todo handle different arguments to Bundler.require return auto_required_gemspecs_from_bundler if require == 'bundler/require' - gemspecs = all_gemspecs_from_bundle - # @type [Gem::Specification, nil] - gemspec = gemspecs.find { |gemspec| gemspec.name == require } - if gemspec.nil? - # TODO: this seems hinky - gem_name_guess = require.split('/').first - begin - # this can happen when the gem is included via a local path in - # a Gemfile; Gem doesn't try to index the paths in that case. - # - # See if we can make a good guess: - potential_gemspec = Gem::Specification.find_by_name(gem_name_guess) + # Determine gem name based on the require path + file = "lib/#{require}.rb" + begin + spec_with_path = Gem::Specification.find_by_path(file) + rescue Gem::MissingSpecError + logger.debug do + "Require path #{require} could not be resolved to a gem via find_by_name or guess of #{gem_name_guess}" + end + [] + end + + all_gemspecs = all_gemspecs_from_bundle - return nil if potential_gemspec.nil? - file = "lib/#{require}.rb" - gemspec = potential_gemspec if potential_gemspec&.files&.any? { |gemspec_file| file == gemspec_file } + gem_names_to_try = [ + spec_with_path&.name, + require.tr('/', '-'), + require.split('/').first + ].compact.uniq + gem_names_to_try.each do |gem_name| + gemspec = all_gemspecs.find { |gemspec| gemspec.name == gem_name } + return [gemspec_or_preference(gemspec)] if gemspec + + begin + gemspec = Gem::Specification.find_by_name(gem_name) + return [gemspec_or_preference(gemspec)] if gemspec rescue Gem::MissingSpecError - logger.debug do - "Require path #{require} could not be resolved to a gem via find_by_path or guess of #{gem_name_guess}" - end - [] + logger.debug "Gem #{gem_name} not found in the current Ruby environment" + end + + # look ourselves just in case this is hanging out somewhere + # that find_by_path doesn't index' + gemspec = all_gemspecs.find do |spec| + spec&.files&.any? { |gemspec_file| file == gemspec_file } end + return [gemspec_or_preference(gemspec)] if gemspec end - return nil if gemspec.nil? - [gemspec_or_preference(gemspec)] + + nil end # @param name [String] @@ -109,7 +127,6 @@ def all_gemspecs_from_bundle # @sg-ignore Need a JSON type # @yield [undefined] def query_external_bundle command, &block - # TODO: probably combine with logic in require_paths.rb Solargraph.with_clean_env do cmd = [ 'ruby', '-e', @@ -178,21 +195,14 @@ def auto_required_gemspecs_from_bundler end end - # TODO: "Astute readers will notice that the correct way to - # require the rack-cache gem is require 'rack/cache', not - # require 'rack-cache'. To tell bundler to use require - # 'rack/cache', update your Gemfile:" - # - # gem 'rack-cache', require: 'rack/cache' - # @return [Array] def auto_required_gemspecs_from_this_bundle - deps = Bundler.definition.locked_gems.dependencies - - all_gemspecs_from_bundle.select do |gemspec| - deps.key?(gemspec.name) && - deps[gemspec.name].autorequire != [] + # Adapted from require() in lib/bundler/runtime.rb + deps = Bundler.definition.dependencies.select do |dep| + dep.groups.include?(:default) && dep.should_include? end + + all_gemspecs_from_bundle.select { |gemspec| deps.key?(gemspec.name) } end # @sg-ignore @@ -200,6 +210,7 @@ def auto_required_gemspecs_from_this_bundle # return type could not be inferred # @return [Array] def auto_required_gemspecs_from_external_bundle + # TODO adjust for logic above @auto_required_gemspecs_from_external_bundle ||= begin logger.info 'Fetching auto-required gemspecs from Bundler (bundler/require)' diff --git a/lib/solargraph/workspace/require_paths.rb b/lib/solargraph/workspace/require_paths.rb index e427c4439..22b04b40a 100644 --- a/lib/solargraph/workspace/require_paths.rb +++ b/lib/solargraph/workspace/require_paths.rb @@ -44,7 +44,7 @@ def require_paths_from_gemspec_files # # @return [Array] def gemspec_file_paths - # TODO: Document what '*' means and how a user should use it + # @todo Document what '*' means and how a user should use it return [] if directory.empty? || directory == '*' @gemspec_file_paths ||= Dir[File.join(directory, '**/*.gemspec')].select do |gs| config.nil? || config.allow?(gs) @@ -73,7 +73,6 @@ def gemspec? # @param gemspec_file_path [String] # @return [Array] def require_path_from_gemspec_file gemspec_file_path - # TODO: this needs to work with external bundles too base = File.dirname(gemspec_file_path) # HACK: Evaluating gemspec files violates the goal of not running # workspace code, but this is how Gem::Specification.load does it diff --git a/spec/shell_spec.rb b/spec/shell_spec.rb index dc85a7413..f0df98121 100644 --- a/spec/shell_spec.rb +++ b/spec/shell_spec.rb @@ -47,11 +47,19 @@ def bundle_exec(*cmd) end end - describe 'cache' do + describe 'gem' do it 'caches without erroring out' do output = bundle_exec('solargraph', 'gem', 'solargraph') expect(output).to include('Caching these gems') end end + + describe 'cache' do + it 'caches without erroring out' do + output = bundle_exec('solargraph', 'cache', 'solargraph') + + expect(output).to include('Caching these gems') + end + end end From 2cf1dfebd0ccf91d73a89a45e33097d1757c2c9e Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 19 Jul 2025 14:28:03 -0400 Subject: [PATCH 258/561] Finalize some TODOs --- lib/solargraph/workspace/gemspecs.rb | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/lib/solargraph/workspace/gemspecs.rb b/lib/solargraph/workspace/gemspecs.rb index 8bc2b4ba3..4bc90fcc1 100644 --- a/lib/solargraph/workspace/gemspecs.rb +++ b/lib/solargraph/workspace/gemspecs.rb @@ -38,14 +38,13 @@ def resolve_require require spec_with_path = Gem::Specification.find_by_path(file) rescue Gem::MissingSpecError logger.debug do - "Require path #{require} could not be resolved to a gem via find_by_name or guess of #{gem_name_guess}" + "Require path #{require} could not be resolved to a gem via find_by_name or path of #{file}" end [] end all_gemspecs = all_gemspecs_from_bundle - gem_names_to_try = [ spec_with_path&.name, require.tr('/', '-'), @@ -198,11 +197,11 @@ def auto_required_gemspecs_from_bundler # @return [Array] def auto_required_gemspecs_from_this_bundle # Adapted from require() in lib/bundler/runtime.rb - deps = Bundler.definition.dependencies.select do |dep| + dep_names = Bundler.definition.dependencies.select do |dep| dep.groups.include?(:default) && dep.should_include? - end + end.map(&:name) - all_gemspecs_from_bundle.select { |gemspec| deps.key?(gemspec.name) } + all_gemspecs_from_bundle.select { |gemspec| dep_names.include?(gemspec.name) } end # @sg-ignore @@ -210,21 +209,17 @@ def auto_required_gemspecs_from_this_bundle # return type could not be inferred # @return [Array] def auto_required_gemspecs_from_external_bundle - # TODO adjust for logic above @auto_required_gemspecs_from_external_bundle ||= begin logger.info 'Fetching auto-required gemspecs from Bundler (bundler/require)' command = - 'dependencies = Bundler.definition.dependencies; ' \ - 'all_specs = Bundler.definition.locked_gems.specs; ' \ - 'autorequired_specs = all_specs.' \ - 'select { |gemspec| dependencies.key?(gemspec.name) && dependencies[gemspec.name].autorequire != [] }; ' \ - 'autorequired_specs.map { |spec| [spec.name, spec.version] }' - query_external_bundle command do |dependencies| - dependencies.map do |name, requirement| - resolve_gem_ignoring_local_bundle name, requirement - end.compact - end + 'Bundler.definition.dependencies' \ + '.select { |dep| dep.groups.include?(:default) && dep.should_include? }.map(&:name)' + # @sg-ignore + # @type [Array] + dep_names = query_external_bundle(command) { |dependency_names| dependency_names } + + all_gemspecs_from_bundle.select { |gemspec| dep_names.include?(gemspec.name) } end end From 771160df65ba013c50108df93199fd61a7a3bafc Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 19 Jul 2025 14:42:06 -0400 Subject: [PATCH 259/561] Fix specs --- lib/solargraph/workspace.rb | 6 +++++- spec/workspace_spec.rb | 3 ++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/solargraph/workspace.rb b/lib/solargraph/workspace.rb index cb6663f83..90c104077 100644 --- a/lib/solargraph/workspace.rb +++ b/lib/solargraph/workspace.rb @@ -23,7 +23,11 @@ class Workspace # @param config [Config, nil] # @param server [Hash] def initialize directory = '', config = nil, server = {} - @directory = File.absolute_path(directory) + @directory = if ['*', ''].include?(directory) + directory + else + File.absolute_path(directory) + end @config = config @server = server load_sources diff --git a/spec/workspace_spec.rb b/spec/workspace_spec.rb index d308e3be2..f152a0a85 100644 --- a/spec/workspace_spec.rb +++ b/spec/workspace_spec.rb @@ -117,7 +117,8 @@ it "uses configured require paths" do workspace = Solargraph::Workspace.new('spec/fixtures/workspace') - expect(workspace.require_paths).to eq(['spec/fixtures/workspace/lib', 'spec/fixtures/workspace/ext']) + expect(workspace.require_paths).to eq([File.absolute_path('spec/fixtures/workspace/lib'), + File.absolute_path('spec/fixtures/workspace/ext')]) end it 'rescues errors loading files into sources' do From 9b94026ba1252a80c6750bda7400e9f65af3b08b Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 19 Jul 2025 15:00:31 -0400 Subject: [PATCH 260/561] Add Gem::Specification RBS fill --- rbs/fills/rubygems/0/specification.rbs | 1753 ++++++++++++++++++++++++ 1 file changed, 1753 insertions(+) create mode 100644 rbs/fills/rubygems/0/specification.rbs diff --git a/rbs/fills/rubygems/0/specification.rbs b/rbs/fills/rubygems/0/specification.rbs new file mode 100644 index 000000000..80c13f429 --- /dev/null +++ b/rbs/fills/rubygems/0/specification.rbs @@ -0,0 +1,1753 @@ +# +# The Specification class contains the information for a gem. Typically defined +# in a .gemspec file or a Rakefile, and looks like this: +# +# Gem::Specification.new do |s| +# s.name = 'example' +# s.version = '0.1.0' +# s.licenses = ['MIT'] +# s.summary = "This is an example!" +# s.description = "Much longer explanation of the example!" +# s.authors = ["Ruby Coder"] +# s.email = 'rubycoder@example.com' +# s.files = ["lib/example.rb"] +# s.homepage = 'https://rubygems.org/gems/example' +# s.metadata = { "source_code_uri" => "https://github.com/example/example" } +# end +# +# Starting in RubyGems 2.0, a Specification can hold arbitrary metadata. See +# #metadata for restrictions on the format and size of metadata items you may +# add to a specification. +# +class Gem::Specification < Gem::BasicSpecification + @@required_attributes: untyped + + @@default_value: untyped + + @@attributes: untyped + + @@array_attributes: untyped + + @@nil_attributes: untyped + + @@non_nil_attributes: untyped + + @@dirs: untyped + + self.@load_cache: untyped + + self.@load_cache_mutex: untyped + + self.@specification_record: untyped + + self.@unresolved_deps: untyped + + @removed_method_calls: untyped + + # DO NOT CHANGE TO ||= ! This is not a normal accessor. (yes, it sucks) + # DOC: Why isn't it normal? Why does it suck? How can we fix this? + @files: untyped + + @authors: untyped + + @licenses: untyped + + @original_platform: untyped + + @new_platform: untyped + + @platform: untyped + + @require_paths: untyped + + @executables: untyped + + @extensions: untyped + + @extra_rdoc_files: untyped + + @installed_by_version: untyped + + @rdoc_options: untyped + + @required_ruby_version: untyped + + @required_rubygems_version: untyped + + @requirements: untyped + + @test_files: untyped + + @extensions_dir: untyped + + @activated: untyped + + @loaded: untyped + + @bin_dir: untyped + + @cache_dir: untyped + + @cache_file: untyped + + @date: untyped + + @dependencies: untyped + + @description: untyped + + @doc_dir: untyped + + @full_name: untyped + + @gems_dir: untyped + + @has_rdoc: untyped + + @base_dir: untyped + + @loaded_from: untyped + + @ri_dir: untyped + + @spec_dir: untyped + + @spec_file: untyped + + @summary: untyped + + @test_suite_file: untyped + + @version: untyped + + extend Gem::Deprecate + + # + # The version number of a specification that does not specify one (i.e. RubyGems + # 0.7 or earlier). + # + NONEXISTENT_SPECIFICATION_VERSION: -1 + + CURRENT_SPECIFICATION_VERSION: 4 + + SPECIFICATION_VERSION_HISTORY: { -1 => ::Array["(RubyGems versions up to and including 0.7 did not have versioned specifications)"], 1 => ::Array["Deprecated \"test_suite_file\" in favor of the new, but equivalent, \"test_files\"" | "\"test_file=x\" is a shortcut for \"test_files=[x]\""], 2 => ::Array["Added \"required_rubygems_version\"" | "Now forward-compatible with future versions"], 3 => ::Array["Added Fixnum validation to the specification_version"], 4 => ::Array["Added sandboxed freeform metadata to the specification version."] } + + MARSHAL_FIELDS: { -1 => 16, 1 => 16, 2 => 16, 3 => 17, 4 => 18 } + + TODAY: untyped + + VALID_NAME_PATTERN: ::Regexp + + # rubocop:disable Style/MutableConstant + INITIALIZE_CODE_FOR_DEFAULTS: ::Hash[untyped, untyped] + + # Sentinel object to represent "not found" stubs + NOT_FOUND: untyped + + # Tracking removed method calls to warn users during build time. + REMOVED_METHODS: ::Array[:rubyforge_project= | :mark_version] + + # + # + def removed_method_calls: () -> untyped + + # + # This gem's name. + # + # Usage: + # + # spec.name = 'rake' + # + attr_accessor name: String + + # + # This gem's version. + # + # The version string can contain numbers and periods, such as `1.0.0`. A gem is + # a 'prerelease' gem if the version has a letter in it, such as `1.0.0.pre`. + # + # Usage: + # + # spec.version = '0.4.1' + # + attr_reader version: String + + # + # A short summary of this gem's description. Displayed in `gem list -d`. + # + # The #description should be more detailed than the summary. + # + # Usage: + # + # spec.summary = "This is a small summary of my gem" + # + attr_reader summary: untyped + + # + # Files included in this gem. You cannot append to this accessor, you must + # assign to it. + # + # Only add files you can require to this list, not directories, etc. + # + # Directories are automatically stripped from this list when building a gem, + # other non-files cause an error. + # + # Usage: + # + # require 'rake' + # spec.files = FileList['lib/**/*.rb', + # 'bin/*', + # '[A-Z]*'].to_a + # + # # or without Rake... + # spec.files = Dir['lib/**/*.rb'] + Dir['bin/*'] + # spec.files += Dir['[A-Z]*'] + # spec.files.reject! { |fn| fn.include? "CVS" } + # + def files: () -> Enumerable[String] + + # + # A list of authors for this gem. + # + # Alternatively, a single author can be specified by assigning a string to + # `spec.author` + # + # Usage: + # + # spec.authors = ['John Jones', 'Mary Smith'] + # + def authors=: (untyped value) -> untyped + + # + # The version of Ruby required by this gem + # + # Usage: + # + # spec.required_ruby_version = '>= 2.7.0' + # + attr_reader required_ruby_version: untyped + + # + # A long description of this gem + # + # The description should be more detailed than the summary but not excessively + # long. A few paragraphs is a recommended length with no examples or + # formatting. + # + # Usage: + # + # spec.description = <<-EOF + # Rake is a Make-like program implemented in Ruby. Tasks and + # dependencies are specified in standard Ruby syntax. + # EOF + # + attr_reader description: untyped + + # + # A contact email address (or addresses) for this gem + # + # Usage: + # + # spec.email = 'john.jones@example.com' + # spec.email = ['jack@example.com', 'jill@example.com'] + # + attr_accessor email: untyped + + # + # The URL of this gem's home page + # + # Usage: + # + # spec.homepage = 'https://github.com/ruby/rake' + # + attr_accessor homepage: untyped + + # + # The license for this gem. + # + # The license must be no more than 64 characters. + # + # This should just be the name of your license. The full text of the license + # should be inside of the gem (at the top level) when you build it. + # + # The simplest way is to specify the standard SPDX ID https://spdx.org/licenses/ + # for the license. Ideally, you should pick one that is OSI (Open Source + # Initiative) http://opensource.org/licenses/alphabetical approved. + # + # The most commonly used OSI-approved licenses are MIT and Apache-2.0. GitHub + # also provides a license picker at http://choosealicense.com/. + # + # You can also use a custom license file along with your gemspec and specify a + # LicenseRef-, where idstring is the name of the file containing the + # license text. + # + # You should specify a license for your gem so that people know how they are + # permitted to use it and any restrictions you're placing on it. Not specifying + # a license means all rights are reserved; others have no right to use the code + # for any purpose. + # + # You can set multiple licenses with #licenses= + # + # Usage: + # spec.license = 'MIT' + # + def license=: (untyped o) -> untyped + + # + # The license(s) for the library. + # + # Each license must be a short name, no more than 64 characters. + # + # This should just be the name of your license. The full text of the license + # should be inside of the gem when you build it. + # + # See #license= for more discussion + # + # Usage: + # spec.licenses = ['MIT', 'GPL-2.0'] + # + def licenses=: (untyped licenses) -> untyped + + # + # The metadata holds extra data for this gem that may be useful to other + # consumers and is settable by gem authors. + # + # Metadata items have the following restrictions: + # + # * The metadata must be a Hash object + # * All keys and values must be Strings + # * Keys can be a maximum of 128 bytes and values can be a maximum of 1024 + # bytes + # * All strings must be UTF-8, no binary data is allowed + # + # You can use metadata to specify links to your gem's homepage, codebase, + # documentation, wiki, mailing list, issue tracker and changelog. + # + # s.metadata = { + # "bug_tracker_uri" => "https://example.com/user/bestgemever/issues", + # "changelog_uri" => "https://example.com/user/bestgemever/CHANGELOG.md", + # "documentation_uri" => "https://www.example.info/gems/bestgemever/0.0.1", + # "homepage_uri" => "https://bestgemever.example.io", + # "mailing_list_uri" => "https://groups.example.com/bestgemever", + # "source_code_uri" => "https://example.com/user/bestgemever", + # "wiki_uri" => "https://example.com/user/bestgemever/wiki" + # "funding_uri" => "https://example.com/donate" + # } + # + # These links will be used on your gem's page on rubygems.org and must pass + # validation against following regex. + # + # %r{\Ahttps?:\/\/([^\s:@]+:[^\s:@]*@)?[A-Za-z\d\-]+(\.[A-Za-z\d\-]+)+\.?(:\d{1,5})?([\/?]\S*)?\z} + # + attr_accessor metadata: untyped + + # + # Singular (alternative) writer for #authors + # + # Usage: + # + # spec.author = 'John Jones' + # + def author=: (untyped o) -> untyped + + # + # The path in the gem for executable scripts. Usually 'bin' + # + # Usage: + # + # spec.bindir = 'bin' + # + attr_accessor bindir: untyped + + # + # The certificate chain used to sign this gem. See Gem::Security for details. + # + attr_accessor cert_chain: untyped + + # + # A message that gets displayed after the gem is installed. + # + # Usage: + # + # spec.post_install_message = "Thanks for installing!" + # + attr_accessor post_install_message: untyped + + # + # The platform this gem runs on. + # + # This is usually Gem::Platform::RUBY or Gem::Platform::CURRENT. + # + # Most gems contain pure Ruby code; they should simply leave the default value + # in place. Some gems contain C (or other) code to be compiled into a Ruby + # "extension". The gem should leave the default value in place unless the code + # will only compile on a certain type of system. Some gems consist of + # pre-compiled code ("binary gems"). It's especially important that they set + # the platform attribute appropriately. A shortcut is to set the platform to + # Gem::Platform::CURRENT, which will cause the gem builder to set the platform + # to the appropriate value for the system on which the build is being performed. + # + # If this attribute is set to a non-default value, it will be included in the + # filename of the gem when it is built such as: nokogiri-1.6.0-x86-mingw32.gem + # + # Usage: + # + # spec.platform = Gem::Platform.local + # + def platform=: (untyped platform) -> untyped + + # + # Paths in the gem to add to `$LOAD_PATH` when this gem is activated. If you + # have an extension you do not need to add `"ext"` to the require path, the + # extension build process will copy the extension files into "lib" for you. + # + # The default value is `"lib"` + # + # Usage: + # + # # If all library files are in the root directory... + # spec.require_paths = ['.'] + # + def require_paths=: (untyped val) -> untyped + + # + # The RubyGems version required by this gem + # + attr_reader required_rubygems_version: untyped + + # + # The key used to sign this gem. See Gem::Security for details. + # + attr_accessor signing_key: untyped + + # + # Adds a development dependency named `gem` with `requirements` to this gem. + # + # Usage: + # + # spec.add_development_dependency 'example', '~> 1.1', '>= 1.1.4' + # + # Development dependencies aren't installed by default and aren't activated when + # a gem is required. + # + def add_development_dependency: (untyped gem, *untyped requirements) -> untyped + + # + # + def add_dependency: (untyped gem, *untyped requirements) -> untyped + + # + # Executables included in the gem. + # + # For example, the rake gem has rake as an executable. You don’t specify the + # full path (as in bin/rake); all application-style files are expected to be + # found in bindir. These files must be executable Ruby files. Files that use + # bash or other interpreters will not work. + # + # Executables included may only be ruby scripts, not scripts for other languages + # or compiled binaries. + # + # Usage: + # + # spec.executables << 'rake' + # + def executables: () -> untyped + + # + # Extensions to build when installing the gem, specifically the paths to + # extconf.rb-style files used to compile extensions. + # + # These files will be run when the gem is installed, causing the C (or whatever) + # code to be compiled on the user’s machine. + # + # Usage: + # + # spec.extensions << 'ext/rmagic/extconf.rb' + # + # See Gem::Ext::Builder for information about writing extensions for gems. + # + def extensions: () -> untyped + + # + # Extra files to add to RDoc such as README or doc/examples.txt + # + # When the user elects to generate the RDoc documentation for a gem (typically + # at install time), all the library files are sent to RDoc for processing. This + # option allows you to have some non-code files included for a more complete set + # of documentation. + # + # Usage: + # + # spec.extra_rdoc_files = ['README', 'doc/user-guide.txt'] + # + def extra_rdoc_files: () -> untyped + + def installed_by_version: () -> untyped + + def installed_by_version=: (untyped version) -> untyped + + # + # Specifies the rdoc options to be used when generating API documentation. + # + # Usage: + # + # spec.rdoc_options << '--title' << 'Rake -- Ruby Make' << + # '--main' << 'README' << + # '--line-numbers' + # + def rdoc_options: () -> untyped + + LATEST_RUBY_WITHOUT_PATCH_VERSIONS: untyped + + # + # The version of Ruby required by this gem. The ruby version can be specified + # to the patch-level: + # + # $ ruby -v -e 'p Gem.ruby_version' + # ruby 2.0.0p247 (2013-06-27 revision 41674) [x86_64-darwin12.4.0] + # # + # + # Prereleases can also be specified. + # + # Usage: + # + # # This gem will work with 1.8.6 or greater... + # spec.required_ruby_version = '>= 1.8.6' + # + # # Only with final releases of major version 2 where minor version is at least 3 + # spec.required_ruby_version = '~> 2.3' + # + # # Only prereleases or final releases after 2.6.0.preview2 + # spec.required_ruby_version = '> 2.6.0.preview2' + # + # # This gem will work with 2.3.0 or greater, including major version 3, but lesser than 4.0.0 + # spec.required_ruby_version = '>= 2.3', '< 4' + # + def required_ruby_version=: (untyped req) -> untyped + + # + # The RubyGems version required by this gem + # + def required_rubygems_version=: (untyped req) -> untyped + + # + # Lists the external (to RubyGems) requirements that must be met for this gem to + # work. It's simply information for the user. + # + # Usage: + # + # spec.requirements << 'libmagick, v6.0' + # spec.requirements << 'A good graphics card' + # + def requirements: () -> untyped + + def test_files=: (untyped files) -> untyped + + # + # The version of RubyGems used to create this gem. + # + # Do not set this, it is set automatically when the gem is packaged. + # + attr_accessor rubygems_version: untyped + + def extensions_dir: () -> untyped + + # + # True when this gemspec has been activated. This attribute is not persisted. + # + attr_accessor activated: untyped + + # + # True when this gemspec has been activated. This attribute is not persisted. + # + alias activated? activated + + attr_accessor autorequire: untyped + + attr_writer default_executable: untyped + + attr_writer original_platform: untyped + + # + # The Gem::Specification version of this gemspec. + # + # Do not set this, it is set automatically when the gem is packaged. + # + attr_accessor specification_version: untyped + + def self._all: () -> untyped + + def self.clear_load_cache: () -> untyped + + def self.gem_path: () -> untyped + + def self.each_gemspec: (untyped dirs) { (untyped) -> untyped } -> untyped + + # + # + def self.gemspec_stubs_in: (untyped dir, untyped pattern) { (untyped) -> untyped } -> untyped + + def self.each_spec: (untyped dirs) { (untyped) -> untyped } -> untyped + + # + # Returns a Gem::StubSpecification for every installed gem + # + def self.stubs: () -> untyped + + # + # Returns a Gem::StubSpecification for default gems + # + def self.default_stubs: (?::String pattern) -> untyped + + # + # Returns a Gem::StubSpecification for installed gem named `name` only returns + # stubs that match Gem.platforms + # + def self.stubs_for: (untyped name) -> untyped + + # + # Finds stub specifications matching a pattern from the standard locations, + # optionally filtering out specs not matching the current platform + # + def self.stubs_for_pattern: (untyped pattern, ?bool match_platform) -> untyped + + def self._resort!: (untyped specs) -> untyped + + # + # Loads the default specifications. It should be called only once. + # + def self.load_defaults: () -> untyped + + # + # Adds `spec` to the known specifications, keeping the collection properly + # sorted. + # + def self.add_spec: (untyped spec) -> untyped + + # + # Removes `spec` from the known specs. + # + def self.remove_spec: (untyped spec) -> untyped + + # + # Returns all specifications. This method is discouraged from use. You probably + # want to use one of the Enumerable methods instead. + # + def self.all: () -> untyped + + # + # Sets the known specs to `specs`. Not guaranteed to work for you in the future. + # Use at your own risk. Caveat emptor. Doomy doom doom. Etc etc. + # + def self.all=: (untyped specs) -> untyped + + # + # Return full names of all specs in sorted order. + # + def self.all_names: () -> untyped + + # + # Return the list of all array-oriented instance variables. + # + def self.array_attributes: () -> untyped + + # + # Return the list of all instance variables. + # + def self.attribute_names: () -> untyped + + # + # Return the directories that Specification uses to find specs. + # + def self.dirs: () -> untyped + + # + # Set the directories that Specification uses to find specs. Setting this resets + # the list of known specs. + # + def self.dirs=: (untyped dirs) -> untyped + + extend Enumerable[Gem::Specification] + + # + # Enumerate every known spec. See ::dirs= and ::add_spec to set the list of + # specs. + # + def self.each: () { (Gem::Specification) -> untyped } -> untyped + + # + # Returns every spec that matches `name` and optional `requirements`. + # + def self.find_all_by_name: (untyped name, *untyped requirements) -> untyped + + # + # Returns every spec that has the given `full_name` + # + def self.find_all_by_full_name: (untyped full_name) -> untyped + + # + # Find the best specification matching a `name` and `requirements`. Raises if + # the dependency doesn't resolve to a valid specification. + # + def self.find_by_name: (untyped name, *untyped requirements) -> instance + + # + # Find the best specification matching a +full_name+. + def self.find_by_full_name: (untyped full_name) -> instance + + # + # Return the best specification that contains the file matching `path`. + # + def self.find_by_path: (untyped path) -> instance + + # + # Return the best specification that contains the file matching `path` amongst + # the specs that are not activated. + # + def self.find_inactive_by_path: (untyped path) -> untyped + + # + # + def self.find_active_stub_by_path: (untyped path) -> untyped + + # + # Return currently unresolved specs that contain the file matching `path`. + # + def self.find_in_unresolved: (untyped path) -> untyped + + # + # Search through all unresolved deps and sub-dependencies and return specs that + # contain the file matching `path`. + # + def self.find_in_unresolved_tree: (untyped path) -> (untyped | ::Array[untyped]) + + # + # + def self.unresolved_specs: () -> untyped + + # + # Special loader for YAML files. When a Specification object is loaded from a + # YAML file, it bypasses the normal Ruby object initialization routine + # (#initialize). This method makes up for that and deals with gems of different + # ages. + # + # `input` can be anything that YAML.load() accepts: String or IO. + # + def self.from_yaml: (untyped input) -> untyped + + # + # Return the latest specs, optionally including prerelease specs if `prerelease` + # is true. + # + def self.latest_specs: (?bool prerelease) -> untyped + + # + # Return the latest installed spec for gem `name`. + # + def self.latest_spec_for: (untyped name) -> untyped + + def self._latest_specs: (untyped specs, ?bool prerelease) -> untyped + + # + # Loads Ruby format gemspec from `file`. + # + def self.load: (untyped file) -> (nil | untyped) + + # + # Specification attributes that must be non-nil + # + def self.non_nil_attributes: () -> untyped + + # + # Make sure the YAML specification is properly formatted with dashes + # + def self.normalize_yaml_input: (untyped input) -> untyped + + # + # Return a list of all outdated local gem names. This method is HEAVY as it + # must go fetch specifications from the server. + # + # Use outdated_and_latest_version if you wish to retrieve the latest remote + # version as well. + # + def self.outdated: () -> untyped + + # + # Enumerates the outdated local gems yielding the local specification and the + # latest remote version. + # + # This method may take some time to return as it must check each local gem + # against the server's index. + # + def self.outdated_and_latest_version: () ?{ (untyped) -> untyped } -> (untyped | nil) + + # + # Is `name` a required attribute? + # + def self.required_attribute?: (untyped name) -> untyped + + # + # Required specification attributes + # + def self.required_attributes: () -> untyped + + # + # Reset the list of known specs, running pre and post reset hooks registered in + # Gem. + # + def self.reset: () -> untyped + + def self.specification_record: () -> untyped + + # + # DOC: This method needs documented or nodoc'd + # + def self.unresolved_deps: () -> untyped + + # + # Load custom marshal format, re-initializing defaults as needed + # + def self._load: (untyped str) -> untyped + + def <=>: (untyped other) -> untyped + + def ==: (untyped other) -> untyped + + # + # Dump only crucial instance variables. + # + def _dump: (untyped limit) -> untyped + + # + # Activate this spec, registering it as a loaded spec and adding it's lib paths + # to $LOAD_PATH. Returns true if the spec was activated, false if it was + # previously activated. Freaks out if there are conflicts upon activation. + # + def activate: () -> (false | true) + + # + # Activate all unambiguously resolved runtime dependencies of this spec. Add any + # ambiguous dependencies to the unresolved list to be resolved later, as needed. + # + def activate_dependencies: () -> untyped + + # + # Abbreviate the spec for downloading. Abbreviated specs are only used for + # searching, downloading and related activities and do not need deployment + # specific information (e.g. list of files). So we abbreviate the spec, making + # it much smaller for quicker downloads. + # + def abbreviate: () -> untyped + + # + # Sanitize the descriptive fields in the spec. Sometimes non-ASCII characters + # will garble the site index. Non-ASCII characters will be replaced by their + # XML entity equivalent. + # + def sanitize: () -> untyped + + # + # Sanitize a single string. + # + def sanitize_string: (untyped string) -> untyped + + # + # Returns an array with bindir attached to each executable in the `executables` + # list + # + def add_bindir: (untyped executables) -> untyped + + private + + # + # Adds a dependency on gem `dependency` with type `type` that requires + # `requirements`. Valid types are currently `:runtime` and `:development`. + # + def add_dependency_with_type: (untyped dependency, untyped type, untyped requirements) -> untyped + + public + + # + # Adds a runtime dependency named `gem` with `requirements` to this gem. + # + # Usage: + # + # spec.add_runtime_dependency 'example', '~> 1.1', '>= 1.1.4' + # + alias add_runtime_dependency add_dependency + + # + # Adds this spec's require paths to LOAD_PATH, in the proper location. + # + def add_self_to_load_path: () -> (nil | untyped) + + # + # Singular reader for #authors. Returns the first author in the list + # + def author: () -> untyped + + # + # The list of author names who wrote this gem. + # + # spec.authors = ['Chad Fowler', 'Jim Weirich', 'Rich Kilmer'] + # + def authors: () -> untyped + + # + # Returns the full path to installed gem's bin directory. + # + # NOTE: do not confuse this with `bindir`, which is just 'bin', not a full path. + # + def bin_dir: () -> untyped + + # + # Returns the full path to an executable named `name` in this gem. + # + def bin_file: (untyped name) -> untyped + + # + # Returns the build_args used to install the gem + # + def build_args: () -> (untyped | ::Array[untyped]) + + def build_extensions: () -> (nil | untyped) + + # + # Returns the full path to the build info directory + # + def build_info_dir: () -> untyped + + # + # Returns the full path to the file containing the build information generated + # when the gem was installed + # + def build_info_file: () -> untyped + + # + # Returns the full path to the cache directory containing this spec's cached + # gem. + # + def cache_dir: () -> untyped + + # + # Returns the full path to the cached gem for this spec. + # + def cache_file: () -> untyped + + # + # Return any possible conflicts against the currently loaded specs. + # + def conflicts: () -> untyped + + def conficts_when_loaded_with?: (untyped list_of_specs) -> untyped + + # + # Return true if there are possible conflicts against the currently loaded + # specs. + # + def has_conflicts?: () -> untyped + + # + # The date this gem was created. + # + # If SOURCE_DATE_EPOCH is set as an environment variable, use that to support + # reproducible builds; otherwise, default to the current UTC date. + # + # Details on SOURCE_DATE_EPOCH: + # https://reproducible-builds.org/specs/source-date-epoch/ + # + def date: () -> untyped + + DateLike: untyped + + def self.===: (untyped obj) -> untyped + + DateTimeFormat: ::Regexp + + # + # The date this gem was created + # + # DO NOT set this, it is set automatically when the gem is packaged. + # + def date=: (untyped date) -> untyped + + def default_executable: () -> untyped + + # + # The default value for specification attribute `name` + # + def default_value: (untyped name) -> untyped + + # + # A list of Gem::Dependency objects this gem depends on. + # + # Use #add_dependency or #add_development_dependency to add dependencies to a + # gem. + # + def dependencies: () -> Array[Gem::Dependency] + + # + # Return a list of all gems that have a dependency on this gemspec. The list is + # structured with entries that conform to: + # + # [depending_gem, dependency, [list_of_gems_that_satisfy_dependency]] + # + def dependent_gems: (?bool check_dev) -> untyped + + # + # Returns all specs that matches this spec's runtime dependencies. + # + def dependent_specs: () -> untyped + + # + # A detailed description of this gem. See also #summary + # + def description=: (untyped str) -> untyped + + # + # List of dependencies that are used for development + # + def development_dependencies: () -> Array[Gem::Dependency] + + # + # Returns the full path to this spec's documentation directory. If `type` is + # given it will be appended to the end. For example: + # + # spec.doc_dir # => "/path/to/gem_repo/doc/a-1" + # + # spec.doc_dir 'ri' # => "/path/to/gem_repo/doc/a-1/ri" + # + def doc_dir: (?untyped? type) -> untyped + + def encode_with: (untyped coder) -> untyped + + def eql?: (untyped other) -> untyped + + # + # Singular accessor for #executables + # + def executable: () -> untyped + + # + # Singular accessor for #executables + # + def executable=: (untyped o) -> untyped + + # + # Sets executables to `value`, ensuring it is an array. + # + def executables=: (untyped value) -> untyped + + # + # Sets extensions to `extensions`, ensuring it is an array. + # + def extensions=: (untyped extensions) -> untyped + + # + # Sets extra_rdoc_files to `files`, ensuring it is an array. + # + def extra_rdoc_files=: (untyped files) -> untyped + + # + # The default (generated) file name of the gem. See also #spec_name. + # + # spec.file_name # => "example-1.0.gem" + # + def file_name: () -> ::String + + # + # Sets files to `files`, ensuring it is an array. + # + def files=: (untyped files) -> untyped + + private + + # + # Finds all gems that satisfy `dep` + # + def find_all_satisfiers: (untyped dep) { (untyped) -> untyped } -> untyped + + public + + # + # Creates a duplicate spec without large blobs that aren't used at runtime. + # + def for_cache: () -> untyped + + # + # + def full_name: () -> untyped + + def gem_dir: () -> untyped + + # + # + def gems_dir: () -> untyped + + def has_rdoc: () -> true + + def has_rdoc=: (untyped ignored) -> untyped + + alias has_rdoc? has_rdoc + + def has_unit_tests?: () -> untyped + + # :stopdoc: + alias has_test_suite? has_unit_tests? + + def hash: () -> untyped + + def init_with: (untyped coder) -> untyped + + # + # Specification constructor. Assigns the default values to the attributes and + # yields itself for further initialization. Optionally takes `name` and + # `version`. + # + def initialize: (?untyped? name, ?untyped? version) ?{ (untyped) -> untyped } -> void + + # + # Duplicates array_attributes from `other_spec` so state isn't shared. + # + def initialize_copy: (untyped other_spec) -> untyped + + # + # + def base_dir: () -> untyped + + private + + # + # Expire memoized instance variables that can incorrectly generate, replace or + # miss files due changes in certain attributes used to compute them. + # + def invalidate_memoized_attributes: () -> untyped + + public + + def inspect: () -> (untyped | ::String) + + # + # Files in the Gem under one of the require_paths + # + def lib_files: () -> untyped + + # + # Singular accessor for #licenses + # + def license: () -> untyped + + # + # Plural accessor for setting licenses + # + # See #license= for details + # + def licenses: () -> untyped + + def internal_init: () -> untyped + + def method_missing: (untyped sym, *untyped a) { (?) -> untyped } -> (nil | untyped) + + # + # Is this specification missing its extensions? When this returns true you + # probably want to build_extensions + # + def missing_extensions?: () -> (false | true) + + # + # Normalize the list of files so that: + # * All file lists have redundancies removed. + # * Files referenced in the extra_rdoc_files are included in the package file + # list. + # + def normalize: () -> untyped + + # + # Return a NameTuple that represents this Specification + # + def name_tuple: () -> untyped + + def original_name: () -> ::String + + def original_platform: () -> untyped + + # + # The platform this gem runs on. See Gem::Platform for details. + # + def platform: () -> untyped + + def pretty_print: (untyped q) -> untyped + + private + + def check_version_conflict: (untyped other) -> (nil | untyped) + + public + + def raise_if_conflicts: () -> (untyped | nil) + + # + # Sets rdoc_options to `value`, ensuring it is an array. + # + def rdoc_options=: (untyped options) -> untyped + + # + # Singular accessor for #require_paths + # + def require_path: () -> untyped + + # + # Singular accessor for #require_paths + # + def require_path=: (untyped path) -> untyped + + # + # Set requirements to `req`, ensuring it is an array. + # + def requirements=: (untyped req) -> untyped + + def respond_to_missing?: (untyped m, ?bool include_private) -> false + + # + # Returns the full path to this spec's ri directory. + # + def ri_dir: () -> untyped + + private + + # + # Return a string containing a Ruby code representation of the given object. + # + def ruby_code: (untyped obj) -> untyped + + public + + # + # List of dependencies that will automatically be activated at runtime. + # + def runtime_dependencies: () -> untyped + + private + + # + # True if this gem has the same attributes as `other`. + # + def same_attributes?: (untyped spec) -> untyped + + public + + # + # Checks if this specification meets the requirement of `dependency`. + # + def satisfies_requirement?: (untyped dependency) -> untyped + + # + # Returns an object you can use to sort specifications in #sort_by. + # + def sort_obj: () -> ::Array[untyped] + + def source: () -> untyped + + # + # Returns the full path to the directory containing this spec's gemspec file. + # eg: /usr/local/lib/ruby/gems/1.8/specifications + # + def spec_dir: () -> untyped + + # + # Returns the full path to this spec's gemspec file. eg: + # /usr/local/lib/ruby/gems/1.8/specifications/mygem-1.0.gemspec + # + def spec_file: () -> untyped + + # + # The default name of the gemspec. See also #file_name + # + # spec.spec_name # => "example-1.0.gemspec" + # + def spec_name: () -> ::String + + # + # A short summary of this gem's description. + # + def summary=: (untyped str) -> untyped + + def test_file: () -> untyped + + def test_file=: (untyped file) -> untyped + + def test_files: () -> untyped + + # + # Returns a Ruby code representation of this specification, such that it can be + # eval'ed and reconstruct the same specification later. Attributes that still + # have their default values are omitted. + # + def to_ruby: () -> untyped + + # + # Returns a Ruby lighter-weight code representation of this specification, used + # for indexing only. + # + # See #to_ruby. + # + def to_ruby_for_cache: () -> untyped + + def to_s: () -> ::String + + # + # Returns self + # + def to_spec: () -> self + + def to_yaml: (?::Hash[untyped, untyped] opts) -> untyped + + # + # Recursively walk dependencies of this spec, executing the `block` for each + # hop. + # + def traverse: (?untyped trail, ?::Hash[untyped, untyped] visited) { (?) -> untyped } -> untyped + + # + # Checks that the specification contains all required fields, and does a very + # basic sanity check. + # + # Raises InvalidSpecificationException if the spec does not pass the checks.. + # + def validate: (?bool packaging, ?bool strict) -> untyped + + # + # + def keep_only_files_and_directories: () -> untyped + + def validate_for_resolution: () -> untyped + + # + # + def validate_metadata: () -> untyped + + # + # + def validate_dependencies: () -> untyped + + # + # + def validate_permissions: () -> untyped + + # + # Set the version to `version`, potentially also setting + # required_rubygems_version if `version` indicates it is a prerelease. + # + def version=: (untyped version) -> (nil | untyped) + + # + # + def stubbed?: () -> false + + def yaml_initialize: (untyped tag, untyped vals) -> untyped + + # + # Reset nil attributes to their default values to make the spec valid + # + def reset_nil_attributes_to_default: () -> nil + + def flatten_require_paths: () -> (nil | untyped) + + def raw_require_paths: () -> untyped +end From 55ee9f79c32e5390dbbe10d30f937a70092bfef6 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 20 Jul 2025 11:41:34 -0400 Subject: [PATCH 261/561] Quiet specs --- lib/solargraph/api_map.rb | 2 +- lib/solargraph/doc_map.rb | 9 +++++---- lib/solargraph/workspace.rb | 2 +- spec/api_map_spec.rb | 2 +- spec/doc_map_spec.rb | 10 +++++----- spec/gem_pins_spec.rb | 4 ++-- spec/type_checker/levels/strict_spec.rb | 2 +- spec/yard_map/mapper_spec.rb | 12 +++++++----- 8 files changed, 23 insertions(+), 20 deletions(-) diff --git a/lib/solargraph/api_map.rb b/lib/solargraph/api_map.rb index d3f4f6827..85de6304e 100755 --- a/lib/solargraph/api_map.rb +++ b/lib/solargraph/api_map.rb @@ -104,7 +104,7 @@ def catalog bench @doc_map.any_uncached? if recreate_docmap - @doc_map = DocMap.new(unresolved_requires, bench.workspace) # @todo Implement gem preferences + @doc_map = DocMap.new(unresolved_requires, bench.workspace, out: nil) # @todo Implement gem preferences @gemspecs = @doc_map.workspace.gemspecs @unresolved_requires = @doc_map.unresolved_requires end diff --git a/lib/solargraph/doc_map.rb b/lib/solargraph/doc_map.rb index 3d23ef2b3..af2cc7735 100644 --- a/lib/solargraph/doc_map.rb +++ b/lib/solargraph/doc_map.rb @@ -31,11 +31,12 @@ def uncached_gemspecs # @param requires [Array] # @param workspace [Workspace] - def initialize requires, workspace + # @param out [IO, nil] output stream for logging + def initialize requires, workspace, out: $stderr @requires = requires.compact @workspace = workspace @global_environ = Convention.for_global(self) - load_serialized_gem_pins + load_serialized_gem_pins(out: out) pins.concat global_environ.pins end @@ -64,7 +65,7 @@ def cache_doc_map_gems! out end logger.debug { "Caching: #{uncached_gemspecs.map(&:name)}" } PinCache.cache_core unless PinCache.core? - load_serialized_gem_pins + load_serialized_gem_pins(out: out) time = Benchmark.measure do uncached_gemspecs.each do |gemspec| cache(gemspec, out: out) @@ -74,7 +75,7 @@ def cache_doc_map_gems! out if (milliseconds > 500) && uncached_gemspecs.any? && out && uncached_gemspecs.any? out.puts "Built #{uncached_gemspecs.length} gems in #{milliseconds} ms" end - load_serialized_gem_pins + load_serialized_gem_pins(out: out) end # @return [Array] diff --git a/lib/solargraph/workspace.rb b/lib/solargraph/workspace.rb index 90c104077..19aa729ac 100644 --- a/lib/solargraph/workspace.rb +++ b/lib/solargraph/workspace.rb @@ -68,7 +68,7 @@ def resolve_require require def global_environ # empty docmap, since the result needs to work in any possible # context here - @environ ||= Convention.for_global(DocMap.new([], self)) + @environ ||= Convention.for_global(DocMap.new([], self, out: nil)) end # @param gemspec [Gem::Specification, Bundler::LazySpecification] diff --git a/spec/api_map_spec.rb b/spec/api_map_spec.rb index 8dc6db842..4100dede9 100755 --- a/spec/api_map_spec.rb +++ b/spec/api_map_spec.rb @@ -2,7 +2,7 @@ describe Solargraph::ApiMap do before :all do - @api_map = Solargraph::ApiMap.new + @api_map = Solargraph::ApiMap.load_with_cache(Dir.pwd, nil) end it 'returns core methods' do diff --git a/spec/doc_map_spec.rb b/spec/doc_map_spec.rb index c9481d354..751b40b37 100644 --- a/spec/doc_map_spec.rb +++ b/spec/doc_map_spec.rb @@ -4,7 +4,7 @@ describe Solargraph::DocMap do subject(:doc_map) do - Solargraph::DocMap.new(requires, workspace) + Solargraph::DocMap.new(requires, workspace, out: nil) end let(:pre_cache) { true } @@ -14,10 +14,10 @@ Solargraph::Workspace.new(Dir.pwd) end - let(:plain_doc_map) { Solargraph::DocMap.new([], workspace) } + let(:plain_doc_map) { Solargraph::DocMap.new([], workspace, out: nil) } before do - doc_map.cache_doc_map_gems!($stderr) if pre_cache + doc_map.cache_doc_map_gems!(nil) if pre_cache end context 'with a require in solargraph test bundle' do @@ -64,8 +64,8 @@ context 'with require as bundle/require' do it 'imports all gems when bundler/require used' do - doc_map_with_bundler_require = Solargraph::DocMap.new(['bundler/require'], workspace) - doc_map_with_bundler_require.cache_doc_map_gems!($stderr) + doc_map_with_bundler_require = Solargraph::DocMap.new(['bundler/require'], workspace, out: nil) + doc_map_with_bundler_require.cache_doc_map_gems!(nil) expect(doc_map_with_bundler_require.pins.length - plain_doc_map.pins.length).to be_positive end end diff --git a/spec/gem_pins_spec.rb b/spec/gem_pins_spec.rb index ecab06001..093aa4cc1 100644 --- a/spec/gem_pins_spec.rb +++ b/spec/gem_pins_spec.rb @@ -3,8 +3,8 @@ describe Solargraph::GemPins do it 'can merge YARD and RBS' do workspace = Solargraph::Workspace.new(Dir.pwd) - doc_map = Solargraph::DocMap.new(['rbs'], workspace) - doc_map.cache_doc_map_gems!($stderr) + doc_map = Solargraph::DocMap.new(['rbs'], workspace, out: nil) + doc_map.cache_doc_map_gems!(nil) core_root = doc_map.pins.find { |pin| pin.path == 'RBS::EnvironmentLoader#core_root' } expect(core_root.return_type.to_s).to eq('Pathname, nil') diff --git a/spec/type_checker/levels/strict_spec.rb b/spec/type_checker/levels/strict_spec.rb index 70fb0797c..13c20e85f 100644 --- a/spec/type_checker/levels/strict_spec.rb +++ b/spec/type_checker/levels/strict_spec.rb @@ -59,7 +59,7 @@ def bar(a); end require 'kramdown-parser-gfm' Kramdown::Parser::GFM.undefined_call ), 'test.rb') - api_map = Solargraph::ApiMap.load_with_cache('.', $stdout) + api_map = Solargraph::ApiMap.load_with_cache('.', nil) api_map.catalog Solargraph::Bench.new(source_maps: [source_map], external_requires: ['kramdown-parser-gfm']) checker = Solargraph::TypeChecker.new('test.rb', api_map: api_map, level: :strict) expect(checker.problems).to be_empty diff --git a/spec/yard_map/mapper_spec.rb b/spec/yard_map/mapper_spec.rb index d4d1127ad..d60e0fcd5 100644 --- a/spec/yard_map/mapper_spec.rb +++ b/spec/yard_map/mapper_spec.rb @@ -1,13 +1,15 @@ describe Solargraph::YardMap::Mapper do - before do - Solargraph::ApiMap.load_with_cache('.', $stderr) + before :all do # rubocop:disable RSpec/BeforeAfterAll + # this takes some time to load, so we do it once - tests must not + # mutate it without creating their own + @api_map = Solargraph::ApiMap.load_with_cache('.', nil) end - let(:workspace) { Solargraph::Workspace.new(Dir.pwd) } + let(:workspace) { @api_map.workspace } # rubocop:disable RSpec/InstanceVariable def pins_with require - doc_map = Solargraph::DocMap.new([require], workspace) - doc_map.cache_doc_map_gems!($stderr) + doc_map = Solargraph::DocMap.new([require], workspace, out: nil) + doc_map.cache_doc_map_gems!(nil) doc_map.pins end From a4282e63b53a708899f80596ec85e03cbae77856 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 20 Jul 2025 11:42:42 -0400 Subject: [PATCH 262/561] Add spec --- spec/api_map_spec.rb | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/spec/api_map_spec.rb b/spec/api_map_spec.rb index 4100dede9..c56f1be4b 100755 --- a/spec/api_map_spec.rb +++ b/spec/api_map_spec.rb @@ -10,6 +10,33 @@ expect(pins.map(&:path)).to include('String#upcase') end + describe '.load_with_cache' do + context 'without core already cached' do + before do + Solargraph::PinCache.uncache_core(out: nil) + end + + it 'automatically caches core' do + api_map = Solargraph::ApiMap.load_with_cache(Dir.pwd, nil) + pins = api_map.get_methods('String') + expect(pins.map(&:path)).to include('String#upcase') + end + end + + context 'without gem already cached' do + before do + gemspec = @api_map.find_gem('rubocop') + @api_map.workspace.uncache_gem(gemspec) + end + + it 'automatically caches gems' do + api_map = Solargraph::ApiMap.load_with_cache(Dir.pwd, nil) + pins = api_map.get_methods('RuboCop::Cop::Base') + expect(pins.map(&:path)).to include('RuboCop::Cop::Base#active_support_extensions_enabled?') + end + end + end + it 'returns core classes' do pins = @api_map.get_constants('') expect(pins.map(&:path)).to include('String') From 3341939d29b5b33eaf765713d870e811901bdd4c Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 20 Jul 2025 11:53:22 -0400 Subject: [PATCH 263/561] Avoid full load_with_cache in spec --- lib/solargraph/api_map.rb | 2 +- spec/yard_map/mapper_spec.rb | 8 ++------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/lib/solargraph/api_map.rb b/lib/solargraph/api_map.rb index 85de6304e..c62160868 100755 --- a/lib/solargraph/api_map.rb +++ b/lib/solargraph/api_map.rb @@ -228,7 +228,7 @@ class << self # @todo IO::NULL is incorrectly inferred to be a String. # @sg-ignore # - # @param directory [String] + # @param directory [String] workspace directory # @param out [IO] The output stream for messages # @return [ApiMap] def self.load_with_cache directory, out diff --git a/spec/yard_map/mapper_spec.rb b/spec/yard_map/mapper_spec.rb index d60e0fcd5..450b459e4 100644 --- a/spec/yard_map/mapper_spec.rb +++ b/spec/yard_map/mapper_spec.rb @@ -1,14 +1,10 @@ describe Solargraph::YardMap::Mapper do before :all do # rubocop:disable RSpec/BeforeAfterAll - # this takes some time to load, so we do it once - tests must not - # mutate it without creating their own - @api_map = Solargraph::ApiMap.load_with_cache('.', nil) + @api_map = Solargraph::ApiMap.load('.') end - let(:workspace) { @api_map.workspace } # rubocop:disable RSpec/InstanceVariable - def pins_with require - doc_map = Solargraph::DocMap.new([require], workspace, out: nil) + doc_map = Solargraph::DocMap.new([require], @api_map.workspace, out: nil) # rubocop:disable RSpec/InstanceVariable doc_map.cache_doc_map_gems!(nil) doc_map.pins end From 12bad52bf231df1e51097ef53dfc3990389ea00a Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 20 Jul 2025 13:02:58 -0400 Subject: [PATCH 264/561] Add another shell spec --- spec/shell_spec.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/spec/shell_spec.rb b/spec/shell_spec.rb index f0df98121..bd0de9042 100644 --- a/spec/shell_spec.rb +++ b/spec/shell_spec.rb @@ -55,6 +55,14 @@ def bundle_exec(*cmd) end end + describe 'gems' do + it 'caches all without erroring out' do + output = bundle_exec('solargraph', 'gems') + + expect(output).to include('Documentation cached for all') + end + end + describe 'cache' do it 'caches without erroring out' do output = bundle_exec('solargraph', 'cache', 'solargraph') From 2af7f35be7e1197acc6b27b9c333a5205e3b1687 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 20 Jul 2025 13:51:15 -0400 Subject: [PATCH 265/561] Consolidate logging code --- lib/solargraph/doc_map.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/solargraph/doc_map.rb b/lib/solargraph/doc_map.rb index af2cc7735..e7a21e4d9 100644 --- a/lib/solargraph/doc_map.rb +++ b/lib/solargraph/doc_map.rb @@ -58,12 +58,12 @@ def any_uncached? # @param out [IO, nil] output stream for logging # @return [void] def cache_doc_map_gems! out - # if we log at debug level: - if logger.info? - gem_desc = uncached_gemspecs.map { |gemspec| "#{gemspec.name}:#{gemspec.version}" }.join(', ') - logger.info "Caching pins for gems: #{gem_desc}" unless uncached_gemspecs.empty? + unless uncached_gemspecs.empty? + logger.info do + gem_desc = uncached_gemspecs.map { |gemspec| "#{gemspec.name}:#{gemspec.version}" }.join(', ') + "Caching pins for gems: #{gem_desc}" + end end - logger.debug { "Caching: #{uncached_gemspecs.map(&:name)}" } PinCache.cache_core unless PinCache.core? load_serialized_gem_pins(out: out) time = Benchmark.measure do From a2f42cf59dc219c1a9a707a8e211e9716cf27b25 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 20 Jul 2025 13:56:07 -0400 Subject: [PATCH 266/561] Reduce need to covnert gemspecs, save time --- lib/solargraph/workspace.rb | 1 - lib/solargraph/workspace/gemspecs.rb | 93 +++++++++++++++------------- rbs/fills/bundler/0/bundler.rbs | 4 +- 3 files changed, 52 insertions(+), 46 deletions(-) diff --git a/lib/solargraph/workspace.rb b/lib/solargraph/workspace.rb index 19aa729ac..6089fc8fa 100644 --- a/lib/solargraph/workspace.rb +++ b/lib/solargraph/workspace.rb @@ -49,7 +49,6 @@ def config # @param gemspec [Gem::Specification] # @return [Array] def fetch_dependencies gemspec - raise ArgumentError, 'gemspec must be a Gem::Specification' unless gemspec.is_a?(Gem::Specification) gemspecs.fetch_dependencies(gemspec) end diff --git a/lib/solargraph/workspace/gemspecs.rb b/lib/solargraph/workspace/gemspecs.rb index 4bc90fcc1..edb3b83b2 100644 --- a/lib/solargraph/workspace/gemspecs.rb +++ b/lib/solargraph/workspace/gemspecs.rb @@ -89,8 +89,6 @@ def find_gem name, version # @param gemspec [Gem::Specification] # @return [Array] def fetch_dependencies gemspec - raise ArgumentError, 'gemspec must be a Gem::Specification' unless gemspec.is_a?(Gem::Specification) - gemspecs = all_gemspecs_from_bundle # @param runtime_dep [Gem::Dependency] @@ -122,10 +120,41 @@ def all_gemspecs_from_bundle private + # @param specish [Gem::Specification, Bundler::LazySpecification, Bundler::StubSpecification] + # @return [Gem::Specification, nil] + def to_gem_specification specish + # print time including milliseconds + case specish + when Gem::Specification + # yay! + specish + when Bundler::LazySpecification + # materializing didn't work. Let's look in the local + # rubygems without bundler's help + resolve_gem_ignoring_local_bundle specish.name, specish.version + when Bundler::StubSpecification + # turns a Bundler::StubSpecification into a + # Gem::StubSpecification into a Gem::Specification + specish = specish.stub + if specish.respond_to?(:spec) + specish.spec + else + resolve_gem_ignoring_local_bundle specish.name, specish.version + end + else + @@warned_on_gem_type ||= false # rubocop:disable Style/ClassVars + unless @@warned_on_gem_type + logger.warn "Unexpected type while resolving gem: #{specish.class}" + @@warned_on_gem_type = true # rubocop:disable Style/ClassVars + end + nil + end + end + # @param command [String] The expression to evaluate in the external bundle # @sg-ignore Need a JSON type - # @yield [undefined] - def query_external_bundle command, &block + # @yield [undefined, nil] + def query_external_bundle command Solargraph.with_clean_env do cmd = [ 'ruby', '-e', @@ -135,8 +164,7 @@ def query_external_bundle command, &block o, e, s = Open3.capture3(*cmd) if s.success? Solargraph.logger.debug "External bundle: #{o}" - data = o && !o.empty? ? JSON.parse(o.split("\n").last) : {} - block.yield data + o && !o.empty? ? JSON.parse(o.split("\n").last) : nil else Solargraph.logger.warn e raise BundleNotFoundError, "Failed to load gems from bundle at #{directory}" @@ -148,7 +176,7 @@ def in_this_bundle? directory && Bundler.definition&.lockfile&.to_s&.start_with?(directory) # rubocop:disable Style/SafeNavigationChainLength end - # @return [Array] + # @return [Array] def all_gemspecs_from_this_bundle # Find only the gems bundler is now using specish_objects = Bundler.definition.locked_gems.specs @@ -156,34 +184,16 @@ def all_gemspecs_from_this_bundle specish_objects = specish_objects.map(&:materialize_for_installation) end specish_objects.map do |specish| - case specish - when Gem::Specification - # yay! + if specish.respond_to?(:name) && specish.respond_to?(:version) + # good enough for most uses! specish - when Bundler::LazySpecification - # materializing didn't work. Let's look in the local - # rubygems without bundler's help - resolve_gem_ignoring_local_bundle specish.name, specish.version - when Bundler::StubSpecification - # turns a Bundler::StubSpecification into a - # Gem::StubSpecification into a Gem::Specification - specish = specish.stub - if specish.respond_to?(:spec) - specish.spec - else - resolve_gem_ignoring_local_bundle specish.name, specish.version - end else - @@warned_on_gem_type ||= false # rubocop:disable Style/ClassVars - unless @@warned_on_gem_type - logger.warn "Unexpected type while resolving gem: #{specish.class}" - @@warned_on_gem_type = true # rubocop:disable Style/ClassVars - end + to_gem_specification(specish) end - end + end.compact end - # @return [Array] + # @return [Array] def auto_required_gemspecs_from_bundler logger.info 'Fetching gemspecs autorequired from Bundler (bundler/require)' @auto_required_gemspecs_from_bundler ||= @@ -194,7 +204,7 @@ def auto_required_gemspecs_from_bundler end end - # @return [Array] + # @return [Array] def auto_required_gemspecs_from_this_bundle # Adapted from require() in lib/bundler/runtime.rb dep_names = Bundler.definition.dependencies.select do |dep| @@ -207,27 +217,26 @@ def auto_required_gemspecs_from_this_bundle # @sg-ignore # Solargraph::Workspace::Gemspecs#auto_required_gemspecs_from_external_bundle # return type could not be inferred - # @return [Array] + # @return [Array] def auto_required_gemspecs_from_external_bundle @auto_required_gemspecs_from_external_bundle ||= begin logger.info 'Fetching auto-required gemspecs from Bundler (bundler/require)' command = 'Bundler.definition.dependencies' \ - '.select { |dep| dep.groups.include?(:default) && dep.should_include? }.map(&:name)' + '.select { |dep| dep.groups.include?(:default) && dep.should_include? }' \ + '.map { |dep| [dep.name, dep. version] }' # @sg-ignore - # @type [Array] - dep_names = query_external_bundle(command) { |dependency_names| dependency_names } + # @type [Array] + dep_details = query_external_bundle command - all_gemspecs_from_bundle.select { |gemspec| dep_names.include?(gemspec.name) } + dep_details.map { |name, version| find_gem(name, version) }.compact end end # @param gemspec [Gem::Specification] # @return [Array] def only_runtime_dependencies gemspec - raise ArgumentError, 'gemspec must be a Gem::Specification' unless gemspec.is_a?(Gem::Specification) - gemspec.dependencies - gemspec.development_dependencies end @@ -257,11 +266,9 @@ def all_gemspecs_from_external_bundle command = 'Bundler.definition.locked_gems&.specs&.map { |spec| [spec.name, spec.version] }.to_h' - query_external_bundle command do |names_and_versions| - names_and_versions.map do |name, version| - resolve_gem_ignoring_local_bundle(name, version) - end.compact - end + query_external_bundle(command).map do |name, version| + resolve_gem_ignoring_local_bundle(name, version) + end.compact end end diff --git a/rbs/fills/bundler/0/bundler.rbs b/rbs/fills/bundler/0/bundler.rbs index 4af422af1..8b23710d4 100644 --- a/rbs/fills/bundler/0/bundler.rbs +++ b/rbs/fills/bundler/0/bundler.rbs @@ -3076,7 +3076,7 @@ class Bundler::RemoteSpecification def initialize: (untyped name, untyped version, untyped platform, untyped spec_fetcher) -> void - def name: () -> untyped + def name: () -> String def platform: () -> untyped @@ -3106,7 +3106,7 @@ class Bundler::RemoteSpecification def to_s: () -> untyped - def version: () -> untyped + def version: () -> String end class Bundler::Resolver From d3a4d642930fba7159ddbaadced2fc04f2a677db Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 20 Jul 2025 14:08:45 -0400 Subject: [PATCH 267/561] Handle a fake-Gem::Specification issue --- lib/solargraph/workspace/gemspecs.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/solargraph/workspace/gemspecs.rb b/lib/solargraph/workspace/gemspecs.rb index edb3b83b2..be8d28438 100644 --- a/lib/solargraph/workspace/gemspecs.rb +++ b/lib/solargraph/workspace/gemspecs.rb @@ -64,6 +64,8 @@ def resolve_require require # look ourselves just in case this is hanging out somewhere # that find_by_path doesn't index' gemspec = all_gemspecs.find do |spec| + spec = to_gem_specification(spec) unless spec.respond_to?(:files) + spec&.files&.any? { |gemspec_file| file == gemspec_file } end return [gemspec_or_preference(gemspec)] if gemspec @@ -108,7 +110,7 @@ def fetch_dependencies gemspec # Returns all gemspecs directly depended on by this workspace's # bundle (does not include transitive dependencies). # - # @return [Array] + # @return [Array] def all_gemspecs_from_bundle @all_gemspecs_from_bundle ||= if in_this_bundle? From e3150d55aca5a0a81aded5cea3717742f1161a42 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 20 Jul 2025 14:27:21 -0400 Subject: [PATCH 268/561] Handle a fake-Gem::Specification issue --- lib/solargraph/workspace/gemspecs.rb | 63 +++++++++++++++++----------- 1 file changed, 38 insertions(+), 25 deletions(-) diff --git a/lib/solargraph/workspace/gemspecs.rb b/lib/solargraph/workspace/gemspecs.rb index be8d28438..1b0589c4f 100644 --- a/lib/solargraph/workspace/gemspecs.rb +++ b/lib/solargraph/workspace/gemspecs.rb @@ -122,35 +122,45 @@ def all_gemspecs_from_bundle private + # @return [Hash{Gem::Specification, Bundler::LazySpecification, Bundler::StubSpecification => Gem::Specification}] + private_class_method def self.gem_specification_cache + @gem_specification_cache ||= {} + end + # @param specish [Gem::Specification, Bundler::LazySpecification, Bundler::StubSpecification] + # @sg-ignore # @return [Gem::Specification, nil] def to_gem_specification specish # print time including milliseconds - case specish - when Gem::Specification - # yay! - specish - when Bundler::LazySpecification - # materializing didn't work. Let's look in the local - # rubygems without bundler's help - resolve_gem_ignoring_local_bundle specish.name, specish.version - when Bundler::StubSpecification - # turns a Bundler::StubSpecification into a - # Gem::StubSpecification into a Gem::Specification - specish = specish.stub - if specish.respond_to?(:spec) - specish.spec - else - resolve_gem_ignoring_local_bundle specish.name, specish.version - end - else - @@warned_on_gem_type ||= false # rubocop:disable Style/ClassVars - unless @@warned_on_gem_type - logger.warn "Unexpected type while resolving gem: #{specish.class}" - @@warned_on_gem_type = true # rubocop:disable Style/ClassVars - end - nil - end + self.class.gem_specification_cache[specish] ||= case specish + when Gem::Specification + # yay! + specish + when Bundler::LazySpecification + # materializing didn't work. Let's look in the local + # rubygems without bundler's help + resolve_gem_ignoring_local_bundle specish.name, + specish.version + when Bundler::StubSpecification + # turns a Bundler::StubSpecification into a + # Gem::StubSpecification into a Gem::Specification + specish = specish.stub + if specish.respond_to?(:spec) + specish.spec + else + resolve_gem_ignoring_local_bundle specish.name, + specish.version + end + else + @@warned_on_gem_type ||= # rubocop:disable Style/ClassVars + false + unless @@warned_on_gem_type + logger.warn 'Unexpected type while resolving gem: ' \ + "#{specish.class}" + @@warned_on_gem_type = true # rubocop:disable Style/ClassVars + end + nil + end end # @param command [String] The expression to evaluate in the external bundle @@ -239,6 +249,9 @@ def auto_required_gemspecs_from_external_bundle # @param gemspec [Gem::Specification] # @return [Array] def only_runtime_dependencies gemspec + unless gemspec.respond_to?(:dependencies) && gemspec.respond_to?(:development_dependencies) + gemspec = to_gem_specification(gemspec) + end gemspec.dependencies - gemspec.development_dependencies end From f362c433713ab48932c10c71db88640e561d2648 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 20 Jul 2025 14:42:03 -0400 Subject: [PATCH 269/561] Fix caching output --- lib/solargraph/workspace.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/solargraph/workspace.rb b/lib/solargraph/workspace.rb index 6089fc8fa..a4a29f45e 100644 --- a/lib/solargraph/workspace.rb +++ b/lib/solargraph/workspace.rb @@ -191,7 +191,7 @@ def rbs_collection_config_path # @param rebuild [Boolean] whether to rebuild the pins even if they are cached # @return [void] def cache_all_for_workspace! out, rebuild: false - PinCache.cache_core(out: $stdout) unless PinCache.core? + PinCache.cache_core(out: out) unless PinCache.core? # @type [Array] specs = gemspecs.all_gemspecs_from_bundle specs.each do |spec| From 2cd2139af8a6f253ef19a7b833cf108421986d20 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 20 Jul 2025 14:42:20 -0400 Subject: [PATCH 270/561] Drop unneeded &. --- lib/solargraph/api_map.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/solargraph/api_map.rb b/lib/solargraph/api_map.rb index c62160868..1998f67b3 100755 --- a/lib/solargraph/api_map.rb +++ b/lib/solargraph/api_map.rb @@ -185,7 +185,7 @@ def self.load directory # @param out [IO, nil] # @return [void] def cache_all_for_doc_map! out - @doc_map&.cache_doc_map_gems!(out) + @doc_map.cache_doc_map_gems!(out) end # @param out [IO, nil] From 8af693f2d8d9935f6a2994cf0d9b0c4da366712e Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 20 Jul 2025 15:03:49 -0400 Subject: [PATCH 271/561] Fix permissions issue --- lib/solargraph/workspace/gemspecs.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/solargraph/workspace/gemspecs.rb b/lib/solargraph/workspace/gemspecs.rb index 1b0589c4f..25a89acea 100644 --- a/lib/solargraph/workspace/gemspecs.rb +++ b/lib/solargraph/workspace/gemspecs.rb @@ -120,13 +120,13 @@ def all_gemspecs_from_bundle end end - private - # @return [Hash{Gem::Specification, Bundler::LazySpecification, Bundler::StubSpecification => Gem::Specification}] - private_class_method def self.gem_specification_cache + def self.gem_specification_cache @gem_specification_cache ||= {} end + private + # @param specish [Gem::Specification, Bundler::LazySpecification, Bundler::StubSpecification] # @sg-ignore # @return [Gem::Specification, nil] From de28f001e86102d55e5ac43a72665582e466f26d Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 20 Jul 2025 15:18:53 -0400 Subject: [PATCH 272/561] Handle another duck type case for Gem::Sepcification-alikes --- lib/solargraph/workspace/gemspecs.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/solargraph/workspace/gemspecs.rb b/lib/solargraph/workspace/gemspecs.rb index 25a89acea..aa203e43a 100644 --- a/lib/solargraph/workspace/gemspecs.rb +++ b/lib/solargraph/workspace/gemspecs.rb @@ -196,8 +196,8 @@ def all_gemspecs_from_this_bundle specish_objects = specish_objects.map(&:materialize_for_installation) end specish_objects.map do |specish| - if specish.respond_to?(:name) && specish.respond_to?(:version) - # good enough for most uses! + if specish.respond_to?(:name) && specish.respond_to?(:version) && specish.respond_to?(:gem_dir) + # duck type is good enough for outside uses! specish else to_gem_specification(specish) From 1c65656730e3ac9abb892fbac9371161899993c1 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 20 Jul 2025 15:28:16 -0400 Subject: [PATCH 273/561] Fix another issue snaking logging through --- lib/solargraph/rbs_map.rb | 2 +- lib/solargraph/rbs_map/stdlib_map.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/solargraph/rbs_map.rb b/lib/solargraph/rbs_map.rb index f56ad4729..263f9f2dd 100644 --- a/lib/solargraph/rbs_map.rb +++ b/lib/solargraph/rbs_map.rb @@ -117,7 +117,7 @@ def self.from_gemspec gemspec, rbs_collection_path, rbs_collection_config_path # @return [Array] def pins out: $stderr @pins ||= if resolved? - loader.libs.each { |lib| log_caching(lib, out: $stderr) } + loader.libs.each { |lib| log_caching(lib, out: out) } conversions.pins else [] diff --git a/lib/solargraph/rbs_map/stdlib_map.rb b/lib/solargraph/rbs_map/stdlib_map.rb index 4ab83eb5f..89142b429 100644 --- a/lib/solargraph/rbs_map/stdlib_map.rb +++ b/lib/solargraph/rbs_map/stdlib_map.rb @@ -13,7 +13,7 @@ class StdlibMap < RbsMap @stdlib_maps_hash = {} def log_caching gemspec, out: $stderr - out.puts("Caching RBS pins for standard library #{gemspec.name}") + out&.puts("Caching RBS pins for standard library #{gemspec.name}") end # @param library [String] From b9afcf7197159b5bcb30b0bd83cab8bebf7f4138 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 20 Jul 2025 17:21:56 -0400 Subject: [PATCH 274/561] Fix function defaults --- lib/solargraph/complex_type/unique_type.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/solargraph/complex_type/unique_type.rb b/lib/solargraph/complex_type/unique_type.rb index 1023d080e..972bb0dbb 100644 --- a/lib/solargraph/complex_type/unique_type.rb +++ b/lib/solargraph/complex_type/unique_type.rb @@ -200,10 +200,10 @@ def parameter_variance situation, default = :covariant # matched in the expected qualifies as a match def conforms_to_unique_type?(api_map, expected, situation = :method_call, variance: erased_variance(situation), - allow_subtype_skew: allow_subtype_skew, - allow_empty_params: allow_empty_params, - allow_reverse_match: allow_reverse_match, - allow_any_match: allow_any_match) + allow_subtype_skew:, + allow_empty_params:, + allow_reverse_match:, + allow_any_match:) expected = expected.downcast_to_literal_if_possible inferred = downcast_to_literal_if_possible From c2f4d73dc435b7fe591f8c92a5a40dc2866b821a Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 20 Jul 2025 17:34:59 -0400 Subject: [PATCH 275/561] Adapt specs --- lib/solargraph/complex_type/unique_type.rb | 7 ++++ .../conforms_to_spec.rb} | 42 +++++++++++-------- 2 files changed, 32 insertions(+), 17 deletions(-) rename spec/{type_checker/checks_spec.rb => complex_type/conforms_to_spec.rb} (75%) diff --git a/lib/solargraph/complex_type/unique_type.rb b/lib/solargraph/complex_type/unique_type.rb index 972bb0dbb..6bbe9a1bd 100644 --- a/lib/solargraph/complex_type/unique_type.rb +++ b/lib/solargraph/complex_type/unique_type.rb @@ -204,6 +204,13 @@ def conforms_to_unique_type?(api_map, expected, situation = :method_call, allow_empty_params:, allow_reverse_match:, allow_any_match:) + if allow_reverse_match + reversed_match = expected.conforms_to_unique_type? api_map, self, situation, allow_subtype_skew: allow_subtype_skew, + allow_empty_params: allow_empty_params, + allow_reverse_match: false, + allow_any_match: allow_any_match + return true if reversed_match + end expected = expected.downcast_to_literal_if_possible inferred = downcast_to_literal_if_possible diff --git a/spec/type_checker/checks_spec.rb b/spec/complex_type/conforms_to_spec.rb similarity index 75% rename from spec/type_checker/checks_spec.rb rename to spec/complex_type/conforms_to_spec.rb index 41119cefd..847da8563 100644 --- a/spec/type_checker/checks_spec.rb +++ b/spec/complex_type/conforms_to_spec.rb @@ -1,9 +1,9 @@ -describe Solargraph::TypeChecker::Checks do +describe Solargraph::ComplexType do it 'validates simple core types' do api_map = Solargraph::ApiMap.new exp = Solargraph::ComplexType.parse('String') inf = Solargraph::ComplexType.parse('String') - match = Solargraph::TypeChecker::Checks.types_match?(api_map, exp, inf) + match = inf.conforms_to?(api_map, exp, :method_call) expect(match).to be(true) end @@ -11,7 +11,7 @@ api_map = Solargraph::ApiMap.new exp = Solargraph::ComplexType.parse('String') inf = Solargraph::ComplexType.parse('Integer') - match = Solargraph::TypeChecker::Checks.types_match?(api_map, exp, inf) + match = inf.conforms_to?(api_map, exp, :method_call) expect(match).to be(false) end @@ -24,7 +24,7 @@ class Sub < Sup; end api_map.map source sup = Solargraph::ComplexType.parse('Sup') sub = Solargraph::ComplexType.parse('Sub') - match = Solargraph::TypeChecker::Checks.types_match?(api_map, sup, sub) + match = sub.conforms_to?(api_map, sup, :method_call) expect(match).to be(true) end @@ -48,7 +48,7 @@ class Sub < Sup; end api_map = Solargraph::ApiMap.new exp = Solargraph::ComplexType.parse('Array') inf = Solargraph::ComplexType.parse('Array') - match = Solargraph::TypeChecker::Checks.types_match?(api_map, exp, inf) + match = inf.conforms_to?(api_map, exp, :method_call) expect(match).to be(true) end @@ -59,7 +59,7 @@ class Sub < Sup; end api_map.catalog Solargraph::Bench.new(source_maps: [source_map], external_requires: ['set']) exp = Solargraph::ComplexType.parse('Set') inf = Solargraph::ComplexType.parse('Set') - match = Solargraph::TypeChecker::Checks.types_match?(api_map, exp, inf) + match = inf.conforms_to?(api_map, exp, :method_call) expect(match).to be(true) end @@ -67,7 +67,7 @@ class Sub < Sup; end api_map = Solargraph::ApiMap.new exp = Solargraph::ComplexType.parse('Hash{ Symbol => String}') inf = Solargraph::ComplexType.parse('Hash') - match = Solargraph::TypeChecker::Checks.types_match?(api_map, exp, inf) + match = inf.conforms_to?(api_map, exp, :method_call, allow_empty_params: true) expect(match).to be(true) end @@ -75,7 +75,7 @@ class Sub < Sup; end api_map = Solargraph::ApiMap.new exp = Solargraph::ComplexType.parse('String, Integer') inf = Solargraph::ComplexType.parse('String, Integer') - match = Solargraph::TypeChecker::Checks.types_match?(api_map, exp, inf) + match = inf.conforms_to?(api_map, exp, :method_call) expect(match).to be(true) end @@ -83,7 +83,7 @@ class Sub < Sup; end api_map = Solargraph::ApiMap.new exp = Solargraph::ComplexType.parse('String, Integer') inf = Solargraph::ComplexType.parse('Integer, String') - match = Solargraph::TypeChecker::Checks.types_match?(api_map, exp, inf) + match = inf.conforms_to?(api_map, exp, :method_call) expect(match).to be(true) end @@ -91,7 +91,7 @@ class Sub < Sup; end api_map = Solargraph::ApiMap.new exp = Solargraph::ComplexType.parse('String') inf = Solargraph::ComplexType.parse('String, Integer') - match = Solargraph::TypeChecker::Checks.types_match?(api_map, exp, inf) + match = inf.conforms_to?(api_map, exp, :method_call) expect(match).to be(false) end @@ -99,7 +99,7 @@ class Sub < Sup; end api_map = Solargraph::ApiMap.new exp = Solargraph::ComplexType.parse('nil') inf = Solargraph::ComplexType.parse('nil') - match = Solargraph::TypeChecker::Checks.types_match?(api_map, exp, inf) + match = inf.conforms_to?(api_map, exp, :method_call) expect(match).to be(true) end @@ -107,7 +107,7 @@ class Sub < Sup; end api_map = Solargraph::ApiMap.new exp = Solargraph::ComplexType.parse('Class') inf = Solargraph::ComplexType.parse('Class') - match = Solargraph::TypeChecker::Checks.types_match?(api_map, exp, inf) + match = inf.conforms_to?(api_map, exp, :method_call) expect(match).to be(true) end @@ -115,7 +115,15 @@ class Sub < Sup; end api_map = Solargraph::ApiMap.new exp = Solargraph::ComplexType.parse('Class') inf = Solargraph::ComplexType.parse('Class') - match = Solargraph::TypeChecker::Checks.types_match?(api_map, exp, inf) + match = inf.conforms_to?(api_map, exp, :method_call, allow_empty_params: true) + expect(match).to be(true) + end + + it 'validates generic classes with expected Class' do + api_map = Solargraph::ApiMap.new + inf = Solargraph::ComplexType.parse('Class') + exp = Solargraph::ComplexType.parse('Class') + match = inf.conforms_to?(api_map, exp, :method_call) expect(match).to be(true) end @@ -128,9 +136,9 @@ class Sub < Sup; end api_map.map source sup = Solargraph::ComplexType.parse('Sup') sub = Solargraph::ComplexType.parse('Sub') - match = Solargraph::TypeChecker::Checks.either_way?(api_map, sup, sub) + match = sub.conforms_to?(api_map, sup, :method_call, allow_reverse_match: true) expect(match).to be(true) - match = Solargraph::TypeChecker::Checks.either_way?(api_map, sub, sup) + match = sup.conforms_to?(api_map, sub, :method_call, allow_reverse_match: true) expect(match).to be(true) end @@ -138,9 +146,9 @@ class Sub < Sup; end api_map = Solargraph::ApiMap.new sup = Solargraph::ComplexType.parse('String') sub = Solargraph::ComplexType.parse('Array') - match = Solargraph::TypeChecker::Checks.either_way?(api_map, sup, sub) + match = sub.conforms_to?(api_map, sup, :method_call, allow_reverse_match: true) expect(match).to be(false) - match = Solargraph::TypeChecker::Checks.either_way?(api_map, sub, sup) + match = sup.conforms_to?(api_map, sub, :method_call, allow_reverse_match: true) expect(match).to be(false) end end From e0cead948bde8952ebd9241dfd41ff96a0a92887 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 20 Jul 2025 18:41:10 -0400 Subject: [PATCH 276/561] Fix some annotations --- lib/solargraph/api_map/store.rb | 2 +- lib/solargraph/pin/method.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/solargraph/api_map/store.rb b/lib/solargraph/api_map/store.rb index 47f92194c..15fb00827 100644 --- a/lib/solargraph/api_map/store.rb +++ b/lib/solargraph/api_map/store.rb @@ -116,7 +116,7 @@ def get_instance_variables(fqns, scope = :instance) end # @param fqns [String] - # @return [Enumerable] + # @return [Enumerable] def get_class_variables(fqns) namespace_children(fqns).select { |pin| pin.is_a?(Pin::ClassVariable)} end diff --git a/lib/solargraph/pin/method.rb b/lib/solargraph/pin/method.rb index 2f807f444..749868246 100644 --- a/lib/solargraph/pin/method.rb +++ b/lib/solargraph/pin/method.rb @@ -385,7 +385,7 @@ def probe api_map attribute? ? infer_from_iv(api_map) : infer_from_return_nodes(api_map) end - # @return [::Array] + # @return [::Array] def overloads # Ignore overload tags with nil parameters. If it's not an array, the # tag's source is likely malformed. From 5e34afadb99b93ca76b1f05eeefa403f94d040e7 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 21 Jul 2025 07:17:22 -0400 Subject: [PATCH 277/561] Other shims from typechecking solargraph --- rbs/fills/rubygems/0/basic_specification.rbs | 326 ++++ rbs/fills/rubygems/0/errors.rbs | 364 ++++ rbs/fills/rubygems/0/specification.rbs | 1753 ++++++++++++++++++ sig/shims/ast/2.4/.rbs_meta.yaml | 9 + sig/shims/ast/2.4/ast.rbs | 73 + 5 files changed, 2525 insertions(+) create mode 100644 rbs/fills/rubygems/0/basic_specification.rbs create mode 100644 rbs/fills/rubygems/0/errors.rbs create mode 100644 rbs/fills/rubygems/0/specification.rbs create mode 100644 sig/shims/ast/2.4/.rbs_meta.yaml create mode 100644 sig/shims/ast/2.4/ast.rbs diff --git a/rbs/fills/rubygems/0/basic_specification.rbs b/rbs/fills/rubygems/0/basic_specification.rbs new file mode 100644 index 000000000..361027e67 --- /dev/null +++ b/rbs/fills/rubygems/0/basic_specification.rbs @@ -0,0 +1,326 @@ +# +# BasicSpecification is an abstract class which implements some common code used +# by both Specification and StubSpecification. +# +class Gem::BasicSpecification + @ignored: untyped + + @extension_dir: untyped + + @full_gem_path: untyped + + @full_require_paths: untyped + + @paths_map: untyped + + @gem_dir: untyped + + attr_writer base_dir: untyped + + attr_writer extension_dir: untyped + + attr_writer ignored: untyped + + # + # The path this gemspec was loaded from. This attribute is not persisted. + # + attr_accessor loaded_from: untyped + + attr_writer full_gem_path: untyped + + # + # + def initialize: () -> void + + # + # + def self.default_specifications_dir: () -> untyped + + extend Gem::Deprecate + + def gem_build_complete_path: () -> untyped + + # + # True when the gem has been activated + # + def activated?: () -> untyped + + # + # Returns the full path to the base gem directory. + # + # eg: /usr/local/lib/ruby/gems/1.8 + # + def base_dir: () -> untyped + + # + # Return true if this spec can require `file`. + # + def contains_requirable_file?: (untyped file) -> (false | untyped) + + # + # Return true if this spec should be ignored because it's missing extensions. + # + def ignored?: () -> untyped + + # + # + def default_gem?: () -> untyped + + # + # Regular gems take precedence over default gems + # + def default_gem_priority: () -> (1 | -1) + + # + # Gems higher up in `gem_path` take precedence + # + def base_dir_priority: (untyped gem_path) -> untyped + + # + # Returns full path to the directory where gem's extensions are installed. + # + def extension_dir: () -> untyped + + # + # Returns path to the extensions directory. + # + def extensions_dir: () -> untyped + + private + + def find_full_gem_path: () -> untyped + + public + + # + # The full path to the gem (install path + full name). + # + # TODO: This is duplicated with #gem_dir. Eventually either of them should be + # deprecated. + # + def full_gem_path: () -> untyped + + # + # Returns the full name (name-version) of this Gem. Platform information is + # included (name-version-platform) if it is specified and not the default Ruby + # platform. + # + def full_name: () -> ::String + + # + # Returns the full name of this Gem (see `Gem::BasicSpecification#full_name`). + # Information about where the gem is installed is also included if not installed + # in the default GEM_HOME. + # + def full_name_with_location: () -> (::String | untyped) + + # + # Full paths in the gem to add to `$LOAD_PATH` when this gem is activated. + # + def full_require_paths: () -> untyped + + # + # The path to the data directory for this gem. + # + def datadir: () -> untyped + + # + # Full path of the target library file. If the file is not in this gem, return + # nil. + # + def to_fullpath: (untyped path) -> (untyped | nil) + + # + # Returns the full path to this spec's gem directory. eg: + # /usr/local/lib/ruby/1.8/gems/mygem-1.0 + # + # TODO: This is duplicated with #full_gem_path. Eventually either of them should + # be deprecated. + # + def gem_dir: () -> untyped + + # + # Returns the full path to the gems directory containing this spec's gem + # directory. eg: /usr/local/lib/ruby/1.8/gems + # + def gems_dir: () -> untyped + + def internal_init: () -> untyped + + # + # Name of the gem + # + def name: () -> untyped + + # + # Platform of the gem + # + def platform: () -> untyped + + def raw_require_paths: () -> untyped + + # + # Paths in the gem to add to `$LOAD_PATH` when this gem is activated. + # + # See also #require_paths= + # + # If you have an extension you do not need to add `"ext"` to the require path, + # the extension build process will copy the extension files into "lib" for you. + # + # The default value is `"lib"` + # + # Usage: + # + # # If all library files are in the root directory... + # spec.require_path = '.' + # + def require_paths: () -> untyped + + # + # Returns the paths to the source files for use with analysis and documentation + # tools. These paths are relative to full_gem_path. + # + def source_paths: () -> untyped + + # + # Return all files in this gem that match for `glob`. + # + def matches_for_glob: (untyped glob) -> untyped + + # + # Returns the list of plugins in this spec. + # + def plugins: () -> untyped + + # + # Returns a string usable in Dir.glob to match all requirable paths for this + # spec. + # + def lib_dirs_glob: () -> ::String + + # + # Return a Gem::Specification from this gem + # + def to_spec: () -> untyped + + # + # Version of the gem + # + def version: () -> untyped + + # + # Whether this specification is stubbed - i.e. we have information about the gem + # from a stub line, without having to evaluate the entire gemspec file. + # + def stubbed?: () -> untyped + + # + # + def this: () -> self + + private + + # + # + def have_extensions?: () -> untyped + + # + # + def have_file?: (untyped file, untyped suffixes) -> (true | untyped) +end diff --git a/rbs/fills/rubygems/0/errors.rbs b/rbs/fills/rubygems/0/errors.rbs new file mode 100644 index 000000000..34b97fcf1 --- /dev/null +++ b/rbs/fills/rubygems/0/errors.rbs @@ -0,0 +1,364 @@ +# +# RubyGems is the Ruby standard for publishing and managing third party +# libraries. +# +# For user documentation, see: +# +# * `gem help` and `gem help [command]` +# * [RubyGems User Guide](https://guides.rubygems.org/) +# * [Frequently Asked Questions](https://guides.rubygems.org/faqs) +# +# For gem developer documentation see: +# +# * [Creating Gems](https://guides.rubygems.org/make-your-own-gem) +# * Gem::Specification +# * Gem::Version for version dependency notes +# +# Further RubyGems documentation can be found at: +# +# * [RubyGems Guides](https://guides.rubygems.org) +# * [RubyGems API](https://www.rubydoc.info/github/rubygems/rubygems) (also +# available from `gem server`) +# +# ## RubyGems Plugins +# +# RubyGems will load plugins in the latest version of each installed gem or +# $LOAD_PATH. Plugins must be named 'rubygems_plugin' (.rb, .so, etc) and +# placed at the root of your gem's #require_path. Plugins are installed at a +# special location and loaded on boot. +# +# For an example plugin, see the [Graph gem](https://github.com/seattlerb/graph) +# which adds a `gem graph` command. +# +# ## RubyGems Defaults, Packaging +# +# RubyGems defaults are stored in lib/rubygems/defaults.rb. If you're packaging +# RubyGems or implementing Ruby you can change RubyGems' defaults. +# +# For RubyGems packagers, provide lib/rubygems/defaults/operating_system.rb and +# override any defaults from lib/rubygems/defaults.rb. +# +# For Ruby implementers, provide lib/rubygems/defaults/#{RUBY_ENGINE}.rb and +# override any defaults from lib/rubygems/defaults.rb. +# +# If you need RubyGems to perform extra work on install or uninstall, your +# defaults override file can set pre/post install and uninstall hooks. See +# Gem::pre_install, Gem::pre_uninstall, Gem::post_install, Gem::post_uninstall. +# +# ## Bugs +# +# You can submit bugs to the [RubyGems bug +# tracker](https://github.com/rubygems/rubygems/issues) on GitHub +# +# ## Credits +# +# RubyGems is currently maintained by Eric Hodel. +# +# RubyGems was originally developed at RubyConf 2003 by: +# +# * Rich Kilmer -- rich(at)infoether.com +# * Chad Fowler -- chad(at)chadfowler.com +# * David Black -- dblack(at)wobblini.net +# * Paul Brannan -- paul(at)atdesk.com +# * Jim Weirich -- jim(at)weirichhouse.org +# +# Contributors: +# +# * Gavin Sinclair -- gsinclair(at)soyabean.com.au +# * George Marrows -- george.marrows(at)ntlworld.com +# * Dick Davies -- rasputnik(at)hellooperator.net +# * Mauricio Fernandez -- batsman.geo(at)yahoo.com +# * Simon Strandgaard -- neoneye(at)adslhome.dk +# * Dave Glasser -- glasser(at)mit.edu +# * Paul Duncan -- pabs(at)pablotron.org +# * Ville Aine -- vaine(at)cs.helsinki.fi +# * Eric Hodel -- drbrain(at)segment7.net +# * Daniel Berger -- djberg96(at)gmail.com +# * Phil Hagelberg -- technomancy(at)gmail.com +# * Ryan Davis -- ryand-ruby(at)zenspider.com +# * Evan Phoenix -- evan(at)fallingsnow.net +# * Steve Klabnik -- steve(at)steveklabnik.com +# +# (If your name is missing, PLEASE let us know!) +# +# ## License +# +# See +# [LICENSE.txt](https://github.com/rubygems/rubygems/blob/master/LICENSE.txt) +# for permissions. +# +# Thanks! +# +# -The RubyGems Team +# +# +# Provides 3 methods for declaring when something is going away. +# +# +deprecate(name, repl, year, month)+: +# Indicate something may be removed on/after a certain date. +# +# +rubygems_deprecate(name, replacement=:none)+: +# Indicate something will be removed in the next major RubyGems version, +# and (optionally) a replacement for it. +# +# `rubygems_deprecate_command`: +# Indicate a RubyGems command (in +lib/rubygems/commands/*.rb+) will be +# removed in the next RubyGems version. +# +# Also provides `skip_during` for temporarily turning off deprecation warnings. +# This is intended to be used in the test suite, so deprecation warnings don't +# cause test failures if you need to make sure stderr is otherwise empty. +# +# Example usage of `deprecate` and `rubygems_deprecate`: +# +# class Legacy +# def self.some_class_method +# # ... +# end +# +# def some_instance_method +# # ... +# end +# +# def some_old_method +# # ... +# end +# +# extend Gem::Deprecate +# deprecate :some_instance_method, "X.z", 2011, 4 +# rubygems_deprecate :some_old_method, "Modern#some_new_method" +# +# class << self +# extend Gem::Deprecate +# deprecate :some_class_method, :none, 2011, 4 +# end +# end +# +# Example usage of `rubygems_deprecate_command`: +# +# class Gem::Commands::QueryCommand < Gem::Command +# extend Gem::Deprecate +# rubygems_deprecate_command +# +# # ... +# end +# +# Example usage of `skip_during`: +# +# class TestSomething < Gem::Testcase +# def test_some_thing_with_deprecations +# Gem::Deprecate.skip_during do +# actual_stdout, actual_stderr = capture_output do +# Gem.something_deprecated +# end +# assert_empty actual_stdout +# assert_equal(expected, actual_stderr) +# end +# end +# end +# +module Gem + # + # Raised when RubyGems is unable to load or activate a gem. Contains the name + # and version requirements of the gem that either conflicts with already + # activated gems or that RubyGems is otherwise unable to activate. + # + class LoadError < ::LoadError + # + # Name of gem + # + attr_accessor name: untyped + + # + # Version requirement of gem + # + attr_accessor requirement: untyped + end + + # + # Raised when trying to activate a gem, and that gem does not exist on the + # system. Instead of rescuing from this class, make sure to rescue from the + # superclass Gem::LoadError to catch all types of load errors. + # + class MissingSpecError < Gem::LoadError + @name: untyped + + @requirement: untyped + + @extra_message: untyped + + # + # + def initialize: (untyped name, untyped requirement, ?untyped? extra_message) -> void + + def message: () -> untyped + + private + + # + # + def build_message: () -> ::String + end + + # + # Raised when trying to activate a gem, and the gem exists on the system, but + # not the requested version. Instead of rescuing from this class, make sure to + # rescue from the superclass Gem::LoadError to catch all types of load errors. + # + class MissingSpecVersionError < MissingSpecError + @specs: untyped + + attr_reader specs: untyped + + # + # + def initialize: (untyped name, untyped requirement, untyped specs) -> void + + private + + # + # + def build_message: () -> ::String + end + + # + # Raised when there are conflicting gem specs loaded + # + class ConflictError < LoadError + @target: untyped + + @conflicts: untyped + + @name: untyped + + # + # A Hash mapping conflicting specifications to the dependencies that caused the + # conflict + # + attr_reader conflicts: untyped + + # + # The specification that had the conflict + # + attr_reader target: untyped + + # + # + def initialize: (untyped target, untyped conflicts) -> void + end + + class ErrorReason + end + + # + # Generated when trying to lookup a gem to indicate that the gem was found, but + # that it isn't usable on the current platform. + # + # fetch and install read these and report them to the user to aid in figuring + # out why a gem couldn't be installed. + # + class PlatformMismatch < ErrorReason + @name: untyped + + @version: untyped + + @platforms: untyped + + # + # the name of the gem + # + attr_reader name: untyped + + # + # the version + # + attr_reader version: untyped + + # + # The platforms that are mismatched + # + attr_reader platforms: untyped + + # + # + def initialize: (untyped name, untyped version) -> void + + # + # append a platform to the list of mismatched platforms. + # + # Platforms are added via this instead of injected via the constructor so that + # we can loop over a list of mismatches and just add them rather than perform + # some kind of calculation mismatch summary before creation. + # + def add_platform: (untyped platform) -> untyped + + # + # A wordy description of the error. + # + def wordy: () -> untyped + end + + # + # An error that indicates we weren't able to fetch some data from a source + # + class SourceFetchProblem < ErrorReason + @source: untyped + + @error: untyped + + # + # Creates a new SourceFetchProblem for the given `source` and `error`. + # + def initialize: (untyped source, untyped error) -> void + + # + # The source that had the fetch problem. + # + attr_reader source: untyped + + # + # The fetch error which is an Exception subclass. + # + attr_reader error: untyped + + # + # An English description of the error. + # + def wordy: () -> ::String + + # + # The fetch error which is an Exception subclass. + # + alias exception error + end +end diff --git a/rbs/fills/rubygems/0/specification.rbs b/rbs/fills/rubygems/0/specification.rbs new file mode 100644 index 000000000..ff51ef0b1 --- /dev/null +++ b/rbs/fills/rubygems/0/specification.rbs @@ -0,0 +1,1753 @@ +# +# The Specification class contains the information for a gem. Typically defined +# in a .gemspec file or a Rakefile, and looks like this: +# +# Gem::Specification.new do |s| +# s.name = 'example' +# s.version = '0.1.0' +# s.licenses = ['MIT'] +# s.summary = "This is an example!" +# s.description = "Much longer explanation of the example!" +# s.authors = ["Ruby Coder"] +# s.email = 'rubycoder@example.com' +# s.files = ["lib/example.rb"] +# s.homepage = 'https://rubygems.org/gems/example' +# s.metadata = { "source_code_uri" => "https://github.com/example/example" } +# end +# +# Starting in RubyGems 2.0, a Specification can hold arbitrary metadata. See +# #metadata for restrictions on the format and size of metadata items you may +# add to a specification. +# +class Gem::Specification < Gem::BasicSpecification + @@required_attributes: untyped + + @@default_value: untyped + + @@attributes: untyped + + @@array_attributes: untyped + + @@nil_attributes: untyped + + @@non_nil_attributes: untyped + + @@dirs: untyped + + self.@load_cache: untyped + + self.@load_cache_mutex: untyped + + self.@specification_record: untyped + + self.@unresolved_deps: untyped + + @removed_method_calls: untyped + + # DO NOT CHANGE TO ||= ! This is not a normal accessor. (yes, it sucks) + # DOC: Why isn't it normal? Why does it suck? How can we fix this? + @files: untyped + + @authors: untyped + + @licenses: untyped + + @original_platform: untyped + + @new_platform: untyped + + @platform: untyped + + @require_paths: untyped + + @executables: untyped + + @extensions: untyped + + @extra_rdoc_files: untyped + + @installed_by_version: untyped + + @rdoc_options: untyped + + @required_ruby_version: untyped + + @required_rubygems_version: untyped + + @requirements: untyped + + @test_files: untyped + + @extensions_dir: untyped + + @activated: untyped + + @loaded: untyped + + @bin_dir: untyped + + @cache_dir: untyped + + @cache_file: untyped + + @date: untyped + + @dependencies: untyped + + @description: untyped + + @doc_dir: untyped + + @full_name: untyped + + @gems_dir: untyped + + @has_rdoc: untyped + + @base_dir: untyped + + @loaded_from: untyped + + @ri_dir: untyped + + @spec_dir: untyped + + @spec_file: untyped + + @summary: untyped + + @test_suite_file: untyped + + @version: untyped + + extend Gem::Deprecate + + # + # The version number of a specification that does not specify one (i.e. RubyGems + # 0.7 or earlier). + # + NONEXISTENT_SPECIFICATION_VERSION: -1 + + CURRENT_SPECIFICATION_VERSION: 4 + + SPECIFICATION_VERSION_HISTORY: { -1 => ::Array["(RubyGems versions up to and including 0.7 did not have versioned specifications)"], 1 => ::Array["Deprecated \"test_suite_file\" in favor of the new, but equivalent, \"test_files\"" | "\"test_file=x\" is a shortcut for \"test_files=[x]\""], 2 => ::Array["Added \"required_rubygems_version\"" | "Now forward-compatible with future versions"], 3 => ::Array["Added Fixnum validation to the specification_version"], 4 => ::Array["Added sandboxed freeform metadata to the specification version."] } + + MARSHAL_FIELDS: { -1 => 16, 1 => 16, 2 => 16, 3 => 17, 4 => 18 } + + TODAY: untyped + + VALID_NAME_PATTERN: ::Regexp + + # rubocop:disable Style/MutableConstant + INITIALIZE_CODE_FOR_DEFAULTS: ::Hash[untyped, untyped] + + # Sentinel object to represent "not found" stubs + NOT_FOUND: untyped + + # Tracking removed method calls to warn users during build time. + REMOVED_METHODS: ::Array[:rubyforge_project= | :mark_version] + + # + # + def removed_method_calls: () -> untyped + + # + # This gem's name. + # + # Usage: + # + # spec.name = 'rake' + # + attr_accessor name: String + + # + # This gem's version. + # + # The version string can contain numbers and periods, such as `1.0.0`. A gem is + # a 'prerelease' gem if the version has a letter in it, such as `1.0.0.pre`. + # + # Usage: + # + # spec.version = '0.4.1' + # + attr_reader version: String + + # + # A short summary of this gem's description. Displayed in `gem list -d`. + # + # The #description should be more detailed than the summary. + # + # Usage: + # + # spec.summary = "This is a small summary of my gem" + # + attr_reader summary: untyped + + # + # Files included in this gem. You cannot append to this accessor, you must + # assign to it. + # + # Only add files you can require to this list, not directories, etc. + # + # Directories are automatically stripped from this list when building a gem, + # other non-files cause an error. + # + # Usage: + # + # require 'rake' + # spec.files = FileList['lib/**/*.rb', + # 'bin/*', + # '[A-Z]*'].to_a + # + # # or without Rake... + # spec.files = Dir['lib/**/*.rb'] + Dir['bin/*'] + # spec.files += Dir['[A-Z]*'] + # spec.files.reject! { |fn| fn.include? "CVS" } + # + def files: () -> Enumerable[String] + + # + # A list of authors for this gem. + # + # Alternatively, a single author can be specified by assigning a string to + # `spec.author` + # + # Usage: + # + # spec.authors = ['John Jones', 'Mary Smith'] + # + def authors=: (untyped value) -> untyped + + # + # The version of Ruby required by this gem + # + # Usage: + # + # spec.required_ruby_version = '>= 2.7.0' + # + attr_reader required_ruby_version: untyped + + # + # A long description of this gem + # + # The description should be more detailed than the summary but not excessively + # long. A few paragraphs is a recommended length with no examples or + # formatting. + # + # Usage: + # + # spec.description = <<-EOF + # Rake is a Make-like program implemented in Ruby. Tasks and + # dependencies are specified in standard Ruby syntax. + # EOF + # + attr_reader description: untyped + + # + # A contact email address (or addresses) for this gem + # + # Usage: + # + # spec.email = 'john.jones@example.com' + # spec.email = ['jack@example.com', 'jill@example.com'] + # + attr_accessor email: untyped + + # + # The URL of this gem's home page + # + # Usage: + # + # spec.homepage = 'https://github.com/ruby/rake' + # + attr_accessor homepage: untyped + + # + # The license for this gem. + # + # The license must be no more than 64 characters. + # + # This should just be the name of your license. The full text of the license + # should be inside of the gem (at the top level) when you build it. + # + # The simplest way is to specify the standard SPDX ID https://spdx.org/licenses/ + # for the license. Ideally, you should pick one that is OSI (Open Source + # Initiative) http://opensource.org/licenses/alphabetical approved. + # + # The most commonly used OSI-approved licenses are MIT and Apache-2.0. GitHub + # also provides a license picker at http://choosealicense.com/. + # + # You can also use a custom license file along with your gemspec and specify a + # LicenseRef-, where idstring is the name of the file containing the + # license text. + # + # You should specify a license for your gem so that people know how they are + # permitted to use it and any restrictions you're placing on it. Not specifying + # a license means all rights are reserved; others have no right to use the code + # for any purpose. + # + # You can set multiple licenses with #licenses= + # + # Usage: + # spec.license = 'MIT' + # + def license=: (untyped o) -> untyped + + # + # The license(s) for the library. + # + # Each license must be a short name, no more than 64 characters. + # + # This should just be the name of your license. The full text of the license + # should be inside of the gem when you build it. + # + # See #license= for more discussion + # + # Usage: + # spec.licenses = ['MIT', 'GPL-2.0'] + # + def licenses=: (untyped licenses) -> untyped + + # + # The metadata holds extra data for this gem that may be useful to other + # consumers and is settable by gem authors. + # + # Metadata items have the following restrictions: + # + # * The metadata must be a Hash object + # * All keys and values must be Strings + # * Keys can be a maximum of 128 bytes and values can be a maximum of 1024 + # bytes + # * All strings must be UTF-8, no binary data is allowed + # + # You can use metadata to specify links to your gem's homepage, codebase, + # documentation, wiki, mailing list, issue tracker and changelog. + # + # s.metadata = { + # "bug_tracker_uri" => "https://example.com/user/bestgemever/issues", + # "changelog_uri" => "https://example.com/user/bestgemever/CHANGELOG.md", + # "documentation_uri" => "https://www.example.info/gems/bestgemever/0.0.1", + # "homepage_uri" => "https://bestgemever.example.io", + # "mailing_list_uri" => "https://groups.example.com/bestgemever", + # "source_code_uri" => "https://example.com/user/bestgemever", + # "wiki_uri" => "https://example.com/user/bestgemever/wiki" + # "funding_uri" => "https://example.com/donate" + # } + # + # These links will be used on your gem's page on rubygems.org and must pass + # validation against following regex. + # + # %r{\Ahttps?:\/\/([^\s:@]+:[^\s:@]*@)?[A-Za-z\d\-]+(\.[A-Za-z\d\-]+)+\.?(:\d{1,5})?([\/?]\S*)?\z} + # + attr_accessor metadata: untyped + + # + # Singular (alternative) writer for #authors + # + # Usage: + # + # spec.author = 'John Jones' + # + def author=: (untyped o) -> untyped + + # + # The path in the gem for executable scripts. Usually 'bin' + # + # Usage: + # + # spec.bindir = 'bin' + # + attr_accessor bindir: untyped + + # + # The certificate chain used to sign this gem. See Gem::Security for details. + # + attr_accessor cert_chain: untyped + + # + # A message that gets displayed after the gem is installed. + # + # Usage: + # + # spec.post_install_message = "Thanks for installing!" + # + attr_accessor post_install_message: untyped + + # + # The platform this gem runs on. + # + # This is usually Gem::Platform::RUBY or Gem::Platform::CURRENT. + # + # Most gems contain pure Ruby code; they should simply leave the default value + # in place. Some gems contain C (or other) code to be compiled into a Ruby + # "extension". The gem should leave the default value in place unless the code + # will only compile on a certain type of system. Some gems consist of + # pre-compiled code ("binary gems"). It's especially important that they set + # the platform attribute appropriately. A shortcut is to set the platform to + # Gem::Platform::CURRENT, which will cause the gem builder to set the platform + # to the appropriate value for the system on which the build is being performed. + # + # If this attribute is set to a non-default value, it will be included in the + # filename of the gem when it is built such as: nokogiri-1.6.0-x86-mingw32.gem + # + # Usage: + # + # spec.platform = Gem::Platform.local + # + def platform=: (untyped platform) -> untyped + + # + # Paths in the gem to add to `$LOAD_PATH` when this gem is activated. If you + # have an extension you do not need to add `"ext"` to the require path, the + # extension build process will copy the extension files into "lib" for you. + # + # The default value is `"lib"` + # + # Usage: + # + # # If all library files are in the root directory... + # spec.require_paths = ['.'] + # + def require_paths=: (untyped val) -> untyped + + # + # The RubyGems version required by this gem + # + attr_reader required_rubygems_version: untyped + + # + # The key used to sign this gem. See Gem::Security for details. + # + attr_accessor signing_key: untyped + + # + # Adds a development dependency named `gem` with `requirements` to this gem. + # + # Usage: + # + # spec.add_development_dependency 'example', '~> 1.1', '>= 1.1.4' + # + # Development dependencies aren't installed by default and aren't activated when + # a gem is required. + # + def add_development_dependency: (untyped gem, *untyped requirements) -> untyped + + # + # + def add_dependency: (untyped gem, *untyped requirements) -> untyped + + # + # Executables included in the gem. + # + # For example, the rake gem has rake as an executable. You don’t specify the + # full path (as in bin/rake); all application-style files are expected to be + # found in bindir. These files must be executable Ruby files. Files that use + # bash or other interpreters will not work. + # + # Executables included may only be ruby scripts, not scripts for other languages + # or compiled binaries. + # + # Usage: + # + # spec.executables << 'rake' + # + def executables: () -> untyped + + # + # Extensions to build when installing the gem, specifically the paths to + # extconf.rb-style files used to compile extensions. + # + # These files will be run when the gem is installed, causing the C (or whatever) + # code to be compiled on the user’s machine. + # + # Usage: + # + # spec.extensions << 'ext/rmagic/extconf.rb' + # + # See Gem::Ext::Builder for information about writing extensions for gems. + # + def extensions: () -> untyped + + # + # Extra files to add to RDoc such as README or doc/examples.txt + # + # When the user elects to generate the RDoc documentation for a gem (typically + # at install time), all the library files are sent to RDoc for processing. This + # option allows you to have some non-code files included for a more complete set + # of documentation. + # + # Usage: + # + # spec.extra_rdoc_files = ['README', 'doc/user-guide.txt'] + # + def extra_rdoc_files: () -> untyped + + def installed_by_version: () -> untyped + + def installed_by_version=: (untyped version) -> untyped + + # + # Specifies the rdoc options to be used when generating API documentation. + # + # Usage: + # + # spec.rdoc_options << '--title' << 'Rake -- Ruby Make' << + # '--main' << 'README' << + # '--line-numbers' + # + def rdoc_options: () -> untyped + + LATEST_RUBY_WITHOUT_PATCH_VERSIONS: untyped + + # + # The version of Ruby required by this gem. The ruby version can be specified + # to the patch-level: + # + # $ ruby -v -e 'p Gem.ruby_version' + # ruby 2.0.0p247 (2013-06-27 revision 41674) [x86_64-darwin12.4.0] + # # + # + # Prereleases can also be specified. + # + # Usage: + # + # # This gem will work with 1.8.6 or greater... + # spec.required_ruby_version = '>= 1.8.6' + # + # # Only with final releases of major version 2 where minor version is at least 3 + # spec.required_ruby_version = '~> 2.3' + # + # # Only prereleases or final releases after 2.6.0.preview2 + # spec.required_ruby_version = '> 2.6.0.preview2' + # + # # This gem will work with 2.3.0 or greater, including major version 3, but lesser than 4.0.0 + # spec.required_ruby_version = '>= 2.3', '< 4' + # + def required_ruby_version=: (untyped req) -> untyped + + # + # The RubyGems version required by this gem + # + def required_rubygems_version=: (untyped req) -> untyped + + # + # Lists the external (to RubyGems) requirements that must be met for this gem to + # work. It's simply information for the user. + # + # Usage: + # + # spec.requirements << 'libmagick, v6.0' + # spec.requirements << 'A good graphics card' + # + def requirements: () -> untyped + + def test_files=: (untyped files) -> untyped + + # + # The version of RubyGems used to create this gem. + # + # Do not set this, it is set automatically when the gem is packaged. + # + attr_accessor rubygems_version: untyped + + def extensions_dir: () -> untyped + + # + # True when this gemspec has been activated. This attribute is not persisted. + # + attr_accessor activated: untyped + + # + # True when this gemspec has been activated. This attribute is not persisted. + # + alias activated? activated + + attr_accessor autorequire: untyped + + attr_writer default_executable: untyped + + attr_writer original_platform: untyped + + # + # The Gem::Specification version of this gemspec. + # + # Do not set this, it is set automatically when the gem is packaged. + # + attr_accessor specification_version: untyped + + def self._all: () -> untyped + + def self.clear_load_cache: () -> untyped + + def self.gem_path: () -> untyped + + def self.each_gemspec: (untyped dirs) { (untyped) -> untyped } -> untyped + + # + # + def self.gemspec_stubs_in: (untyped dir, untyped pattern) { (untyped) -> untyped } -> untyped + + def self.each_spec: (untyped dirs) { (untyped) -> untyped } -> untyped + + # + # Returns a Gem::StubSpecification for every installed gem + # + def self.stubs: () -> untyped + + # + # Returns a Gem::StubSpecification for default gems + # + def self.default_stubs: (?::String pattern) -> untyped + + # + # Returns a Gem::StubSpecification for installed gem named `name` only returns + # stubs that match Gem.platforms + # + def self.stubs_for: (untyped name) -> untyped + + # + # Finds stub specifications matching a pattern from the standard locations, + # optionally filtering out specs not matching the current platform + # + def self.stubs_for_pattern: (untyped pattern, ?bool match_platform) -> untyped + + def self._resort!: (untyped specs) -> untyped + + # + # Loads the default specifications. It should be called only once. + # + def self.load_defaults: () -> untyped + + # + # Adds `spec` to the known specifications, keeping the collection properly + # sorted. + # + def self.add_spec: (untyped spec) -> untyped + + # + # Removes `spec` from the known specs. + # + def self.remove_spec: (untyped spec) -> untyped + + # + # Returns all specifications. This method is discouraged from use. You probably + # want to use one of the Enumerable methods instead. + # + def self.all: () -> untyped + + # + # Sets the known specs to `specs`. Not guaranteed to work for you in the future. + # Use at your own risk. Caveat emptor. Doomy doom doom. Etc etc. + # + def self.all=: (untyped specs) -> untyped + + # + # Return full names of all specs in sorted order. + # + def self.all_names: () -> untyped + + # + # Return the list of all array-oriented instance variables. + # + def self.array_attributes: () -> untyped + + # + # Return the list of all instance variables. + # + def self.attribute_names: () -> untyped + + # + # Return the directories that Specification uses to find specs. + # + def self.dirs: () -> untyped + + # + # Set the directories that Specification uses to find specs. Setting this resets + # the list of known specs. + # + def self.dirs=: (untyped dirs) -> untyped + + extend Enumerable[Gem::Specification] + + # + # Enumerate every known spec. See ::dirs= and ::add_spec to set the list of + # specs. + # + def self.each: () { (Gem::Specification) -> untyped } -> untyped + + # + # Returns every spec that matches `name` and optional `requirements`. + # + def self.find_all_by_name: (untyped name, *untyped requirements) -> untyped + + # + # Returns every spec that has the given `full_name` + # + def self.find_all_by_full_name: (untyped full_name) -> untyped + + # + # Find the best specification matching a `name` and `requirements`. Raises if + # the dependency doesn't resolve to a valid specification. + # + def self.find_by_name: (String name, *untyped requirements) -> instance + + # + # Find the best specification matching a +full_name+. + def self.find_by_full_name: (untyped full_name) -> instance + + # + # Return the best specification that contains the file matching `path`. + # + def self.find_by_path: (String path) -> instance + + # + # Return the best specification that contains the file matching `path` amongst + # the specs that are not activated. + # + def self.find_inactive_by_path: (untyped path) -> untyped + + # + # + def self.find_active_stub_by_path: (untyped path) -> untyped + + # + # Return currently unresolved specs that contain the file matching `path`. + # + def self.find_in_unresolved: (untyped path) -> untyped + + # + # Search through all unresolved deps and sub-dependencies and return specs that + # contain the file matching `path`. + # + def self.find_in_unresolved_tree: (untyped path) -> (untyped | ::Array[untyped]) + + # + # + def self.unresolved_specs: () -> untyped + + # + # Special loader for YAML files. When a Specification object is loaded from a + # YAML file, it bypasses the normal Ruby object initialization routine + # (#initialize). This method makes up for that and deals with gems of different + # ages. + # + # `input` can be anything that YAML.load() accepts: String or IO. + # + def self.from_yaml: (untyped input) -> untyped + + # + # Return the latest specs, optionally including prerelease specs if `prerelease` + # is true. + # + def self.latest_specs: (?bool prerelease) -> untyped + + # + # Return the latest installed spec for gem `name`. + # + def self.latest_spec_for: (untyped name) -> untyped + + def self._latest_specs: (untyped specs, ?bool prerelease) -> untyped + + # + # Loads Ruby format gemspec from `file`. + # + def self.load: (untyped file) -> (nil | untyped) + + # + # Specification attributes that must be non-nil + # + def self.non_nil_attributes: () -> untyped + + # + # Make sure the YAML specification is properly formatted with dashes + # + def self.normalize_yaml_input: (untyped input) -> untyped + + # + # Return a list of all outdated local gem names. This method is HEAVY as it + # must go fetch specifications from the server. + # + # Use outdated_and_latest_version if you wish to retrieve the latest remote + # version as well. + # + def self.outdated: () -> untyped + + # + # Enumerates the outdated local gems yielding the local specification and the + # latest remote version. + # + # This method may take some time to return as it must check each local gem + # against the server's index. + # + def self.outdated_and_latest_version: () ?{ (untyped) -> untyped } -> (untyped | nil) + + # + # Is `name` a required attribute? + # + def self.required_attribute?: (untyped name) -> untyped + + # + # Required specification attributes + # + def self.required_attributes: () -> untyped + + # + # Reset the list of known specs, running pre and post reset hooks registered in + # Gem. + # + def self.reset: () -> untyped + + def self.specification_record: () -> untyped + + # + # DOC: This method needs documented or nodoc'd + # + def self.unresolved_deps: () -> untyped + + # + # Load custom marshal format, re-initializing defaults as needed + # + def self._load: (untyped str) -> untyped + + def <=>: (untyped other) -> untyped + + def ==: (untyped other) -> untyped + + # + # Dump only crucial instance variables. + # + def _dump: (untyped limit) -> untyped + + # + # Activate this spec, registering it as a loaded spec and adding it's lib paths + # to $LOAD_PATH. Returns true if the spec was activated, false if it was + # previously activated. Freaks out if there are conflicts upon activation. + # + def activate: () -> (false | true) + + # + # Activate all unambiguously resolved runtime dependencies of this spec. Add any + # ambiguous dependencies to the unresolved list to be resolved later, as needed. + # + def activate_dependencies: () -> untyped + + # + # Abbreviate the spec for downloading. Abbreviated specs are only used for + # searching, downloading and related activities and do not need deployment + # specific information (e.g. list of files). So we abbreviate the spec, making + # it much smaller for quicker downloads. + # + def abbreviate: () -> untyped + + # + # Sanitize the descriptive fields in the spec. Sometimes non-ASCII characters + # will garble the site index. Non-ASCII characters will be replaced by their + # XML entity equivalent. + # + def sanitize: () -> untyped + + # + # Sanitize a single string. + # + def sanitize_string: (untyped string) -> untyped + + # + # Returns an array with bindir attached to each executable in the `executables` + # list + # + def add_bindir: (untyped executables) -> untyped + + private + + # + # Adds a dependency on gem `dependency` with type `type` that requires + # `requirements`. Valid types are currently `:runtime` and `:development`. + # + def add_dependency_with_type: (untyped dependency, untyped type, untyped requirements) -> untyped + + public + + # + # Adds a runtime dependency named `gem` with `requirements` to this gem. + # + # Usage: + # + # spec.add_runtime_dependency 'example', '~> 1.1', '>= 1.1.4' + # + alias add_runtime_dependency add_dependency + + # + # Adds this spec's require paths to LOAD_PATH, in the proper location. + # + def add_self_to_load_path: () -> (nil | untyped) + + # + # Singular reader for #authors. Returns the first author in the list + # + def author: () -> untyped + + # + # The list of author names who wrote this gem. + # + # spec.authors = ['Chad Fowler', 'Jim Weirich', 'Rich Kilmer'] + # + def authors: () -> untyped + + # + # Returns the full path to installed gem's bin directory. + # + # NOTE: do not confuse this with `bindir`, which is just 'bin', not a full path. + # + def bin_dir: () -> untyped + + # + # Returns the full path to an executable named `name` in this gem. + # + def bin_file: (untyped name) -> untyped + + # + # Returns the build_args used to install the gem + # + def build_args: () -> (untyped | ::Array[untyped]) + + def build_extensions: () -> (nil | untyped) + + # + # Returns the full path to the build info directory + # + def build_info_dir: () -> untyped + + # + # Returns the full path to the file containing the build information generated + # when the gem was installed + # + def build_info_file: () -> untyped + + # + # Returns the full path to the cache directory containing this spec's cached + # gem. + # + def cache_dir: () -> untyped + + # + # Returns the full path to the cached gem for this spec. + # + def cache_file: () -> untyped + + # + # Return any possible conflicts against the currently loaded specs. + # + def conflicts: () -> untyped + + def conficts_when_loaded_with?: (untyped list_of_specs) -> untyped + + # + # Return true if there are possible conflicts against the currently loaded + # specs. + # + def has_conflicts?: () -> untyped + + # + # The date this gem was created. + # + # If SOURCE_DATE_EPOCH is set as an environment variable, use that to support + # reproducible builds; otherwise, default to the current UTC date. + # + # Details on SOURCE_DATE_EPOCH: + # https://reproducible-builds.org/specs/source-date-epoch/ + # + def date: () -> untyped + + DateLike: untyped + + def self.===: (untyped obj) -> untyped + + DateTimeFormat: ::Regexp + + # + # The date this gem was created + # + # DO NOT set this, it is set automatically when the gem is packaged. + # + def date=: (untyped date) -> untyped + + def default_executable: () -> untyped + + # + # The default value for specification attribute `name` + # + def default_value: (untyped name) -> untyped + + # + # A list of Gem::Dependency objects this gem depends on. + # + # Use #add_dependency or #add_development_dependency to add dependencies to a + # gem. + # + def dependencies: () -> Array[Gem::Dependency] + + # + # Return a list of all gems that have a dependency on this gemspec. The list is + # structured with entries that conform to: + # + # [depending_gem, dependency, [list_of_gems_that_satisfy_dependency]] + # + def dependent_gems: (?bool check_dev) -> untyped + + # + # Returns all specs that matches this spec's runtime dependencies. + # + def dependent_specs: () -> untyped + + # + # A detailed description of this gem. See also #summary + # + def description=: (untyped str) -> untyped + + # + # List of dependencies that are used for development + # + def development_dependencies: () -> Array[Gem::Dependency] + + # + # Returns the full path to this spec's documentation directory. If `type` is + # given it will be appended to the end. For example: + # + # spec.doc_dir # => "/path/to/gem_repo/doc/a-1" + # + # spec.doc_dir 'ri' # => "/path/to/gem_repo/doc/a-1/ri" + # + def doc_dir: (?untyped? type) -> untyped + + def encode_with: (untyped coder) -> untyped + + def eql?: (untyped other) -> untyped + + # + # Singular accessor for #executables + # + def executable: () -> untyped + + # + # Singular accessor for #executables + # + def executable=: (untyped o) -> untyped + + # + # Sets executables to `value`, ensuring it is an array. + # + def executables=: (untyped value) -> untyped + + # + # Sets extensions to `extensions`, ensuring it is an array. + # + def extensions=: (untyped extensions) -> untyped + + # + # Sets extra_rdoc_files to `files`, ensuring it is an array. + # + def extra_rdoc_files=: (untyped files) -> untyped + + # + # The default (generated) file name of the gem. See also #spec_name. + # + # spec.file_name # => "example-1.0.gem" + # + def file_name: () -> ::String + + # + # Sets files to `files`, ensuring it is an array. + # + def files=: (untyped files) -> untyped + + private + + # + # Finds all gems that satisfy `dep` + # + def find_all_satisfiers: (untyped dep) { (untyped) -> untyped } -> untyped + + public + + # + # Creates a duplicate spec without large blobs that aren't used at runtime. + # + def for_cache: () -> untyped + + # + # + def full_name: () -> untyped + + def gem_dir: () -> untyped + + # + # + def gems_dir: () -> untyped + + def has_rdoc: () -> true + + def has_rdoc=: (untyped ignored) -> untyped + + alias has_rdoc? has_rdoc + + def has_unit_tests?: () -> untyped + + # :stopdoc: + alias has_test_suite? has_unit_tests? + + def hash: () -> untyped + + def init_with: (untyped coder) -> untyped + + # + # Specification constructor. Assigns the default values to the attributes and + # yields itself for further initialization. Optionally takes `name` and + # `version`. + # + def initialize: (?untyped? name, ?untyped? version) ?{ (untyped) -> untyped } -> void + + # + # Duplicates array_attributes from `other_spec` so state isn't shared. + # + def initialize_copy: (untyped other_spec) -> untyped + + # + # + def base_dir: () -> untyped + + private + + # + # Expire memoized instance variables that can incorrectly generate, replace or + # miss files due changes in certain attributes used to compute them. + # + def invalidate_memoized_attributes: () -> untyped + + public + + def inspect: () -> (untyped | ::String) + + # + # Files in the Gem under one of the require_paths + # + def lib_files: () -> untyped + + # + # Singular accessor for #licenses + # + def license: () -> untyped + + # + # Plural accessor for setting licenses + # + # See #license= for details + # + def licenses: () -> untyped + + def internal_init: () -> untyped + + def method_missing: (untyped sym, *untyped a) { (?) -> untyped } -> (nil | untyped) + + # + # Is this specification missing its extensions? When this returns true you + # probably want to build_extensions + # + def missing_extensions?: () -> (false | true) + + # + # Normalize the list of files so that: + # * All file lists have redundancies removed. + # * Files referenced in the extra_rdoc_files are included in the package file + # list. + # + def normalize: () -> untyped + + # + # Return a NameTuple that represents this Specification + # + def name_tuple: () -> untyped + + def original_name: () -> ::String + + def original_platform: () -> untyped + + # + # The platform this gem runs on. See Gem::Platform for details. + # + def platform: () -> untyped + + def pretty_print: (untyped q) -> untyped + + private + + def check_version_conflict: (untyped other) -> (nil | untyped) + + public + + def raise_if_conflicts: () -> (untyped | nil) + + # + # Sets rdoc_options to `value`, ensuring it is an array. + # + def rdoc_options=: (untyped options) -> untyped + + # + # Singular accessor for #require_paths + # + def require_path: () -> untyped + + # + # Singular accessor for #require_paths + # + def require_path=: (untyped path) -> untyped + + # + # Set requirements to `req`, ensuring it is an array. + # + def requirements=: (untyped req) -> untyped + + def respond_to_missing?: (untyped m, ?bool include_private) -> false + + # + # Returns the full path to this spec's ri directory. + # + def ri_dir: () -> untyped + + private + + # + # Return a string containing a Ruby code representation of the given object. + # + def ruby_code: (untyped obj) -> untyped + + public + + # + # List of dependencies that will automatically be activated at runtime. + # + def runtime_dependencies: () -> untyped + + private + + # + # True if this gem has the same attributes as `other`. + # + def same_attributes?: (untyped spec) -> untyped + + public + + # + # Checks if this specification meets the requirement of `dependency`. + # + def satisfies_requirement?: (untyped dependency) -> untyped + + # + # Returns an object you can use to sort specifications in #sort_by. + # + def sort_obj: () -> ::Array[untyped] + + def source: () -> untyped + + # + # Returns the full path to the directory containing this spec's gemspec file. + # eg: /usr/local/lib/ruby/gems/1.8/specifications + # + def spec_dir: () -> untyped + + # + # Returns the full path to this spec's gemspec file. eg: + # /usr/local/lib/ruby/gems/1.8/specifications/mygem-1.0.gemspec + # + def spec_file: () -> untyped + + # + # The default name of the gemspec. See also #file_name + # + # spec.spec_name # => "example-1.0.gemspec" + # + def spec_name: () -> ::String + + # + # A short summary of this gem's description. + # + def summary=: (untyped str) -> untyped + + def test_file: () -> untyped + + def test_file=: (untyped file) -> untyped + + def test_files: () -> untyped + + # + # Returns a Ruby code representation of this specification, such that it can be + # eval'ed and reconstruct the same specification later. Attributes that still + # have their default values are omitted. + # + def to_ruby: () -> untyped + + # + # Returns a Ruby lighter-weight code representation of this specification, used + # for indexing only. + # + # See #to_ruby. + # + def to_ruby_for_cache: () -> untyped + + def to_s: () -> ::String + + # + # Returns self + # + def to_spec: () -> self + + def to_yaml: (?::Hash[untyped, untyped] opts) -> untyped + + # + # Recursively walk dependencies of this spec, executing the `block` for each + # hop. + # + def traverse: (?untyped trail, ?::Hash[untyped, untyped] visited) { (?) -> untyped } -> untyped + + # + # Checks that the specification contains all required fields, and does a very + # basic sanity check. + # + # Raises InvalidSpecificationException if the spec does not pass the checks.. + # + def validate: (?bool packaging, ?bool strict) -> untyped + + # + # + def keep_only_files_and_directories: () -> untyped + + def validate_for_resolution: () -> untyped + + # + # + def validate_metadata: () -> untyped + + # + # + def validate_dependencies: () -> untyped + + # + # + def validate_permissions: () -> untyped + + # + # Set the version to `version`, potentially also setting + # required_rubygems_version if `version` indicates it is a prerelease. + # + def version=: (untyped version) -> (nil | untyped) + + # + # + def stubbed?: () -> false + + def yaml_initialize: (untyped tag, untyped vals) -> untyped + + # + # Reset nil attributes to their default values to make the spec valid + # + def reset_nil_attributes_to_default: () -> nil + + def flatten_require_paths: () -> (nil | untyped) + + def raw_require_paths: () -> untyped +end diff --git a/sig/shims/ast/2.4/.rbs_meta.yaml b/sig/shims/ast/2.4/.rbs_meta.yaml new file mode 100644 index 000000000..f361b3112 --- /dev/null +++ b/sig/shims/ast/2.4/.rbs_meta.yaml @@ -0,0 +1,9 @@ +--- +name: ast +version: '2.4' +source: + type: git + name: ruby/gem_rbs_collection + revision: c604d278dd6c14a1bb6cf0c0051af643b268a981 + remote: https://github.com/ruby/gem_rbs_collection.git + repo_dir: gems diff --git a/sig/shims/ast/2.4/ast.rbs b/sig/shims/ast/2.4/ast.rbs new file mode 100644 index 000000000..caba287ad --- /dev/null +++ b/sig/shims/ast/2.4/ast.rbs @@ -0,0 +1,73 @@ +module AST + interface _ToAst + def to_ast: () -> Node + end + + interface _ToSym + def to_sym: () -> Symbol + end + + class Node + public + + attr_reader children: Array[Node, Object] + attr_reader hash: String + attr_reader type: Symbol + + alias + concat + + alias << append + + def ==: (untyped other) -> bool + + def append: (untyped element) -> self + + alias clone dup + + def concat: (_ToA[untyped] array) -> self + + def dup: () -> self + + def eql?: (untyped other) -> bool + + def inspect: (?Integer indent) -> String + + alias to_a children + + def to_ast: () -> self + + alias to_s to_sexp + + def to_sexp: (?Integer indent) -> String + + def to_sexp_array: () -> Array[untyped] + + def updated: (?_ToSym? `type`, ?_ToA[untyped]? children, ?Hash[Symbol, untyped]? properties) -> self + + private + + def initialize: (_ToSym `type`, ?_ToA[untyped]? children, ?Hash[Symbol, untyped] properties) -> void + + alias original_dup dup + end + + class Processor + include Mixin + + module Mixin + public + + def handler_missing: (Node node) -> Node? + + def process: (_ToAst? node) -> Node? + + def process_all: (Array[_ToAst] nodes) -> Array[Node] + end + end + + module Sexp + public + + def s: (_ToSym `type`, *untyped children) -> Node + end +end From af31d2ab6ba26f671ac1a1550087a9d4a6ed6578 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 21 Jul 2025 07:38:03 -0400 Subject: [PATCH 278/561] Internal strict type-checking fixes --- lib/solargraph/api_map/cache.rb | 5 +++-- lib/solargraph/api_map/index.rb | 8 ++++---- lib/solargraph/api_map/store.rb | 1 + lib/solargraph/complex_type/unique_type.rb | 4 ++-- lib/solargraph/parser/node_processor/base.rb | 2 +- .../parser/parser_gem/node_processors/block_node.rb | 5 +++-- lib/solargraph/rbs_map/conversions.rb | 2 +- lib/solargraph/source.rb | 2 +- lib/solargraph/source_map/clip.rb | 2 +- lib/solargraph/yard_map/mapper/to_method.rb | 2 +- 10 files changed, 18 insertions(+), 15 deletions(-) diff --git a/lib/solargraph/api_map/cache.rb b/lib/solargraph/api_map/cache.rb index 329a1e5e1..0052d91ea 100644 --- a/lib/solargraph/api_map/cache.rb +++ b/lib/solargraph/api_map/cache.rb @@ -4,9 +4,9 @@ module Solargraph class ApiMap class Cache def initialize - # @type [Hash{Array => Array}] + # @type [Hash{String => Array}] @methods = {} - # @type [Hash{(String, Array) => Array}] + # @type [Hash{String, Array => Array}] @constants = {} # @type [Hash{String => String}] @qualified_namespaces = {} @@ -101,6 +101,7 @@ def empty? private + # @return [Array] def all_caches [@methods, @constants, @qualified_namespaces, @receiver_definitions, @clips] end diff --git a/lib/solargraph/api_map/index.rb b/lib/solargraph/api_map/index.rb index 810600534..0c65685ee 100644 --- a/lib/solargraph/api_map/index.rb +++ b/lib/solargraph/api_map/index.rb @@ -39,22 +39,22 @@ def pins_by_class klass @pin_select_cache[klass] ||= pin_class_hash.each_with_object(s) { |(key, o), n| n.merge(o) if key <= klass } end - # @return [Hash{String => Array}] + # @return [Hash{String => Array}] def include_references @include_references ||= Hash.new { |h, k| h[k] = [] } end - # @return [Hash{String => Array}] + # @return [Hash{String => Array}] def extend_references @extend_references ||= Hash.new { |h, k| h[k] = [] } end - # @return [Hash{String => Array}] + # @return [Hash{String => Array}] def prepend_references @prepend_references ||= Hash.new { |h, k| h[k] = [] } end - # @return [Hash{String => Array}] + # @return [Hash{String => Array}] def superclass_references @superclass_references ||= Hash.new { |h, k| h[k] = [] } end diff --git a/lib/solargraph/api_map/store.rb b/lib/solargraph/api_map/store.rb index 47f92194c..8677cc98c 100644 --- a/lib/solargraph/api_map/store.rb +++ b/lib/solargraph/api_map/store.rb @@ -206,6 +206,7 @@ def index def catalog pinsets @pinsets = pinsets + # @type [Array] @indexes = [] pinsets.each do |pins| if @indexes.last && pins.empty? diff --git a/lib/solargraph/complex_type/unique_type.rb b/lib/solargraph/complex_type/unique_type.rb index 0f4ec430d..3f919f566 100644 --- a/lib/solargraph/complex_type/unique_type.rb +++ b/lib/solargraph/complex_type/unique_type.rb @@ -353,9 +353,9 @@ def to_a # @param new_name [String, nil] # @param make_rooted [Boolean, nil] - # @param new_key_types [Array, nil] + # @param new_key_types [Array, nil] # @param rooted [Boolean, nil] - # @param new_subtypes [Array, nil] + # @param new_subtypes [Array, nil] # @return [self] def recreate(new_name: nil, make_rooted: nil, new_key_types: nil, new_subtypes: nil) raise "Please remove leading :: and set rooted instead - #{new_name}" if new_name&.start_with?('::') diff --git a/lib/solargraph/parser/node_processor/base.rb b/lib/solargraph/parser/node_processor/base.rb index d87268a91..fad31e95b 100644 --- a/lib/solargraph/parser/node_processor/base.rb +++ b/lib/solargraph/parser/node_processor/base.rb @@ -13,7 +13,7 @@ class Base # @return [Array] attr_reader :pins - # @return [Array] + # @return [Array] attr_reader :locals # @param node [Parser::AST::Node] diff --git a/lib/solargraph/parser/parser_gem/node_processors/block_node.rb b/lib/solargraph/parser/parser_gem/node_processors/block_node.rb index 70a2d9e68..d773e8e50 100644 --- a/lib/solargraph/parser/parser_gem/node_processors/block_node.rb +++ b/lib/solargraph/parser/parser_gem/node_processors/block_node.rb @@ -19,7 +19,7 @@ def process else region.closure end - pins.push Solargraph::Pin::Block.new( + block_pin = Solargraph::Pin::Block.new( location: location, closure: parent, node: node, @@ -28,7 +28,8 @@ def process scope: region.scope || region.closure.context.scope, source: :parser ) - process_children region.update(closure: pins.last) + pins.push block_pin + process_children region.update(closure: block_pin) end private diff --git a/lib/solargraph/rbs_map/conversions.rb b/lib/solargraph/rbs_map/conversions.rb index 6e50c022a..f8deec251 100644 --- a/lib/solargraph/rbs_map/conversions.rb +++ b/lib/solargraph/rbs_map/conversions.rb @@ -345,7 +345,7 @@ def global_decl_to_pin decl } # @param decl [RBS::AST::Members::MethodDefinition, RBS::AST::Members::AttrReader, RBS::AST::Members::AttrAccessor] - # @param closure [Pin::Namespace] + # @param closure [Pin::Closure] # @param context [Context] # @param scope [Symbol] :instance or :class # @param name [String] The name of the method diff --git a/lib/solargraph/source.rb b/lib/solargraph/source.rb index 11ab215ed..0684f8af8 100644 --- a/lib/solargraph/source.rb +++ b/lib/solargraph/source.rb @@ -317,7 +317,7 @@ def string_nodes @string_nodes ||= string_nodes_in(node) end - # @return [Array] + # @return [Array] def comment_ranges @comment_ranges ||= comments.values.map(&:range) end diff --git a/lib/solargraph/source_map/clip.rb b/lib/solargraph/source_map/clip.rb index ba69b1b93..16a4ec845 100644 --- a/lib/solargraph/source_map/clip.rb +++ b/lib/solargraph/source_map/clip.rb @@ -65,7 +65,7 @@ def infer # position. Locals can be local variables, method parameters, or block # parameters. The array starts with the nearest local pin. # - # @return [::Array] + # @return [::Array] def locals @locals ||= source_map.locals_at(location) end diff --git a/lib/solargraph/yard_map/mapper/to_method.rb b/lib/solargraph/yard_map/mapper/to_method.rb index df431bb3c..6bb4fa261 100644 --- a/lib/solargraph/yard_map/mapper/to_method.rb +++ b/lib/solargraph/yard_map/mapper/to_method.rb @@ -27,7 +27,7 @@ def self.make code_object, name = nil, scope = nil, visibility = nil, closure = final_scope = scope || code_object.scope override_key = [closure.path, final_scope, name] final_visibility = VISIBILITY_OVERRIDE[override_key] - final_visibility ||= VISIBILITY_OVERRIDE[override_key[0..-2]] + final_visibility ||= VISIBILITY_OVERRIDE[[closure.path, final_scope]] final_visibility ||= :private if closure.path == 'Kernel' && Kernel.private_instance_methods(false).include?(name) final_visibility ||= visibility final_visibility ||= :private if code_object.module_function? && final_scope == :instance From be879a091d4d5324cc8f997ba17be9349e049004 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 21 Jul 2025 07:52:04 -0400 Subject: [PATCH 279/561] Add annotation --- lib/solargraph/parser/parser_gem/node_processors/if_node.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/solargraph/parser/parser_gem/node_processors/if_node.rb b/lib/solargraph/parser/parser_gem/node_processors/if_node.rb index 5784afcbe..d10ba91bd 100644 --- a/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +++ b/lib/solargraph/parser/parser_gem/node_processors/if_node.rb @@ -11,6 +11,7 @@ def process process_children position = get_node_start_position(node) + # @type [Solargraph::Pin::Breakable, nil] enclosing_breakable_pin = pins.select{|pin| pin.is_a?(Pin::Breakable) && pin.location.range.contain?(position)}.last FlowSensitiveTyping.new(locals, enclosing_breakable_pin).process_if(node) end From c45d95e1b0ff3378006f811c8ca5af46cbcdb4b6 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 21 Jul 2025 08:06:28 -0400 Subject: [PATCH 280/561] Add annotation --- lib/solargraph/diagnostics/rubocop_helpers.rb | 2 -- lib/solargraph/library.rb | 3 ++- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/solargraph/diagnostics/rubocop_helpers.rb b/lib/solargraph/diagnostics/rubocop_helpers.rb index 8e2e13359..21c16be97 100644 --- a/lib/solargraph/diagnostics/rubocop_helpers.rb +++ b/lib/solargraph/diagnostics/rubocop_helpers.rb @@ -18,8 +18,6 @@ def require_rubocop(version = nil) gem_path = Gem::Specification.find_by_name('rubocop', version).full_gem_path gem_lib_path = File.join(gem_path, 'lib') $LOAD_PATH.unshift(gem_lib_path) unless $LOAD_PATH.include?(gem_lib_path) - # @todo Gem::MissingSpecVersionError is undocumented for some reason - # @sg-ignore rescue Gem::MissingSpecVersionError => e raise InvalidRubocopVersionError, "could not find '#{e.name}' (#{e.requirement}) - "\ diff --git a/lib/solargraph/library.rb b/lib/solargraph/library.rb index 740d700b3..1b1e7053b 100644 --- a/lib/solargraph/library.rb +++ b/lib/solargraph/library.rb @@ -511,7 +511,7 @@ def external_requires private - # @return [Hash{String => Set}] + # @return [Hash{String => Array}] def source_map_external_require_hash @source_map_external_require_hash ||= {} end @@ -519,6 +519,7 @@ def source_map_external_require_hash # @param source_map [SourceMap] # @return [void] def find_external_requires source_map + # @type [Set] new_set = source_map.requires.map(&:name).to_set # return if new_set == source_map_external_require_hash[source_map.filename] _filenames = nil From 8b990c48d30f1e47a16e6047aa21b4b8018db727 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 21 Jul 2025 08:45:02 -0400 Subject: [PATCH 281/561] Force build --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 98df20346..d234e2780 100755 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ The RSpec framework is supported via [solargraph-rspec](https://github.com/lekem 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. -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. +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. To ensure you have types for gems which contain neither RBS nor YARD information, use From 36b526da15bd2f5bc909153b73748172d63ad2ea Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 21 Jul 2025 08:56:02 -0400 Subject: [PATCH 282/561] Force build --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d234e2780..b3cb2ef5a 100755 --- a/README.md +++ b/README.md @@ -81,7 +81,7 @@ Once installed, you can also insert your own local overrides and definitions in ### Type Checking -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. +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. ### The Documentation Cache From 36525df58d3a13260ba608b4afa6d79c1721c971 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 21 Jul 2025 09:50:13 -0400 Subject: [PATCH 283/561] Add another spec, clean up existing and ratchet rubocop --- .rubocop_todo.yml | 23 ++++++++--------------- spec/api_map_spec.rb | 43 +++++++++++++++++++++++++------------------ 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index b4f40f056..5ee8f20d0 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1005,13 +1005,12 @@ RSpec/BeNil: - 'spec/api_map/source_to_yard_spec.rb' - 'spec/language_server/host_spec.rb' -# Offense count: 4 +# Offense count: 3 RSpec/BeforeAfterAll: Exclude: - '**/spec/spec_helper.rb' - '**/spec/rails_helper.rb' - '**/spec/support/**/*.rb' - - 'spec/api_map_spec.rb' - 'spec/language_server/host/dispatch_spec.rb' - 'spec/language_server/protocol_spec.rb' @@ -1038,7 +1037,7 @@ RSpec/DescribeClass: - 'spec/complex_type_spec.rb' - 'spec/source_map/node_processor_spec.rb' -# Offense count: 396 +# Offense count: 388 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: SkipBlocks, EnforcedStyle, OnlyStaticConstants. # SupportedStyles: described_class, explicit @@ -1047,7 +1046,6 @@ RSpec/DescribedClass: - 'spec/api_map/cache_spec.rb' - 'spec/api_map/source_to_yard_spec.rb' - 'spec/api_map/store_spec.rb' - - 'spec/api_map_spec.rb' - 'spec/diagnostics/base_spec.rb' - 'spec/diagnostics/require_not_found_spec.rb' - 'spec/diagnostics/rubocop_helpers_spec.rb' @@ -1205,12 +1203,11 @@ RSpec/HookArgument: RSpec/ImplicitExpect: EnforcedStyle: should -# Offense count: 247 +# Offense count: 126 # Configuration parameters: AssignmentOnly. RSpec/InstanceVariable: Exclude: - 'spec/api_map/config_spec.rb' - - 'spec/api_map_spec.rb' - 'spec/diagnostics/require_not_found_spec.rb' - 'spec/language_server/host/dispatch_spec.rb' - 'spec/language_server/host_spec.rb' @@ -1330,13 +1327,12 @@ RSpec/NoExpectationExample: - 'spec/type_checker/checks_spec.rb' - 'spec/type_checker/levels/typed_spec.rb' -# Offense count: 2 +# Offense count: 1 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: not_to, to_not RSpec/NotToNot: Exclude: - - 'spec/api_map_spec.rb' - 'spec/rbs_map/core_map_spec.rb' # Offense count: 28 @@ -1374,10 +1370,9 @@ RSpec/RemoveConst: Exclude: - 'spec/diagnostics/rubocop_helpers_spec.rb' -# Offense count: 50 +# Offense count: 48 RSpec/RepeatedDescription: Exclude: - - 'spec/api_map_spec.rb' - 'spec/language_server/protocol_spec.rb' - 'spec/parser/node_methods_spec.rb' - 'spec/source/chain/call_spec.rb' @@ -1387,7 +1382,7 @@ RSpec/RepeatedDescription: - 'spec/type_checker/levels/normal_spec.rb' - 'spec/type_checker/levels/strict_spec.rb' -# Offense count: 30 +# Offense count: 28 RSpec/RepeatedExample: Exclude: - 'spec/api_map_spec.rb' @@ -1809,7 +1804,7 @@ Style/FloatDivision: - 'lib/solargraph/library.rb' - 'lib/solargraph/pin/search.rb' -# Offense count: 127 +# Offense count: 126 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: EnforcedStyle. # SupportedStyles: always, always_true, never @@ -1839,7 +1834,6 @@ Style/FrozenStringLiteralComment: - 'spec/api_map/cache_spec.rb' - 'spec/api_map/config_spec.rb' - 'spec/api_map/source_to_yard_spec.rb' - - 'spec/api_map_spec.rb' - 'spec/complex_type_spec.rb' - 'spec/convention/struct_definition_spec.rb' - 'spec/convention_spec.rb' @@ -2790,7 +2784,7 @@ YARD/TagTypeSyntax: - 'lib/solargraph/parser/comment_ripper.rb' - 'lib/solargraph/type_checker.rb' -# Offense count: 186 +# Offense count: 185 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: Max, AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, IgnoreCopDirectives, AllowedPatterns, SplitStrings. # URISchemes: http, https @@ -2849,7 +2843,6 @@ Layout/LineLength: - 'lib/solargraph/workspace.rb' - 'lib/solargraph/workspace/config.rb' - 'lib/solargraph/yard_map/mapper/to_method.rb' - - 'spec/api_map_spec.rb' - 'spec/complex_type_spec.rb' - 'spec/language_server/message/completion_item/resolve_spec.rb' - 'spec/language_server/message/extended/check_gem_version_spec.rb' diff --git a/spec/api_map_spec.rb b/spec/api_map_spec.rb index c56f1be4b..4d7077ccd 100755 --- a/spec/api_map_spec.rb +++ b/spec/api_map_spec.rb @@ -1,12 +1,16 @@ +# frozen_string_literal: true + require 'tmpdir' describe Solargraph::ApiMap do - before :all do - @api_map = Solargraph::ApiMap.load_with_cache(Dir.pwd, nil) + # avoid performance hit of doing this many times + # rubocop:disable RSpec/InstanceVariable + before :all do # rubocop:disable RSpec/BeforeAfterAll + @api_map = described_class.load_with_cache(Dir.pwd, nil) end it 'returns core methods' do - pins = @api_map.get_methods('String') + pins = @api_map.get_methods('String') # rubocop:disable RSpec/InstanceVariable expect(pins.map(&:path)).to include('String#upcase') end @@ -17,7 +21,7 @@ end it 'automatically caches core' do - api_map = Solargraph::ApiMap.load_with_cache(Dir.pwd, nil) + api_map = described_class.load_with_cache(Dir.pwd, nil) pins = api_map.get_methods('String') expect(pins.map(&:path)).to include('String#upcase') end @@ -25,12 +29,12 @@ context 'without gem already cached' do before do - gemspec = @api_map.find_gem('rubocop') + gemspec = @api_map.find_gem('rubocop') # rubocop:disable RSpec/InstanceVariable @api_map.workspace.uncache_gem(gemspec) end it 'automatically caches gems' do - api_map = Solargraph::ApiMap.load_with_cache(Dir.pwd, nil) + api_map = described_class.load_with_cache(Dir.pwd, nil) pins = api_map.get_methods('RuboCop::Cop::Base') expect(pins.map(&:path)).to include('RuboCop::Cop::Base#active_support_extensions_enabled?') end @@ -220,7 +224,7 @@ def prot end it 'adds Object instance methods to duck types' do - api_map = Solargraph::ApiMap.new + api_map = described_class.new type = Solargraph::ComplexType.parse('#foo') pins = api_map.get_complex_type_methods(type) expect(pins.any? { |p| p.namespace == 'BasicObject' }).to be(true) @@ -461,14 +465,14 @@ class Sup xit 'understands tuples inherit from regular arrays' do method_pins = @api_map.get_method_stack("Array(1, 2, 'a')", 'include?') method_pin = method_pins.first - expect(method_pin).to_not be_nil + expect(method_pin).not_to be_nil expect(method_pin.path).to eq('Array#include?') parameter_type = method_pin.signatures.first.parameters.first.return_type expect(parameter_type.rooted_tags).to eq("1, 2, 'a'") end it 'loads workspaces from directories' do - api_map = Solargraph::ApiMap.load('spec/fixtures/workspace') + api_map = described_class.load('spec/fixtures/workspace') expect(api_map.source_map(File.absolute_path('spec/fixtures/workspace/app.rb'))).to be_a(Solargraph::SourceMap) end @@ -558,7 +562,7 @@ module Includer expect(fqns).to eq('Foo::Bar') end - it 'handles multiple type parameters without losing cache coherence' do + it 'understands type parameters' do tag = @api_map.qualify('Array') expect(tag).to eq('Array') tag = @api_map.qualify('Array') @@ -782,18 +786,18 @@ def bar; end end it 'can qualify "Boolean"' do - api_map = Solargraph::ApiMap.new + api_map = described_class.new expect(api_map.qualify('Boolean')).to eq('Boolean') end it 'knows that true is a "subtype" of Boolean' do - api_map = Solargraph::ApiMap.new + api_map = described_class.new expect(api_map.super_and_sub?('Boolean', 'true')).to be(true) end it 'knows that false is a "subtype" of Boolean' do - api_map = Solargraph::ApiMap.new - expect(api_map.super_and_sub?('Boolean', 'true')).to be(true) + api_map = described_class.new + expect(api_map.super_and_sub?('Boolean', 'false')).to be(true) end it 'resolves aliases for YARD methods' do @@ -808,7 +812,7 @@ class Foo alias baz foo end )).pins - # api_map = Solargraph::ApiMap.new(pins: yard_pins + source_pins) + # api_map = described_class.new(pins: yard_pins + source_pins) @api_map.index yard_pins + source_pins baz = @api_map.get_method_stack('Foo', 'baz').first expect(baz).to be_a(Solargraph::Pin::Method) @@ -817,8 +821,10 @@ class Foo it 'ignores malformed mixins' do closure = Solargraph::Pin::Namespace.new(name: 'Foo', closure: Solargraph::Pin::ROOT_PIN, type: :class) - mixin = Solargraph::Pin::Reference::Include.new(name: 'defined?(DidYouMean::SpellChecker) && defined?(DidYouMean::Correctable)', closure: closure) - api_map = Solargraph::ApiMap.new(pins: [closure, mixin]) + mixin = Solargraph::Pin::Reference::Include.new( + name: 'defined?(DidYouMean::SpellChecker) && defined?(DidYouMean::Correctable)', closure: closure + ) + api_map = described_class.new(pins: [closure, mixin]) expect(api_map.get_method_stack('Foo', 'foo')).to be_empty end @@ -839,9 +845,10 @@ def baz end ), 'test.rb') - api_map = Solargraph::ApiMap.new.map(source) + api_map = described_class.new.map(source) clip = api_map.clip_at('test.rb', [11, 10]) expect(clip.infer.to_s).to eq('Symbol') end + # rubocop:enable RSpec/InstanceVariable end From d68f448bf463ab6da007b2917b175dbd2d02c214 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 21 Jul 2025 10:20:04 -0400 Subject: [PATCH 284/561] Fix RuboCop issues --- .rubocop_todo.yml | 3 +-- lib/solargraph/yardoc.rb | 10 +++++----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 5ee8f20d0..97a41ccd1 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -2075,7 +2075,7 @@ Style/MapToSet: - 'lib/solargraph/library.rb' - 'spec/source_map/clip_spec.rb' -# Offense count: 202 +# Offense count: 197 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: require_parentheses, require_no_parentheses, require_no_parentheses_except_multiline @@ -2131,7 +2131,6 @@ Style/MethodDefParentheses: - 'lib/solargraph/type_checker.rb' - 'lib/solargraph/type_checker/checks.rb' - 'lib/solargraph/yard_map/helpers.rb' - - 'lib/solargraph/yardoc.rb' - 'spec/fixtures/rdoc-lib/lib/example.rb' - 'spec/source_map_spec.rb' - 'spec/spec_helper.rb' diff --git a/lib/solargraph/yardoc.rb b/lib/solargraph/yardoc.rb index cbc2af910..e21ae1a00 100644 --- a/lib/solargraph/yardoc.rb +++ b/lib/solargraph/yardoc.rb @@ -13,7 +13,7 @@ module Yardoc # @param gemspec [Gem::Specification] # # @return [void] - def build_docs(gem_yardoc_path, yard_plugins, gemspec) + def build_docs gem_yardoc_path, yard_plugins, gemspec return if docs_built?(gem_yardoc_path) Solargraph.logger.info "Saving yardoc for #{gemspec.name} #{gemspec.version} into #{gem_yardoc_path}" @@ -34,7 +34,7 @@ def build_docs(gem_yardoc_path, yard_plugins, gemspec) # @param gemspec [Gem::Specification] # @param out [IO, nil] where to log messages # @return [Array] - def build_pins(gem_yardoc_path, gemspec, out: $stderr) + def build_pins gem_yardoc_path, gemspec, out: $stderr yardoc = load!(gem_yardoc_path) YardMap::Mapper.new(yardoc, gemspec).map end @@ -42,7 +42,7 @@ def build_pins(gem_yardoc_path, gemspec, out: $stderr) # True if the gem yardoc is cached. # # @param gem_yardoc_path [String] - def docs_built?(gem_yardoc_path) + def docs_built? gem_yardoc_path yardoc = File.join(gem_yardoc_path, 'complete') File.exist?(yardoc) end @@ -50,7 +50,7 @@ def docs_built?(gem_yardoc_path) # True if another process is currently building the yardoc cache. # # @param gem_yardoc_path [String] the path to the yardoc cache of a particular gem - def processing?(gem_yardoc_path) + def processing? gem_yardoc_path yardoc = File.join(gem_yardoc_path, 'processing') File.exist?(yardoc) end @@ -61,7 +61,7 @@ def processing?(gem_yardoc_path) # # @param gem_yardoc_path [String] the path to the yardoc cache of a particular gem # @return [Array] - def load!(gem_yardoc_path) + def load! gem_yardoc_path YARD::Registry.load! gem_yardoc_path YARD::Registry.all end From e08588f910290d30214df3f35b99d2120c97d06a Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 21 Jul 2025 10:44:23 -0400 Subject: [PATCH 285/561] Add Yardoc spec --- spec/yardoc_spec.rb | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 spec/yardoc_spec.rb diff --git a/spec/yardoc_spec.rb b/spec/yardoc_spec.rb new file mode 100644 index 000000000..50a533dcf --- /dev/null +++ b/spec/yardoc_spec.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +require 'tmpdir' + +describe Solargraph::Yardoc do + describe '#build_docs' do + around do |testobj| + @tmpdir = Dir.mktmpdir + + testobj.run + ensure + FileUtils.remove_entry(@tmpdir) # rubocop:disable RSpec/InstanceVariable + end + + it 'builds docs for a gem' do + gem_yardoc_path = File.join(@tmpdir, 'solargraph', 'yardoc', 'test_gem') # rubocop:disable RSpec/InstanceVariable + api_map = Solargraph::ApiMap.load(Dir.pwd) + gem = api_map.find_gem('rubocop') + described_class.build_docs(gem.gem_yardoc_path, [], gemspec) + expect(File.exist?(File.join(gem_yardoc_path, 'complete'))).to be true + end + end +end From a7cc850ffe8bcd48688fd6722b7aa9db2dbbd08f Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 21 Jul 2025 10:59:57 -0400 Subject: [PATCH 286/561] Fix spec --- spec/yardoc_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/yardoc_spec.rb b/spec/yardoc_spec.rb index 50a533dcf..2f863cbaa 100644 --- a/spec/yardoc_spec.rb +++ b/spec/yardoc_spec.rb @@ -15,8 +15,8 @@ it 'builds docs for a gem' do gem_yardoc_path = File.join(@tmpdir, 'solargraph', 'yardoc', 'test_gem') # rubocop:disable RSpec/InstanceVariable api_map = Solargraph::ApiMap.load(Dir.pwd) - gem = api_map.find_gem('rubocop') - described_class.build_docs(gem.gem_yardoc_path, [], gemspec) + gemspec = api_map.find_gem('rubocop') + described_class.build_docs(gem_yardoc_path, [], gemspec) expect(File.exist?(File.join(gem_yardoc_path, 'complete'))).to be true end end From 91df9e7f2d8e28ba12e7ab60698f70b14c1a052c Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 21 Jul 2025 11:17:57 -0400 Subject: [PATCH 287/561] Add more specs --- spec/yardoc_spec.rb | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/spec/yardoc_spec.rb b/spec/yardoc_spec.rb index 2f863cbaa..92d2840ca 100644 --- a/spec/yardoc_spec.rb +++ b/spec/yardoc_spec.rb @@ -12,12 +12,38 @@ FileUtils.remove_entry(@tmpdir) # rubocop:disable RSpec/InstanceVariable end + let(:gem_yardoc_path) do + File.join(@tmpdir, 'solargraph', 'yardoc', 'test_gem') # rubocop:disable RSpec/InstanceVariable + end + + describe '#processing?' do + it 'returns true if the yardoc is being processed' do + gem_yardoc_path = File.join(@tmpdir, 'yardoc') + FileUtils.mkdir_p(gem_yardoc_path) + FileUtils.touch(File.join(gem_yardoc_path, 'processing')) + expect(Solargraph::Yardoc.processing?(gem_yardoc_path)).to be(true) + end + + it 'returns false if the yardoc is not being processed' do + gem_yardoc_path = File.join(@tmpdir, 'yardoc') + FileUtils.mkdir_p(gem_yardoc_path) + expect(Solargraph::Yardoc.processing?(gem_yardoc_path)).to be(false) + end + end + it 'builds docs for a gem' do - gem_yardoc_path = File.join(@tmpdir, 'solargraph', 'yardoc', 'test_gem') # rubocop:disable RSpec/InstanceVariable api_map = Solargraph::ApiMap.load(Dir.pwd) gemspec = api_map.find_gem('rubocop') described_class.build_docs(gem_yardoc_path, [], gemspec) expect(File.exist?(File.join(gem_yardoc_path, 'complete'))).to be true end + + it 'is idempotent' do + api_map = Solargraph::ApiMap.load(Dir.pwd) + gemspec = api_map.find_gem('rubocop') + described_class.build_docs(gem_yardoc_path, [], gemspec) + described_class.build_docs(gem_yardoc_path, [], gemspec) + expect(File.exist?(File.join(gem_yardoc_path, 'complete'))).to be true + end end end From 5c27b7408936398452708f6488a356d4718328c7 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 21 Jul 2025 11:51:32 -0400 Subject: [PATCH 288/561] Add more specs --- spec/yardoc_spec.rb | 72 +++++++++++++++++++++++++++++---------------- 1 file changed, 47 insertions(+), 25 deletions(-) diff --git a/spec/yardoc_spec.rb b/spec/yardoc_spec.rb index 92d2840ca..84b1b6fb6 100644 --- a/spec/yardoc_spec.rb +++ b/spec/yardoc_spec.rb @@ -1,49 +1,71 @@ # frozen_string_literal: true require 'tmpdir' +require 'open3' describe Solargraph::Yardoc do - describe '#build_docs' do - around do |testobj| - @tmpdir = Dir.mktmpdir + around do |testobj| + @tmpdir = Dir.mktmpdir + + testobj.run + ensure + FileUtils.remove_entry(@tmpdir) # rubocop:disable RSpec/InstanceVariable + end + + let(:gem_yardoc_path) do + File.join(@tmpdir, 'solargraph', 'yardoc', 'test_gem') # rubocop:disable RSpec/InstanceVariable + end - testobj.run - ensure - FileUtils.remove_entry(@tmpdir) # rubocop:disable RSpec/InstanceVariable + before do + FileUtils.mkdir_p(gem_yardoc_path) + end + + describe '#processing?' do + it 'returns true if the yardoc is being processed' do + FileUtils.touch(File.join(gem_yardoc_path, 'processing')) + expect(described_class.processing?(gem_yardoc_path)).to be(true) end - let(:gem_yardoc_path) do - File.join(@tmpdir, 'solargraph', 'yardoc', 'test_gem') # rubocop:disable RSpec/InstanceVariable + it 'returns false if the yardoc is not being processed' do + expect(described_class.processing?(gem_yardoc_path)).to be(false) end + end - describe '#processing?' do - it 'returns true if the yardoc is being processed' do - gem_yardoc_path = File.join(@tmpdir, 'yardoc') - FileUtils.mkdir_p(gem_yardoc_path) - FileUtils.touch(File.join(gem_yardoc_path, 'processing')) - expect(Solargraph::Yardoc.processing?(gem_yardoc_path)).to be(true) - end + describe '#build_docs' do + let(:api_map) { Solargraph::ApiMap.load(Dir.pwd) } + let(:gemspec) { api_map.find_gem('rubocop') } + let(:output) { '' } - it 'returns false if the yardoc is not being processed' do - gem_yardoc_path = File.join(@tmpdir, 'yardoc') - FileUtils.mkdir_p(gem_yardoc_path) - expect(Solargraph::Yardoc.processing?(gem_yardoc_path)).to be(false) - end + before do + allow(Solargraph.logger).to receive(:warn) + allow(Solargraph.logger).to receive(:info) end it 'builds docs for a gem' do - api_map = Solargraph::ApiMap.load(Dir.pwd) - gemspec = api_map.find_gem('rubocop') described_class.build_docs(gem_yardoc_path, [], gemspec) expect(File.exist?(File.join(gem_yardoc_path, 'complete'))).to be true end it 'is idempotent' do - api_map = Solargraph::ApiMap.load(Dir.pwd) - gemspec = api_map.find_gem('rubocop') - described_class.build_docs(gem_yardoc_path, [], gemspec) described_class.build_docs(gem_yardoc_path, [], gemspec) + described_class.build_docs(gem_yardoc_path, [], gemspec) # second time expect(File.exist?(File.join(gem_yardoc_path, 'complete'))).to be true end + + context 'with an error from yard' do + before do + allow(Open3).to receive(:capture2e).and_return([output, result]) + end + + let(:result) { instance_double(Process::Status) } + + it 'does not raise on error from yard' do + allow(result).to receive(:success?).and_return(false) + + expect do + described_class.build_docs(gem_yardoc_path, [], gemspec) + end.not_to raise_error + end + end end end From 4be3f2782e201ef03072cea82344e99e6c4f2f76 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 21 Jul 2025 12:25:25 -0400 Subject: [PATCH 289/561] Add more specs --- lib/solargraph/workspace/require_paths.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/solargraph/workspace/require_paths.rb b/lib/solargraph/workspace/require_paths.rb index 22b04b40a..07207dfe1 100644 --- a/lib/solargraph/workspace/require_paths.rb +++ b/lib/solargraph/workspace/require_paths.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require 'open3' + module Solargraph # A workspace consists of the files in a project's directory and the # project's configuration. It provides a Source for each file to be used From 5400ac812ef6e916288e7e95263c6c53bb8b8049 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 21 Jul 2025 12:32:55 -0400 Subject: [PATCH 290/561] Add more specs --- spec/workspace/require_paths_spec.rb | 34 ++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 spec/workspace/require_paths_spec.rb diff --git a/spec/workspace/require_paths_spec.rb b/spec/workspace/require_paths_spec.rb new file mode 100644 index 000000000..2f0d9082d --- /dev/null +++ b/spec/workspace/require_paths_spec.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +require 'fileutils' +require 'tmpdir' + +describe Solargraph::Workspace::RequirePaths do + subject(:paths) { described_class.new(dir_path, config).generate } + + let(:config) { Solargraph::Workspace::Config.new(dir_path) } + + context 'with current bundle' do + let(:dir_path) { Dir.pwd } + + it 'includes the lib directory' do + expect(paths).to include(File.join(dir_path, 'lib')) + end + + it 'queried via Open3.capture3' do + allow(Open3).to receive(:capture3).and_call_original + + paths + + expect(Open3).to have_received(:capture3) + end + end + + context 'with no gemspec file' do + let(:dir_path) { File.realpath(Dir.mktmpdir) } + + it 'includes the lib directory' do + expect(paths).to include(File.join(dir_path, 'lib')) + end + end +end From 96f8e2be2df7b40cb76acf29df91180e5da72185 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 21 Jul 2025 12:33:40 -0400 Subject: [PATCH 291/561] Drop unused method --- lib/solargraph/workspace/require_paths.rb | 7 ------- 1 file changed, 7 deletions(-) diff --git a/lib/solargraph/workspace/require_paths.rb b/lib/solargraph/workspace/require_paths.rb index 07207dfe1..c12b66724 100644 --- a/lib/solargraph/workspace/require_paths.rb +++ b/lib/solargraph/workspace/require_paths.rb @@ -62,13 +62,6 @@ def configured_require_paths config.require_paths.map { |p| File.join(directory, p) } end - # True if the workspace contains at least one gemspec file. - # - # @return [Boolean] - def gemspec? - !gemspec_file_paths.empty? - end - # Generate require paths from gemspecs if they exist or assume the default # lib directory. # From c6655354b4d85137ebcfc3a64c50f8d4f08fbbcf Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 21 Jul 2025 12:42:19 -0400 Subject: [PATCH 292/561] Add more specs --- spec/shell_spec.rb | 18 ++++++++++++++++++ spec/workspace/require_paths_spec.rb | 8 ++++++++ 2 files changed, 26 insertions(+) diff --git a/spec/shell_spec.rb b/spec/shell_spec.rb index bd0de9042..ec98f7892 100644 --- a/spec/shell_spec.rb +++ b/spec/shell_spec.rb @@ -15,6 +15,8 @@ raise "Failure installing bundle: #{output}" unless status.success? end + # @type cmd [Array] + # @return [String] def bundle_exec(*cmd) # run the command in the temporary directory with bundle exec output, status = Open3.capture2e("bundle exec #{cmd.join(' ')}", chdir: temp_dir) @@ -45,6 +47,14 @@ def bundle_exec(*cmd) expect(output).to include('Clearing pin cache in') end + + it 'uncaches stdlib without erroring out' do + expect { bundle_exec('solargraph', 'uncache', 'stdlib') }.not_to raise_error + end + + it 'uncaches core without erroring out' do + expect { bundle_exec('solargraph', 'uncache', 'core') }.not_to raise_error + end end describe 'gem' do @@ -56,6 +66,14 @@ def bundle_exec(*cmd) end describe 'gems' do + it 'caches a stdlib gem without erroring out' do + expect { bundle_exec('solargraph', 'cache', 'stringio') }.not_to raise_error + end + + it 'caches core without erroring out' do + expect { bundle_exec('solargraph', 'cache', 'core') }.not_to raise_error + end + it 'caches all without erroring out' do output = bundle_exec('solargraph', 'gems') diff --git a/spec/workspace/require_paths_spec.rb b/spec/workspace/require_paths_spec.rb index 2f0d9082d..97cf9c285 100644 --- a/spec/workspace/require_paths_spec.rb +++ b/spec/workspace/require_paths_spec.rb @@ -8,6 +8,14 @@ let(:config) { Solargraph::Workspace::Config.new(dir_path) } + context 'with no config' do + let(:config) { nil } + + it 'includes the lib directory' do + expect(paths).to include(File.join(dir_path, 'lib')) + end + end + context 'with current bundle' do let(:dir_path) { Dir.pwd } From e6c5a589fc1351baae3e45d8c107c93450da50fd Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 22 Jul 2025 08:43:45 -0400 Subject: [PATCH 293/561] Generic typechecking improvements --- lib/solargraph/api_map/index.rb | 19 ++- lib/solargraph/complex_type.rb | 57 ++++--- lib/solargraph/complex_type/unique_type.rb | 120 +++++++------ .../data_definition/data_assignment_node.rb | 1 + .../data_definition/data_definition_node.rb | 3 +- .../struct_assignment_node.rb | 2 + .../struct_definition_node.rb | 3 +- lib/solargraph/gem_pins.rb | 5 +- lib/solargraph/language_server/host.rb | 3 +- .../parser/flow_sensitive_typing.rb | 6 + lib/solargraph/parser/node_processor.rb | 5 +- .../parser/parser_gem/node_methods.rb | 2 +- .../parser_gem/node_processors/if_node.rb | 2 + lib/solargraph/parser/snippet.rb | 2 +- lib/solargraph/pin/base.rb | 8 +- lib/solargraph/pin/method.rb | 2 +- lib/solargraph/pin/parameter.rb | 6 +- lib/solargraph/rbs_map/conversions.rb | 6 +- lib/solargraph/source/chain/if.rb | 2 +- lib/solargraph/source/chain/or.rb | 2 +- lib/solargraph/type_checker.rb | 61 ++++--- lib/solargraph/type_checker/rules.rb | 26 ++- lib/solargraph/yard_map/mapper/to_method.rb | 4 +- spec/complex_type/conforms_to_spec.rb | 12 +- spec/complex_type_spec.rb | 8 +- spec/rbs_map/conversions_spec.rb | 87 ++++++---- spec/type_checker/levels/strong_spec.rb | 161 ++++++++++++++++++ 27 files changed, 435 insertions(+), 180 deletions(-) diff --git a/lib/solargraph/api_map/index.rb b/lib/solargraph/api_map/index.rb index 810600534..42bb6cc32 100644 --- a/lib/solargraph/api_map/index.rb +++ b/lib/solargraph/api_map/index.rb @@ -34,7 +34,7 @@ def path_pin_hash # @param klass [Class>] # @return [Set>] def pins_by_class klass - # @type [Set] + # @type [Set>] s = Set.new @pin_select_cache[klass] ||= pin_class_hash.each_with_object(s) { |(key, o), n| n.merge(o) if key <= klass } end @@ -59,7 +59,8 @@ def superclass_references @superclass_references ||= Hash.new { |h, k| h[k] = [] } end - # @param pins [Array] + # @param pins [Enumerable] + # @return [self] def merge pins deep_clone.catalog pins end @@ -69,8 +70,9 @@ def merge pins attr_writer :pins, :pin_select_cache, :namespace_hash, :pin_class_hash, :path_pin_hash, :include_references, :extend_references, :prepend_references, :superclass_references + # @return [Solargraph::ApiMap::Index] def deep_clone - Index.allocate.tap do |copy| + out = Index.allocate.tap do |copy| copy.pin_select_cache = {} copy.pins = pins.clone %i[ @@ -81,9 +83,11 @@ def deep_clone copy.send(sym)&.transform_values!(&:clone) end end + out end - # @param new_pins [Array] + # @param new_pins [Enumerable] + # @return [self] def catalog new_pins @pin_select_cache = {} pins.concat new_pins @@ -104,7 +108,7 @@ def catalog new_pins end # @param klass [Class] - # @param hash [Hash{String => Array}] + # @param hash [Hash{String => Array}] # @return [void] def map_references klass, hash pins_by_class(klass).each do |pin| @@ -114,7 +118,7 @@ def map_references klass, hash # Add references to a map # - # @param hash [Hash{String => Array}] + # @param hash [Hash{String => Array}] # @param reference_pin [Pin::Reference] # # @return [void] @@ -138,9 +142,12 @@ def map_overrides pins = path_pin_hash[ovr.name] logger.debug { "ApiMap::Index#map_overrides: pins for path=#{ovr.name}: #{pins}" } pins.each do |pin| + next unless pin.is_a?(Pin::Reference::Override) + new_pin = if pin.path.end_with?('#initialize') path_pin_hash[pin.path.sub(/#initialize/, '.new')].first end + next unless new_pin.nil? || new_pin.is_a?(Pin::Method) (ovr.tags.map(&:tag_name) + ovr.delete).uniq.each do |tag| pin.docstring.delete_tags tag new_pin.docstring.delete_tags tag if new_pin diff --git a/lib/solargraph/complex_type.rb b/lib/solargraph/complex_type.rb index 53c28ed6e..05e499998 100644 --- a/lib/solargraph/complex_type.rb +++ b/lib/solargraph/complex_type.rb @@ -17,15 +17,15 @@ def initialize types = [UniqueType::UNDEFINED] # @todo @items here should not need an annotation # @type [Array] items = types.flat_map(&:items).uniq(&:to_s) + if items.any? { |i| i.name == 'false' } && items.any? { |i| i.name == 'true' } items.delete_if { |i| i.name == 'false' || i.name == 'true' } - items.unshift(ComplexType::BOOLEAN) + items.unshift(UniqueType::BOOLEAN) end items = [UniqueType::UNDEFINED] if items.any?(&:undefined?) @items = items end - # @sg-ignore Fix "Not enough arguments to Module#protected" protected def equality_fields [self.class, items] end @@ -76,9 +76,13 @@ def self_to_type dst end # @yieldparam [UniqueType] + # @yieldreturn [UniqueType] # @return [Array] - def map &block - @items.map &block + # @sg-ignore Declared return type + # ::Array<::Solargraph::ComplexType::UniqueType> does not match + # inferred type ::Array<::Proc> for Solargraph::ComplexType#map + def map(&block) + @items.map(&block) end # @yieldparam [UniqueType] @@ -155,10 +159,12 @@ def to_s map(&:tag).join(', ') end + # @return [String] def tags map(&:tag).join(', ') end + # @return [String] def simple_tags simplify_literals.tags end @@ -172,6 +178,7 @@ def downcast_to_literal_if_possible ComplexType.new(items.map(&:downcast_to_literal_if_possible)) end + # @return [String] def desc rooted_tags end @@ -184,36 +191,34 @@ def desc # @param allow_reverse_match [Boolean] if true, check if any subtypes # of the expected type match the inferred type # @param allow_empty_params [Boolean] if true, allow a general - # inferred type without parameters to allow a more specific - # expcted type + # inferred type without parameters to conform to a more specific + # expected type # @param allow_any_match [Boolean] if true, any unique type - # matched in the expected qualifies as a match + # matched in the inferred qualifies as a match + # @param allow_undefined [Boolean] if true, treat undefined as a + # wildcard that matches anything + # @param rules [Array<:allow_subtype_skew, :allow_empty_params, :allow_reverse_match, :allow_any_match, :allow_undefined, :allow_unresolved_generic, :allow_unmatched_interface>] + # @param variance [:invariant, :covariant, :contravariant] # @return [Boolean] - def conforms_to? api_map, expected, + def conforms_to?(api_map, expected, situation, - variance: erased_variance(situation), - allow_subtype_skew: false, - allow_empty_params: false, - allow_reverse_match: false, - allow_any_match: false #, -# allow_undefined_in_expected: false + rules = [], + variance: erased_variance(situation)) expected = expected.downcast_to_literal_if_possible inferred = downcast_to_literal_if_possible return duck_types_match?(api_map, expected, inferred) if expected.duck_type? - if allow_any_match - inferred.any? { |inf| inf.conforms_to?(api_map, expected, situation, - allow_subtype_skew: allow_subtype_skew, - allow_empty_params: allow_empty_params, - allow_reverse_match: allow_reverse_match, - allow_any_match: allow_any_match) } + if rules.include? :allow_any_match + inferred.any? do |inf| + inf.conforms_to?(api_map, expected, situation, rules, + variance: variance) + end else - inferred.all? { |inf| inf.conforms_to?(api_map, expected, situation, - allow_subtype_skew: allow_subtype_skew, - allow_empty_params: allow_empty_params, - allow_reverse_match: allow_reverse_match, - allow_any_match: allow_any_match) } + inferred.all? do |inf| + inf.conforms_to?(api_map, expected, situation, rules, + variance: variance) + end end end @@ -231,6 +236,7 @@ def duck_types_match? api_map, expected, inferred true end + # @return [String] def rooted_tags map(&:rooted_tag).join(', ') end @@ -255,6 +261,7 @@ def generic? any?(&:generic?) end + # @return [ComplexType] def simplify_literals ComplexType.new(map(&:simplify_literals)) end diff --git a/lib/solargraph/complex_type/unique_type.rb b/lib/solargraph/complex_type/unique_type.rb index 6bbe9a1bd..361fe06bb 100644 --- a/lib/solargraph/complex_type/unique_type.rb +++ b/lib/solargraph/complex_type/unique_type.rb @@ -11,7 +11,6 @@ class UniqueType attr_reader :all_params, :subtypes, :key_types - # @sg-ignore Fix "Not enough arguments to Module#protected" protected def equality_fields [@name, @all_params, @subtypes, @key_types] end @@ -78,6 +77,7 @@ def initialize(name, key_types = [], subtypes = [], rooted:, parameters_type: ni if parameters_type.nil? raise "You must supply parameters_type if you provide parameters" unless key_types.empty? && subtypes.empty? end + raise "Please remove leading :: and set rooted instead - #{name.inspect}" if name.start_with?('::') @name = name @parameters_type = parameters_type @@ -105,6 +105,7 @@ def to_s tag end + # @return [self] def simplify_literals transform do |t| next t unless t.literal? @@ -116,10 +117,12 @@ def literal? non_literal_name != name end + # @return [String] def non_literal_name @non_literal_name ||= determine_non_literal_name end + # @return [String] def determine_non_literal_name # https://github.com/ruby/rbs/blob/master/docs/syntax.md # @@ -186,35 +189,37 @@ def parameter_variance situation, default = :covariant end end + # Whether this is an RBS interface like _ToAry or _Each. + def interface? + name.start_with?('_') + end + # @param api_map [ApiMap] - # @param expected [ComplexType, ComplexType::UniqueType] + # @param expected [ComplexType::UniqueType] # @param situation [:method_call, :return_type] - # @param allow_subtype_skew [Boolean] if false, check if any - # subtypes of the expected type match the inferred type - # @param allow_empty_params [Boolean] if true, allow a general - # inferred type without parameters to allow a more specific - # expcted type - # @param allow_reverse_match [Boolean] if true, check if any subtypes - # of the expected type match the inferred type - # @param allow_any_match [Boolean] if true, any unique type - # matched in the expected qualifies as a match + # @param rules [Array<:allow_subtype_skew, :allow_empty_params, :allow_reverse_match, :allow_any_match, :allow_undefined, :allow_unresolved_generic, :allow_unmatched_interface>] + # @param variance [:invariant, :covariant, :contravariant] def conforms_to_unique_type?(api_map, expected, situation = :method_call, - variance: erased_variance(situation), - allow_subtype_skew:, - allow_empty_params:, - allow_reverse_match:, - allow_any_match:) - if allow_reverse_match - reversed_match = expected.conforms_to_unique_type? api_map, self, situation, allow_subtype_skew: allow_subtype_skew, - allow_empty_params: allow_empty_params, - allow_reverse_match: false, - allow_any_match: allow_any_match + rules = [], + variance: erased_variance(situation)) + raise "Expected type must be a UniqueType, got #{expected.class} in #{expected.inspect}" unless expected.is_a?(UniqueType) + if literal? && !expected.literal? + return simplify_literals.conforms_to_unique_type?(api_map, expected, situation, + rules, variance: variance) + end + return true if expected.any?(&:interface?) && rules.include?(:allow_unmatched_interface) + return true if interface? && rules.include?(:allow_unmatched_interface) + + if rules.include? :allow_reverse_match + reversed_match = expected.conforms_to?(api_map, self, situation, + rules - [:allow_reverse_match], + variance: variance) return true if reversed_match end expected = expected.downcast_to_literal_if_possible inferred = downcast_to_literal_if_possible - if allow_subtype_skew + if rules.include? :allow_subtype_skew # parameters are not considered in this case expected = expected.erase_parameters end @@ -223,6 +228,10 @@ def conforms_to_unique_type?(api_map, expected, situation = :method_call, inferred = inferred.erase_parameters end + if expected.parameters? && !inferred.parameters? && rules.include?(:allow_empty_params) + expected = expected.erase_parameters + end + return true if inferred == expected if variance == :invariant @@ -240,13 +249,13 @@ def conforms_to_unique_type?(api_map, expected, situation = :method_call, # we contain the expected mix-in, or we have a more general type return false unless api_map.type_include?(inferred.name, expected.name) || - map.super_and_sub?(inferred.name, expected.name) || + api_map.super_and_sub?(inferred.name, expected.name) || inferred.name == expected.name else raise "Unknown erased variance: #{erased_variance.inspect}" end - return true if inferred.all_params.empty? && allow_empty_params + return true if inferred.all_params.empty? && rules.include?(:allow_empty_params) # at this point we know the erased type is fine - time to look at parameters @@ -260,54 +269,50 @@ def conforms_to_unique_type?(api_map, expected, situation = :method_call, return false unless ComplexType.new(inferred.key_types).conforms_to?(api_map, ComplexType.new(expected.key_types), situation, - variance: parameter_variance(situation), - allow_subtype_skew: allow_subtype_skew, - allow_empty_params: allow_empty_params, - allow_reverse_match: allow_reverse_match, - allow_any_match: allow_any_match) + rules, + variance: parameter_variance(situation)) end return true if expected.subtypes.empty? + return true if expected.subtypes.any?(&:undefined?) && rules.include?(:allow_undefined) + + return true if inferred.subtypes.any?(&:undefined?) && rules.include?(:allow_undefined) + + return true if inferred.subtypes.all?(&:generic?) && rules.include?(:allow_unresolved_generic) + + return true if expected.subtypes.all?(&:generic?) && rules.include?(:allow_unresolved_generic) + return false if inferred.subtypes.empty? - ComplexType.new(inferred.subtypes).conforms_to?(api_map, ComplexType.new(expected.subtypes), situation, - variance: parameter_variance(situation), - allow_subtype_skew: allow_subtype_skew, - allow_empty_params: allow_empty_params, - allow_reverse_match: allow_reverse_match, - allow_any_match: allow_any_match) + ComplexType.new(inferred.subtypes).conforms_to?(api_map, + ComplexType.new(expected.subtypes), + situation, + rules, + variance: parameter_variance(situation)) end # @param api_map [ApiMap] - # @param expected [ComplexType::UniqueType] + # @param expected [ComplexType::UniqueType, ComplexType] # @param situation [:method_call, :assignment, :return] - # @param allow_subtype_skew [Boolean] if false, check if any - # subtypes of the expected type match the inferred type - # @param allow_empty_params [Boolean] if true, allow a general - # inferred type without parameters to allow a more specific - # expcted type - # @param allow_reverse_match [Boolean] if true, check if any subtypes - # of the expected type match the inferred type - # @param allow_any_match [Boolean] if true, any unique type - # matched in the expected qualifies as a match + # @param rules [Array<:allow_subtype_skew, :allow_empty_params, :allow_reverse_match, :allow_any_match, :allow_undefined, :allow_unresolved_generic>] + # @param variance [:invariant, :covariant, :contravariant] def conforms_to?(api_map, expected, situation = :method_call, - allow_subtype_skew:, - allow_empty_params:, - allow_reverse_match:, - allow_any_match:) + rules, + variance:) + + return true if undefined? && rules.include?(:allow_undefined) + # @todo teach this to validate duck types as inferred type return true if duck_type? # complex types as expectations are unions - we only need to # match one of their unique types expected.any? do |expected_unique_type| + raise "Expected type must be a UniqueType, got #{expected_unique_type.class} in #{expected.inspect}" unless expected.is_a?(UniqueType) unless expected_unique_type.instance_of?(UniqueType) conforms_to_unique_type?(api_map, expected_unique_type, situation, - allow_subtype_skew: allow_subtype_skew, - allow_empty_params: allow_empty_params, - allow_reverse_match: allow_reverse_match, - allow_any_match: allow_any_match) + rules, variance: variance) end end @@ -315,6 +320,7 @@ def hash [self.class, @name, @key_types, @sub_types, @rooted, @all_params, @parameters_type].hash end + # @return [self] def erase_parameters UniqueType.new(name, rooted: rooted?, parameters_type: parameters_type) end @@ -335,6 +341,7 @@ def rbs_name end end + # @return [String] def desc rooted_tags end @@ -407,7 +414,7 @@ def downcast_to_literal_if_possible # @param generics_to_resolve [Enumerable] # @param context_type [UniqueType, nil] - # @param resolved_generic_values [Hash{String => ComplexType}] Added to as types are encountered or resolved + # @param resolved_generic_values [Hash{String => ComplexType, UniqueType}] Added to as types are encountered or resolved # @return [UniqueType, ComplexType] def resolve_generics_from_context generics_to_resolve, context_type, resolved_generic_values: {} if name == ComplexType::GENERIC_TAG_NAME @@ -505,9 +512,9 @@ def to_a # @param new_name [String, nil] # @param make_rooted [Boolean, nil] - # @param new_key_types [Array, nil] + # @param new_key_types [Array, nil] # @param rooted [Boolean, nil] - # @param new_subtypes [Array, nil] + # @param new_subtypes [Array, nil] # @return [self] def recreate(new_name: nil, make_rooted: nil, new_key_types: nil, new_subtypes: nil) raise "Please remove leading :: and set rooted instead - #{new_name}" if new_name&.start_with?('::') @@ -589,6 +596,7 @@ def self_to_type dst end end + # @yieldreturn [Boolean] def any? &block block.yield self end diff --git a/lib/solargraph/convention/data_definition/data_assignment_node.rb b/lib/solargraph/convention/data_definition/data_assignment_node.rb index 7aadcf190..0ecfb88eb 100644 --- a/lib/solargraph/convention/data_definition/data_assignment_node.rb +++ b/lib/solargraph/convention/data_definition/data_assignment_node.rb @@ -22,6 +22,7 @@ class << self # s(:def, :foo, # s(:args), # s(:send, nil, :bar)))) + # @param node [Parser::AST::Node] def match?(node) return false unless node&.type == :casgn return false if node.children[2].nil? diff --git a/lib/solargraph/convention/data_definition/data_definition_node.rb b/lib/solargraph/convention/data_definition/data_definition_node.rb index dd5929822..5ee79b73d 100644 --- a/lib/solargraph/convention/data_definition/data_definition_node.rb +++ b/lib/solargraph/convention/data_definition/data_definition_node.rb @@ -25,6 +25,7 @@ class << self # s(:def, :foo, # s(:args), # s(:send, nil, :bar))) + # @param node [Parser::AST::Node] def match?(node) return false unless node&.type == :class @@ -46,7 +47,7 @@ def data_definition_node?(data_node) end end - # @return [Parser::AST::Node] + # @param node [Parser::AST::Node] def initialize(node) @node = node end diff --git a/lib/solargraph/convention/struct_definition/struct_assignment_node.rb b/lib/solargraph/convention/struct_definition/struct_assignment_node.rb index 04f96d40e..2816de6ed 100644 --- a/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +++ b/lib/solargraph/convention/struct_definition/struct_assignment_node.rb @@ -22,6 +22,8 @@ class << self # s(:def, :foo, # s(:args), # s(:send, nil, :bar)))) + # + # @param node [Parser::AST::Node] def match?(node) return false unless node&.type == :casgn return false if node.children[2].nil? diff --git a/lib/solargraph/convention/struct_definition/struct_definition_node.rb b/lib/solargraph/convention/struct_definition/struct_definition_node.rb index 540320c37..7c3d722d0 100644 --- a/lib/solargraph/convention/struct_definition/struct_definition_node.rb +++ b/lib/solargraph/convention/struct_definition/struct_definition_node.rb @@ -25,6 +25,7 @@ class << self # s(:def, :foo, # s(:args), # s(:send, nil, :bar))) + # @param node [Parser::AST::Node] def match?(node) return false unless node&.type == :class @@ -46,7 +47,7 @@ def struct_definition_node?(struct_node) end end - # @return [Parser::AST::Node] + # @param node [Parser::AST::Node] def initialize(node) @node = node end diff --git a/lib/solargraph/gem_pins.rb b/lib/solargraph/gem_pins.rb index f1dd25a9f..2a3f392f6 100644 --- a/lib/solargraph/gem_pins.rb +++ b/lib/solargraph/gem_pins.rb @@ -25,13 +25,14 @@ def self.combine_method_pins_by_path(pins) # bad_pins = pins.select { |pin| pin.is_a?(Pin::Method) && pin.path == 'StringIO.open' && pin.source == :rbs }; raise "wtf: #{bad_pins}" if bad_pins.length > 1 method_pins, alias_pins = pins.partition { |pin| pin.class == Pin::Method } by_path = method_pins.group_by(&:path) - by_path.transform_values! do |pins| + combined_by_path = by_path.transform_values do |pins| GemPins.combine_method_pins(*pins) end - by_path.values + alias_pins + combined_by_path.values + alias_pins end def self.combine_method_pins(*pins) + # @type [Pin::Method, nil] out = pins.reduce(nil) do |memo, pin| next pin if memo.nil? if memo == pin && memo.source != :combined diff --git a/lib/solargraph/language_server/host.rb b/lib/solargraph/language_server/host.rb index 1c5831bda..e85fc813a 100644 --- a/lib/solargraph/language_server/host.rb +++ b/lib/solargraph/language_server/host.rb @@ -299,6 +299,7 @@ def prepare directory, name = nil end end + # @return [String] def command_path options['commandPath'] || 'solargraph' end @@ -716,7 +717,7 @@ def diagnoser # A hash of client requests by ID. The host uses this to keep track of # pending responses. # - # @return [Hash{Integer => Solargraph::LanguageServer::Host}] + # @return [Hash{Integer => Request}] def requests @requests ||= {} end diff --git a/lib/solargraph/parser/flow_sensitive_typing.rb b/lib/solargraph/parser/flow_sensitive_typing.rb index 8fb26d498..8dd80a5a0 100644 --- a/lib/solargraph/parser/flow_sensitive_typing.rb +++ b/lib/solargraph/parser/flow_sensitive_typing.rb @@ -11,7 +11,9 @@ def initialize(locals, enclosing_breakable_pin = nil) # @param and_node [Parser::AST::Node] def process_and(and_node, true_ranges = []) + # @type [Parser::AST::Node] lhs = and_node.children[0] + # @type [Parser::AST::Node] rhs = and_node.children[1] before_rhs_loc = rhs.location.expression.adjust(begin_pos: -1) @@ -36,7 +38,9 @@ def process_if(if_node) # s(:send, nil, :bar)) # [4] pry(main)> conditional_node = if_node.children[0] + # @type [Parser::AST::Node] then_clause = if_node.children[1] + # @type [Parser::AST::Node] else_clause = if_node.children[2] true_ranges = [] @@ -142,6 +146,8 @@ def process_facts(facts_by_pin, presences) end # @param conditional_node [Parser::AST::Node] + # @param true_ranges [Array] + # @return [void] def process_conditional(conditional_node, true_ranges) if conditional_node.type == :send process_isa(conditional_node, true_ranges) diff --git a/lib/solargraph/parser/node_processor.rb b/lib/solargraph/parser/node_processor.rb index a55b7120b..a1a4d811f 100644 --- a/lib/solargraph/parser/node_processor.rb +++ b/lib/solargraph/parser/node_processor.rb @@ -23,6 +23,9 @@ def register type, cls @@processors[type] << cls end + # @param type [Symbol] + # @param cls [Class] + # @return [void] def deregister type, cls @@processors[type].delete(cls) end @@ -31,7 +34,7 @@ def deregister type, cls # @param node [Parser::AST::Node] # @param region [Region] # @param pins [Array] - # @param locals [Array] + # @param locals [Array] # @return [Array(Array, Array)] def self.process node, region = Region.new, pins = [], locals = [] if pins.empty? diff --git a/lib/solargraph/parser/parser_gem/node_methods.rb b/lib/solargraph/parser/parser_gem/node_methods.rb index bc0c37eb6..674013257 100644 --- a/lib/solargraph/parser/parser_gem/node_methods.rb +++ b/lib/solargraph/parser/parser_gem/node_methods.rb @@ -345,7 +345,7 @@ def value_position_nodes_only(node) # Look at known control statements and use them to find # more specific return nodes. # - # @param node [Parser::AST::Node] Statement which is in + # @param node [AST::Node] Statement which is in # value position for a method body # @param include_explicit_returns [Boolean] If true, # include the value nodes of the parameter of the diff --git a/lib/solargraph/parser/parser_gem/node_processors/if_node.rb b/lib/solargraph/parser/parser_gem/node_processors/if_node.rb index 5784afcbe..2452b9cc5 100644 --- a/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +++ b/lib/solargraph/parser/parser_gem/node_processors/if_node.rb @@ -11,6 +11,8 @@ def process process_children position = get_node_start_position(node) + # @sg-ignore + # @type [Solargraph::Pin::Breakable, nil] enclosing_breakable_pin = pins.select{|pin| pin.is_a?(Pin::Breakable) && pin.location.range.contain?(position)}.last FlowSensitiveTyping.new(locals, enclosing_breakable_pin).process_if(node) end diff --git a/lib/solargraph/parser/snippet.rb b/lib/solargraph/parser/snippet.rb index d28c57c8c..3b609d31b 100644 --- a/lib/solargraph/parser/snippet.rb +++ b/lib/solargraph/parser/snippet.rb @@ -1,7 +1,7 @@ module Solargraph module Parser class Snippet - # @return [Range] + # @return [Solargraph::Range] attr_reader :range # @return [String] attr_reader :text diff --git a/lib/solargraph/pin/base.rb b/lib/solargraph/pin/base.rb index cdd6a5ace..c9e308056 100644 --- a/lib/solargraph/pin/base.rb +++ b/lib/solargraph/pin/base.rb @@ -269,9 +269,13 @@ def assert_same_count(other, attr) # @param other [self] # @param attr [::Symbol] # - # @return [Object, nil] + # @return [undefined] def assert_same(other, attr) - return false if other.nil? + if other.nil? + Solargraph.assert_or_log("combine_with_#{attr}".to_sym, + "Inconsistent #{attr.inspect} values between \nself =#{inspect} and \nother=#{other.inspect}:\n\n self.#{attr} = #{val1.inspect}\nother.#{attr} = #{val2.inspect}") + return send(attr) + end val1 = send(attr) val2 = other.send(attr) return val1 if val1 == val2 diff --git a/lib/solargraph/pin/method.rb b/lib/solargraph/pin/method.rb index 749868246..c3e29b8e3 100644 --- a/lib/solargraph/pin/method.rb +++ b/lib/solargraph/pin/method.rb @@ -50,7 +50,7 @@ def combine_all_signature_pins(*signature_pins) end # @param other [Pin::Method] - # @return [Symbol] + # @return [::Symbol] def combine_visibility(other) if dodgy_visibility_source? && !other.dodgy_visibility_source? other.visibility diff --git a/lib/solargraph/pin/parameter.rb b/lib/solargraph/pin/parameter.rb index b4fc3d9b2..d806d6de1 100644 --- a/lib/solargraph/pin/parameter.rb +++ b/lib/solargraph/pin/parameter.rb @@ -171,11 +171,7 @@ def compatible_arg?(atype, api_map) return true if atype.conforms_to?(api_map, ptype, :method_call, - allow_subtype_skew: false, - allow_reverse_match: false, - allow_empty_params: true, - allow_any_match: false) - + [:allow_empty_params, :allow_undefined]) ptype.generic? end diff --git a/lib/solargraph/rbs_map/conversions.rb b/lib/solargraph/rbs_map/conversions.rb index 6e50c022a..44fb72946 100644 --- a/lib/solargraph/rbs_map/conversions.rb +++ b/lib/solargraph/rbs_map/conversions.rb @@ -95,7 +95,7 @@ def convert_self_type_to_pins decl, closure type = build_type(decl.name, decl.args) generic_values = type.all_params.map(&:to_s) include_pin = Solargraph::Pin::Reference::Include.new( - name: decl.name.relative!.to_s, + name: type.rooted_name, type_location: location_decl_to_pin_location(decl.location), generic_values: generic_values, closure: closure, @@ -184,7 +184,7 @@ def class_decl_to_pin decl type_location: location_decl_to_pin_location(decl.super_class.location), closure: class_pin, generic_values: generic_values, - name: decl.super_class.name.relative!.to_s, + name: type.rooted_name, source: :rbs ) end @@ -229,6 +229,8 @@ def module_decl_to_pin decl convert_self_types_to_pins decl, module_pin convert_members_to_pins decl, module_pin + raise "Invalid type for module declaration: #{module_pin.class}" unless module_pin.is_a?(Pin::Namespace) + add_mixins decl, module_pin.closure end diff --git a/lib/solargraph/source/chain/if.rb b/lib/solargraph/source/chain/if.rb index c14d00ddf..3a7fa0ca9 100644 --- a/lib/solargraph/source/chain/if.rb +++ b/lib/solargraph/source/chain/if.rb @@ -8,7 +8,7 @@ def word '' end - # @param links [::Array] + # @param links [::Array] def initialize links @links = links end diff --git a/lib/solargraph/source/chain/or.rb b/lib/solargraph/source/chain/or.rb index 1e3a70f40..9264d4107 100644 --- a/lib/solargraph/source/chain/or.rb +++ b/lib/solargraph/source/chain/or.rb @@ -8,7 +8,7 @@ def word '' end - # @param links [::Array] + # @param links [::Array] def initialize links @links = links end diff --git a/lib/solargraph/type_checker.rb b/lib/solargraph/type_checker.rb index ab87d5863..953832a36 100644 --- a/lib/solargraph/type_checker.rb +++ b/lib/solargraph/type_checker.rb @@ -36,6 +36,35 @@ def source_map @source_map ||= api_map.source_map(filename) end + # @return [Source] + def source + @source_map.source + end + + def return_type_conforms_to?(inferred, expected) + conforms_to?(inferred, expected, :return_type) + end + + def arg_conforms_to?(inferred, expected) + conforms_to?(inferred, expected, :method_call) + end + + def assignment_conforms_to?(inferred, expected) + conforms_to?(inferred, expected, :assignment) + end + + def conforms_to?(inferred, expected, scenario) + rules_arr = [] + rules_arr << :allow_empty_params unless rules.require_inferred_type_params? + rules_arr << :allow_any_match unless rules.require_all_unique_types_match_declared? + rules_arr << :allow_undefined unless rules.require_no_undefined_args? + rules_arr << :allow_unresolved_generic unless rules.require_generics_resolved? + rules_arr << :allow_unmatched_interface unless rules.require_interfaces_resolved? + rules_arr << :allow_reverse_match unless rules.require_downcasts? + inferred.conforms_to?(api_map, expected, scenario, + rules_arr) + end + # @return [Array] def problems @problems ||= begin @@ -111,11 +140,7 @@ def method_return_type_problems_for pin result.push Problem.new(pin.location, "#{pin.path} return type could not be inferred", pin: pin) end else - unless inferred.conforms_to?(api_map, declared, :return_type, - allow_subtype_skew: false, - allow_empty_params: !rules.require_inferred_type_params, - allow_reverse_match: false, - allow_any_match: !rules.require_all_unique_types_match_declared?) + unless return_type_conforms_to?(inferred, declared) result.push Problem.new(pin.location, "Declared return type #{declared.rooted_tags} does not match inferred type #{inferred.rooted_tags} for #{pin.path}", pin: pin) end end @@ -204,11 +229,7 @@ def variable_type_tag_problems result.push Problem.new(pin.location, "Variable type could not be inferred for #{pin.name}", pin: pin) end else - unless inferred.conforms_to?(api_map, declared, :assignment, - allow_subtype_skew: false, - allow_empty_params: !rules.require_inferred_type_params, - allow_reverse_match: false, - allow_any_match: !rules.require_all_unique_types_match_declared?) + unless assignment_conforms_to?(inferred, declared) result.push Problem.new(pin.location, "Declared type #{declared} does not match inferred type #{inferred} for variable #{pin.name}", pin: pin) end end @@ -389,11 +410,7 @@ def argument_problems_for chain, api_map, block_pin, locals, location # @todo Some level (strong, I guess) should require the param here else argtype = argchain.infer(api_map, block_pin, locals) - if argtype.defined? && ptype.defined? && !argtype.conforms_to?(api_map, ptype, :method_call, - allow_subtype_skew: false, - allow_empty_params: !rules.require_inferred_type_params, - allow_reverse_match: false, - allow_any_match: !rules.require_all_unique_types_match_declared?) + if argtype.defined? && ptype.defined? && !arg_conforms_to?(argtype, ptype) errors.push Problem.new(location, "Wrong argument type for #{pin.path}: #{par.name} expected #{ptype}, received #{argtype}") next end @@ -443,13 +460,8 @@ def kwarg_problems_for sig, argchain, api_map, block_pin, locals, location, pin, else ptype = data[:qualified] unless ptype.undefined? - argtype = argchain.infer(api_map, block_pin, locals) - if argtype.defined? && ptype && !argtype.conforms_to?(api_map, ptype, :method_call, - allow_subtype_skew: false, - allow_empty_params: !rules.require_inferred_type_params, - allow_reverse_match: false, - allow_any_match: !rules.require_all_unique_types_match_declared?) + if argtype.defined? && ptype && !arg_conforms_to?(argtype, ptype) result.push Problem.new(location, "Wrong argument type for #{pin.path}: #{par.name} expected #{ptype}, received #{argtype}") end end @@ -475,12 +487,7 @@ def kwrestarg_problems_for(api_map, block_pin, locals, location, pin, params, kw next unless params.key?(pname.to_s) ptype = params[pname.to_s][:qualified] argtype = argchain.infer(api_map, block_pin, locals) - if argtype.defined? && ptype && !argtype.conforms_to?(api_map, ptype, :method_call, - allow_subtype_skew: false, - allow_empty_params: !rules.require_inferred_type_params, - allow_reverse_match: false, - allow_any_match: !rules.require_all_unique_types_match_declared?) - + if argtype.defined? && ptype && !arg_conforms_to?(argtype, ptype) result.push Problem.new(location, "Wrong argument type for #{pin.path}: #{pname} expected #{ptype}, received #{argtype}") end end diff --git a/lib/solargraph/type_checker/rules.rb b/lib/solargraph/type_checker/rules.rb index 8f2027d30..33ec0c4d0 100644 --- a/lib/solargraph/type_checker/rules.rb +++ b/lib/solargraph/type_checker/rules.rb @@ -54,13 +54,37 @@ def validate_tags? rank > LEVELS[:normal] end - def require_inferred_type_params + def require_inferred_type_params? rank >= LEVELS[:alpha] end def require_all_unique_types_match_declared? rank >= LEVELS[:alpha] end + + def require_no_undefined_args? + rank >= LEVELS[:alpha] + end + + def require_generics_resolved? + rank >= LEVELS[:alpha] + end + + def require_interfaces_resolved? + rank >= LEVELS[:alpha] + end + + def require_downcasts? + rank >= LEVELS[:alpha] + end + + # We keep this at strong because if you added an @sg-ignore to + # address a strong-level issue, then ran at a lower level, you'd + # get a false positive - we don't run stronger level checks than + # requested for performance reasons + def validate_sg_ignores? + rank >= LEVELS[:strong] + end end end end diff --git a/lib/solargraph/yard_map/mapper/to_method.rb b/lib/solargraph/yard_map/mapper/to_method.rb index df431bb3c..d8e3b8b43 100644 --- a/lib/solargraph/yard_map/mapper/to_method.rb +++ b/lib/solargraph/yard_map/mapper/to_method.rb @@ -27,8 +27,8 @@ def self.make code_object, name = nil, scope = nil, visibility = nil, closure = final_scope = scope || code_object.scope override_key = [closure.path, final_scope, name] final_visibility = VISIBILITY_OVERRIDE[override_key] - final_visibility ||= VISIBILITY_OVERRIDE[override_key[0..-2]] - final_visibility ||= :private if closure.path == 'Kernel' && Kernel.private_instance_methods(false).include?(name) + final_visibility ||= VISIBILITY_OVERRIDE[[closure.path, final_scope]] + final_visibility ||= :private if closure.path == 'Kernel' && Kernel.private_instance_methods(false).include?(name.to_sym) final_visibility ||= visibility final_visibility ||= :private if code_object.module_function? && final_scope == :instance final_visibility ||= :public if code_object.module_function? && final_scope == :class diff --git a/spec/complex_type/conforms_to_spec.rb b/spec/complex_type/conforms_to_spec.rb index 847da8563..5755721b4 100644 --- a/spec/complex_type/conforms_to_spec.rb +++ b/spec/complex_type/conforms_to_spec.rb @@ -67,7 +67,7 @@ class Sub < Sup; end api_map = Solargraph::ApiMap.new exp = Solargraph::ComplexType.parse('Hash{ Symbol => String}') inf = Solargraph::ComplexType.parse('Hash') - match = inf.conforms_to?(api_map, exp, :method_call, allow_empty_params: true) + match = inf.conforms_to?(api_map, exp, :method_call, [:allow_empty_params]) expect(match).to be(true) end @@ -115,7 +115,7 @@ class Sub < Sup; end api_map = Solargraph::ApiMap.new exp = Solargraph::ComplexType.parse('Class') inf = Solargraph::ComplexType.parse('Class') - match = inf.conforms_to?(api_map, exp, :method_call, allow_empty_params: true) + match = inf.conforms_to?(api_map, exp, :method_call, [:allow_empty_params]) expect(match).to be(true) end @@ -136,9 +136,9 @@ class Sub < Sup; end api_map.map source sup = Solargraph::ComplexType.parse('Sup') sub = Solargraph::ComplexType.parse('Sub') - match = sub.conforms_to?(api_map, sup, :method_call, allow_reverse_match: true) + match = sub.conforms_to?(api_map, sup, :method_call, [:allow_reverse_match]) expect(match).to be(true) - match = sup.conforms_to?(api_map, sub, :method_call, allow_reverse_match: true) + match = sup.conforms_to?(api_map, sub, :method_call, [:allow_reverse_match]) expect(match).to be(true) end @@ -146,9 +146,9 @@ class Sub < Sup; end api_map = Solargraph::ApiMap.new sup = Solargraph::ComplexType.parse('String') sub = Solargraph::ComplexType.parse('Array') - match = sub.conforms_to?(api_map, sup, :method_call, allow_reverse_match: true) + match = sub.conforms_to?(api_map, sup, :method_call, [:allow_reverse_match]) expect(match).to be(false) - match = sup.conforms_to?(api_map, sub, :method_call, allow_reverse_match: true) + match = sup.conforms_to?(api_map, sub, :method_call, [:allow_reverse_match]) expect(match).to be(false) end end diff --git a/spec/complex_type_spec.rb b/spec/complex_type_spec.rb index 2c060ceed..dd20099eb 100644 --- a/spec/complex_type_spec.rb +++ b/spec/complex_type_spec.rb @@ -738,28 +738,28 @@ def make_bar api_map = Solargraph::ApiMap.new ptype = Solargraph::ComplexType.parse('String') atype = Solargraph::ComplexType.parse('String') - expect(ptype.conforms_to?(api_map, atype, :method_call)).to be(true) + expect(atype.conforms_to?(api_map, ptype, :method_call)).to be(true) end it 'recognizes an erased container type conforms with itself' do api_map = Solargraph::ApiMap.new ptype = Solargraph::ComplexType.parse('Hash') atype = Solargraph::ComplexType.parse('Hash') - expect(ptype.conforms_to?(api_map, atype, :method_call)).to be(true) + expect(atype.conforms_to?(api_map, ptype, :method_call)).to be(true) end it 'recognizes an unerased container type conforms with itself' do api_map = Solargraph::ApiMap.new ptype = Solargraph::ComplexType.parse('Array') atype = Solargraph::ComplexType.parse('Array') - expect(ptype.conforms_to?(api_map, atype, :method_call)).to be(true) + expect(atype.conforms_to?(api_map, ptype, :method_call)).to be(true) end it 'recognizes a literal conforms with its type' do api_map = Solargraph::ApiMap.new ptype = Solargraph::ComplexType.parse('Symbol') atype = Solargraph::ComplexType.parse(':foo') - expect(ptype.conforms_to?(api_map, atype, :method_call)).to be(true) + expect(atype.conforms_to?(api_map, ptype, :method_call)).to be(true) end end end diff --git a/spec/rbs_map/conversions_spec.rb b/spec/rbs_map/conversions_spec.rb index 09c203687..75ec1c311 100644 --- a/spec/rbs_map/conversions_spec.rb +++ b/spec/rbs_map/conversions_spec.rb @@ -1,54 +1,75 @@ describe Solargraph::RbsMap::Conversions do - # create a temporary directory with the scope of the spec - around do |example| - require 'tmpdir' - Dir.mktmpdir("rspec-solargraph-") do |dir| - @temp_dir = dir - example.run + context 'with custom RBS files' do + # create a temporary directory with the scope of the spec + around do |example| + require 'tmpdir' + Dir.mktmpdir("rspec-solargraph-") do |dir| + @temp_dir = dir + example.run + end end - end - let(:rbs_repo) do - RBS::Repository.new(no_stdlib: false) - end + let(:rbs_repo) do + RBS::Repository.new(no_stdlib: false) + end - let(:loader) do - RBS::EnvironmentLoader.new(core_root: nil, repository: rbs_repo) - end + let(:loader) do + RBS::EnvironmentLoader.new(core_root: nil, repository: rbs_repo) + end - let(:conversions) do - Solargraph::RbsMap::Conversions.new(loader: loader) - end + let(:conversions) do + Solargraph::RbsMap::Conversions.new(loader: loader) + end - let(:pins) do - conversions.pins - end + let(:pins) do + conversions.pins + end - before do - rbs_file = File.join(temp_dir, 'foo.rbs') - File.write(rbs_file, rbs) - loader.add(path: Pathname(temp_dir)) - end + before do + rbs_file = File.join(temp_dir, 'foo.rbs') + File.write(rbs_file, rbs) + loader.add(path: Pathname(temp_dir)) + end - attr_reader :temp_dir + attr_reader :temp_dir - context 'with untyped response' do - let(:rbs) do - <<~RBS + context 'with untyped response' do + let(:rbs) do + <<~RBS class Foo def bar: () -> untyped end RBS + end + + subject(:method_pin) { pins.find { |pin| pin.path == 'Foo#bar' } } + + it { should_not be_nil } + + it { should be_a(Solargraph::Pin::Method) } + + it 'maps untyped in RBS to undefined in Solargraph 'do + expect(method_pin.return_type.tag).to eq('undefined') + end end + end - subject(:method_pin) { pins.find { |pin| pin.path == 'Foo#bar' } } + context 'with standard loads for solargraph project' do + let(:api_map) { Solargraph::ApiMap.load('.') } - it { should_not be_nil } + let(:superclass_pin) do + api_map.pins.find do |pin| + pin.is_a?(Solargraph::Pin::Reference::Superclass) && pin.context.namespace == 'Parser::AST::Node' + end + end - it { should be_a(Solargraph::Pin::Method) } + it 'finds a superclass pin for Parser::AST::Node' do + expect(superclass_pin).not_to be_nil + end - it 'maps untyped in RBS to undefined in Solargraph 'do - expect(method_pin.return_type.tag).to eq('undefined') + it 'generates a rooted pin for superclass of Parser::AST::Node' do + # rooted! + expect(superclass_pin.name) .to eq('::AST::Node') end end end diff --git a/spec/type_checker/levels/strong_spec.rb b/spec/type_checker/levels/strong_spec.rb index 054a09efa..4c45056ea 100644 --- a/spec/type_checker/levels/strong_spec.rb +++ b/spec/type_checker/levels/strong_spec.rb @@ -141,6 +141,167 @@ def bar &block expect(checker.problems).to be_empty end + it 'does not need fully specified container types' do + checker = type_checker(%( + class Foo + # @param foo [Array] + # @return [void] + def bar foo: []; end + + # @param bing [Array] + # @return [void] + def baz(bing) + bar(foo: bing) + generic_values = [1,2,3].map(&:to_s) + bar(foo: generic_values) + end + end + )) + expect(checker.problems.map(&:message)).to be_empty + end + + it 'treats a parameter type of undefined as not provided' do + checker = type_checker(%( + class Foo + # @param foo [Array] + # @return [void] + def bar foo: []; end + + # @param bing [Array] + # @return [void] + def baz(bing) + bar(foo: bing) + generic_values = [1,2,3].map(&:to_s) + bar(foo: generic_values) + end + end + )) + expect(checker.problems.map(&:message)).to be_empty + end + + it 'treats a parameter type of undefined as not provided' do + checker = type_checker(%( + class Foo + # @param foo [Class] + # @return [void] + def bar foo:; end + + # @param bing [Class] + # @return [void] + def baz(bing) + bar(foo: bing) + end + end + )) + expect(checker.problems.map(&:message)).to be_empty + end + + it 'ignores generic resolution failures' do + checker = type_checker(%( + class Foo + # @param foo [Class] + # @return [void] + def bar foo:; end + + # @param bing [Class>] + # @return [void] + def baz(bing) + bar(foo: bing) + end + end + )) + expect(checker.problems.map(&:message)).to be_empty + end + + it 'ignores undefined resolution failures' do + checker = type_checker(%( + class Foo + # @generic T + # @param klass [Class>] + # @return [Set>] + def pins_by_class klass; [].to_set; end + end + class Bar + # @return [Enumerable] + def block_pins + foo = Foo.new + foo.pins_by_class(Integer) + end + end + )) + expect(checker.problems.map(&:message)).to be_empty + end + + + it 'ignores generic resolution failures' do + checker = type_checker(%( + class Foo + # @generic T + # @param klass [Class>] + # @return [Set>] + def pins_by_class klass; [].to_set; end + end + class Bar + # @return [Enumerable] + def block_pins + foo = Foo.new + foo.pins_by_class(Integer) + end + end + )) + expect(checker.problems.map(&:message)).to be_empty + end + + it 'ignores generic resolution failures' do + checker = type_checker(%( + # @generic T + # @param path [String] + # @param klass [Class>] + # @return [void] + def code_object_at path, klass = Integer + end + )) + expect(checker.problems.map(&:message)).to be_empty + end + + it 'does not complain on select { is_a? } pattern' do + checker = type_checker(%( + # @param arr [Enumerable} + # @return [Enumerable] + def downcast_arr(arr) + arr.select { |pin| pin.is_a?(Integer) } + end + )) + expect(checker.problems.map(&:message)).to be_empty + end + + it 'does not complain on adding nil to types via return value' do + checker = type_checker(%( + # @param bar [Integer] + # @return [Integer, nil] + def foo(bar) + bar + end + )) + expect(checker.problems.map(&:message)).to be_empty + end + + it 'does not complain on adding nil to types via select' do + checker = type_checker(%( + # @return [Float, nil]} + def bar; rand; end + + # @param arr [Enumerable} + # @return [Integer, nil] + def downcast_arr(arr) + # @type [Object, nil] + foo = arr.select { |pin| pin.is_a?(Integer) && bar }.last + foo + end + )) + expect(checker.problems.map(&:message)).to be_empty + end + it 'inherits param tags from superclass methods' do checker = type_checker(%( class Foo From 898bb87e30bdf9f9837bb986deb467e7b5324404 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 22 Jul 2025 09:11:41 -0400 Subject: [PATCH 294/561] Fix specs --- lib/solargraph/api_map/index.rb | 11 ++++------- lib/solargraph/doc_map.rb | 2 ++ lib/solargraph/rbs_map/core_map.rb | 5 ++++- spec/rbs_map/core_map_spec.rb | 2 +- spec/type_checker/levels/strict_spec.rb | 13 +++++++++++++ spec/type_checker/levels/typed_spec.rb | 12 ------------ 6 files changed, 24 insertions(+), 21 deletions(-) diff --git a/lib/solargraph/api_map/index.rb b/lib/solargraph/api_map/index.rb index 42bb6cc32..9015cd6d5 100644 --- a/lib/solargraph/api_map/index.rb +++ b/lib/solargraph/api_map/index.rb @@ -39,22 +39,22 @@ def pins_by_class klass @pin_select_cache[klass] ||= pin_class_hash.each_with_object(s) { |(key, o), n| n.merge(o) if key <= klass } end - # @return [Hash{String => Array}] + # @return [Hash{String => Array}] def include_references @include_references ||= Hash.new { |h, k| h[k] = [] } end - # @return [Hash{String => Array}] + # @return [Hash{String => Array}] def extend_references @extend_references ||= Hash.new { |h, k| h[k] = [] } end - # @return [Hash{String => Array}] + # @return [Hash{String => Array}] def prepend_references @prepend_references ||= Hash.new { |h, k| h[k] = [] } end - # @return [Hash{String => Array}] + # @return [Hash{String => Array}] def superclass_references @superclass_references ||= Hash.new { |h, k| h[k] = [] } end @@ -142,12 +142,9 @@ def map_overrides pins = path_pin_hash[ovr.name] logger.debug { "ApiMap::Index#map_overrides: pins for path=#{ovr.name}: #{pins}" } pins.each do |pin| - next unless pin.is_a?(Pin::Reference::Override) - new_pin = if pin.path.end_with?('#initialize') path_pin_hash[pin.path.sub(/#initialize/, '.new')].first end - next unless new_pin.nil? || new_pin.is_a?(Pin::Method) (ovr.tags.map(&:tag_name) + ovr.delete).uniq.each do |tag| pin.docstring.delete_tags tag new_pin.docstring.delete_tags tag if new_pin diff --git a/lib/solargraph/doc_map.rb b/lib/solargraph/doc_map.rb index d51fc3022..186037460 100644 --- a/lib/solargraph/doc_map.rb +++ b/lib/solargraph/doc_map.rb @@ -150,6 +150,8 @@ def load_serialized_gem_pins @uncached_yard_gemspecs = [] @uncached_rbs_collection_gemspecs = [] with_gemspecs, without_gemspecs = required_gems_map.partition { |_, v| v } + # @sg-ignore Need Hash[] support + # @type [Array] paths = Hash[without_gemspecs].keys gemspecs = Hash[with_gemspecs].values.flatten.compact + dependencies.to_a diff --git a/lib/solargraph/rbs_map/core_map.rb b/lib/solargraph/rbs_map/core_map.rb index 0d265d773..5e030d9f6 100644 --- a/lib/solargraph/rbs_map/core_map.rb +++ b/lib/solargraph/rbs_map/core_map.rb @@ -24,8 +24,11 @@ def pins else loader.add(path: Pathname(FILLS_DIRECTORY)) @pins = conversions.pins + # add some overrides @pins.concat RbsMap::CoreFills::ALL - processed = ApiMap::Store.new(pins).pins.reject { |p| p.is_a?(Solargraph::Pin::Reference::Override) } + # process overrides, then remove any which couldn't be resolved + processed = ApiMap::Store.new(@pins).pins.reject { |p| p.is_a?(Solargraph::Pin::Reference::Override) } + STDOUT.puts "RBS core pins cache size: #{@pins.size}" @pins.replace processed PinCache.serialize_core @pins diff --git a/spec/rbs_map/core_map_spec.rb b/spec/rbs_map/core_map_spec.rb index 352d29937..88590925b 100644 --- a/spec/rbs_map/core_map_spec.rb +++ b/spec/rbs_map/core_map_spec.rb @@ -6,7 +6,7 @@ pin = store.get_path_pins("Errno::#{const}").first expect(pin).to be_a(Solargraph::Pin::Namespace) superclass = store.get_superclass(pin.path) - expect(superclass).to eq('SystemCallError') + expect(superclass).to eq('::SystemCallError') end end diff --git a/spec/type_checker/levels/strict_spec.rb b/spec/type_checker/levels/strict_spec.rb index b198cec89..7861c8817 100644 --- a/spec/type_checker/levels/strict_spec.rb +++ b/spec/type_checker/levels/strict_spec.rb @@ -666,6 +666,19 @@ def test(foo: nil) expect(checker.problems).to be_empty end + + it 'validates parameters in function calls' do + checker = type_checker(%( + # @param bar [String] + def foo(bar); end + + def baz + foo(123) + end + )) + expect(checker.problems.map(&:message)).to eq(['Wrong argument type for #foo: bar expected String, received 123']) + end + it 'validates inferred return types with complex tags' do checker = type_checker(%( # @param foo [Numeric, nil] a foo diff --git a/spec/type_checker/levels/typed_spec.rb b/spec/type_checker/levels/typed_spec.rb index 659ccee39..6e71ee9ff 100644 --- a/spec/type_checker/levels/typed_spec.rb +++ b/spec/type_checker/levels/typed_spec.rb @@ -202,18 +202,6 @@ def foo expect(checker.problems).to be_empty end - it 'validates parameters in function calls' do - checker = type_checker(%( - # @param bar [String] - def foo(bar); end - - def baz - foo(123) - end - )) - expect(checker.problems.map(&:message)).to eq(['123']) - end - it 'validates default values of parameters' do checker = type_checker(%( # @param bar [String] From b6bfe7b4fd9c8a52038cc096bd29050e244b6fed Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 22 Jul 2025 10:11:45 -0400 Subject: [PATCH 295/561] Generic typechecking improvements --- .../language_server/message/base.rb | 2 +- .../message/extended/check_gem_version.rb | 6 - rbs/fills/rubygems/0/spec_fetcher.rbs | 107 ++++++++++++++++++ 3 files changed, 108 insertions(+), 7 deletions(-) create mode 100644 rbs/fills/rubygems/0/spec_fetcher.rbs diff --git a/lib/solargraph/language_server/message/base.rb b/lib/solargraph/language_server/message/base.rb index fbc55ccbd..b2df0c46a 100644 --- a/lib/solargraph/language_server/message/base.rb +++ b/lib/solargraph/language_server/message/base.rb @@ -16,7 +16,7 @@ class Base # @return [String] attr_reader :method - # @return [Hash{String => Array, Hash{String => undefined}, String, Integer}] + # @return [Hash{String => undefined}] attr_reader :params # @return [Hash, Array, nil] diff --git a/lib/solargraph/language_server/message/extended/check_gem_version.rb b/lib/solargraph/language_server/message/extended/check_gem_version.rb index 2e80f40c6..06892ed19 100644 --- a/lib/solargraph/language_server/message/extended/check_gem_version.rb +++ b/lib/solargraph/language_server/message/extended/check_gem_version.rb @@ -1,12 +1,6 @@ # frozen_string_literal: true -# @todo PR the RBS gem to add this -# @!parse -# module ::Gem -# class SpecFetcher; end -# end - module Solargraph module LanguageServer module Message diff --git a/rbs/fills/rubygems/0/spec_fetcher.rbs b/rbs/fills/rubygems/0/spec_fetcher.rbs new file mode 100644 index 000000000..9914dc85d --- /dev/null +++ b/rbs/fills/rubygems/0/spec_fetcher.rbs @@ -0,0 +1,107 @@ +# +# SpecFetcher handles metadata updates from remote gem repositories. +# +class Gem::SpecFetcher + self.@fetcher: untyped + + @sources: untyped + + @update_cache: untyped + + @specs: untyped + + @latest_specs: untyped + + @prerelease_specs: untyped + + @caches: untyped + + @fetcher: untyped + + include Gem::UserInteraction + + include Gem::Text + + attr_reader latest_specs: untyped + + attr_reader sources: untyped + + attr_reader specs: untyped + + attr_reader prerelease_specs: untyped + + # + # Default fetcher instance. Use this instead of ::new to reduce object + # allocation. + # + def self.fetcher: () -> untyped + + def self.fetcher=: (untyped fetcher) -> untyped + + # + # Creates a new SpecFetcher. Ordinarily you want to use the default fetcher + # from Gem::SpecFetcher::fetcher which uses the Gem.sources. + # + # If you need to retrieve specifications from a different `source`, you can send + # it as an argument. + # + def initialize: (?untyped? sources) -> void + + # + # Find and fetch gem name tuples that match `dependency`. + # + # If `matching_platform` is false, gems for all platforms are returned. + # + def search_for_dependency: (untyped dependency, ?bool matching_platform) -> ::Array[untyped] + + # + # Return all gem name tuples who's names match `obj` + # + def detect: (?::Symbol type) { (untyped) -> untyped } -> untyped + + # + # Find and fetch specs that match `dependency`. + # + # If `matching_platform` is false, gems for all platforms are returned. + # + def spec_for_dependency: (untyped dependency, ?bool matching_platform) -> ::Array[untyped] + + # + # Suggests gems based on the supplied `gem_name`. Returns an array of + # alternative gem names. + # + def suggest_gems_from_name: (untyped gem_name, ?::Symbol type, ?::Integer num_results) -> (::Array[untyped] | untyped) + + # + # Returns a list of gems available for each source in Gem::sources. + # + # `type` can be one of 3 values: :released => Return the list of all released + # specs :complete => Return the list of all specs :latest => Return the + # list of only the highest version of each gem :prerelease => Return the list of + # all prerelease only specs + # + def available_specs: (untyped type) -> ::Array[untyped] + + def tuples_for: (untyped source, untyped type, ?bool gracefully_ignore) -> untyped +end From 363b47dfc07d0b00093656b6ef691da1e0cc5a82 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 22 Jul 2025 16:43:30 -0400 Subject: [PATCH 296/561] Add asserts and RBS collection during overcommit --- .github/workflows/linting.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index ff46725de..932c901a9 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -51,10 +51,13 @@ jobs: path: | /home/runner/.cache/solargraph + - name: Install gem types + run: bundle exec rbs collection install + - name: Overcommit run: | bundle exec overcommit --sign - bundle exec overcommit --run --diff origin/master + SOLARGRAPH_ASSERTS=on bundle exec overcommit --run --diff origin/master rubocop: name: rubocop runs-on: ubuntu-latest From 24698bea906bcd49bb432d5291fa05dac662e34e Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 22 Jul 2025 16:52:03 -0400 Subject: [PATCH 297/561] Add RBS validate check --- .github/workflows/linting.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index 932c901a9..fbd80a490 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -77,6 +77,23 @@ jobs: fail_level: info rubocop_version: Gemfile level: info + rbs_validate: + name: rbs validate + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: 3.4 + bundler-cache: false + + - name: Install gems + run: bundle install + + - name: Run rbs validate + run: bundle exec rbs validate rubocop_todo: name: .rubocop_todo.yml runs-on: ubuntu-latest From 0d0bf6419c0deed85388a454b534ba4a2c5b28f8 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 22 Jul 2025 17:33:33 -0400 Subject: [PATCH 298/561] Type annotation improvements --- lib/solargraph/api_map/index.rb | 2 + lib/solargraph/bench.rb | 1 + lib/solargraph/complex_type/type_methods.rb | 1 + .../convention/struct_definition.rb | 2 +- lib/solargraph/doc_map.rb | 1 + lib/solargraph/gem_pins.rb | 6 +- .../language_server/host/dispatch.rb | 2 + .../language_server/host/message_worker.rb | 3 + .../language_server/message/base.rb | 1 + .../message/text_document/definition.rb | 2 + .../message/text_document/type_definition.rb | 1 + .../workspace/did_change_workspace_folders.rb | 2 + lib/solargraph/language_server/progress.rb | 8 + lib/solargraph/language_server/request.rb | 1 + lib/solargraph/location.rb | 2 + lib/solargraph/parser/comment_ripper.rb | 7 + lib/solargraph/parser/node_processor.rb | 4 +- lib/solargraph/parser/region.rb | 3 + lib/solargraph/parser/snippet.rb | 2 + lib/solargraph/pin/base.rb | 15 +- lib/solargraph/pin/callable.rb | 9 + lib/solargraph/pin/constant.rb | 1 + lib/solargraph/pin/local_variable.rb | 2 +- lib/solargraph/pin/method.rb | 2 +- lib/solargraph/pin/method_alias.rb | 2 + lib/solargraph/pin/proxy_type.rb | 1 + lib/solargraph/pin/reference/override.rb | 16 +- lib/solargraph/pin/search.rb | 2 + lib/solargraph/pin/signature.rb | 2 + lib/solargraph/pin/symbol.rb | 1 + lib/solargraph/position.rb | 1 + lib/solargraph/rbs_map/conversions.rb | 4 +- lib/solargraph/source.rb | 2 + lib/solargraph/source/chain.rb | 5 +- lib/solargraph/source_map.rb | 6 +- lib/solargraph/source_map/data.rb | 4 + lib/solargraph/type_checker/param_def.rb | 2 + lib/solargraph/workspace.rb | 1 + sig/shims/parser/3.2.0.1/builders.rb | 2 + sig/shims/parser/3.2.0.1/builders/default.rbs | 195 ++++++++++++++++++ sig/shims/thor/1.2.0.1/.rbs_meta.yaml | 9 + sig/shims/thor/1.2.0.1/manifest.yaml | 7 + sig/shims/thor/1.2.0.1/thor.rbs | 17 ++ solargraph.gemspec | 5 +- spec/source_map/mapper_spec.rb | 6 +- 45 files changed, 350 insertions(+), 20 deletions(-) create mode 100644 sig/shims/parser/3.2.0.1/builders.rb create mode 100644 sig/shims/parser/3.2.0.1/builders/default.rbs create mode 100644 sig/shims/thor/1.2.0.1/.rbs_meta.yaml create mode 100644 sig/shims/thor/1.2.0.1/manifest.yaml create mode 100644 sig/shims/thor/1.2.0.1/thor.rbs diff --git a/lib/solargraph/api_map/index.rb b/lib/solargraph/api_map/index.rb index 810600534..a51a1fc58 100644 --- a/lib/solargraph/api_map/index.rb +++ b/lib/solargraph/api_map/index.rb @@ -36,6 +36,7 @@ def path_pin_hash def pins_by_class klass # @type [Set] s = Set.new + # @sg-ignore need to support destructured args in blocks @pin_select_cache[klass] ||= pin_class_hash.each_with_object(s) { |(key, o), n| n.merge(o) if key <= klass } end @@ -85,6 +86,7 @@ def deep_clone # @param new_pins [Array] def catalog new_pins + # @type [Hash{Class> => Set>}] @pin_select_cache = {} pins.concat new_pins set = new_pins.to_set diff --git a/lib/solargraph/bench.rb b/lib/solargraph/bench.rb index 3cb19a64b..e6180c933 100644 --- a/lib/solargraph/bench.rb +++ b/lib/solargraph/bench.rb @@ -37,6 +37,7 @@ def source_map_hash .to_h end + # @return [Set] def icebox @icebox ||= (source_maps - [live_map]) end diff --git a/lib/solargraph/complex_type/type_methods.rb b/lib/solargraph/complex_type/type_methods.rb index e6d596244..e68f5100e 100644 --- a/lib/solargraph/complex_type/type_methods.rb +++ b/lib/solargraph/complex_type/type_methods.rb @@ -130,6 +130,7 @@ def namespace end.call end + # @return [ComplexType, UniqueType] def namespace_type return ComplexType.parse('::Object') if duck_type? return ComplexType.parse('::NilClass') if nil_type? diff --git a/lib/solargraph/convention/struct_definition.rb b/lib/solargraph/convention/struct_definition.rb index bc3adeada..4748f4aa2 100644 --- a/lib/solargraph/convention/struct_definition.rb +++ b/lib/solargraph/convention/struct_definition.rb @@ -88,7 +88,7 @@ def process private - # @return [StructDefintionNode, nil] + # @return [StructDefintionNode, StructAssignmentNode, nil] def struct_definition_node @struct_definition_node ||= if StructDefintionNode.match?(node) StructDefintionNode.new(node) diff --git a/lib/solargraph/doc_map.rb b/lib/solargraph/doc_map.rb index d51fc3022..68f911bca 100644 --- a/lib/solargraph/doc_map.rb +++ b/lib/solargraph/doc_map.rb @@ -151,6 +151,7 @@ def load_serialized_gem_pins @uncached_rbs_collection_gemspecs = [] with_gemspecs, without_gemspecs = required_gems_map.partition { |_, v| v } paths = Hash[without_gemspecs].keys + # @type [Array] gemspecs = Hash[with_gemspecs].values.flatten.compact + dependencies.to_a paths.each do |path| diff --git a/lib/solargraph/gem_pins.rb b/lib/solargraph/gem_pins.rb index b92cbd6af..83954748f 100644 --- a/lib/solargraph/gem_pins.rb +++ b/lib/solargraph/gem_pins.rb @@ -30,8 +30,12 @@ def self.combine_method_pins_by_path(pins) by_path.values + alias_pins end + # @param pins [Array] + # @return [Pin::Method, nil] def self.combine_method_pins(*pins) - out = pins.reduce(nil) do |memo, pin| + # @type [Pin::Method, nil] + combined_pin = nil + out = pins.reduce(combined_pin) do |memo, pin| next pin if memo.nil? if memo == pin && memo.source != :combined # @todo we should track down situations where we are handled diff --git a/lib/solargraph/language_server/host/dispatch.rb b/lib/solargraph/language_server/host/dispatch.rb index 1480e20a2..1ff1227b8 100644 --- a/lib/solargraph/language_server/host/dispatch.rb +++ b/lib/solargraph/language_server/host/dispatch.rb @@ -95,6 +95,7 @@ def implicit_library_for uri nil end + # @return [Hash{String => undefined}] def options @options ||= {}.freeze end @@ -118,6 +119,7 @@ def generic_library end # @param library [Solargraph::Library] + # @param progress [Solargraph::LanguageServer::Progress, nil] # @return [void] def update progress progress&.send(self) diff --git a/lib/solargraph/language_server/host/message_worker.rb b/lib/solargraph/language_server/host/message_worker.rb index 482a40e56..ec426b99f 100644 --- a/lib/solargraph/language_server/host/message_worker.rb +++ b/lib/solargraph/language_server/host/message_worker.rb @@ -72,10 +72,12 @@ def tick private + # @return [Hash, nil] def next_message cancel_message || next_priority end + # @return [Hash, nil] def cancel_message # Handle cancellations first idx = messages.find_index { |msg| msg['method'] == '$/cancelRequest' } @@ -86,6 +88,7 @@ def cancel_message msg end + # @return [Hash, nil] def next_priority # Prioritize updates and version-dependent messages for performance idx = messages.find_index do |msg| diff --git a/lib/solargraph/language_server/message/base.rb b/lib/solargraph/language_server/message/base.rb index fbc55ccbd..2a871419f 100644 --- a/lib/solargraph/language_server/message/base.rb +++ b/lib/solargraph/language_server/message/base.rb @@ -79,6 +79,7 @@ def send_response private + # @return [void] def accept_or_cancel if host.cancel?(id) # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#cancelRequest diff --git a/lib/solargraph/language_server/message/text_document/definition.rb b/lib/solargraph/language_server/message/text_document/definition.rb index 47bf7a60d..5f143cc82 100644 --- a/lib/solargraph/language_server/message/text_document/definition.rb +++ b/lib/solargraph/language_server/message/text_document/definition.rb @@ -10,6 +10,7 @@ def process private + # @return [Array] def code_location suggestions = host.definitions_at(params['textDocument']['uri'], @line, @column) return nil if suggestions.empty? @@ -21,6 +22,7 @@ def code_location end end + # @return [Array] def require_location # @todo Terrible hack lib = host.library_for(params['textDocument']['uri']) diff --git a/lib/solargraph/language_server/message/text_document/type_definition.rb b/lib/solargraph/language_server/message/text_document/type_definition.rb index 8143d7710..8c95c231e 100644 --- a/lib/solargraph/language_server/message/text_document/type_definition.rb +++ b/lib/solargraph/language_server/message/text_document/type_definition.rb @@ -10,6 +10,7 @@ def process private + # @return [Array] def code_location suggestions = host.type_definitions_at(params['textDocument']['uri'], @line, @column) return nil if suggestions.empty? diff --git a/lib/solargraph/language_server/message/workspace/did_change_workspace_folders.rb b/lib/solargraph/language_server/message/workspace/did_change_workspace_folders.rb index 2e7b4130c..e1e83fc1e 100644 --- a/lib/solargraph/language_server/message/workspace/did_change_workspace_folders.rb +++ b/lib/solargraph/language_server/message/workspace/did_change_workspace_folders.rb @@ -9,11 +9,13 @@ def process private + # @return [void] def add_folders return unless params['event'] && params['event']['added'] host.prepare_folders params['event']['added'] end + # @return [void] def remove_folders return unless params['event'] && params['event']['removed'] params['event']['removed'].each do |folder| diff --git a/lib/solargraph/language_server/progress.rb b/lib/solargraph/language_server/progress.rb index 0b2aac5fe..10900a37e 100644 --- a/lib/solargraph/language_server/progress.rb +++ b/lib/solargraph/language_server/progress.rb @@ -39,6 +39,7 @@ def initialize title # @param message [String] # @param percentage [Integer] + # @return [void] def begin message, percentage @kind = 'begin' @message = message @@ -47,6 +48,7 @@ def begin message, percentage # @param message [String] # @param percentage [Integer] + # @return [void] def report message, percentage @kind = 'report' @message = message @@ -54,6 +56,7 @@ def report message, percentage end # @param message [String] + # @return [void] def finish message @kind = 'end' @message = message @@ -62,6 +65,7 @@ def finish message end # @param host [Solargraph::LanguageServer::Host] + # @return [void] def send host return unless host.client_supports_progress? && !finished? @@ -91,6 +95,7 @@ def create host @status = CREATED end + # @return [Hash] def build { token: uuid, @@ -101,6 +106,7 @@ def build } end + # @return [Hash] def build_value case kind when 'begin' @@ -115,6 +121,7 @@ def build_value end # @param host [Host] + # @return [void] def keep_alive host mutex.synchronize { @last = Time.now } @keep_alive ||= Thread.new do @@ -127,6 +134,7 @@ def keep_alive host end end + # @return [Mutex] def mutex @mutex ||= Mutex.new end diff --git a/lib/solargraph/language_server/request.rb b/lib/solargraph/language_server/request.rb index e9aea65eb..dcad7084d 100644 --- a/lib/solargraph/language_server/request.rb +++ b/lib/solargraph/language_server/request.rb @@ -16,6 +16,7 @@ def process result @block.call(result) unless @block.nil? end + # @return [void] def send_response # noop end diff --git a/lib/solargraph/location.rb b/lib/solargraph/location.rb index 74d1318df..713b4fef1 100644 --- a/lib/solargraph/location.rb +++ b/lib/solargraph/location.rb @@ -25,6 +25,7 @@ def initialize filename, range [filename, range] end + # @param other [self] def <=>(other) return nil unless other.is_a?(Location) if filename == other.filename @@ -60,6 +61,7 @@ def to_hash end # @param node [Parser::AST::Node, nil] + # @return [Location, nil] def self.from_node(node) return nil if node.nil? || node.loc.nil? range = Range.from_node(node) diff --git a/lib/solargraph/parser/comment_ripper.rb b/lib/solargraph/parser/comment_ripper.rb index 62a4dacc5..ae5b3cc60 100644 --- a/lib/solargraph/parser/comment_ripper.rb +++ b/lib/solargraph/parser/comment_ripper.rb @@ -3,6 +3,13 @@ module Solargraph module Parser class CommentRipper < Ripper::SexpBuilderPP + # @!override Ripper::SexpBuilder#on_embdoc_beg + # @return [Array(Symbol, String, Array)] + # @!override Ripper::SexpBuilder#on_embdoc + # @return [Array(Symbol, String, Array)] + # @!override Ripper::SexpBuilder#on_embdoc_end + # @return [Array(Symbol, String, Array)] + # @param src [String] # @param filename [String] # @param lineno [Integer] diff --git a/lib/solargraph/parser/node_processor.rb b/lib/solargraph/parser/node_processor.rb index a55b7120b..0067af647 100644 --- a/lib/solargraph/parser/node_processor.rb +++ b/lib/solargraph/parser/node_processor.rb @@ -9,7 +9,7 @@ module NodeProcessor autoload :Base, 'solargraph/parser/node_processor/base' class << self - # @type [Hash>>] + # @type [Hash{Symbol => Array>}] @@processors ||= {} # Register a processor for a node type. You can register multiple processors for the same type. @@ -17,7 +17,7 @@ class << self # # @param type [Symbol] # @param cls [Class] - # @return [Class] + # @return [Array>] def register type, cls @@processors[type] ||= [] @@processors[type] << cls diff --git a/lib/solargraph/parser/region.rb b/lib/solargraph/parser/region.rb index 8280c99b6..a6559bc8a 100644 --- a/lib/solargraph/parser/region.rb +++ b/lib/solargraph/parser/region.rb @@ -23,8 +23,10 @@ class Region # @param source [Source] # @param namespace [String] + # @param closure [Pin::Closure, nil] # @param scope [Symbol, nil] # @param visibility [Symbol] + # @param lvars [Array] def initialize source: Solargraph::Source.load_string(''), closure: nil, scope: nil, visibility: :public, lvars: [] @source = source @@ -45,6 +47,7 @@ def filename # @param closure [Pin::Closure, nil] # @param scope [Symbol, nil] # @param visibility [Symbol, nil] + # @param lvars [Array, nil] # @return [Region] def update closure: nil, scope: nil, visibility: nil, lvars: nil Region.new( diff --git a/lib/solargraph/parser/snippet.rb b/lib/solargraph/parser/snippet.rb index d28c57c8c..081dec3e0 100644 --- a/lib/solargraph/parser/snippet.rb +++ b/lib/solargraph/parser/snippet.rb @@ -6,6 +6,8 @@ class Snippet # @return [String] attr_reader :text + # @param range [Solargraph::Range] + # @param text [String] def initialize range, text @range = range @text = text diff --git a/lib/solargraph/pin/base.rb b/lib/solargraph/pin/base.rb index cdd6a5ace..1261963df 100644 --- a/lib/solargraph/pin/base.rb +++ b/lib/solargraph/pin/base.rb @@ -62,7 +62,7 @@ def assert_location_provided end # @param other [self] - # @param attrs [Hash{Symbol => Object}] + # @param attrs [Hash{::Symbol => Object}] # # @return [self] def combine_with(other, attrs={}) @@ -188,7 +188,7 @@ def choose(other, attr) end # @param other [self] - # @param attr [Symbol] + # @param attr [::Symbol] # @sg-ignore # @return [undefined] def choose_node(other, attr) @@ -269,9 +269,13 @@ def assert_same_count(other, attr) # @param other [self] # @param attr [::Symbol] # - # @return [Object, nil] + # @sg-ignore + # @return [undefined] def assert_same(other, attr) - return false if other.nil? + if other.nil? + Solargraph.assert_or_log("combine_with_#{attr}".to_sym, "Sent nil for comparison") + return send(attr) + end val1 = send(attr) val2 = other.send(attr) return val1 if val1 == val2 @@ -300,6 +304,8 @@ def choose_pin_attr_with_same_name(other, attr) # @param other [self] # @param attr [::Symbol] + # + # @sg-ignore # @return [undefined] def choose_pin_attr(other, attr) # @type [Pin::Base, nil] @@ -312,6 +318,7 @@ def choose_pin_attr(other, attr) return val1 end # arbitrary way of choosing a pin + # @sg-ignore Need _1 support [val1, val2].compact.min_by { _1.best_location.to_s } end diff --git a/lib/solargraph/pin/callable.rb b/lib/solargraph/pin/callable.rb index 20d2301eb..504dd4862 100644 --- a/lib/solargraph/pin/callable.rb +++ b/lib/solargraph/pin/callable.rb @@ -21,10 +21,13 @@ def initialize block: nil, return_type: nil, parameters: [], **splat @parameters = parameters end + # @return [String] def method_namespace closure.namespace end + # @param other [self] + # @return [Pin::Block, nil] def combine_blocks(other) if block.nil? other.block @@ -57,6 +60,8 @@ def generics [] end + # @param other [self] + # @return [Array] def choose_parameters(other) raise "Trying to combine two pins with different arities - \nself =#{inspect}, \nother=#{other.inspect}, \n\n self.arity=#{self.arity}, \nother.arity=#{other.arity}" if other.arity != arity parameters.zip(other.parameters).map do |param, other_param| @@ -70,6 +75,7 @@ def choose_parameters(other) end end + # @return [Array] def blockless_parameters if parameters.last&.block? parameters[0..-2] @@ -78,6 +84,7 @@ def blockless_parameters end end + # @return [Array] def arity [generics, blockless_parameters.map(&:arity_decl), block&.arity] end @@ -125,6 +132,7 @@ def typify api_map end end + # @return [String] def method_name raise "closure was nil in #{self.inspect}" if closure.nil? @method_name ||= closure.name @@ -197,6 +205,7 @@ def arity_matches? arguments, with_block true end + # @return [Integer] def mandatory_positional_param_count parameters.count(&:arg?) end diff --git a/lib/solargraph/pin/constant.rb b/lib/solargraph/pin/constant.rb index 345bbd50a..a1e0c47f3 100644 --- a/lib/solargraph/pin/constant.rb +++ b/lib/solargraph/pin/constant.rb @@ -5,6 +5,7 @@ module Pin class Constant < BaseVariable attr_reader :visibility + # @param visibility [::Symbol] The visibility of the constant (:public, :protected, or :private) def initialize visibility: :public, **splat super(**splat) @visibility = visibility diff --git a/lib/solargraph/pin/local_variable.rb b/lib/solargraph/pin/local_variable.rb index c680bebd0..36b75773c 100644 --- a/lib/solargraph/pin/local_variable.rb +++ b/lib/solargraph/pin/local_variable.rb @@ -26,7 +26,7 @@ def combine_with(other, attrs={}) assignment: assert_same(other, :assignment), presence_certain: assert_same(other, :presence_certain?), }.merge(attrs) - new_attrs[:presence] = assert_same(other, :presence) unless attrs.key?(:presence) + new_attrs[:presence] = assert_same(other, :presence) unless attrs.key?(:presence) super(other, new_attrs) end diff --git a/lib/solargraph/pin/method.rb b/lib/solargraph/pin/method.rb index 2f807f444..9098f9d67 100644 --- a/lib/solargraph/pin/method.rb +++ b/lib/solargraph/pin/method.rb @@ -50,7 +50,7 @@ def combine_all_signature_pins(*signature_pins) end # @param other [Pin::Method] - # @return [Symbol] + # @return [::Symbol] def combine_visibility(other) if dodgy_visibility_source? && !other.dodgy_visibility_source? other.visibility diff --git a/lib/solargraph/pin/method_alias.rb b/lib/solargraph/pin/method_alias.rb index 8636169a8..210794976 100644 --- a/lib/solargraph/pin/method_alias.rb +++ b/lib/solargraph/pin/method_alias.rb @@ -13,6 +13,8 @@ class MethodAlias < Method # @return [String] attr_reader :original + # @param scope [::Symbol] + # @param original [String, nil] The name of the original method def initialize scope: :instance, original: nil, **splat super(**splat) @scope = scope diff --git a/lib/solargraph/pin/proxy_type.rb b/lib/solargraph/pin/proxy_type.rb index 819c97481..2323489a7 100644 --- a/lib/solargraph/pin/proxy_type.rb +++ b/lib/solargraph/pin/proxy_type.rb @@ -4,6 +4,7 @@ module Solargraph module Pin class ProxyType < Base # @param return_type [ComplexType] + # @param binder [ComplexType, ComplexType::UniqueType, nil] def initialize return_type: ComplexType::UNDEFINED, binder: nil, **splat super(**splat) @return_type = return_type diff --git a/lib/solargraph/pin/reference/override.rb b/lib/solargraph/pin/reference/override.rb index d547e3caf..878c309db 100644 --- a/lib/solargraph/pin/reference/override.rb +++ b/lib/solargraph/pin/reference/override.rb @@ -14,16 +14,30 @@ def closure nil end + # @param location [Location, nil] + # @param name [String] + # @param tags [::Array] + # @param delete [::Array] + # @param splat [Hash] def initialize location, name, tags, delete = [], **splat super(location: location, name: name, **splat) @tags = tags @delete = delete end + # @param name [String] + # @param tags [::Array] + # @param delete [::Array] + # @param splat [Hash] + # @return [Solargraph::Pin::Reference::Override] def self.method_return name, *tags, delete: [], **splat - new(nil, name, [YARD::Tags::Tag.new('return', nil, tags)], delete, **splat) + new(nil, name, [YARD::Tags::Tag.new('return', '', tags)], delete, **splat) end + # @param name [String] + # @param comment [String] + # @param splat [Hash] + # @return [Solargraph::Pin::Reference::Override] def self.from_comment name, comment, **splat new(nil, name, Solargraph::Source.parse_docstring(comment).to_docstring.tags, **splat) end diff --git a/lib/solargraph/pin/search.rb b/lib/solargraph/pin/search.rb index fc0f000cd..33f02e027 100644 --- a/lib/solargraph/pin/search.rb +++ b/lib/solargraph/pin/search.rb @@ -12,6 +12,8 @@ class Result # @return [Pin::Base] attr_reader :pin + # @param match [Float] The match score for the pin + # @param pin [Pin::Base] def initialize match, pin @match = match @pin = pin diff --git a/lib/solargraph/pin/signature.rb b/lib/solargraph/pin/signature.rb index 818d66411..4c25e028b 100644 --- a/lib/solargraph/pin/signature.rb +++ b/lib/solargraph/pin/signature.rb @@ -39,6 +39,8 @@ def typify api_map end return ComplexType::UNDEFINED if closure.nil? return ComplexType::UNDEFINED unless closure.is_a?(Pin::Method) + # @sg-ignore need is_a? support + # @type [Array] method_stack = closure.rest_of_stack api_map logger.debug { "Signature#typify(self=#{self}) - method_stack: #{method_stack}" } method_stack.each do |pin| diff --git a/lib/solargraph/pin/symbol.rb b/lib/solargraph/pin/symbol.rb index 9e11c3d7d..82c1b1c6e 100644 --- a/lib/solargraph/pin/symbol.rb +++ b/lib/solargraph/pin/symbol.rb @@ -36,6 +36,7 @@ def directives [] end + # @return [::Symbol] def visibility :public end diff --git a/lib/solargraph/position.rb b/lib/solargraph/position.rb index 1bd31e0f5..1197038ef 100644 --- a/lib/solargraph/position.rb +++ b/lib/solargraph/position.rb @@ -26,6 +26,7 @@ def initialize line, character [line, character] end + # @param other [Position] def <=>(other) return nil unless other.is_a?(Position) if line == other.line diff --git a/lib/solargraph/rbs_map/conversions.rb b/lib/solargraph/rbs_map/conversions.rb index 6e50c022a..a6291c4a0 100644 --- a/lib/solargraph/rbs_map/conversions.rb +++ b/lib/solargraph/rbs_map/conversions.rb @@ -699,13 +699,13 @@ def method_type_to_tag type # @return [ComplexType::UniqueType] def build_type(type_name, type_args = []) base = RBS_TO_YARD_TYPE[type_name.relative!.to_s] || type_name.relative!.to_s - params = type_args.map { |a| other_type_to_tag(a) }.reject { |t| t == 'undefined' }.map do |t| + params = type_args.map { |a| other_type_to_tag(a) }.map do |t| ComplexType.try_parse(t).force_rooted end if base == 'Hash' && params.length == 2 ComplexType::UniqueType.new(base, [params.first], [params.last], rooted: true, parameters_type: :hash) else - ComplexType::UniqueType.new(base, [], params, rooted: true, parameters_type: :list) + ComplexType::UniqueType.new(base, [], params.reject(&:undefined?), rooted: true, parameters_type: :list) end end diff --git a/lib/solargraph/source.rb b/lib/solargraph/source.rb index 11ab215ed..4190b073a 100644 --- a/lib/solargraph/source.rb +++ b/lib/solargraph/source.rb @@ -386,6 +386,7 @@ def changes # @return [Integer] attr_writer :version + # @return [void] def finalize return if @finalized && changes.empty? @@ -440,6 +441,7 @@ def code=(val) # @return [String] attr_writer :repaired + # @return [String] def repaired finalize @repaired diff --git a/lib/solargraph/source/chain.rb b/lib/solargraph/source/chain.rb index 8fdeed228..4567005b2 100644 --- a/lib/solargraph/source/chain.rb +++ b/lib/solargraph/source/chain.rb @@ -265,7 +265,10 @@ def infer_from_definitions pins, context, api_map, locals else ComplexType.new(types) end - return type if context.nil? || context.return_type.undefined? + if context.nil? || context.return_type.undefined? + # up to downstream to resolve self type + return type + end type.self_to_type(context.return_type) end diff --git a/lib/solargraph/source_map.rb b/lib/solargraph/source_map.rb index 84b3a4bcc..61b08eea8 100644 --- a/lib/solargraph/source_map.rb +++ b/lib/solargraph/source_map.rb @@ -32,11 +32,13 @@ def initialize source environ.merge Convention.for_local(self) unless filename.nil? self.convention_pins = environ.pins + # @type [Hash{Class> => Array>}] @pin_select_cache = {} end - # @param klass [Class] - # @return [Array] + # @generic T + # @param klass [Class>] + # @return [Array>] def pins_by_class klass @pin_select_cache[klass] ||= pin_class_hash.select { |key, _| key <= klass }.values.flatten end diff --git a/lib/solargraph/source_map/data.rb b/lib/solargraph/source_map/data.rb index 9d1b30bac..453520414 100644 --- a/lib/solargraph/source_map/data.rb +++ b/lib/solargraph/source_map/data.rb @@ -3,15 +3,18 @@ module Solargraph class SourceMap class Data + # @param source [Solargraph::Source] def initialize source @source = source end + # @return [Array] def pins generate @pins || [] end + # @return [Array] def locals generate @locals || [] @@ -19,6 +22,7 @@ def locals private + # @return [void] def generate return if @generated diff --git a/lib/solargraph/type_checker/param_def.rb b/lib/solargraph/type_checker/param_def.rb index 2c626270a..a60448c98 100644 --- a/lib/solargraph/type_checker/param_def.rb +++ b/lib/solargraph/type_checker/param_def.rb @@ -12,6 +12,8 @@ class ParamDef # @return [Symbol] attr_reader :type + # @param name [String] + # @param type [Symbol] def initialize name, type @name = name @type = type diff --git a/lib/solargraph/workspace.rb b/lib/solargraph/workspace.rb index aabadd333..ffd653d96 100644 --- a/lib/solargraph/workspace.rb +++ b/lib/solargraph/workspace.rb @@ -133,6 +133,7 @@ def rbs_collection_path @gem_rbs_collection ||= read_rbs_collection_path end + # @return [String, nil] def rbs_collection_config_path @rbs_collection_config_path ||= begin unless directory.empty? || directory == '*' diff --git a/sig/shims/parser/3.2.0.1/builders.rb b/sig/shims/parser/3.2.0.1/builders.rb new file mode 100644 index 000000000..b932108a9 --- /dev/null +++ b/sig/shims/parser/3.2.0.1/builders.rb @@ -0,0 +1,2 @@ +module Builders +end diff --git a/sig/shims/parser/3.2.0.1/builders/default.rbs b/sig/shims/parser/3.2.0.1/builders/default.rbs new file mode 100644 index 000000000..861a9e371 --- /dev/null +++ b/sig/shims/parser/3.2.0.1/builders/default.rbs @@ -0,0 +1,195 @@ +# frozen_string_literal: true + +module Parser + ## + # Default AST builder. Uses {AST::Node}s. + # + module Builders + class Default + ## + # AST compatibility attribute; since `-> {}` is not semantically + # equivalent to `lambda {}`, all new code should set this attribute + # to true. + # + # If set to false (the default), `-> {}` is emitted as + # `s(:block, s(:send, nil, :lambda), s(:args), nil)`. + # + # If set to true, `-> {}` is emitted as + # `s(:block, s(:lambda), s(:args), nil)`. + # + # @return [Boolean] + attr_accessor self.emit_lambda: bool + + ## + # AST compatibility attribute; block arguments of `m { |a| }` are + # not semantically equivalent to block arguments of `m { |a,| }` or `m { |a, b| }`, + # all new code should set this attribute to true. + # + # If set to false (the default), arguments of `m { |a| }` are emitted as + # `s(:args, s(:arg, :a))`. + # + # If set to true, arguments of `m { |a| }` are emitted as + # `s(:args, s(:procarg0, :a)). + # + # @return [Boolean] + attr_accessor self.emit_procarg0: bool + + ## + # AST compatibility attribute; locations of `__ENCODING__` are not the same + # as locations of `Encoding::UTF_8` causing problems during rewriting, + # all new code should set this attribute to true. + # + # If set to false (the default), `__ENCODING__` is emitted as + # ` s(:const, s(:const, nil, :Encoding), :UTF_8)`. + # + # If set to true, `__ENCODING__` is emitted as + # `s(:__ENCODING__)`. + # + # @return [Boolean] + attr_accessor self.emit_encoding: bool + + ## + # AST compatibility attribute; indexed assignment, `x[] = 1`, is not + # semantically equivalent to calling the method directly, `x.[]=(1)`. + # Specifically, in the former case, the expression's value is always 1, + # and in the latter case, the expression's value is the return value + # of the `[]=` method. + # + # If set to false (the default), `self[1]` is emitted as + # `s(:send, s(:self), :[], s(:int, 1))`, and `self[1] = 2` is + # emitted as `s(:send, s(:self), :[]=, s(:int, 1), s(:int, 2))`. + # + # If set to true, `self[1]` is emitted as + # `s(:index, s(:self), s(:int, 1))`, and `self[1] = 2` is + # emitted as `s(:indexasgn, s(:self), s(:int, 1), s(:int, 2))`. + # + # @return [Boolean] + attr_accessor self.emit_index: bool + + ## + # AST compatibility attribute; causes a single non-mlhs + # block argument to be wrapped in s(:procarg0). + # + # If set to false (the default), block arguments `|a|` are emitted as + # `s(:args, s(:procarg0, :a))` + # + # If set to true, block arguments `|a|` are emitted as + # `s(:args, s(:procarg0, s(:arg, :a))` + # + # @return [Boolean] + attr_accessor self.emit_arg_inside_procarg0: bool + + ## + # AST compatibility attribute; arguments forwarding initially + # didn't have support for leading arguments + # (i.e. `def m(a, ...); end` was a syntax error). However, Ruby 3.0 + # added support for any number of arguments in front of the `...`. + # + # If set to false (the default): + # 1. `def m(...) end` is emitted as + # s(:def, :m, s(:forward_args), nil) + # 2. `def m(a, b, ...) end` is emitted as + # s(:def, :m, + # s(:args, s(:arg, :a), s(:arg, :b), s(:forward_arg))) + # + # If set to true it uses a single format: + # 1. `def m(...) end` is emitted as + # s(:def, :m, s(:args, s(:forward_arg))) + # 2. `def m(a, b, ...) end` is emitted as + # s(:def, :m, s(:args, s(:arg, :a), s(:arg, :b), s(:forward_arg))) + # + # It does't matter that much on 2.7 (because there can't be any leading arguments), + # but on 3.0 it should be better enabled to use a single AST format. + # + # @return [Boolean] + attr_accessor self.emit_forward_arg: bool + + ## + # AST compatibility attribute; Starting from Ruby 2.7 keyword arguments + # of method calls that are passed explicitly as a hash (i.e. with curly braces) + # are treated as positional arguments and Ruby 2.7 emits a warning on such method + # call. Ruby 3.0 given an ArgumentError. + # + # If set to false (the default) the last hash argument is emitted as `hash`: + # + # ``` + # (send nil :foo + # (hash + # (pair + # (sym :bar) + # (int 42)))) + # ``` + # + # If set to true it is emitted as `kwargs`: + # + # ``` + # (send nil :foo + # (kwargs + # (pair + # (sym :bar) + # (int 42)))) + # ``` + # + # Note that `kwargs` node is just a replacement for `hash` argument, + # so if there's are multiple arguments (or a `kwsplat`) all of them + # are wrapped into `kwargs` instead of `hash`: + # + # ``` + # (send nil :foo + # (kwargs + # (pair + # (sym :a) + # (int 42)) + # (kwsplat + # (send nil :b)) + # (pair + # (sym :c) + # (int 10)))) + # ``` + attr_accessor self.emit_kwargs: bool + + ## + # AST compatibility attribute; Starting from 3.0 Ruby returns + # true/false from single-line pattern matching with `in` keyword. + # + # Before 3.0 there was an exception if given value doesn't match pattern. + # + # NOTE: This attribute affects only Ruby 2.7 grammar. + # 3.0 grammar always emits `match_pattern`/`match_pattern_p` + # + # If compatibility attribute set to false `foo in bar` is emitted as `in_match`: + # + # ``` + # (in-match + # (send nil :foo) + # (match-var :bar)) + # ``` + # + # If set to true it's emitted as `match_pattern_p`: + # ``` + # (match-pattern-p + # (send nil :foo) + # (match-var :bar)) + # ``` + attr_accessor self.emit_match_pattern: bool + + ## + # If set to true (the default), `__FILE__` and `__LINE__` are transformed to + # literal nodes. For example, `s(:str, "lib/foo.rb")` and `s(:int, 10)`. + # + # If set to false, `__FILE__` and `__LINE__` are emitted as-is, + # i.e. as `s(:__FILE__)` and `s(:__LINE__)` nodes. + # + # Source maps are identical in both cases. + # + # @return [Boolean] + attr_accessor emit_file_line_as_literals: bool + + def value: (untyped token) -> untyped + + def string_value: (untyped token) -> String + + def loc: (untyped token) -> untyped + end + end +end diff --git a/sig/shims/thor/1.2.0.1/.rbs_meta.yaml b/sig/shims/thor/1.2.0.1/.rbs_meta.yaml new file mode 100644 index 000000000..27c2fb2e4 --- /dev/null +++ b/sig/shims/thor/1.2.0.1/.rbs_meta.yaml @@ -0,0 +1,9 @@ +--- +name: thor +version: '1.2' +source: + type: git + name: ruby/gem_rbs_collection + revision: 98541aabafdf403b16ebae6fe4060d18bee75e93 + remote: https://github.com/ruby/gem_rbs_collection.git + repo_dir: gems diff --git a/sig/shims/thor/1.2.0.1/manifest.yaml b/sig/shims/thor/1.2.0.1/manifest.yaml new file mode 100644 index 000000000..a0cb4d149 --- /dev/null +++ b/sig/shims/thor/1.2.0.1/manifest.yaml @@ -0,0 +1,7 @@ +# manifest.yaml describes dependencies which do not appear in the gemspec. +# If this gem includes such dependencies, comment-out the following lines and +# declare the dependencies. +# If all dependencies appear in the gemspec, you should remove this file. +# +# dependencies: +# - name: pathname diff --git a/sig/shims/thor/1.2.0.1/thor.rbs b/sig/shims/thor/1.2.0.1/thor.rbs new file mode 100644 index 000000000..2247507e7 --- /dev/null +++ b/sig/shims/thor/1.2.0.1/thor.rbs @@ -0,0 +1,17 @@ +class Thor + class Group + end + + module Actions + class CreateFile + end + + def create_file: (String destination, String data, ?verbose: bool) -> String + | (String destination, ?verbose: bool) { () -> String } -> String + end + + class Error + end + + def self.start: (Array[String] given_args, ?Hash[Symbol, untyped] config) -> void +end diff --git a/solargraph.gemspec b/solargraph.gemspec index 666f19cba..535c73010 100755 --- a/solargraph.gemspec +++ b/solargraph.gemspec @@ -11,7 +11,10 @@ Gem::Specification.new do |s| s.authors = ["Fred Snyder"] s.email = 'admin@castwide.com' s.files = Dir.chdir(File.expand_path('..', __FILE__)) do - `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } + # @sg-ignore Need backtick support + # @type [String] + all_files = `git ls-files -z` + all_files.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } end s.homepage = 'https://solargraph.org' s.license = 'MIT' diff --git a/spec/source_map/mapper_spec.rb b/spec/source_map/mapper_spec.rb index af3678cfd..96d2bdab5 100644 --- a/spec/source_map/mapper_spec.rb +++ b/spec/source_map/mapper_spec.rb @@ -1525,9 +1525,9 @@ def bar; end def quz; end end )) - expect(map.first_pin('Foo#bar').visibility).to be(:public) - expect(map.first_pin('Foo#baz').visibility).to be(:private) - expect(map.first_pin('Foo#quz').visibility).to be(:public) + expect(map.first_pin('Foo#bar').visibility).to eq(:public) + expect(map.first_pin('Foo#baz').visibility).to eq(:private) + expect(map.first_pin('Foo#quz').visibility).to eq(:public) end it 'encloses class_eval calls in receivers' do From 1d0b23d13f20646992a4b17a44c492826f433cab Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 22 Jul 2025 17:53:02 -0400 Subject: [PATCH 299/561] Generic typechecking improvements --- lib/solargraph/api_map/index.rb | 3 +-- lib/solargraph/complex_type/type_methods.rb | 2 ++ lib/solargraph/doc_map.rb | 1 + lib/solargraph/location.rb | 1 - lib/solargraph/position.rb | 1 - lib/solargraph/range.rb | 1 - lib/solargraph/type_checker.rb | 11 +++++++++++ lib/solargraph/type_checker/rules.rb | 2 +- 8 files changed, 16 insertions(+), 6 deletions(-) diff --git a/lib/solargraph/api_map/index.rb b/lib/solargraph/api_map/index.rb index 9015cd6d5..fbfb5c218 100644 --- a/lib/solargraph/api_map/index.rb +++ b/lib/solargraph/api_map/index.rb @@ -72,7 +72,7 @@ def merge pins # @return [Solargraph::ApiMap::Index] def deep_clone - out = Index.allocate.tap do |copy| + Index.allocate.tap do |copy| copy.pin_select_cache = {} copy.pins = pins.clone %i[ @@ -83,7 +83,6 @@ def deep_clone copy.send(sym)&.transform_values!(&:clone) end end - out end # @param new_pins [Enumerable] diff --git a/lib/solargraph/complex_type/type_methods.rb b/lib/solargraph/complex_type/type_methods.rb index 4fcaadb7f..791ab80b0 100644 --- a/lib/solargraph/complex_type/type_methods.rb +++ b/lib/solargraph/complex_type/type_methods.rb @@ -70,6 +70,8 @@ def undefined? end # Variance of the type ignoring any type parameters + # @return [Symbol] + # @param situation [Symbol] The situation in which the variance is being considered. def erased_variance situation = :method_call if [:method_call, :return_type, :assignment].include?(situation) :covariant diff --git a/lib/solargraph/doc_map.rb b/lib/solargraph/doc_map.rb index 186037460..9136da26b 100644 --- a/lib/solargraph/doc_map.rb +++ b/lib/solargraph/doc_map.rb @@ -153,6 +153,7 @@ def load_serialized_gem_pins # @sg-ignore Need Hash[] support # @type [Array] paths = Hash[without_gemspecs].keys + # @type [Array] gemspecs = Hash[with_gemspecs].values.flatten.compact + dependencies.to_a paths.each do |path| diff --git a/lib/solargraph/location.rb b/lib/solargraph/location.rb index 74d1318df..3af8016b3 100644 --- a/lib/solargraph/location.rb +++ b/lib/solargraph/location.rb @@ -20,7 +20,6 @@ def initialize filename, range @range = range end - # @sg-ignore Fix "Not enough arguments to Module#protected" protected def equality_fields [filename, range] end diff --git a/lib/solargraph/position.rb b/lib/solargraph/position.rb index 1bd31e0f5..27289d28f 100644 --- a/lib/solargraph/position.rb +++ b/lib/solargraph/position.rb @@ -21,7 +21,6 @@ def initialize line, character @character = character end - # @sg-ignore Fix "Not enough arguments to Module#protected" protected def equality_fields [line, character] end diff --git a/lib/solargraph/range.rb b/lib/solargraph/range.rb index c508e48fa..2bea62797 100644 --- a/lib/solargraph/range.rb +++ b/lib/solargraph/range.rb @@ -19,7 +19,6 @@ def initialize start, ending @ending = ending end - # @sg-ignore Fix "Not enough arguments to Module#protected" protected def equality_fields [start, ending] end diff --git a/lib/solargraph/type_checker.rb b/lib/solargraph/type_checker.rb index 953832a36..8b86f02df 100644 --- a/lib/solargraph/type_checker.rb +++ b/lib/solargraph/type_checker.rb @@ -41,18 +41,27 @@ def source @source_map.source end + # @param inferred [ComplexType] + # @param expected [ComplexType] def return_type_conforms_to?(inferred, expected) conforms_to?(inferred, expected, :return_type) end + # @param inferred [ComplexType] + # @param expected [ComplexType] def arg_conforms_to?(inferred, expected) conforms_to?(inferred, expected, :method_call) end + # @param inferred [ComplexType] + # @param expected [ComplexType] def assignment_conforms_to?(inferred, expected) conforms_to?(inferred, expected, :assignment) end + # @param inferred [ComplexType] + # @param expected [ComplexType] + # @param scenario [Symbol] def conforms_to?(inferred, expected, scenario) rules_arr = [] rules_arr << :allow_empty_params unless rules.require_inferred_type_params? @@ -486,7 +495,9 @@ def kwrestarg_problems_for(api_map, block_pin, locals, location, pin, params, kw kwargs.each_pair do |pname, argchain| next unless params.key?(pname.to_s) ptype = params[pname.to_s][:qualified] + ptype = ptype.self_to_type(pin.context) argtype = argchain.infer(api_map, block_pin, locals) + argtype = argtype.self_to_type(block_pin.context) if argtype.defined? && ptype && !arg_conforms_to?(argtype, ptype) result.push Problem.new(location, "Wrong argument type for #{pin.path}: #{pname} expected #{ptype}, received #{argtype}") end diff --git a/lib/solargraph/type_checker/rules.rb b/lib/solargraph/type_checker/rules.rb index 33ec0c4d0..5290c8c12 100644 --- a/lib/solargraph/type_checker/rules.rb +++ b/lib/solargraph/type_checker/rules.rb @@ -78,7 +78,7 @@ def require_downcasts? rank >= LEVELS[:alpha] end - # We keep this at strong because if you added an @sg-ignore to + # We keep this at strong because if you added an @ sg-ignore to # address a strong-level issue, then ran at a lower level, you'd # get a false positive - we don't run stronger level checks than # requested for performance reasons From d48e4970d8375222f2b5df497ab260c12635e7ce Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 22 Jul 2025 20:49:22 -0400 Subject: [PATCH 300/561] Fix spec --- spec/workspace/require_paths_spec.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spec/workspace/require_paths_spec.rb b/spec/workspace/require_paths_spec.rb index 97cf9c285..0b57432a9 100644 --- a/spec/workspace/require_paths_spec.rb +++ b/spec/workspace/require_paths_spec.rb @@ -4,11 +4,13 @@ require 'tmpdir' describe Solargraph::Workspace::RequirePaths do + subject(:paths) { described_class.new(dir_path, config).generate } let(:config) { Solargraph::Workspace::Config.new(dir_path) } context 'with no config' do + let(:dir_path) { Dir.pwd } let(:config) { nil } it 'includes the lib directory' do From e17f7d731a3cceeb983b7fbff335770935ffe32e Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 22 Jul 2025 20:51:24 -0400 Subject: [PATCH 301/561] Fix annotations --- lib/solargraph/parser/parser_gem/class_methods.rb | 7 ------- lib/solargraph/pin_cache.rb | 1 - lib/solargraph/shell.rb | 2 -- lib/solargraph/workspace.rb | 1 - lib/solargraph/workspace/gemspecs.rb | 5 ----- lib/solargraph/workspace/require_paths.rb | 1 - lib/solargraph/yardoc.rb | 2 -- 7 files changed, 19 deletions(-) diff --git a/lib/solargraph/parser/parser_gem/class_methods.rb b/lib/solargraph/parser/parser_gem/class_methods.rb index f86759c04..cb4f6fc72 100644 --- a/lib/solargraph/parser/parser_gem/class_methods.rb +++ b/lib/solargraph/parser/parser_gem/class_methods.rb @@ -59,9 +59,6 @@ def references source, name # @return [Array(Integer, Integer), Array(nil, nil)] extract_offset = ->(code, offset) { [soff = code.index(name, offset), soff + name.length] } end - # @sg-ignore Wrong argument type for - # Solargraph::Parser::ParserGem::ClassMethods#inner_node_references: - # top expected AST::Node, received Parser::AST::Node, nil inner_node_references(name, source.node).map do |n| rng = Range.from_node(n) offset = Position.to_offset(source.code, rng.start) @@ -131,9 +128,6 @@ def node_range node # @param node [Parser::AST::Node] # @return [Array] def string_ranges node - # @sg-ignore Wrong argument type for - # Solargraph::Parser::ParserGem::ClassMethods#is_ast_node?: - # node expected Object, received Parser::AST::Node return [] unless is_ast_node?(node) result = [] if node.type == :str @@ -144,7 +138,6 @@ def string_ranges node end if node.type == :dstr && node.children.last.nil? last = node.children[-2] - # @sg-ignore Unresolved call to nil? unless last.nil? rng = Range.from_node(last) pos = Position.new(rng.ending.line, rng.ending.column - 1) diff --git a/lib/solargraph/pin_cache.rb b/lib/solargraph/pin_cache.rb index 14da097e4..a12a672aa 100644 --- a/lib/solargraph/pin_cache.rb +++ b/lib/solargraph/pin_cache.rb @@ -21,7 +21,6 @@ def initialize(rbs_collection_path:, rbs_collection_config_path:, @yard_plugins = yard_plugins end - # @sg-ignore # @param gemspec [Gem::Specification, Bundler::LazySpecification] def cached?(gemspec) rbs_version_cache_key = lookup_rbs_version_cache_key(gemspec) diff --git a/lib/solargraph/shell.rb b/lib/solargraph/shell.rb index bb93446f6..5b6028a11 100755 --- a/lib/solargraph/shell.rb +++ b/lib/solargraph/shell.rb @@ -67,7 +67,6 @@ def stdio def config(directory = '.') matches = [] if options[:extensions] - # @sg-ignore Unresolved call to each Gem::Specification.each do |g| if g.name.match(/^solargraph\-[A-Za-z0-9_\-]*?\-ext/) require g.name @@ -82,7 +81,6 @@ def config(directory = '.') end end File.open(File.join(directory, '.solargraph.yml'), 'w') do |file| - # @sg-ignore Unresolved call to to_yaml file.puts conf.to_yaml end STDOUT.puts "Configuration file initialized." diff --git a/lib/solargraph/workspace.rb b/lib/solargraph/workspace.rb index a4a29f45e..b037ab2fc 100644 --- a/lib/solargraph/workspace.rb +++ b/lib/solargraph/workspace.rb @@ -264,7 +264,6 @@ def require_plugins def read_rbs_collection_path return unless rbs_collection_config_path - # @sg-ignore Unresolved call to load_file path = YAML.load_file(rbs_collection_config_path)&.fetch('path') # make fully qualified File.expand_path(path, directory) diff --git a/lib/solargraph/workspace/gemspecs.rb b/lib/solargraph/workspace/gemspecs.rb index aa203e43a..465e0f3d5 100644 --- a/lib/solargraph/workspace/gemspecs.rb +++ b/lib/solargraph/workspace/gemspecs.rb @@ -128,7 +128,6 @@ def self.gem_specification_cache private # @param specish [Gem::Specification, Bundler::LazySpecification, Bundler::StubSpecification] - # @sg-ignore # @return [Gem::Specification, nil] def to_gem_specification specish # print time including milliseconds @@ -172,7 +171,6 @@ def query_external_bundle command 'ruby', '-e', "require 'bundler'; require 'json'; Dir.chdir('#{directory}') { puts #{command}.to_json }" ] - # @sg-ignore Unresolved call to capture3 o, e, s = Open3.capture3(*cmd) if s.success? Solargraph.logger.debug "External bundle: #{o}" @@ -226,9 +224,6 @@ def auto_required_gemspecs_from_this_bundle all_gemspecs_from_bundle.select { |gemspec| dep_names.include?(gemspec.name) } end - # @sg-ignore - # Solargraph::Workspace::Gemspecs#auto_required_gemspecs_from_external_bundle - # return type could not be inferred # @return [Array] def auto_required_gemspecs_from_external_bundle @auto_required_gemspecs_from_external_bundle ||= diff --git a/lib/solargraph/workspace/require_paths.rb b/lib/solargraph/workspace/require_paths.rb index c12b66724..18364b141 100644 --- a/lib/solargraph/workspace/require_paths.rb +++ b/lib/solargraph/workspace/require_paths.rb @@ -78,7 +78,6 @@ def require_path_from_gemspec_file gemspec_file_path "spec = eval(File.read('#{gemspec_file_path}'), TOPLEVEL_BINDING, '#{gemspec_file_path}'); " \ 'return unless Gem::Specification === spec; ' \ 'puts({name: spec.name, paths: spec.require_paths}.to_json)'] - # @sg-ignore Unresolved call to capture3 o, e, s = Open3.capture3(*cmd) if s.success? begin diff --git a/lib/solargraph/yardoc.rb b/lib/solargraph/yardoc.rb index e21ae1a00..8f9c2f04d 100644 --- a/lib/solargraph/yardoc.rb +++ b/lib/solargraph/yardoc.rb @@ -22,8 +22,6 @@ def build_docs gem_yardoc_path, yard_plugins, gemspec Solargraph.logger.debug { "Running: #{cmd}" } # @todo set these up to run in parallel # - # @sg-ignore RBS gem doesn't reflect that Open3.* also include - # kwopts from Process.spawn() stdout_and_stderr_str, status = Open3.capture2e(cmd, chdir: gemspec.gem_dir) return if status.success? Solargraph.logger.warn { "YARD failed running #{cmd.inspect} in #{gemspec.gem_dir}" } From 9c415e58bbb5dbfd31ac0daf3ccb2dfc10219ef8 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 22 Jul 2025 20:54:35 -0400 Subject: [PATCH 302/561] Fix merge issue --- lib/solargraph/shell.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/solargraph/shell.rb b/lib/solargraph/shell.rb index cab43c606..8676db1e0 100755 --- a/lib/solargraph/shell.rb +++ b/lib/solargraph/shell.rb @@ -292,12 +292,14 @@ def rbs def method_pin path api_map = Solargraph::ApiMap.load_with_cache('.', STDERR) + # @type [Array] pins = if options[:stack] scope, ns, meth = if path.include? '#' [:instance, *path.split('#', 2)] else [:class, *path.split('.', 2)] end + # @sg-ignore need better splat destructuring support api_map.get_method_stack(ns, meth, scope: scope) else api_map.get_path_pins path From 5d0161a74a7ec2f02ca711ac628951b38f23504e Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 22 Jul 2025 21:07:03 -0400 Subject: [PATCH 303/561] Reproduce and fix a ||= (or-asgn) evaluation issue --- .../parser/parser_gem/node_chainer.rb | 3 ++- spec/type_checker/levels/strict_spec.rb | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/lib/solargraph/parser/parser_gem/node_chainer.rb b/lib/solargraph/parser/parser_gem/node_chainer.rb index 32bb186dd..def79ed1b 100644 --- a/lib/solargraph/parser/parser_gem/node_chainer.rb +++ b/lib/solargraph/parser/parser_gem/node_chainer.rb @@ -98,7 +98,8 @@ def generate_links n elsif [:gvar, :gvasgn].include?(n.type) result.push Chain::GlobalVariable.new(n.children[0].to_s) elsif n.type == :or_asgn - result.concat generate_links n.children[1] + new_node = n.updated(n.children[0].type, n.children[0].children + [n.children[1]]) + result.concat generate_links new_node elsif [:class, :module, :def, :defs].include?(n.type) # @todo Undefined or what? result.push Chain::UNDEFINED_CALL diff --git a/spec/type_checker/levels/strict_spec.rb b/spec/type_checker/levels/strict_spec.rb index b198cec89..586dc0a97 100644 --- a/spec/type_checker/levels/strict_spec.rb +++ b/spec/type_checker/levels/strict_spec.rb @@ -941,5 +941,23 @@ def bar(a) )) expect(checker.problems.map(&:message)).to eq([]) end + + it 'does not complain on defaulted reader with detailed expression' do + checker = type_checker(%( + class Foo + # @return [Integer, nil] + def bar + @bar ||= + if rand + 123 + elsif rand + 456 + end + end + end + )) + expect(checker.problems.map(&:message)).to eq([]) + end + end end From 0741a1837eb72a54a3febe5b24da55579367f474 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 22 Jul 2025 21:09:52 -0400 Subject: [PATCH 304/561] Type annotation improvements --- lib/solargraph/parser/parser_gem/flawed_builder.rb | 1 + lib/solargraph/parser/parser_gem/node_methods.rb | 1 + 2 files changed, 2 insertions(+) diff --git a/lib/solargraph/parser/parser_gem/flawed_builder.rb b/lib/solargraph/parser/parser_gem/flawed_builder.rb index b5750413d..acf665e16 100644 --- a/lib/solargraph/parser/parser_gem/flawed_builder.rb +++ b/lib/solargraph/parser/parser_gem/flawed_builder.rb @@ -9,6 +9,7 @@ module ParserGem class FlawedBuilder < ::Parser::Builders::Default # @param token [::Parser::AST::Node] # @return [String] + # @sg-ignore def string_value(token) value(token) end diff --git a/lib/solargraph/parser/parser_gem/node_methods.rb b/lib/solargraph/parser/parser_gem/node_methods.rb index b716b352d..88cf09718 100644 --- a/lib/solargraph/parser/parser_gem/node_methods.rb +++ b/lib/solargraph/parser/parser_gem/node_methods.rb @@ -232,6 +232,7 @@ def find_recipient_node cursor else source.tree_at(position.line, position.column - 1) end + # @type [AST::Node, nil] prev = nil tree.each do |node| if node.type == :send From d79220a01e88440b8438370093fdbebf136aa926 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 22 Jul 2025 21:11:51 -0400 Subject: [PATCH 305/561] Fix merge issue --- lib/solargraph/library.rb | 6 ------ 1 file changed, 6 deletions(-) diff --git a/lib/solargraph/library.rb b/lib/solargraph/library.rb index 33a1db59a..d5441502b 100644 --- a/lib/solargraph/library.rb +++ b/lib/solargraph/library.rb @@ -593,7 +593,6 @@ def cache_next_gemspec pending = api_map.uncached_gemspecs.length - cache_errors.length - 1 if pin_cache.yardoc_processing?(spec) - # @sg-ignore Unresolved call to name logger.info "Enqueuing cache of #{spec.name} #{spec.version} (already being processed)" queued_gemspec_cache.push(spec) return if pending - queued_gemspec_cache.length < 1 @@ -601,19 +600,14 @@ def cache_next_gemspec catalog sync_catalog else - # @sg-ignore Unresolved call to name logger.info "Caching #{spec.name} #{spec.version}" Thread.new do - # @sg-ignore Unresolved call to name report_cache_progress spec.name, pending - # @sg-ignore Unresolved call to capture3 _o, e, s = Open3.capture3(workspace.command_path, 'cache', spec.name, spec.version.to_s) if s.success? - # @sg-ignore Unresolved call to name logger.info "Cached #{spec.name} #{spec.version}" else cache_errors.add spec - # @sg-ignore Unresolved call to name logger.warn "Error caching gemspec #{spec.name} #{spec.version}" logger.warn e end From dfe2d10d0528562de45abc0c923f47fc2e2fb17f Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 22 Jul 2025 21:14:28 -0400 Subject: [PATCH 306/561] Fix children definition --- sig/shims/ast/2.4/ast.rbs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sig/shims/ast/2.4/ast.rbs b/sig/shims/ast/2.4/ast.rbs index caba287ad..e7fb8975e 100644 --- a/sig/shims/ast/2.4/ast.rbs +++ b/sig/shims/ast/2.4/ast.rbs @@ -10,7 +10,7 @@ module AST class Node public - attr_reader children: Array[Node, Object] + attr_reader children: Array[Node] attr_reader hash: String attr_reader type: Symbol From 3850399ccf0fd63e5ccaff9a32751a9633bbac59 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 22 Jul 2025 21:16:31 -0400 Subject: [PATCH 307/561] Generic typechecking improvements --- lib/solargraph/complex_type.rb | 7 +++++++ lib/solargraph/complex_type/unique_type.rb | 5 +++++ lib/solargraph/pin/base.rb | 4 ++++ 3 files changed, 16 insertions(+) diff --git a/lib/solargraph/complex_type.rb b/lib/solargraph/complex_type.rb index 05e499998..a558f3ba5 100644 --- a/lib/solargraph/complex_type.rb +++ b/lib/solargraph/complex_type.rb @@ -315,6 +315,13 @@ def all_rooted? all?(&:all_rooted?) end + # @param other [ComplexType, UniqueType] + def erased_version_of?(other) + return false if items.length != 1 || other.items.length != 1 + + @items.first.erased_version_of?(other.items.first) + end + # every top-level type has resolved to be fully qualified; see # #all_rooted? to check their subtypes as well def rooted? diff --git a/lib/solargraph/complex_type/unique_type.rb b/lib/solargraph/complex_type/unique_type.rb index 361fe06bb..39e5e728f 100644 --- a/lib/solargraph/complex_type/unique_type.rb +++ b/lib/solargraph/complex_type/unique_type.rb @@ -194,6 +194,11 @@ def interface? name.start_with?('_') end + # @param other [UniqueType] + def erased_version_of?(other) + return name == other.name && (all_params.empty? || all_params.all?(&:undefined?)) + end + # @param api_map [ApiMap] # @param expected [ComplexType::UniqueType] # @param situation [:method_call, :return_type] diff --git a/lib/solargraph/pin/base.rb b/lib/solargraph/pin/base.rb index c9e308056..14d324273 100644 --- a/lib/solargraph/pin/base.rb +++ b/lib/solargraph/pin/base.rb @@ -152,6 +152,10 @@ def combine_return_type(other) other.return_type elsif other.return_type.undefined? return_type + elsif return_type.erased_version_of?(other.return_type) + other.return_type + elsif other.return_type.erased_version_of?(return_type) + return_type elsif dodgy_return_type_source? && !other.dodgy_return_type_source? other.return_type elsif other.dodgy_return_type_source? && !dodgy_return_type_source? From 4619f0e9a3d4717d70093458233774e86e142746 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 22 Jul 2025 21:17:21 -0400 Subject: [PATCH 308/561] Generic typechecking improvements --- lib/solargraph/type_checker.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/solargraph/type_checker.rb b/lib/solargraph/type_checker.rb index 71e1ed85a..244502068 100644 --- a/lib/solargraph/type_checker.rb +++ b/lib/solargraph/type_checker.rb @@ -447,6 +447,8 @@ def signature_argument_problems_for location, locals, closure_pin, arguments, si # @todo Some level (strong, I guess) should require the param here else argtype = argchain.infer(api_map, closure_pin, locals) + argtype = argtype.self_to_type(closure_pin.context) + if argtype.defined? && ptype.defined? && !arg_conforms_to?(argtype, ptype) errors.push Problem.new(location, "Wrong argument type for #{pin.path}: #{par.name} expected #{ptype}, received #{argtype}") return errors @@ -488,6 +490,8 @@ def kwarg_problems_for sig, argchain, api_map, closure_pin, locals, location, pi ptype = data[:qualified] unless ptype.undefined? argtype = argchain.infer(api_map, closure_pin, locals) + argtype = argtype.self_to_type(closure_pin.context) + if argtype.defined? && ptype && !arg_conforms_to?(argtype, ptype) result.push Problem.new(location, "Wrong argument type for #{pin.path}: #{par.name} expected #{ptype}, received #{argtype}") end From 766eecfbca7de598c7badca68186db58c13858e8 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 22 Jul 2025 21:45:50 -0400 Subject: [PATCH 309/561] Type annotation improvements --- lib/solargraph/pin/base.rb | 7 +++++++ lib/solargraph/pin/common.rb | 12 ++++-------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/lib/solargraph/pin/base.rb b/lib/solargraph/pin/base.rb index 1261963df..c002c65eb 100644 --- a/lib/solargraph/pin/base.rb +++ b/lib/solargraph/pin/base.rb @@ -61,6 +61,13 @@ def assert_location_provided Solargraph.assert_or_log(:best_location, "Neither location nor type_location provided - #{path} #{source} #{self.class}") end + # @return [Pin::Closure, nil] + def closure + Solargraph.assert_or_log(:closure, "Closure not set on #{self.class} #{name.inspect} from #{source.inspect}") unless @closure + # @type [Pin::Closure, nil] + @closure + end + # @param other [self] # @param attrs [Hash{::Symbol => Object}] # diff --git a/lib/solargraph/pin/common.rb b/lib/solargraph/pin/common.rb index 6d9260abd..17b056098 100644 --- a/lib/solargraph/pin/common.rb +++ b/lib/solargraph/pin/common.rb @@ -3,17 +3,13 @@ module Solargraph module Pin module Common + # @!method closure + # @abstract + # @return [Pin::Closure, nil] + # @return [Location] attr_reader :location - # @return [Pin::Closure, nil] - attr_reader :closure - - def closure - Solargraph.assert_or_log(:closure, "Closure not set on #{self.class} #{name.inspect} from #{source.inspect}") unless @closure - @closure - end - # @return [String] def name @name ||= '' From ec8eea5e19472d95824fabc6d706e469a9495405 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 22 Jul 2025 22:01:54 -0400 Subject: [PATCH 310/561] Fix tag --- lib/solargraph/type_checker.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/solargraph/type_checker.rb b/lib/solargraph/type_checker.rb index ba2284b21..4b37b1054 100644 --- a/lib/solargraph/type_checker.rb +++ b/lib/solargraph/type_checker.rb @@ -466,7 +466,7 @@ def kwrestarg_problems_for(api_map, block_pin, locals, location, pin, params, kw result end - # @param pin [Pin::Method] + # @param pin [Pin::Method, Pin::Signature] # @return [void] def add_restkwarg_param_tag_details(param_details, pin) # see if we have additional tags to pay attention to from YARD - From e473495ec91ab7da1878e9b96517c581c7dcdefa Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 22 Jul 2025 22:04:50 -0400 Subject: [PATCH 311/561] Type annotation improvements --- .../parser_gem/node_processors/send_node.rb | 42 ++++++++++++------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/lib/solargraph/parser/parser_gem/node_processors/send_node.rb b/lib/solargraph/parser/parser_gem/node_processors/send_node.rb index 8a70a0b3f..8b4c378fe 100644 --- a/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +++ b/lib/solargraph/parser/parser_gem/node_processors/send_node.rb @@ -8,32 +8,39 @@ class SendNode < Parser::NodeProcessor::Base include ParserGem::NodeMethods def process + # @sg-ignore + # @type [Symbol] + method_name = node.children[1] + unless method_name.instance_of?(Symbol) + Solargraph.assert_or_log(:parser_method_name, "Expected method name to be a Symbol, got #{method_name.class} for node #{node.inspect}") + return process_children + end if node.children[0].nil? - if [:private, :public, :protected].include?(node.children[1]) + if [:private, :public, :protected].include?(method_name) process_visibility - elsif node.children[1] == :module_function + elsif method_name == :module_function process_module_function - elsif [:attr_reader, :attr_writer, :attr_accessor].include?(node.children[1]) + elsif [:attr_reader, :attr_writer, :attr_accessor].include?(method_name) process_attribute - elsif node.children[1] == :include + elsif method_name == :include process_include - elsif node.children[1] == :extend + elsif method_name == :extend process_extend - elsif node.children[1] == :prepend + elsif method_name == :prepend process_prepend - elsif node.children[1] == :require + elsif method_name == :require process_require - elsif node.children[1] == :autoload + elsif method_name == :autoload process_autoload - elsif node.children[1] == :private_constant + elsif method_name == :private_constant process_private_constant - elsif node.children[1] == :alias_method && node.children[2] && node.children[2] && node.children[2].type == :sym && node.children[3] && node.children[3].type == :sym + elsif method_name == :alias_method && node.children[2] && node.children[2] && node.children[2].type == :sym && node.children[3] && node.children[3].type == :sym process_alias_method - elsif node.children[1] == :private_class_method && node.children[2].is_a?(AST::Node) + elsif method_name == :private_class_method && node.children[2].is_a?(AST::Node) # Processing a private class can potentially handle children on its own return if process_private_class_method end - elsif node.children[1] == :require && node.children[0].to_s == '(const nil :Bundler)' + elsif method_name == :require && node.children[0].to_s == '(const nil :Bundler)' pins.push Pin::Reference::Require.new(Solargraph::Location.new(region.filename, Solargraph::Range.from_to(0, 0, 0, 0)), 'bundler/require', source: :parser) end process_children @@ -48,12 +55,19 @@ def process_visibility if child.is_a?(AST::Node) && (child.type == :sym || child.type == :str) name = child.children[0].to_s matches = pins.select{ |pin| pin.is_a?(Pin::Method) && pin.name == name && pin.namespace == region.closure.full_context.namespace && pin.context.scope == (region.scope || :instance)} + # @sg-ignore + # @type [Symbol] + visibility = node.children[1] + unless visibility.instance_of?(Symbol) + Solargraph.assert_or_log(:parser_visibility, "Expected visibility name to be a Symbol, got #{visibility.class} for node #{node.inspect}") + return process_children + end matches.each do |pin| # @todo Smelly instance variable access - pin.instance_variable_set(:@visibility, node.children[1]) + pin.instance_variable_set(:@visibility, visibility) end else - process_children region.update(visibility: node.children[1]) + process_children region.update(visibility: visibility) end end else From 4e5367b5213619e55a6ef8c27f10bc050f9ee7d8 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 22 Jul 2025 22:19:47 -0400 Subject: [PATCH 312/561] Fix 'all!' config to reporters Solargraph found the type error here! --- lib/solargraph/library.rb | 4 ++-- spec/library_spec.rb | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/lib/solargraph/library.rb b/lib/solargraph/library.rb index 740d700b3..8eb2eb71d 100644 --- a/lib/solargraph/library.rb +++ b/lib/solargraph/library.rb @@ -402,8 +402,8 @@ def diagnose filename repargs = {} workspace.config.reporters.each do |line| if line == 'all!' - Diagnostics.reporters.each do |reporter| - repargs[reporter] ||= [] + Diagnostics.reporters.each do |reporter_name| + repargs[Diagnostics.reporter(reporter_name)] ||= [] end else args = line.split(':').map(&:strip) diff --git a/spec/library_spec.rb b/spec/library_spec.rb index bd7cc25a0..5f2faeed9 100644 --- a/spec/library_spec.rb +++ b/spec/library_spec.rb @@ -110,6 +110,23 @@ def bar baz, key: '' # @todo More tests end + it 'diagnoses using all reporters' do + directory = '' + config = instance_double(Solargraph::Workspace::Config) + allow(config).to receive(:plugins).and_return([]) + allow(config).to receive(:required).and_return([]) + allow(config).to receive(:reporters).and_return(['all!']) + workspace = Solargraph::Workspace.new directory, config + library = Solargraph::Library.new workspace + src = Solargraph::Source.load_string(%( + puts 'hello' + ), 'file.rb', 0) + library.attach src + result = library.diagnose 'file.rb' + expect(result.to_s).to include('rubocop') + end + + it "documents symbols" do library = Solargraph::Library.new src = Solargraph::Source.load_string(%( From f8f86bcea08b12d7f24923578d9fb2e55d6ee453 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 22 Jul 2025 23:22:10 -0400 Subject: [PATCH 313/561] Support YAML stdlib requires --- lib/solargraph/api_map.rb | 9 +++++++-- lib/solargraph/api_map/store.rb | 7 ++++++- lib/solargraph/rbs_map.rb | 15 ++++++++++----- lib/solargraph/rbs_map/stdlib_map.rb | 15 ++++++++++++++- lib/solargraph/workspace/gemspecs.rb | 21 ++++++++++++++------- 5 files changed, 51 insertions(+), 16 deletions(-) diff --git a/lib/solargraph/api_map.rb b/lib/solargraph/api_map.rb index 1998f67b3..95a1a13de 100755 --- a/lib/solargraph/api_map.rb +++ b/lib/solargraph/api_map.rb @@ -550,6 +550,11 @@ def get_method_stack rooted_tag, name, scope: :instance, visibility: [:private, namespace_pin = store.get_path_pins(fqns).select { |p| p.is_a?(Pin::Namespace) }.first methods = get_methods(rooted_tag, scope: scope, visibility: visibility).select { |p| p.name == name } methods = erase_generics(namespace_pin, rooted_type, methods) unless preserve_generics + if methods.empty? && namespace_pin.nil? + # namespace may be set by an alias + constant = store.constant_pins.find { |c| c.name == fqns && visibility.include?(c.visibility) } + return get_method_stack(constant.return_type.tag, name, scope: scope, visibility: visibility, preserve_generics: preserve_generics) if constant + end methods end @@ -558,7 +563,7 @@ def get_method_stack rooted_tag, name, scope: :instance, visibility: [:private, # @deprecated Use #get_path_pins instead. # # @param path [String] The path to find - # @return [Enumerable] + # @return [Array] def get_path_suggestions path return [] if path.nil? resolve_method_aliases store.get_path_pins(path) @@ -567,7 +572,7 @@ def get_path_suggestions path # Get an array of pins that match the specified path. # # @param path [String] - # @return [Enumerable] + # @return [Array] def get_path_pins path get_path_suggestions(path) end diff --git a/lib/solargraph/api_map/store.rb b/lib/solargraph/api_map/store.rb index 47f92194c..7079a0a37 100644 --- a/lib/solargraph/api_map/store.rb +++ b/lib/solargraph/api_map/store.rb @@ -49,7 +49,7 @@ def inspect # @param fqns [String] # @param visibility [Array] - # @return [Enumerable] + # @return [Enumerable] def get_constants fqns, visibility = [:public] namespace_children(fqns).select { |pin| !pin.name.empty? && (pin.is_a?(Pin::Namespace) || pin.is_a?(Pin::Constant)) && visibility.include?(pin.visibility) @@ -142,6 +142,11 @@ def namespace_pins pins_by_class(Solargraph::Pin::Namespace) end + # @return [Enumerable] + def constant_pins + pins_by_class(Solargraph::Pin::Constant) + end + # @return [Enumerable] def method_pins pins_by_class(Solargraph::Pin::Method) diff --git a/lib/solargraph/rbs_map.rb b/lib/solargraph/rbs_map.rb index 263f9f2dd..45bb7c3b2 100644 --- a/lib/solargraph/rbs_map.rb +++ b/lib/solargraph/rbs_map.rb @@ -64,9 +64,10 @@ def self.rbs_source_desc cache_key # for the given library. Must change when the RBS info is # updated upstream for the same library and version. May change # if the config for where information comes form changes. - # @sg-ignore Solargraph::RbsMap#cache_key return type could not be inferred def cache_key @hextdigest ||= begin + return CACHE_KEY_UNRESOLVED unless resolved? + data = nil # @type gem_config [nil, Hash{String => Hash{String => String}}] gem_config = nil @@ -79,7 +80,7 @@ def cache_key end end if gem_config.nil? - CACHE_KEY_UNRESOLVED + CACHE_KEY_STDLIB else # @type [String] source = gem_config.dig('source', 'type') @@ -108,9 +109,13 @@ def self.from_gemspec gemspec, rbs_collection_path, rbs_collection_config_path return rbs_map if rbs_map.resolved? # try any version of the gem in the collection - RbsMap.new(gemspec.name, nil, - rbs_collection_paths: [rbs_collection_path].compact, - rbs_collection_config_path: rbs_collection_config_path) + rbs_map = RbsMap.new(gemspec.name, nil, + rbs_collection_paths: [rbs_collection_path].compact, + rbs_collection_config_path: rbs_collection_config_path) + + return rbs_map if rbs_map.resolved? + + StdlibMap.new(gemspec.name) end # @param out [IO, nil] where to log messages diff --git a/lib/solargraph/rbs_map/stdlib_map.rb b/lib/solargraph/rbs_map/stdlib_map.rb index 89142b429..7794ace5f 100644 --- a/lib/solargraph/rbs_map/stdlib_map.rb +++ b/lib/solargraph/rbs_map/stdlib_map.rb @@ -29,7 +29,7 @@ def initialize library, out: $stderr super unless resolved? @pins = [] - logger.info { "Could not resolve #{library.inspect}" } + logger.debug { "StdlibMap could not resolve #{library.inspect}" } return end generated_pins = pins @@ -38,6 +38,19 @@ def initialize library, out: $stderr end end + def self.source + @source ||= RBS::Collection::Sources::Stdlib.instance + end + + # @return [Array String}>, nil] + def self.stdlib_dependencies(name, version) + if source.has?(name, version) + source.dependencies_of(name, version) + else + [] + end + end + # @param library [String] # @return [StdlibMap] def self.load library diff --git a/lib/solargraph/workspace/gemspecs.rb b/lib/solargraph/workspace/gemspecs.rb index 465e0f3d5..d72d97f41 100644 --- a/lib/solargraph/workspace/gemspecs.rb +++ b/lib/solargraph/workspace/gemspecs.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true +require 'rubygems' require 'bundler' module Solargraph @@ -82,10 +83,7 @@ def find_gem name, version gemspec = all_gemspecs_from_bundle.find { |gemspec| gemspec.name == name && gemspec.version == version } return gemspec if gemspec - Gem::Specification.find_by_name(name, version) - rescue Gem::MissingSpecError - logger.warn "Please install the gem #{name}:#{version} in Solargraph's Ruby environment" - nil + resolve_gem_ignoring_local_bundle name, version end # @param gemspec [Gem::Specification] @@ -95,7 +93,7 @@ def fetch_dependencies gemspec # @param runtime_dep [Gem::Dependency] # @param deps [Set] - only_runtime_dependencies(gemspec).each_with_object(Set.new) do |runtime_dep, deps| + gem_dep_gemspecs = only_runtime_dependencies(gemspec).each_with_object(Set.new) do |runtime_dep, deps| Solargraph.logger.info "Adding #{runtime_dep.name} dependency for #{gemspec.name}" dep = gemspecs.find { |dep| dep.name == runtime_dep.name } dep ||= Gem::Specification.find_by_name(runtime_dep.name, runtime_dep.requirement) @@ -105,6 +103,11 @@ def fetch_dependencies gemspec "for #{gemspec.name} not found in bundle.") nil end.to_a.compact + # RBS tracks implicit dependencies, like how the YAML standard + # library implies pulling in the psych library. + stdlib_deps = RbsMap::StdlibMap.stdlib_dependencies(gemspec.name, gemspec.version) || [] + stdlib_dep_gemspecs = stdlib_deps.map { |dep| find_gem(dep['name'], dep['version']) }.compact + (gem_dep_gemspecs + stdlib_dep_gemspecs).sort.uniq end # Returns all gemspecs directly depended on by this workspace's @@ -127,6 +130,7 @@ def self.gem_specification_cache private + # @sg-ignore # @param specish [Gem::Specification, Bundler::LazySpecification, Bundler::StubSpecification] # @return [Gem::Specification, nil] def to_gem_specification specish @@ -261,8 +265,11 @@ def resolve_gem_ignoring_local_bundle name, version begin Gem::Specification.find_by_name(name) rescue Gem::MissingSpecError - logger.warn "Please install the gem #{name}:#{version} in Solargraph's Ruby environment" - nil + stdlibmap = RbsMap::StdlibMap.new(name) + if !stdlibmap.resolved? + logger.warn "Please install the gem #{name}:#{version} in Solargraph's Ruby environment" + end + nil # either not here or in stdlib end end From 1258a79c5bb254f0c0e5d99b47d6ae7f626af725 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 23 Jul 2025 06:43:49 -0400 Subject: [PATCH 314/561] Fix merge --- lib/solargraph/shell.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/solargraph/shell.rb b/lib/solargraph/shell.rb index 8676db1e0..9c183a5aa 100755 --- a/lib/solargraph/shell.rb +++ b/lib/solargraph/shell.rb @@ -264,7 +264,7 @@ def rbs type = pin.typify(api_map) type = pin.probe(api_map) if type.undefined? - pin.docstring.add_tag YARD::Tags::Tag.new('return', nil, type.items.map(&:to_s)) + pin.docstring.add_tag YARD::Tags::Tag.new('return', '', type.items.map(&:to_s)) pin.instance_variable_set(:@return_type, type) end end From e10fc3e4a8a05129abd1548323584bbc125e17f2 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 23 Jul 2025 06:55:32 -0400 Subject: [PATCH 315/561] Fix merge issue --- lib/solargraph/logging.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/solargraph/logging.rb b/lib/solargraph/logging.rb index c4b0267ba..3301ab1c4 100644 --- a/lib/solargraph/logging.rb +++ b/lib/solargraph/logging.rb @@ -18,7 +18,6 @@ module Logging warn "Invalid value for SOLARGRAPH_LOG: #{configured_level.inspect} - valid values are #{LOG_LEVELS.keys}" if configured_level DEFAULT_LOG_LEVEL end - # @sg-ignore Fix cvar issue @@logger = Logger.new(STDERR, level: level) # @sg-ignore Fix cvar issue @@logger.formatter = proc do |severity, datetime, progname, msg| From a5875989ccd72f704143824296e2c2bb52146003 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 23 Jul 2025 06:55:52 -0400 Subject: [PATCH 316/561] Fix merge issue --- lib/solargraph/workspace/gemspecs.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/solargraph/workspace/gemspecs.rb b/lib/solargraph/workspace/gemspecs.rb index d72d97f41..d6fffb1b0 100644 --- a/lib/solargraph/workspace/gemspecs.rb +++ b/lib/solargraph/workspace/gemspecs.rb @@ -130,7 +130,6 @@ def self.gem_specification_cache private - # @sg-ignore # @param specish [Gem::Specification, Bundler::LazySpecification, Bundler::StubSpecification] # @return [Gem::Specification, nil] def to_gem_specification specish From b284720c79e7a8d4cb6893db3733e15baeb68939 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 23 Jul 2025 07:00:22 -0400 Subject: [PATCH 317/561] Fix annotations --- lib/solargraph/rbs_map/stdlib_map.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/solargraph/rbs_map/stdlib_map.rb b/lib/solargraph/rbs_map/stdlib_map.rb index 7794ace5f..92f68a013 100644 --- a/lib/solargraph/rbs_map/stdlib_map.rb +++ b/lib/solargraph/rbs_map/stdlib_map.rb @@ -38,10 +38,13 @@ def initialize library, out: $stderr end end + # @return [RBS::Collection::Sources::Stdlib] def self.source @source ||= RBS::Collection::Sources::Stdlib.instance end + # @param name [String] + # @param version [String] # @return [Array String}>, nil] def self.stdlib_dependencies(name, version) if source.has?(name, version) From 01f55803cd911b250a14043fb9c4e99a945c095c Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 23 Jul 2025 07:02:07 -0400 Subject: [PATCH 318/561] Fix merge --- lib/solargraph/yardoc.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/solargraph/yardoc.rb b/lib/solargraph/yardoc.rb index 8f9c2f04d..9701018cf 100644 --- a/lib/solargraph/yardoc.rb +++ b/lib/solargraph/yardoc.rb @@ -22,6 +22,7 @@ def build_docs gem_yardoc_path, yard_plugins, gemspec Solargraph.logger.debug { "Running: #{cmd}" } # @todo set these up to run in parallel # + # @sg-ignore stdout_and_stderr_str, status = Open3.capture2e(cmd, chdir: gemspec.gem_dir) return if status.success? Solargraph.logger.warn { "YARD failed running #{cmd.inspect} in #{gemspec.gem_dir}" } From 6ad4805f969c5798c7c0554871de18a9e3d5f235 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 23 Jul 2025 07:05:01 -0400 Subject: [PATCH 319/561] Bugfix --- .../parser_gem/node_processors/send_node.rb | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/solargraph/parser/parser_gem/node_processors/send_node.rb b/lib/solargraph/parser/parser_gem/node_processors/send_node.rb index 8b4c378fe..199d85784 100644 --- a/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +++ b/lib/solargraph/parser/parser_gem/node_processors/send_node.rb @@ -52,22 +52,22 @@ def process def process_visibility if (node.children.length > 2) node.children[2..-1].each do |child| + # @sg-ignore + # @type [Symbol] + visibility = node.children[1] + unless visibility.instance_of?(Symbol) + Solargraph.assert_or_log(:parser_visibility, "Expected visibility name to be a Symbol, got #{visibility.class} for node #{node.inspect}") + return process_children + end if child.is_a?(AST::Node) && (child.type == :sym || child.type == :str) name = child.children[0].to_s matches = pins.select{ |pin| pin.is_a?(Pin::Method) && pin.name == name && pin.namespace == region.closure.full_context.namespace && pin.context.scope == (region.scope || :instance)} - # @sg-ignore - # @type [Symbol] - visibility = node.children[1] - unless visibility.instance_of?(Symbol) - Solargraph.assert_or_log(:parser_visibility, "Expected visibility name to be a Symbol, got #{visibility.class} for node #{node.inspect}") - return process_children - end matches.each do |pin| # @todo Smelly instance variable access pin.instance_variable_set(:@visibility, visibility) end else - process_children region.update(visibility: visibility) + process_children region.update(visibility: node.children[1]) end end else From 9efa608eac04051f950607842148b563fce5d8b3 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 23 Jul 2025 07:06:52 -0400 Subject: [PATCH 320/561] Fix annotation --- lib/solargraph/type_checker.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/solargraph/type_checker.rb b/lib/solargraph/type_checker.rb index f867ba04f..0826c1e66 100644 --- a/lib/solargraph/type_checker.rb +++ b/lib/solargraph/type_checker.rb @@ -348,6 +348,7 @@ def argument_problems_for chain, api_map, closure_pin, locals, location # @param arguments [Array] # @param sig [Pin::Signature] # @param pin [Pin::Method] + # @param pins [Array] # # @return [Array] def signature_argument_problems_for location, locals, closure_pin, params, arguments, sig, pin From dae5c53861eeac18db90b1edfe71c22183de2546 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 23 Jul 2025 07:08:22 -0400 Subject: [PATCH 321/561] Fix annotation --- lib/solargraph/type_checker.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/solargraph/type_checker.rb b/lib/solargraph/type_checker.rb index 4b37b1054..b0165281b 100644 --- a/lib/solargraph/type_checker.rb +++ b/lib/solargraph/type_checker.rb @@ -502,7 +502,9 @@ def signature_param_details(pin) # signatures and method pins can help by adding type information # # @param param_details [Hash{String => Hash{Symbol => String, ComplexType}}] + # @param param_names [Array] # @param new_param_details [Hash{String => Hash{Symbol => String, ComplexType}}] + # # @return [void] def add_to_param_details(param_details, param_names, new_param_details) new_param_details.each do |param_name, details| From c919bf7c290781a587fb178c1eac029e11c468b7 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 23 Jul 2025 07:12:44 -0400 Subject: [PATCH 322/561] Bugfix --- lib/solargraph/parser/parser_gem/node_processors/send_node.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/solargraph/parser/parser_gem/node_processors/send_node.rb b/lib/solargraph/parser/parser_gem/node_processors/send_node.rb index 199d85784..57c74a22c 100644 --- a/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +++ b/lib/solargraph/parser/parser_gem/node_processors/send_node.rb @@ -67,7 +67,7 @@ def process_visibility pin.instance_variable_set(:@visibility, visibility) end else - process_children region.update(visibility: node.children[1]) + process_children region.update(visibility: visibility) end end else From 9d99e151aac200dbbf3c432b5f11e61009dd2870 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 23 Jul 2025 07:13:52 -0400 Subject: [PATCH 323/561] Add annotation --- lib/solargraph/type_checker.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/solargraph/type_checker.rb b/lib/solargraph/type_checker.rb index b0165281b..4c9fd0729 100644 --- a/lib/solargraph/type_checker.rb +++ b/lib/solargraph/type_checker.rb @@ -466,6 +466,7 @@ def kwrestarg_problems_for(api_map, block_pin, locals, location, pin, params, kw result end + # @param param_details [Hash{String => Hash{Symbol => String, ComplexType}}] # @param pin [Pin::Method, Pin::Signature] # @return [void] def add_restkwarg_param_tag_details(param_details, pin) From 42ed6d63f6b40930f2f866c8803953b9c263f4c8 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 23 Jul 2025 07:14:34 -0400 Subject: [PATCH 324/561] Fix merge --- lib/solargraph/yardoc.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/solargraph/yardoc.rb b/lib/solargraph/yardoc.rb index 0530728c7..43cce7380 100644 --- a/lib/solargraph/yardoc.rb +++ b/lib/solargraph/yardoc.rb @@ -24,7 +24,6 @@ def build_docs gem_yardoc_path, yard_plugins, gemspec Solargraph.logger.debug { "Running: #{cmd}" } # @todo set these up to run in parallel # - # @sg-ignore stdout_and_stderr_str, status = Open3.capture2e(cmd, chdir: gemspec.gem_dir) return if status.success? Solargraph.logger.warn { "YARD failed running #{cmd.inspect} in #{gemspec.gem_dir}" } From 9786b51cae593d52757a423b7cec0cc1e3bb5f4d Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 23 Jul 2025 07:19:23 -0400 Subject: [PATCH 325/561] RuboCop fixes --- lib/solargraph/rbs_map/stdlib_map.rb | 2 +- lib/solargraph/workspace/gemspecs.rb | 2 +- spec/workspace/require_paths_spec.rb | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/solargraph/rbs_map/stdlib_map.rb b/lib/solargraph/rbs_map/stdlib_map.rb index 92f68a013..d254b03e8 100644 --- a/lib/solargraph/rbs_map/stdlib_map.rb +++ b/lib/solargraph/rbs_map/stdlib_map.rb @@ -46,7 +46,7 @@ def self.source # @param name [String] # @param version [String] # @return [Array String}>, nil] - def self.stdlib_dependencies(name, version) + def self.stdlib_dependencies name, version if source.has?(name, version) source.dependencies_of(name, version) else diff --git a/lib/solargraph/workspace/gemspecs.rb b/lib/solargraph/workspace/gemspecs.rb index d72d97f41..8de2c785b 100644 --- a/lib/solargraph/workspace/gemspecs.rb +++ b/lib/solargraph/workspace/gemspecs.rb @@ -266,7 +266,7 @@ def resolve_gem_ignoring_local_bundle name, version Gem::Specification.find_by_name(name) rescue Gem::MissingSpecError stdlibmap = RbsMap::StdlibMap.new(name) - if !stdlibmap.resolved? + unless stdlibmap.resolved? logger.warn "Please install the gem #{name}:#{version} in Solargraph's Ruby environment" end nil # either not here or in stdlib diff --git a/spec/workspace/require_paths_spec.rb b/spec/workspace/require_paths_spec.rb index 0b57432a9..db5516447 100644 --- a/spec/workspace/require_paths_spec.rb +++ b/spec/workspace/require_paths_spec.rb @@ -4,7 +4,6 @@ require 'tmpdir' describe Solargraph::Workspace::RequirePaths do - subject(:paths) { described_class.new(dir_path, config).generate } let(:config) { Solargraph::Workspace::Config.new(dir_path) } From bbed2fe8cdddabb8aa3c3a0517df0bcc2867dbab Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 23 Jul 2025 07:26:34 -0400 Subject: [PATCH 326/561] Fix typechecking errors --- lib/solargraph/api_map/index.rb | 3 +++ lib/solargraph/api_map/store.rb | 7 ++++++- lib/solargraph/workspace/require_paths.rb | 1 + 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/solargraph/api_map/index.rb b/lib/solargraph/api_map/index.rb index 810600534..7490769f0 100644 --- a/lib/solargraph/api_map/index.rb +++ b/lib/solargraph/api_map/index.rb @@ -15,6 +15,9 @@ def pins @pins ||= [] end + # @return [Set>, nil] + attr_reader :namespaces + # @return [Hash{String => Array}] def namespace_hash @namespace_hash ||= Hash.new { |h, k| h[k] = [] } diff --git a/lib/solargraph/api_map/store.rb b/lib/solargraph/api_map/store.rb index 7079a0a37..3e453bb35 100644 --- a/lib/solargraph/api_map/store.rb +++ b/lib/solargraph/api_map/store.rb @@ -16,7 +16,7 @@ def pins index.pins end - # @param pinsets [Array>] + # @param pinsets [Array>] # @return [Boolean] True if the index was updated def update *pinsets return catalog(pinsets) if pinsets.length != @pinsets.length @@ -205,12 +205,17 @@ def fqns_pins fqns private + # @return [Index] def index @indexes.last end + # @param pinsets [Array>] + # + # @return [void] def catalog pinsets @pinsets = pinsets + # @type [Array] @indexes = [] pinsets.each do |pins| if @indexes.last && pins.empty? diff --git a/lib/solargraph/workspace/require_paths.rb b/lib/solargraph/workspace/require_paths.rb index 18364b141..443752084 100644 --- a/lib/solargraph/workspace/require_paths.rb +++ b/lib/solargraph/workspace/require_paths.rb @@ -78,6 +78,7 @@ def require_path_from_gemspec_file gemspec_file_path "spec = eval(File.read('#{gemspec_file_path}'), TOPLEVEL_BINDING, '#{gemspec_file_path}'); " \ 'return unless Gem::Specification === spec; ' \ 'puts({name: spec.name, paths: spec.require_paths}.to_json)'] + # @sg-ignore o, e, s = Open3.capture3(*cmd) if s.success? begin From 75c11b97b8c38e7546cdc4201f0e6ab565708801 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 23 Jul 2025 07:28:03 -0400 Subject: [PATCH 327/561] Fix typecheck and rubocop issues --- lib/solargraph/rbs_map.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/solargraph/rbs_map.rb b/lib/solargraph/rbs_map.rb index 45bb7c3b2..613719e90 100644 --- a/lib/solargraph/rbs_map.rb +++ b/lib/solargraph/rbs_map.rb @@ -60,14 +60,15 @@ def self.rbs_source_desc cache_key end end + # @sg-ignore # @return [String] representing the version of the RBS info fetched # for the given library. Must change when the RBS info is # updated upstream for the same library and version. May change # if the config for where information comes form changes. def cache_key - @hextdigest ||= begin - return CACHE_KEY_UNRESOLVED unless resolved? + return CACHE_KEY_UNRESOLVED unless resolved? + @hextdigest ||= begin data = nil # @type gem_config [nil, Hash{String => Hash{String => String}}] gem_config = nil From 4fa1a3aea1891115fac8e2787a58b3b2f132d25c Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 23 Jul 2025 07:30:44 -0400 Subject: [PATCH 328/561] Fix rubocop issues --- .rubocop_todo.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 97a41ccd1..53f64fc3c 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1231,12 +1231,6 @@ RSpec/LetBeforeExamples: Exclude: - 'spec/complex_type_spec.rb' -# Offense count: 3 -# Configuration parameters: . -# SupportedStyles: have_received, receive -RSpec/MessageSpies: - EnforcedStyle: receive - # Offense count: 2 RSpec/MissingExampleGroupArgument: Exclude: From 7d1d0be2d64fe1d8a1edd17eabbe9e7f9a9ad6a1 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 23 Jul 2025 07:36:54 -0400 Subject: [PATCH 329/561] Fix RuboCop issues --- spec/doc_map_spec.rb | 3 ++- spec/language_server/host/diagnoser_spec.rb | 3 ++- spec/language_server/host/message_worker_spec.rb | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/spec/doc_map_spec.rb b/spec/doc_map_spec.rb index 751b40b37..a6f52d033 100644 --- a/spec/doc_map_spec.rb +++ b/spec/doc_map_spec.rb @@ -76,8 +76,9 @@ it 'does not warn' do # Requiring 'set' is unnecessary because it's already included in core. It # might make sense to log redundant requires, but a warning is overkill. - expect(Solargraph.logger).not_to receive(:warn).with(/path set/) + allow(Solargraph.logger).to receive(:warn) doc_map + expect(Solargraph.logger).not_to have_received(:warn).with(/path set/) end end diff --git a/spec/language_server/host/diagnoser_spec.rb b/spec/language_server/host/diagnoser_spec.rb index d59a843f1..69ee0b866 100644 --- a/spec/language_server/host/diagnoser_spec.rb +++ b/spec/language_server/host/diagnoser_spec.rb @@ -3,7 +3,8 @@ host = double(Solargraph::LanguageServer::Host, options: { 'diagnostics' => true }, synchronizing?: false) diagnoser = Solargraph::LanguageServer::Host::Diagnoser.new(host) diagnoser.schedule 'file.rb' - expect(host).to receive(:diagnose).with('file.rb') + allow(host).to receive(:diagnose) diagnoser.tick + expect(host).to have_received(:diagnose).with('file.rb') end end diff --git a/spec/language_server/host/message_worker_spec.rb b/spec/language_server/host/message_worker_spec.rb index b9ce2a41f..9e5ce5721 100644 --- a/spec/language_server/host/message_worker_spec.rb +++ b/spec/language_server/host/message_worker_spec.rb @@ -2,11 +2,12 @@ it "handle requests on queue" do host = double(Solargraph::LanguageServer::Host) message = {'method' => '$/example'} - expect(host).to receive(:receive).with(message).and_return(nil) + allow(host).to receive(:receive).with(message).and_return(nil) worker = Solargraph::LanguageServer::Host::MessageWorker.new(host) worker.queue(message) expect(worker.messages).to eq [message] worker.tick + expect(host).to have_received(:receive).with(message) end end From ee7091a6aa751b7a3be98c4106475df8fb02f5d1 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 23 Jul 2025 07:42:44 -0400 Subject: [PATCH 330/561] Fix spec --- lib/solargraph/workspace/gemspecs.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/solargraph/workspace/gemspecs.rb b/lib/solargraph/workspace/gemspecs.rb index 8de2c785b..952937c68 100644 --- a/lib/solargraph/workspace/gemspecs.rb +++ b/lib/solargraph/workspace/gemspecs.rb @@ -107,7 +107,7 @@ def fetch_dependencies gemspec # library implies pulling in the psych library. stdlib_deps = RbsMap::StdlibMap.stdlib_dependencies(gemspec.name, gemspec.version) || [] stdlib_dep_gemspecs = stdlib_deps.map { |dep| find_gem(dep['name'], dep['version']) }.compact - (gem_dep_gemspecs + stdlib_dep_gemspecs).sort.uniq + (gem_dep_gemspecs + stdlib_dep_gemspecs).uniq(&:name) end # Returns all gemspecs directly depended on by this workspace's From 78d71470379fe5dc118402be40786f945c667f0b Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 23 Jul 2025 07:50:52 -0400 Subject: [PATCH 331/561] Annotation fixes --- lib/solargraph/api_map/index.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/solargraph/api_map/index.rb b/lib/solargraph/api_map/index.rb index 7490769f0..01820a441 100644 --- a/lib/solargraph/api_map/index.rb +++ b/lib/solargraph/api_map/index.rb @@ -39,6 +39,7 @@ def path_pin_hash def pins_by_class klass # @type [Set] s = Set.new + # @sg-ignore Need to handle block parameter destructuring @pin_select_cache[klass] ||= pin_class_hash.each_with_object(s) { |(key, o), n| n.merge(o) if key <= klass } end @@ -63,6 +64,7 @@ def superclass_references end # @param pins [Array] + # @return [self] def merge pins deep_clone.catalog pins end @@ -72,6 +74,7 @@ def merge pins attr_writer :pins, :pin_select_cache, :namespace_hash, :pin_class_hash, :path_pin_hash, :include_references, :extend_references, :prepend_references, :superclass_references + # @return [self] def deep_clone Index.allocate.tap do |copy| copy.pin_select_cache = {} @@ -87,6 +90,8 @@ def deep_clone end # @param new_pins [Array] + # + # @return [self] def catalog new_pins @pin_select_cache = {} pins.concat new_pins From 3df4a6b2caf1d5ab343f40354a17866079c99cb2 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 23 Jul 2025 07:51:25 -0400 Subject: [PATCH 332/561] Deal with type issue --- lib/solargraph/workspace/gemspecs.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/solargraph/workspace/gemspecs.rb b/lib/solargraph/workspace/gemspecs.rb index 952937c68..5c3ef292e 100644 --- a/lib/solargraph/workspace/gemspecs.rb +++ b/lib/solargraph/workspace/gemspecs.rb @@ -175,6 +175,7 @@ def query_external_bundle command 'ruby', '-e', "require 'bundler'; require 'json'; Dir.chdir('#{directory}') { puts #{command}.to_json }" ] + # @sg-ignore o, e, s = Open3.capture3(*cmd) if s.success? Solargraph.logger.debug "External bundle: #{o}" From 841097762b688c7ad2beaaa55402f335e0256501 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 23 Jul 2025 08:11:09 -0400 Subject: [PATCH 333/561] RuboCop fixes to keep todo file under control --- .rubocop_todo.yml | 22 +++------------- lib/solargraph/api_map.rb | 25 ++++++++++++------- lib/solargraph/api_map/index.rb | 4 +-- lib/solargraph/api_map/store.rb | 2 +- lib/solargraph/gem_pins.rb | 8 ++++-- lib/solargraph/library.rb | 16 ++++++------ .../parser/parser_gem/class_methods.rb | 8 +++--- lib/solargraph/rbs_map.rb | 8 +++--- lib/solargraph/shell.rb | 14 ++++++----- lib/solargraph/workspace.rb | 9 ++++--- spec/workspace_spec.rb | 3 ++- 11 files changed, 58 insertions(+), 61 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 53f64fc3c..023e4ea1a 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -264,22 +264,18 @@ Layout/HeredocIndentation: - 'spec/rbs_map/conversions_spec.rb' - 'spec/yard_map/mapper/to_method_spec.rb' -# Offense count: 24 +# Offense count: 19 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: Width, AllowedPatterns. Layout/IndentationWidth: Exclude: - 'lib/solargraph.rb' - - 'lib/solargraph/api_map/store.rb' - 'lib/solargraph/diagnostics/rubocop.rb' - 'lib/solargraph/language_server/message/extended/check_gem_version.rb' - - 'lib/solargraph/library.rb' - 'lib/solargraph/parser/parser_gem/node_methods.rb' - 'lib/solargraph/parser/parser_gem/node_processors/block_node.rb' - 'lib/solargraph/pin/method.rb' - 'lib/solargraph/pin/namespace.rb' - - 'lib/solargraph/rbs_map.rb' - - 'lib/solargraph/shell.rb' - 'lib/solargraph/source/chain/call.rb' - 'lib/solargraph/source_map/clip.rb' - 'lib/solargraph/source_map/mapper.rb' @@ -1392,11 +1388,6 @@ RSpec/ScatteredLet: Exclude: - 'spec/complex_type_spec.rb' -# Offense count: 1 -RSpec/StubbedMock: - Exclude: - - 'spec/language_server/host/message_worker_spec.rb' - # Offense count: 23 # Configuration parameters: IgnoreNameless, IgnoreSymbolicNames. RSpec/VerifiedDoubles: @@ -2001,20 +1992,16 @@ Style/IfInsideElse: - 'lib/solargraph/source_map/clip.rb' - 'lib/solargraph/type_checker.rb' -# Offense count: 62 +# Offense count: 56 # This cop supports safe autocorrection (--autocorrect). Style/IfUnlessModifier: Exclude: - - 'lib/solargraph/api_map.rb' - - 'lib/solargraph/api_map/index.rb' - 'lib/solargraph/complex_type.rb' - 'lib/solargraph/complex_type/unique_type.rb' - 'lib/solargraph/language_server/message/completion_item/resolve.rb' - 'lib/solargraph/language_server/message/initialize.rb' - 'lib/solargraph/language_server/message/text_document/completion.rb' - 'lib/solargraph/language_server/message/text_document/hover.rb' - - 'lib/solargraph/library.rb' - - 'lib/solargraph/parser/parser_gem/class_methods.rb' - 'lib/solargraph/parser/parser_gem/node_chainer.rb' - 'lib/solargraph/parser/parser_gem/node_methods.rb' - 'lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb' @@ -2035,7 +2022,6 @@ Style/IfUnlessModifier: - 'lib/solargraph/source_map/clip.rb' - 'lib/solargraph/source_map/mapper.rb' - 'lib/solargraph/type_checker.rb' - - 'lib/solargraph/workspace.rb' - 'lib/solargraph/workspace/config.rb' - 'lib/solargraph/yard_map/helpers.rb' - 'lib/solargraph/yard_map/mapper/to_method.rb' @@ -2777,7 +2763,7 @@ YARD/TagTypeSyntax: - 'lib/solargraph/parser/comment_ripper.rb' - 'lib/solargraph/type_checker.rb' -# Offense count: 185 +# Offense count: 178 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: Max, AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, IgnoreCopDirectives, AllowedPatterns, SplitStrings. # URISchemes: http, https @@ -2833,7 +2819,6 @@ Layout/LineLength: - 'lib/solargraph/source_map/clip.rb' - 'lib/solargraph/source_map/mapper.rb' - 'lib/solargraph/type_checker.rb' - - 'lib/solargraph/workspace.rb' - 'lib/solargraph/workspace/config.rb' - 'lib/solargraph/yard_map/mapper/to_method.rb' - 'spec/complex_type_spec.rb' @@ -2845,4 +2830,3 @@ Layout/LineLength: - 'spec/source/chain_spec.rb' - 'spec/source_map/clip_spec.rb' - 'spec/source_map_spec.rb' - - 'spec/workspace_spec.rb' diff --git a/lib/solargraph/api_map.rb b/lib/solargraph/api_map.rb index 95a1a13de..28ff32c10 100755 --- a/lib/solargraph/api_map.rb +++ b/lib/solargraph/api_map.rb @@ -544,7 +544,8 @@ def get_complex_type_methods complex_type, context = '', internal = false # @param preserve_generics [Boolean] True to preserve any # unresolved generic parameters, false to erase them # @return [Array] - def get_method_stack rooted_tag, name, scope: :instance, visibility: [:private, :protected, :public], preserve_generics: false + def get_method_stack rooted_tag, name, scope: :instance, visibility: [:private, :protected, :public], + preserve_generics: false rooted_type = ComplexType.parse(rooted_tag) fqns = rooted_type.namespace namespace_pin = store.get_path_pins(fqns).select { |p| p.is_a?(Pin::Namespace) }.first @@ -553,7 +554,10 @@ def get_method_stack rooted_tag, name, scope: :instance, visibility: [:private, if methods.empty? && namespace_pin.nil? # namespace may be set by an alias constant = store.constant_pins.find { |c| c.name == fqns && visibility.include?(c.visibility) } - return get_method_stack(constant.return_type.tag, name, scope: scope, visibility: visibility, preserve_generics: preserve_generics) if constant + if constant + return get_method_stack(constant.return_type.tag, name, scope: scope, visibility: visibility, + preserve_generics: preserve_generics) + end end methods end @@ -694,7 +698,9 @@ def resolve_method_aliases pins, visibility = [:public, :private, :protected] next nil if resolved.respond_to?(:visibility) && !visibility.include?(resolved.visibility) resolved end.compact - logger.debug { "ApiMap#resolve_method_aliases(pins=#{pins.map(&:name)}, visibility=#{visibility}) => #{with_resolved_aliases.map(&:name)}" } + logger.debug do + "ApiMap#resolve_method_aliases(pins=#{pins.map(&:name)}, visibility=#{visibility}) => #{with_resolved_aliases.map(&:name)}" + end GemPins.combine_method_pins_by_path(with_resolved_aliases) end @@ -746,11 +752,13 @@ def inner_get_methods rooted_tag, scope, visibility, deep, skip, no_core = false if scope == :instance store.get_includes(fqns).reverse.each do |include_tag| rooted_include_tag = qualify(include_tag, rooted_tag) - result.concat inner_get_methods_from_reference(rooted_include_tag, namespace_pin, rooted_type, scope, visibility, deep, skip, true) + result.concat inner_get_methods_from_reference(rooted_include_tag, namespace_pin, rooted_type, scope, + visibility, deep, skip, true) end rooted_sc_tag = qualify_superclass(rooted_tag) unless rooted_sc_tag.nil? - result.concat inner_get_methods_from_reference(rooted_sc_tag, namespace_pin, rooted_type, scope, visibility, true, skip, no_core) + result.concat inner_get_methods_from_reference(rooted_sc_tag, namespace_pin, rooted_type, scope, + visibility, true, skip, no_core) end else store.get_extends(fqns).reverse.each do |em| @@ -759,7 +767,8 @@ def inner_get_methods rooted_tag, scope, visibility, deep, skip, no_core = false end rooted_sc_tag = qualify_superclass(rooted_tag) unless rooted_sc_tag.nil? - result.concat inner_get_methods_from_reference(rooted_sc_tag, namespace_pin, rooted_type, scope, visibility, true, skip, true) + result.concat inner_get_methods_from_reference(rooted_sc_tag, namespace_pin, rooted_type, scope, + visibility, true, skip, true) end unless no_core || fqns.empty? type = get_namespace_type(fqns) @@ -827,9 +836,7 @@ def inner_get_constants fqns, visibility, skip result.concat inner_get_constants(qualify(is, fqns), [:public], skip) end fqsc = qualify_superclass(fqns) - unless %w[Object BasicObject].include?(fqsc) - result.concat inner_get_constants(fqsc, [:public], skip) - end + result.concat inner_get_constants(fqsc, [:public], skip) unless %w[Object BasicObject].include?(fqsc) result end diff --git a/lib/solargraph/api_map/index.rb b/lib/solargraph/api_map/index.rb index 01820a441..6577ad188 100644 --- a/lib/solargraph/api_map/index.rb +++ b/lib/solargraph/api_map/index.rb @@ -146,9 +146,7 @@ def map_overrides pins = path_pin_hash[ovr.name] logger.debug { "ApiMap::Index#map_overrides: pins for path=#{ovr.name}: #{pins}" } pins.each do |pin| - new_pin = if pin.path.end_with?('#initialize') - path_pin_hash[pin.path.sub(/#initialize/, '.new')].first - end + new_pin = (path_pin_hash[pin.path.sub(/#initialize/, '.new')].first if pin.path.end_with?('#initialize')) (ovr.tags.map(&:tag_name) + ovr.delete).uniq.each do |tag| pin.docstring.delete_tags tag new_pin.docstring.delete_tags tag if new_pin diff --git a/lib/solargraph/api_map/store.rb b/lib/solargraph/api_map/store.rb index 3e453bb35..830995b3d 100644 --- a/lib/solargraph/api_map/store.rb +++ b/lib/solargraph/api_map/store.rb @@ -31,7 +31,7 @@ def update *pinsets pinsets[changed..].each_with_index do |pins, idx| @pinsets[changed + idx] = pins @indexes[changed + idx] = if pins.empty? - @indexes[changed + idx - 1] + @indexes[changed + idx - 1] else @indexes[changed + idx - 1].merge(pins) end diff --git a/lib/solargraph/gem_pins.rb b/lib/solargraph/gem_pins.rb index 9ef123719..dead29c34 100644 --- a/lib/solargraph/gem_pins.rb +++ b/lib/solargraph/gem_pins.rb @@ -50,12 +50,16 @@ def self.combine(yard_pins, rbs_pins) next yard_pin unless rbs_pin && yard_pin.class == Pin::Method unless rbs_pin - logger.debug { "GemPins.combine: No rbs pin for #{yard_pin.path} - using YARD's '#{yard_pin.inspect} (return_type=#{yard_pin.return_type}; signatures=#{yard_pin.signatures})" } + logger.debug do + "GemPins.combine: No rbs pin for #{yard_pin.path} - using YARD's '#{yard_pin.inspect} (return_type=#{yard_pin.return_type}; signatures=#{yard_pin.signatures})" + end next yard_pin end out = combine_method_pins(rbs_pin, yard_pin) - logger.debug { "GemPins.combine: Combining yard.path=#{yard_pin.path} - rbs=#{rbs_pin.inspect} with yard=#{yard_pin.inspect} into #{out}" } + logger.debug do + "GemPins.combine: Combining yard.path=#{yard_pin.path} - rbs=#{rbs_pin.inspect} with yard=#{yard_pin.inspect} into #{out}" + end out end in_rbs_only = rbs_pins.select do |pin| diff --git a/lib/solargraph/library.rb b/lib/solargraph/library.rb index 85a74f4e5..90f428086 100644 --- a/lib/solargraph/library.rb +++ b/lib/solargraph/library.rb @@ -249,7 +249,7 @@ def references_from filename, line, column, strip: false, only: false return [] unless pin result = [] files = if only - [api_map.source_map(filename)] + [api_map.source_map(filename)] else (workspace.sources + (@current ? [@current] : [])) end @@ -274,12 +274,12 @@ def references_from filename, line, column, strip: false, only: false # HACK: for language clients that exclude special characters from the start of variable names if strip && match = cursor.word.match(/^[^a-z0-9_]+/i) found.map! do |loc| - Solargraph::Location.new(loc.filename, Solargraph::Range.from_to(loc.range.start.line, loc.range.start.column + match[0].length, loc.range.ending.line, loc.range.ending.column)) + Solargraph::Location.new(loc.filename, + Solargraph::Range.from_to(loc.range.start.line, loc.range.start.column + match[0].length, loc.range.ending.line, + loc.range.ending.column)) end end - result.concat(found.sort do |a, b| - a.range.start.line <=> b.range.start.line - end) + result.concat(found.sort { |a, b| a.range.start.line <=> b.range.start.line }) end result.uniq end @@ -304,9 +304,7 @@ def locate_ref location return nil if pin.nil? # @param full [String] return_if_match = proc do |full| - if source_map_hash.key?(full) - return Location.new(full, Solargraph::Range.from_to(0, 0, 0, 0)) - end + return Location.new(full, Solargraph::Range.from_to(0, 0, 0, 0)) if source_map_hash.key?(full) end workspace.require_paths.each do |path| full = File.join path, pin.name @@ -657,7 +655,7 @@ def report_cache_progress gem_name, pending @total = pending if pending > @total finished = @total - pending pct = if @total.zero? - 0 + 0 else ((finished.to_f / @total.to_f) * 100).to_i end diff --git a/lib/solargraph/parser/parser_gem/class_methods.rb b/lib/solargraph/parser/parser_gem/class_methods.rb index cb4f6fc72..c00391af2 100644 --- a/lib/solargraph/parser/parser_gem/class_methods.rb +++ b/lib/solargraph/parser/parser_gem/class_methods.rb @@ -77,9 +77,11 @@ def references source, name # @param top [Parser::AST::Node] # @return [Array] def inner_node_references name, top + # @type [Array] result = [] if top.is_a?(AST::Node) && top.to_s.include?(":#{name}") result.push top if top.children.any? { |c| c.to_s == name } + # @sg-ignore top.children.each { |c| result.concat inner_node_references(name, c) } end result @@ -128,16 +130,16 @@ def node_range node # @param node [Parser::AST::Node] # @return [Array] def string_ranges node + # @sg-ignore return [] unless is_ast_node?(node) result = [] - if node.type == :str - result.push Range.from_node(node) - end + result.push Range.from_node(node) if node.type == :str node.children.each do |child| result.concat string_ranges(child) end if node.type == :dstr && node.children.last.nil? last = node.children[-2] + # @sg-ignore unless last.nil? rng = Range.from_node(last) pos = Position.new(rng.ending.line, rng.ending.column - 1) diff --git a/lib/solargraph/rbs_map.rb b/lib/solargraph/rbs_map.rb index 613719e90..e795b8df2 100644 --- a/lib/solargraph/rbs_map.rb +++ b/lib/solargraph/rbs_map.rb @@ -189,10 +189,10 @@ def log_caching gemspec, out:; end # @return [Boolean] true if adding the library succeeded def add_library loader, library, version, out: $stderr @resolved = if loader.has_library?(library: library, version: version) - # we find our own dependencies from gemfile.lock - loader.add library: library, version: version, resolve_dependencies: false - logger.debug { "#{short_name} successfully loaded library #{library}:#{version}" } - true + # we find our own dependencies from gemfile.lock + loader.add library: library, version: version, resolve_dependencies: false + logger.debug { "#{short_name} successfully loaded library #{library}:#{version}" } + true else logger.info { "#{short_name} did not find data for library #{library}:#{version}" } false diff --git a/lib/solargraph/shell.rb b/lib/solargraph/shell.rb index 5b6028a11..810bdf7ad 100755 --- a/lib/solargraph/shell.rb +++ b/lib/solargraph/shell.rb @@ -193,7 +193,9 @@ def typecheck *files problems = checker.problems next if problems.empty? problems.sort! { |a, b| a.location.range.start.line <=> b.location.range.start.line } - puts problems.map { |prob| "#{prob.location.filename}:#{prob.location.range.start.line + 1} - #{prob.message}" }.join("\n") + puts problems.map { |prob| + "#{prob.location.filename}:#{prob.location.range.start.line + 1} - #{prob.message}" + }.join("\n") filecount += 1 probcount += problems.length end @@ -253,11 +255,11 @@ def list # @return [String] def pin_description pin desc = if pin.path.nil? || pin.path.empty? - if pin.closure - "#{pin.closure.path} | #{pin.name}" - else - "#{pin.context.namespace} | #{pin.name}" - end + if pin.closure + "#{pin.closure.path} | #{pin.name}" + else + "#{pin.context.namespace} | #{pin.name}" + end else pin.path end diff --git a/lib/solargraph/workspace.rb b/lib/solargraph/workspace.rb index b037ab2fc..443d261e1 100644 --- a/lib/solargraph/workspace.rb +++ b/lib/solargraph/workspace.rb @@ -195,9 +195,7 @@ def cache_all_for_workspace! out, rebuild: false # @type [Array] specs = gemspecs.all_gemspecs_from_bundle specs.each do |spec| - unless pin_cache.cached?(spec) - pin_cache.cache_gem(gemspec: spec, rebuild: rebuild, out: out) - end + pin_cache.cache_gem(gemspec: spec, rebuild: rebuild, out: out) unless pin_cache.cached?(spec) end out.puts "Documentation cached for all #{specs.length} gems." end @@ -238,7 +236,10 @@ def load_sources source_hash.clear unless directory.empty? || directory == '*' size = config.calculated.length - raise WorkspaceTooLargeError, "The workspace is too large to index (#{size} files, #{config.max_files} max)" if config.max_files > 0 and size > config.max_files + if config.max_files > 0 and size > config.max_files + raise WorkspaceTooLargeError, + "The workspace is too large to index (#{size} files, #{config.max_files} max)" + end config.calculated.each do |filename| begin source_hash[filename] = Solargraph::Source.load(filename) diff --git a/spec/workspace_spec.rb b/spec/workspace_spec.rb index f152a0a85..2e9203fb7 100644 --- a/spec/workspace_spec.rb +++ b/spec/workspace_spec.rb @@ -122,7 +122,8 @@ end it 'rescues errors loading files into sources' do - config = double(:Config, directory: './path', calculated: ['./path/does_not_exist.rb'], max_files: 5000, require_paths: [], plugins: []) + config = double(:Config, directory: './path', calculated: ['./path/does_not_exist.rb'], max_files: 5000, + require_paths: [], plugins: []) expect { Solargraph::Workspace.new('./path', config) }.not_to raise_error From 759ebf98270de2196a576fc9f1cc6798034de136 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 23 Jul 2025 11:49:01 -0400 Subject: [PATCH 334/561] Increase test coverage --- spec/shell_spec.rb | 29 ++++++++++++++++++ spec/workspace/require_paths_spec.rb | 44 ++++++++++++++++++++++++++++ spec/yardoc_spec.rb | 6 ++++ 3 files changed, 79 insertions(+) diff --git a/spec/shell_spec.rb b/spec/shell_spec.rb index ec98f7892..ce877f68e 100644 --- a/spec/shell_spec.rb +++ b/spec/shell_spec.rb @@ -57,12 +57,41 @@ def bundle_exec(*cmd) end end + def capture_stdout(&block) + original_stdout = $stdout + $stdout = StringIO.new + begin + block.call + $stdout.string + ensure + $stdout = original_stdout + end + end + describe 'gem' do it 'caches without erroring out' do output = bundle_exec('solargraph', 'gem', 'solargraph') expect(output).to include('Caching these gems') end + + it 'caches a YARD-using gem and loads pins' do + shell = Solargraph::Shell.new + output = capture_stdout do + shell.uncache('backport') + end + expect(output).to include('Clearing pin cache in') + + output = capture_stdout do + shell.gems('backport') + end + + expect(output).to include('Caching YARD pins for gem backport') + + api_map = Solargraph::ApiMap.load(Dir.pwd) + methods = api_map.get_method_stack('Backport::Adapter', 'remote') + expect(methods.first.return_type.tag).to eq('Hash{Symbol => String, Integer}') + end end describe 'gems' do diff --git a/spec/workspace/require_paths_spec.rb b/spec/workspace/require_paths_spec.rb index db5516447..2f78b1dd0 100644 --- a/spec/workspace/require_paths_spec.rb +++ b/spec/workspace/require_paths_spec.rb @@ -17,6 +17,16 @@ end end + context 'with config and no gemspec' do + let(:dir_path) { File.realpath(Dir.pwd) } + + let(:config) { instance_double(Solargraph::Workspace::Config, require_paths: [], allow?: true) } + + it 'includes the lib directory' do + expect(paths).to include(File.join(dir_path, 'lib')) + end + end + context 'with current bundle' do let(:dir_path) { Dir.pwd } @@ -33,6 +43,40 @@ end end + context 'with an invalid gemspec file' do + let(:dir_path) { File.realpath(Dir.mktmpdir) } + let(:gemspec_file) { File.join(dir_path, 'invalid.gemspec') } + + before do + File.write(gemspec_file, "bogus") + end + + it 'includes the lib directory' do + expect(paths).to include(File.join(dir_path, 'lib')) + end + + it 'does not raise an error' do + expect { paths }.not_to raise_error + end + end + + context 'with a valid gemspec file that outputs to stdout' do + let(:dir_path) { File.realpath(Dir.mktmpdir) } + let(:gemspec_file) { File.join(dir_path, 'invalid.gemspec') } + + before do + File.write(gemspec_file, "print '{'; Gem::Specification.new") + end + + it 'includes the lib directory' do + expect(paths).to include(File.join(dir_path, 'lib')) + end + + it 'does not raise an error' do + expect { paths }.not_to raise_error + end + end + context 'with no gemspec file' do let(:dir_path) { File.realpath(Dir.mktmpdir) } diff --git a/spec/yardoc_spec.rb b/spec/yardoc_spec.rb index 84b1b6fb6..9096ab0e3 100644 --- a/spec/yardoc_spec.rb +++ b/spec/yardoc_spec.rb @@ -31,6 +31,12 @@ end end + describe '#load!' do + it 'does not blow up when called on empty directory' do + expect { described_class.load!(gem_yardoc_path) }.not_to raise_error + end + end + describe '#build_docs' do let(:api_map) { Solargraph::ApiMap.load(Dir.pwd) } let(:gemspec) { api_map.find_gem('rubocop') } From dae35df74d831191e2ea6b700d0212479fc2b78d Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 23 Jul 2025 11:49:13 -0400 Subject: [PATCH 335/561] Drop unreachable line --- lib/solargraph/workspace/require_paths.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/solargraph/workspace/require_paths.rb b/lib/solargraph/workspace/require_paths.rb index 443752084..d3e48590b 100644 --- a/lib/solargraph/workspace/require_paths.rb +++ b/lib/solargraph/workspace/require_paths.rb @@ -27,7 +27,6 @@ def generate result = require_paths_from_gemspec_files return configured_require_paths if result.empty? result.concat(config.require_paths.map { |p| File.join(directory, p) }) if config - result.push File.join(directory, 'lib') if result.empty? result end From 62df9ba6c9f40807bda63df328d3f47fe28bf5a2 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 23 Jul 2025 12:11:19 -0400 Subject: [PATCH 336/561] Handle an RBS collection case --- lib/solargraph/pin_cache.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/solargraph/pin_cache.rb b/lib/solargraph/pin_cache.rb index a12a672aa..e4256e18f 100644 --- a/lib/solargraph/pin_cache.rb +++ b/lib/solargraph/pin_cache.rb @@ -90,6 +90,7 @@ def lookup_rbs_version_cache_key(gemspec) # @param rbs_collection_pins [Array] # @return [void] def cache_combined_pins(gemspec, rbs_version_cache_key, yard_pins, rbs_collection_pins) + raise "rbs_pins must be provided" unless rbs_pins combined_pins = GemPins.combine(yard_pins, rbs_collection_pins) serialize_combined_gem(gemspec, rbs_version_cache_key, combined_pins) end @@ -172,7 +173,7 @@ def build_combine_and_cache(gemspec, # this can be nil even if we aren't told to build it - see suppress_yard_cache? yard_pins = deserialize_yard_pin_cache(gemspec) || [] cache_rbs_collection_pins(gemspec, out) if build_rbs_collection - rbs_collection_pins = deserialize_rbs_collection_cache(gemspec, rbs_version_cache_key) + rbs_collection_pins = deserialize_rbs_collection_cache(gemspec, rbs_version_cache_key) || [] cache_combined_pins(gemspec, rbs_version_cache_key, yard_pins, rbs_collection_pins) if build_combined end From d557795bd71584b50481c12b6b8375e9541ee697 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 23 Jul 2025 12:12:40 -0400 Subject: [PATCH 337/561] Drop debugging line --- lib/solargraph/pin_cache.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/solargraph/pin_cache.rb b/lib/solargraph/pin_cache.rb index e4256e18f..f3097c6c9 100644 --- a/lib/solargraph/pin_cache.rb +++ b/lib/solargraph/pin_cache.rb @@ -90,7 +90,6 @@ def lookup_rbs_version_cache_key(gemspec) # @param rbs_collection_pins [Array] # @return [void] def cache_combined_pins(gemspec, rbs_version_cache_key, yard_pins, rbs_collection_pins) - raise "rbs_pins must be provided" unless rbs_pins combined_pins = GemPins.combine(yard_pins, rbs_collection_pins) serialize_combined_gem(gemspec, rbs_version_cache_key, combined_pins) end From 0714988048b8d1ab236f9ab686efb63829ca4d44 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 23 Jul 2025 15:50:19 -0400 Subject: [PATCH 338/561] More specs, aggressively cache stdlib in 'solargraph gems' --- lib/solargraph/pin_cache.rb | 24 ++++ lib/solargraph/workspace.rb | 13 ++- lib/solargraph/workspace/gemspecs.rb | 42 +++++-- spec/shell_spec.rb | 6 +- spec/spec_helper.rb | 4 +- .../gemspecs_resolve_require_spec.rb | 110 ++++++++++++++++++ 6 files changed, 182 insertions(+), 17 deletions(-) create mode 100644 spec/workspace/gemspecs_resolve_require_spec.rb diff --git a/lib/solargraph/pin_cache.rb b/lib/solargraph/pin_cache.rb index f3097c6c9..efbf25622 100644 --- a/lib/solargraph/pin_cache.rb +++ b/lib/solargraph/pin_cache.rb @@ -59,6 +59,15 @@ def suppress_yard_cache?(gemspec, rbs_version_cache_key) false end + # @param out [IO, nil] output stream for logging + # + # @return [void] + def cache_all_stdlibs(out: $stderr) + possible_stdlibs.each do |stdlib| + RbsMap::StdlibMap.new(stdlib, out: out) + end + end + # @param path [String] require path that might be in the RBS stdlib collection # @return [void] def cache_stdlib_rbs_map path @@ -125,6 +134,21 @@ def yardoc_processing?(gemspec) Yardoc.processing?(yardoc_path(gemspec)) end + # @return [Array] a list of possible standard library names + def possible_stdlibs + # all dirs and .rb files in Gem::RUBYGEMS_DIR + Dir.glob(File.join(Gem::RUBYGEMS_DIR, '*')).map do |file_or_dir| + basename = File.basename(file_or_dir) + # remove .rb + basename = basename[0..-4] if basename.end_with?('.rb') + basename + end.sort.uniq + rescue StandardError => e + logger.info { "Failed to get possible stdlibs: #{e.message}" } + logger.debug { e.backtrace.join("\n") } + [] + end + private # @param gemspec [Gem::Specification, Bundler::LazySpecification] diff --git a/lib/solargraph/workspace.rb b/lib/solargraph/workspace.rb index 443d261e1..0fb7a62b3 100644 --- a/lib/solargraph/workspace.rb +++ b/lib/solargraph/workspace.rb @@ -192,12 +192,22 @@ def rbs_collection_config_path # @return [void] def cache_all_for_workspace! out, rebuild: false PinCache.cache_core(out: out) unless PinCache.core? + # @type [Array] - specs = gemspecs.all_gemspecs_from_bundle + gem_specs = gemspecs.all_gemspecs_from_bundle + # try any possible standard libraries, but be quiet about it + stdlib_specs = pin_cache.possible_stdlibs.map { |stdlib| gemspecs.find_gem(stdlib, out: nil) }.compact + specs = (gem_specs + stdlib_specs) specs.each do |spec| pin_cache.cache_gem(gemspec: spec, rebuild: rebuild, out: out) unless pin_cache.cached?(spec) end out.puts "Documentation cached for all #{specs.length} gems." + + # do this after so that we prefer stdlib requires from gems, + # which are likely to be newer and have more pins + pin_cache.cache_all_stdlibs(out: out) + + out.puts "Documentation cached for core, standard library and gems." end # Synchronize the workspace from the provided updater. @@ -265,6 +275,7 @@ def require_plugins def read_rbs_collection_path return unless rbs_collection_config_path + # @sg-ignore path = YAML.load_file(rbs_collection_config_path)&.fetch('path') # make fully qualified File.expand_path(path, directory) diff --git a/lib/solargraph/workspace/gemspecs.rb b/lib/solargraph/workspace/gemspecs.rb index 5c3ef292e..999a6a007 100644 --- a/lib/solargraph/workspace/gemspecs.rb +++ b/lib/solargraph/workspace/gemspecs.rb @@ -12,10 +12,20 @@ class Gemspecs attr_reader :directory, :preferences # @param directory [String] - def initialize directory + # @param preferences [Array] + def initialize directory, preferences: [] + # @todo an issue with both external bundles and the potential + # preferences feature is that bundler gives you a 'clean' + # rubygems environment with only the specified versions + # installed. Possible alternatives: + # + # *) prompt the user to run solargraph outside of bundler + # and treat all bundles as external + # *) reinstall the needed gems dynamically each time + # *) manipulate the rubygems/bundler environment @directory = File.absolute_path(directory) - # @todo implement preferences - @preferences = [] + # @todo implement preferences as a config-exposed feature + @preferences = preferences end # Take the path given to a 'require' statement in a source file @@ -39,7 +49,7 @@ def resolve_require require spec_with_path = Gem::Specification.find_by_path(file) rescue Gem::MissingSpecError logger.debug do - "Require path #{require} could not be resolved to a gem via find_by_name or path of #{file}" + "Require #{require.inspect} could not be resolved to a gem via find_by_name or path of #{file}" end [] end @@ -77,13 +87,17 @@ def resolve_require require # @param name [String] # @param version [String, nil] + # @param out [IO, nil] output stream for logging # # @return [Gem::Specification, nil] - def find_gem name, version + def find_gem name, version = nil, out: $stderr gemspec = all_gemspecs_from_bundle.find { |gemspec| gemspec.name == name && gemspec.version == version } return gemspec if gemspec - resolve_gem_ignoring_local_bundle name, version + gemspec = all_gemspecs_from_bundle.find { |gemspec| gemspec.name == name } + return gemspec if gemspec + + resolve_gem_ignoring_local_bundle name, version, out: out end # @param gemspec [Gem::Specification] @@ -130,9 +144,9 @@ def self.gem_specification_cache private - # @sg-ignore # @param specish [Gem::Specification, Bundler::LazySpecification, Bundler::StubSpecification] # @return [Gem::Specification, nil] + # @sg-ignore def to_gem_specification specish # print time including milliseconds self.class.gem_specification_cache[specish] ||= case specish @@ -175,7 +189,6 @@ def query_external_bundle command 'ruby', '-e', "require 'bundler'; require 'json'; Dir.chdir('#{directory}') { puts #{command}.to_json }" ] - # @sg-ignore o, e, s = Open3.capture3(*cmd) if s.success? Solargraph.logger.debug "External bundle: #{o}" @@ -259,8 +272,10 @@ def only_runtime_dependencies gemspec # # @param name [String] # @param version [String] + # @param out [IO, nil] output stream for logging + # # @return [Gem::Specification, nil] - def resolve_gem_ignoring_local_bundle name, version + def resolve_gem_ignoring_local_bundle name, version, out: $stderr Gem::Specification.find_by_name(name, version) rescue Gem::MissingSpecError begin @@ -268,7 +283,9 @@ def resolve_gem_ignoring_local_bundle name, version rescue Gem::MissingSpecError stdlibmap = RbsMap::StdlibMap.new(name) unless stdlibmap.resolved? - logger.warn "Please install the gem #{name}:#{version} in Solargraph's Ruby environment" + gem_desc = name + gem_desc += ":#{version}" if version + out&.puts "Please install the gem #{gem_desc} in Solargraph's Ruby environment" end nil # either not here or in stdlib end @@ -282,7 +299,7 @@ def all_gemspecs_from_external_bundle begin logger.info 'Fetching gemspecs required from external bundle' - command = 'Bundler.definition.locked_gems&.specs&.map { |spec| [spec.name, spec.version] }.to_h' + command = 'Bundler.definition.locked_gems&.specs&.map { |spec| [spec.name, spec.version.to_s] }.to_h' query_external_bundle(command).map do |name, version| resolve_gem_ignoring_local_bundle(name, version) @@ -311,7 +328,8 @@ def gemspec_or_preference gemspec def change_gemspec_version gemspec, version Gem::Specification.find_by_name(gemspec.name, "= #{version}") rescue Gem::MissingSpecError - Solargraph.logger.info "Gem #{gemspec.name} version #{version} not found. Using #{gemspec.version} instead" + Solargraph.logger.info "Gem #{gemspec.name} version #{version.inspect} not found. " \ + "Using #{gemspec.version} instead" gemspec end end diff --git a/spec/shell_spec.rb b/spec/shell_spec.rb index ce877f68e..a8383b32d 100644 --- a/spec/shell_spec.rb +++ b/spec/shell_spec.rb @@ -57,7 +57,7 @@ def bundle_exec(*cmd) end end - def capture_stdout(&block) + def capture_stdout &block original_stdout = $stdout $stdout = StringIO.new begin @@ -75,8 +75,8 @@ def capture_stdout(&block) expect(output).to include('Caching these gems') end - it 'caches a YARD-using gem and loads pins' do - shell = Solargraph::Shell.new + it 'caches a YARD-using gem and loads pins' do # rubocop:disable RSpec/MultipleExpectations + shell = described_class.new output = capture_stdout do shell.uncache('backport') end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index da7a6280e..a9ea4a15a 100755 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -14,7 +14,9 @@ end end require 'solargraph' -# Suppress logger output in specs (if possible) +# execute any logging blocks to make sure they don't blow up +Solargraph::Logging.logger.sev_threshold = Logger::DEBUG +# ...but still suppress logger output in specs (if possible) Solargraph::Logging.logger.reopen(File::NULL) if Solargraph::Logging.logger.respond_to?(:reopen) # @param name [String] diff --git a/spec/workspace/gemspecs_resolve_require_spec.rb b/spec/workspace/gemspecs_resolve_require_spec.rb new file mode 100644 index 000000000..158915ec5 --- /dev/null +++ b/spec/workspace/gemspecs_resolve_require_spec.rb @@ -0,0 +1,110 @@ +# frozen_string_literal: true + +require 'fileutils' +require 'tmpdir' +require 'rubygems/commands/install_command' + +describe Solargraph::Workspace::Gemspecs, '#resolve_require' do + subject(:specs) { gemspecs.resolve_require(require) } + + let(:gemspecs) { described_class.new(dir_path) } + + def find_or_install gem_name, version + Gem::Specification.find_by_name(gem_name, version) + rescue Gem::LoadError + install_gem(gem_name, version) + end + + def install_gem gem_name, version + Bundler.with_unbundled_env do + cmd = Gem::Commands::InstallCommand.new + cmd.handle_options [gem_name, '-v', version] + cmd.execute + rescue Gem::SystemExitException => e + raise unless e.exit_code == 0 + end + end + + context 'with local bundle' do + let(:dir_path) { File.realpath(Dir.pwd) } + + context 'with a known gem' do + let(:require) { 'solargraph' } + + it 'returns a single spec' do + expect(specs.size).to eq(1) + end + + it 'resolves to the right known gem' do + expect(specs.map(&:name)).to eq(['solargraph']) + end + end + + context 'with a less usual require mapping' do + let(:require) { 'diff/lcs' } + + it 'returns a single spec' do + expect(specs.size).to eq(1) + end + + it 'resolves to the right known gem' do + expect(specs.map(&:name)).to eq(['diff-lcs']) + end + end + + context 'with Bundler.require' do + let(:require) { 'bundler/require' } + + it 'returns the gemspec gem' do + expect(specs.map(&:name)).to include('solargraph') + end + end + end + + context 'with external bundle' do + let(:dir_path) { File.realpath(Dir.mktmpdir).to_s } + + before do + # write out Gemfile + File.write(File.join(dir_path, 'Gemfile'), <<~GEMFILE) + source 'https://rubygems.org' + gem 'backport' + GEMFILE + + # run bundle install + output, status = Solargraph.with_clean_env do + Open3.capture2e('bundle install --verbose', chdir: dir_path) + end + raise "Failure installing bundle: #{output}" unless status.success? + + # ensure Gemfile.lock exists + unless File.exist?(File.join(dir_path, 'Gemfile.lock')) + raise "Gemfile.lock not found after bundle install in #{dir_path}" + end + end + + context 'with a gem preference' do + before do + find_or_install('backport', '1.0.0') + Gem::Specification.find_by_name('backport', '= 1.0.0') + end + + let(:preferences) do + [ + Gem::Specification.new.tap do |spec| + spec.name = 'backport' + spec.version = '1.0.0' + end + ] + end + + it 'returns the preferred gemspec' do + gemspecs = described_class.new(dir_path, preferences: preferences) + specs = gemspecs.resolve_require('backport') + backport = specs.find { |spec| spec.name == 'backport' } + + expect(backport.version.to_s).to eq('1.0.0') + end + end + end +end From 6fc79522cce7eeada8c3f14b0cdcf29d25751c62 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 23 Jul 2025 15:55:51 -0400 Subject: [PATCH 339/561] Fix string literal nit --- spec/workspace/require_paths_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/workspace/require_paths_spec.rb b/spec/workspace/require_paths_spec.rb index 2f78b1dd0..eb95d0c5b 100644 --- a/spec/workspace/require_paths_spec.rb +++ b/spec/workspace/require_paths_spec.rb @@ -48,7 +48,7 @@ let(:gemspec_file) { File.join(dir_path, 'invalid.gemspec') } before do - File.write(gemspec_file, "bogus") + File.write(gemspec_file, 'bogus') end it 'includes the lib directory' do From 6cfc56b6fa2b9aac37512e4b882fd79b447343a7 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 23 Jul 2025 16:30:34 -0400 Subject: [PATCH 340/561] Add error handler for Ruby 3.0 situation --- lib/solargraph/workspace/gemspecs.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/solargraph/workspace/gemspecs.rb b/lib/solargraph/workspace/gemspecs.rb index 999a6a007..2d7f71008 100644 --- a/lib/solargraph/workspace/gemspecs.rb +++ b/lib/solargraph/workspace/gemspecs.rb @@ -112,6 +112,16 @@ def fetch_dependencies gemspec dep = gemspecs.find { |dep| dep.name == runtime_dep.name } dep ||= Gem::Specification.find_by_name(runtime_dep.name, runtime_dep.requirement) deps.merge fetch_dependencies(dep) if deps.add?(dep) + rescue NoMethodError => e + raise unless e.message.include?('identifier') && e.message.include?('lib/ruby/gems') + # Can happen on system gems in Ruby 3.0, it seems: + # + # https://github.com/castwide/solargraph/actions/runs/16480452864/job/46593077954?pr=1006 + log.debug do + "Skipping dependency #{runtime_dep.name} for #{gemspec.name} due to NoMethodError: #{e.message}" + end + + nil rescue Gem::MissingSpecError Solargraph.logger.warn("Gem dependency #{runtime_dep.name} #{runtime_dep.requirement} " \ "for #{gemspec.name} not found in bundle.") From 9c931489a5535a7da9ad994b47b674ce2c22655e Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 23 Jul 2025 16:48:04 -0400 Subject: [PATCH 341/561] Disable spec for older Ruby --- lib/solargraph/workspace/gemspecs.rb | 1 + .../gemspecs_resolve_require_spec.rb | 45 ++++++++++--------- 2 files changed, 25 insertions(+), 21 deletions(-) diff --git a/lib/solargraph/workspace/gemspecs.rb b/lib/solargraph/workspace/gemspecs.rb index 2d7f71008..cba593128 100644 --- a/lib/solargraph/workspace/gemspecs.rb +++ b/lib/solargraph/workspace/gemspecs.rb @@ -199,6 +199,7 @@ def query_external_bundle command 'ruby', '-e', "require 'bundler'; require 'json'; Dir.chdir('#{directory}') { puts #{command}.to_json }" ] + # @sg-ignore o, e, s = Open3.capture3(*cmd) if s.success? Solargraph.logger.debug "External bundle: #{o}" diff --git a/spec/workspace/gemspecs_resolve_require_spec.rb b/spec/workspace/gemspecs_resolve_require_spec.rb index 158915ec5..a1cca9d4d 100644 --- a/spec/workspace/gemspecs_resolve_require_spec.rb +++ b/spec/workspace/gemspecs_resolve_require_spec.rb @@ -83,27 +83,30 @@ def install_gem gem_name, version end end - context 'with a gem preference' do - before do - find_or_install('backport', '1.0.0') - Gem::Specification.find_by_name('backport', '= 1.0.0') - end - - let(:preferences) do - [ - Gem::Specification.new.tap do |spec| - spec.name = 'backport' - spec.version = '1.0.0' - end - ] - end - - it 'returns the preferred gemspec' do - gemspecs = described_class.new(dir_path, preferences: preferences) - specs = gemspecs.resolve_require('backport') - backport = specs.find { |spec| spec.name == 'backport' } - - expect(backport.version.to_s).to eq('1.0.0') + # find_or_install helper doesn't seem to work on older versions + if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('3.1.0') + context 'with a gem preference' do + before do + find_or_install('backport', '1.0.0') + Gem::Specification.find_by_name('backport', '= 1.0.0') + end + + let(:preferences) do + [ + Gem::Specification.new.tap do |spec| + spec.name = 'backport' + spec.version = '1.0.0' + end + ] + end + + it 'returns the preferred gemspec' do + gemspecs = described_class.new(dir_path, preferences: preferences) + specs = gemspecs.resolve_require('backport') + backport = specs.find { |spec| spec.name == 'backport' } + + expect(backport.version.to_s).to eq('1.0.0') + end end end end From 551de9ca5fd256ed82a79386d8280d9bbe8df6f2 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 23 Jul 2025 17:16:16 -0400 Subject: [PATCH 342/561] More specs --- .../gemspecs_resolve_require_spec.rb | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/spec/workspace/gemspecs_resolve_require_spec.rb b/spec/workspace/gemspecs_resolve_require_spec.rb index a1cca9d4d..e38b65469 100644 --- a/spec/workspace/gemspecs_resolve_require_spec.rb +++ b/spec/workspace/gemspecs_resolve_require_spec.rb @@ -83,6 +83,15 @@ def install_gem gem_name, version end end + context 'with external bundle that does not exist' do + let(:dir_path) { File.realpath(Dir.mktmpdir).to_s } + let(:require) { 'bundler/require' } + + it 'raises' do + expect { specs }.to raise_error(Solargraph::BundleNotFoundError) + end + end + # find_or_install helper doesn't seem to work on older versions if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('3.1.0') context 'with a gem preference' do @@ -108,6 +117,47 @@ def install_gem gem_name, version expect(backport.version.to_s).to eq('1.0.0') end end + + context 'with a gem preference that does not exist' do + let(:preferences) do + [ + Gem::Specification.new.tap do |spec| + spec.name = 'backport' + spec.version = '99.0.0' + end + ] + end + + it 'returns the gemspec we do have' do + gemspecs = described_class.new(dir_path, preferences: preferences) + specs = gemspecs.resolve_require('backport') + backport = specs.find { |spec| spec.name == 'backport' } + + expect(backport.version.to_s).to eq('1.2.0') + end + end + + context 'with a gem preference already set to the version we use' do + let(:version) { Gem::Specification.find_by_name('backport').version.to_s } + + let(:preferences) do + [ + Gem::Specification.new.tap do |spec| + spec.name = 'backport' + spec.version = version + end + ] + end + + it 'returns the gemspec we do have' do + gemspecs = described_class.new(dir_path, preferences: preferences) + specs = gemspecs.resolve_require('backport') + backport = specs.find { |spec| spec.name == 'backport' } + + expect(backport.version.to_s).to eq(version) + end + end + end end end From 6066b55c4a358f80f56f0e1f54fc2bb954f48bbb Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 24 Jul 2025 08:58:00 -0400 Subject: [PATCH 343/561] Work through a '*' / '' case --- lib/solargraph/workspace.rb | 15 +++++++++++--- lib/solargraph/workspace/gemspecs.rb | 13 +++++++----- lib/solargraph/workspace/require_paths.rb | 7 +++---- .../gemspecs_resolve_require_spec.rb | 20 +++++++++++++++++++ 4 files changed, 43 insertions(+), 12 deletions(-) diff --git a/lib/solargraph/workspace.rb b/lib/solargraph/workspace.rb index 0fb7a62b3..ab788be06 100644 --- a/lib/solargraph/workspace.rb +++ b/lib/solargraph/workspace.rb @@ -19,10 +19,12 @@ class Workspace # @return [String] attr_reader :directory - # @param directory [String] + # @param directory [String] TODO: Document and test '' and '*' semantics # @param config [Config, nil] # @param server [Hash] def initialize directory = '', config = nil, server = {} + raise ArgumentError, 'directory must be a String' unless directory.is_a?(String) + @directory = if ['*', ''].include?(directory) directory else @@ -38,7 +40,8 @@ def initialize directory = '', config = nil, server = {} # # @return [Array] def require_paths - @require_paths ||= RequirePaths.new(directory, config).generate + # @todo are the semantics of '*' the same as '', meaning 'don't send back any require paths'? + @require_paths ||= RequirePaths.new(directory_or_nil, config).generate end # @return [Solargraph::Workspace::Config] @@ -223,9 +226,15 @@ def command_path server['commandPath'] || 'solargraph' end + # @return [String, nil] + def directory_or_nil + return nil if directory.empty? || directory == '*' + directory + end + # @return [Solargraph::Workspace::Gemspecs] def gemspecs - @gemspecs ||= Solargraph::Workspace::Gemspecs.new(directory) + @gemspecs ||= Solargraph::Workspace::Gemspecs.new(directory_or_nil) end private diff --git a/lib/solargraph/workspace/gemspecs.rb b/lib/solargraph/workspace/gemspecs.rb index cba593128..cc30b797c 100644 --- a/lib/solargraph/workspace/gemspecs.rb +++ b/lib/solargraph/workspace/gemspecs.rb @@ -11,7 +11,7 @@ class Gemspecs attr_reader :directory, :preferences - # @param directory [String] + # @param directory [String, nil] If nil, assume no bundle is present # @param preferences [Array] def initialize directory, preferences: [] # @todo an issue with both external bundles and the potential @@ -23,7 +23,7 @@ def initialize directory, preferences: [] # and treat all bundles as external # *) reinstall the needed gems dynamically each time # *) manipulate the rubygems/bundler environment - @directory = File.absolute_path(directory) + @directory = directory && File.absolute_path(directory) # @todo implement preferences as a config-exposed feature @preferences = preferences end @@ -139,6 +139,8 @@ def fetch_dependencies gemspec # # @return [Array] def all_gemspecs_from_bundle + return [] unless directory + @all_gemspecs_from_bundle ||= if in_this_bundle? all_gemspecs_from_this_bundle @@ -212,7 +214,7 @@ def query_external_bundle command end def in_this_bundle? - directory && Bundler.definition&.lockfile&.to_s&.start_with?(directory) # rubocop:disable Style/SafeNavigationChainLength + Bundler.definition&.lockfile&.to_s&.start_with?(directory) # rubocop:disable Style/SafeNavigationChainLength end # @return [Array] @@ -234,6 +236,8 @@ def all_gemspecs_from_this_bundle # @return [Array] def auto_required_gemspecs_from_bundler + return [] unless directory + logger.info 'Fetching gemspecs autorequired from Bundler (bundler/require)' @auto_required_gemspecs_from_bundler ||= if in_this_bundle? @@ -303,9 +307,8 @@ def resolve_gem_ignoring_local_bundle name, version, out: $stderr end # @return [Array] + # @sg-ignore def all_gemspecs_from_external_bundle - return [] unless directory - @all_gemspecs_from_external_bundle ||= begin logger.info 'Fetching gemspecs required from external bundle' diff --git a/lib/solargraph/workspace/require_paths.rb b/lib/solargraph/workspace/require_paths.rb index d3e48590b..57ebf5044 100644 --- a/lib/solargraph/workspace/require_paths.rb +++ b/lib/solargraph/workspace/require_paths.rb @@ -12,7 +12,7 @@ class Workspace class RequirePaths attr_reader :directory, :config - # @param directory [String] + # @param directory [String, nil] # @param config [Config, nil] def initialize directory, config @directory = directory @@ -45,8 +45,7 @@ def require_paths_from_gemspec_files # # @return [Array] def gemspec_file_paths - # @todo Document what '*' means and how a user should use it - return [] if directory.empty? || directory == '*' + return [] if directory.nil? @gemspec_file_paths ||= Dir[File.join(directory, '**/*.gemspec')].select do |gs| config.nil? || config.allow?(gs) end @@ -56,7 +55,7 @@ def gemspec_file_paths # # @return [Array] def configured_require_paths - return ['lib'] if directory.empty? + return ['lib'] unless directory return [File.join(directory, 'lib')] if !config || config.require_paths.empty? config.require_paths.map { |p| File.join(directory, p) } end diff --git a/spec/workspace/gemspecs_resolve_require_spec.rb b/spec/workspace/gemspecs_resolve_require_spec.rb index e38b65469..fe927fc44 100644 --- a/spec/workspace/gemspecs_resolve_require_spec.rb +++ b/spec/workspace/gemspecs_resolve_require_spec.rb @@ -61,6 +61,26 @@ def install_gem gem_name, version end end + context 'with nil as directory' do + let(:dir_path) { nil } + + context 'with simple require' do + let(:require) { 'solargraph' } + + it 'finds solargraph' do + expect(specs.map(&:name)).to eq(['solargraph']) + end + end + + context 'with Bundler.require' do + let(:require) { 'bundler/require' } + + it 'finds nothing' do + expect(specs).to be_empty + end + end + end + context 'with external bundle' do let(:dir_path) { File.realpath(Dir.mktmpdir).to_s } From 1775bac4f2f4d5e83d4763fb0de15804e8ffbc6a Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 24 Jul 2025 12:33:23 -0400 Subject: [PATCH 344/561] More specs --- lib/solargraph/rbs_map.rb | 4 +- spec/workspace/gemspecs_find_gem_spec.rb | 62 +++++++++++++++++++ .../gemspecs_resolve_require_spec.rb | 1 - 3 files changed, 64 insertions(+), 3 deletions(-) create mode 100644 spec/workspace/gemspecs_find_gem_spec.rb diff --git a/lib/solargraph/rbs_map.rb b/lib/solargraph/rbs_map.rb index e795b8df2..b4eab0ea8 100644 --- a/lib/solargraph/rbs_map.rb +++ b/lib/solargraph/rbs_map.rb @@ -177,10 +177,10 @@ def conversions @conversions ||= Conversions.new(loader: loader) end - # @param gemspec [RBS::EnvironmentLoader::Library] + # @param lib [RBS::EnvironmentLoader::Library] # @param out [IO, nil] where to log messages # @return [void] - def log_caching gemspec, out:; end + def log_caching lib, out:; end # @param loader [RBS::EnvironmentLoader] # @param library [String] diff --git a/spec/workspace/gemspecs_find_gem_spec.rb b/spec/workspace/gemspecs_find_gem_spec.rb new file mode 100644 index 000000000..707c27535 --- /dev/null +++ b/spec/workspace/gemspecs_find_gem_spec.rb @@ -0,0 +1,62 @@ +# frozen_string_literal: true + +require 'fileutils' +require 'tmpdir' +require 'rubygems/commands/install_command' + +describe Solargraph::Workspace::Gemspecs, '#find_gem' do + subject(:gemspec) { gemspecs.find_gem(name, version, out: out) } + + let(:gemspecs) { described_class.new(dir_path) } + let(:out) { StringIO.new } + + context 'with local bundle' do + let(:dir_path) { File.realpath(Dir.pwd) } + + context 'with solargraph from bundle' do + let(:name) { 'solargraph' } + let(:version) { nil } + + it 'returns the gem' do + expect(gemspec.name).to eq(name) + end + end + + context 'with random from core' do + let(:name) { 'random' } + let(:version) { nil } + + it 'returns no gemspec' do + expect(gemspec).to be_nil + end + + it 'does not complain' do + expect(out.string).to be_empty + end + end + + context 'with abbrev from stdlib' do + let(:name) { 'abbrev' } + let(:version) { nil } + + it 'returns no gemspec' do + expect(gemspec).to be_nil + end + end + + context 'with gem not in bundle' do + let(:name) { 'checkoff' } + let(:version) { nil } + + it 'returns no gemspec' do + expect(gemspec).to be_nil + end + + it 'complains' do + gemspec + + expect(out.string).to include('install the gem checkoff') + end + end + end +end diff --git a/spec/workspace/gemspecs_resolve_require_spec.rb b/spec/workspace/gemspecs_resolve_require_spec.rb index fe927fc44..719a57427 100644 --- a/spec/workspace/gemspecs_resolve_require_spec.rb +++ b/spec/workspace/gemspecs_resolve_require_spec.rb @@ -177,7 +177,6 @@ def install_gem gem_name, version expect(backport.version.to_s).to eq(version) end end - end end end From e7bb915b0f5bbde0d5a37721f9279dbd870e06a1 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 24 Jul 2025 15:02:40 -0400 Subject: [PATCH 345/561] Add spec and then make it work --- lib/solargraph/rbs_map/stdlib_map.rb | 4 +- lib/solargraph/shell.rb | 7 +- lib/solargraph/workspace/gemspecs.rb | 17 +- spec/shell_spec.rb | 26 ++- spec/workspace/gemspecs_find_gem_spec.rb | 44 ++++- .../gemspecs_resolve_require_spec.rb | 155 ++++++++++-------- 6 files changed, 164 insertions(+), 89 deletions(-) diff --git a/lib/solargraph/rbs_map/stdlib_map.rb b/lib/solargraph/rbs_map/stdlib_map.rb index d254b03e8..bd3fd5a99 100644 --- a/lib/solargraph/rbs_map/stdlib_map.rb +++ b/lib/solargraph/rbs_map/stdlib_map.rb @@ -12,8 +12,8 @@ class StdlibMap < RbsMap # @type [Hash{String => RbsMap}] @stdlib_maps_hash = {} - def log_caching gemspec, out: $stderr - out&.puts("Caching RBS pins for standard library #{gemspec.name}") + def log_caching lib, out: $stderr + out&.puts("Caching RBS pins for standard library #{lib.name}") end # @param library [String] diff --git a/lib/solargraph/shell.rb b/lib/solargraph/shell.rb index 810bdf7ad..b303c8a5c 100755 --- a/lib/solargraph/shell.rb +++ b/lib/solargraph/shell.rb @@ -67,6 +67,7 @@ def stdio def config(directory = '.') matches = [] if options[:extensions] + # @sg-ignore Gem::Specification.each do |g| if g.name.match(/^solargraph\-[A-Za-z0-9_\-]*?\-ext/) require g.name @@ -125,7 +126,11 @@ def gems *names end gemspec = api_map.find_gem(*name.split('=')) - api_map.cache_gem(gemspec, rebuild: options.rebuild, out: $stdout) + if gemspec.nil? + warn "Gem '#{name}' not found" + else + api_map.cache_gem(gemspec, rebuild: options.rebuild, out: $stdout) + end rescue Gem::MissingSpecError warn "Gem '#{name}' not found" end diff --git a/lib/solargraph/workspace/gemspecs.rb b/lib/solargraph/workspace/gemspecs.rb index cc30b797c..b07166db5 100644 --- a/lib/solargraph/workspace/gemspecs.rb +++ b/lib/solargraph/workspace/gemspecs.rb @@ -46,6 +46,7 @@ def resolve_require require # Determine gem name based on the require path file = "lib/#{require}.rb" begin + # @sg-ignore spec_with_path = Gem::Specification.find_by_path(file) rescue Gem::MissingSpecError logger.debug do @@ -199,7 +200,7 @@ def query_external_bundle command Solargraph.with_clean_env do cmd = [ 'ruby', '-e', - "require 'bundler'; require 'json'; Dir.chdir('#{directory}') { puts #{command}.to_json }" + "require 'bundler'; require 'json'; Dir.chdir('#{directory}') { puts begin; #{command}; end.to_json }" ] # @sg-ignore o, e, s = Open3.capture3(*cmd) @@ -213,6 +214,7 @@ def query_external_bundle command end end + # @sg-ignore def in_this_bundle? Bundler.definition&.lockfile&.to_s&.start_with?(directory) # rubocop:disable Style/SafeNavigationChainLength end @@ -265,12 +267,12 @@ def auto_required_gemspecs_from_external_bundle command = 'Bundler.definition.dependencies' \ '.select { |dep| dep.groups.include?(:default) && dep.should_include? }' \ - '.map { |dep| [dep.name, dep. version] }' + '.map(&:name)' # @sg-ignore # @type [Array] - dep_details = query_external_bundle command + dep_names = query_external_bundle command - dep_details.map { |name, version| find_gem(name, version) }.compact + all_gemspecs_from_bundle.select { |gemspec| dep_names.include?(gemspec.name) } end end @@ -313,8 +315,11 @@ def all_gemspecs_from_external_bundle begin logger.info 'Fetching gemspecs required from external bundle' - command = 'Bundler.definition.locked_gems&.specs&.map { |spec| [spec.name, spec.version.to_s] }.to_h' - + command = 'specish_objects = Bundler.definition.locked_gems&.specs; ' \ + 'if specish_objects.first.respond_to?(:materialize_for_installation);' \ + 'specish_objects = specish_objects.map(&:materialize_for_installation);' \ + 'end;' \ + 'specish_objects.map { |specish| [specish.name, specish.version] }' query_external_bundle(command).map do |name, version| resolve_gem_ignoring_local_bundle(name, version) end.compact diff --git a/spec/shell_spec.rb b/spec/shell_spec.rb index a8383b32d..d20526175 100644 --- a/spec/shell_spec.rb +++ b/spec/shell_spec.rb @@ -11,17 +11,21 @@ file.puts "source 'https://rubygems.org'" file.puts "gem 'solargraph', path: '#{File.expand_path('..', __dir__)}'" end - output, status = Open3.capture2e('bundle install', chdir: temp_dir) - raise "Failure installing bundle: #{output}" unless status.success? + Bundler.with_unbundled_env do + output, status = Open3.capture2e('bundle install', chdir: temp_dir) + raise "Failure installing bundle: #{output}" unless status.success? + end end # @type cmd [Array] # @return [String] def bundle_exec(*cmd) # run the command in the temporary directory with bundle exec - output, status = Open3.capture2e("bundle exec #{cmd.join(' ')}", chdir: temp_dir) - expect(status.success?).to be(true), "Command failed: #{output}" - output + Bundler.with_unbundled_env do + output, status = Open3.capture2e("bundle exec #{cmd.join(' ')}", chdir: temp_dir) + expect(status.success?).to be(true), "Command failed: #{output}" + output + end end after do @@ -69,12 +73,24 @@ def capture_stdout &block end describe 'gem' do + it 'has a well set up test enviornment' do + output = bundle_exec('bundle', 'list') + + expect(output).to include('language_server-protocol') + end + it 'caches without erroring out' do output = bundle_exec('solargraph', 'gem', 'solargraph') expect(output).to include('Caching these gems') end + it 'gives sensible error for gem that does not exist' do + output = bundle_exec('solargraph', 'gem', 'solargraph123') + + expect(output).to include('Caching these gems') + end + it 'caches a YARD-using gem and loads pins' do # rubocop:disable RSpec/MultipleExpectations shell = described_class.new output = capture_stdout do diff --git a/spec/workspace/gemspecs_find_gem_spec.rb b/spec/workspace/gemspecs_find_gem_spec.rb index 707c27535..35f5e7a15 100644 --- a/spec/workspace/gemspecs_find_gem_spec.rb +++ b/spec/workspace/gemspecs_find_gem_spec.rb @@ -35,8 +35,8 @@ end end - context 'with abbrev from stdlib' do - let(:name) { 'abbrev' } + context 'with ripper from core' do + let(:name) { 'ripper' } let(:version) { nil } it 'returns no gemspec' do @@ -44,6 +44,15 @@ end end + context 'with base64 from stdlib' do + let(:name) { 'base64' } + let(:version) { nil } + + it 'returns a gemspec' do + expect(gemspec).not_to be_nil + end + end + context 'with gem not in bundle' do let(:name) { 'checkoff' } let(:version) { nil } @@ -55,7 +64,36 @@ it 'complains' do gemspec - expect(out.string).to include('install the gem checkoff') + expect(out.string).to include('install the gem checkoff ') + end + end + + context 'with gem not in bundle but no logger' do + let(:name) { 'checkoff' } + let(:version) { nil } + let(:out) { nil } + + it 'returns no gemspec' do + expect(gemspec).to be_nil + end + + it 'does not fail' do + expect { gemspec }.not_to raise_error + end + end + + context 'with gem not in bundle with version' do + let(:name) { 'checkoff' } + let(:version) { '1.0.0' } + + it 'returns no gemspec' do + expect(gemspec).to be_nil + end + + it 'complains' do + gemspec + + expect(out.string).to include('install the gem checkoff:1.0.0') end end end diff --git a/spec/workspace/gemspecs_resolve_require_spec.rb b/spec/workspace/gemspecs_resolve_require_spec.rb index 719a57427..d09d9d16f 100644 --- a/spec/workspace/gemspecs_resolve_require_spec.rb +++ b/spec/workspace/gemspecs_resolve_require_spec.rb @@ -84,27 +84,7 @@ def install_gem gem_name, version context 'with external bundle' do let(:dir_path) { File.realpath(Dir.mktmpdir).to_s } - before do - # write out Gemfile - File.write(File.join(dir_path, 'Gemfile'), <<~GEMFILE) - source 'https://rubygems.org' - gem 'backport' - GEMFILE - - # run bundle install - output, status = Solargraph.with_clean_env do - Open3.capture2e('bundle install --verbose', chdir: dir_path) - end - raise "Failure installing bundle: #{output}" unless status.success? - - # ensure Gemfile.lock exists - unless File.exist?(File.join(dir_path, 'Gemfile.lock')) - raise "Gemfile.lock not found after bundle install in #{dir_path}" - end - end - - context 'with external bundle that does not exist' do - let(:dir_path) { File.realpath(Dir.mktmpdir).to_s } + context 'with no actual bundle' do let(:require) { 'bundler/require' } it 'raises' do @@ -112,69 +92,100 @@ def install_gem gem_name, version end end - # find_or_install helper doesn't seem to work on older versions - if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('3.1.0') - context 'with a gem preference' do - before do - find_or_install('backport', '1.0.0') - Gem::Specification.find_by_name('backport', '= 1.0.0') - end - - let(:preferences) do - [ - Gem::Specification.new.tap do |spec| - spec.name = 'backport' - spec.version = '1.0.0' - end - ] + context 'with Gemfile' do + before do + # write out Gemfile + File.write(File.join(dir_path, 'Gemfile'), <<~GEMFILE) + source 'https://rubygems.org' + gem 'backport' + GEMFILE + + # run bundle install + output, status = Solargraph.with_clean_env do + Open3.capture2e('bundle install --verbose', chdir: dir_path) end + raise "Failure installing bundle: #{output}" unless status.success? - it 'returns the preferred gemspec' do - gemspecs = described_class.new(dir_path, preferences: preferences) - specs = gemspecs.resolve_require('backport') - backport = specs.find { |spec| spec.name == 'backport' } - - expect(backport.version.to_s).to eq('1.0.0') + # ensure Gemfile.lock exists + unless File.exist?(File.join(dir_path, 'Gemfile.lock')) + raise "Gemfile.lock not found after bundle install in #{dir_path}" end end - context 'with a gem preference that does not exist' do - let(:preferences) do - [ - Gem::Specification.new.tap do |spec| - spec.name = 'backport' - spec.version = '99.0.0' - end - ] - end - - it 'returns the gemspec we do have' do - gemspecs = described_class.new(dir_path, preferences: preferences) - specs = gemspecs.resolve_require('backport') - backport = specs.find { |spec| spec.name == 'backport' } + let(:require) { 'bundler/require' } - expect(backport.version.to_s).to eq('1.2.0') - end + it 'does not raise' do + expect { specs }.not_to raise_error end - context 'with a gem preference already set to the version we use' do - let(:version) { Gem::Specification.find_by_name('backport').version.to_s } + it 'returns gems' do + expect(specs.map(&:name)).to include('backport') + end - let(:preferences) do - [ - Gem::Specification.new.tap do |spec| - spec.name = 'backport' - spec.version = version - end - ] + # find_or_install helper doesn't seem to work on older versions + if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('3.1.0') + context 'with a gem preference' do + before do + find_or_install('backport', '1.0.0') + Gem::Specification.find_by_name('backport', '= 1.0.0') + end + + let(:preferences) do + [ + Gem::Specification.new.tap do |spec| + spec.name = 'backport' + spec.version = '1.0.0' + end + ] + end + + it 'returns the preferred gemspec' do + gemspecs = described_class.new(dir_path, preferences: preferences) + specs = gemspecs.resolve_require('backport') + backport = specs.find { |spec| spec.name == 'backport' } + + expect(backport.version.to_s).to eq('1.0.0') + end end - it 'returns the gemspec we do have' do - gemspecs = described_class.new(dir_path, preferences: preferences) - specs = gemspecs.resolve_require('backport') - backport = specs.find { |spec| spec.name == 'backport' } + context 'with a gem preference that does not exist' do + let(:preferences) do + [ + Gem::Specification.new.tap do |spec| + spec.name = 'backport' + spec.version = '99.0.0' + end + ] + end + + it 'returns the gemspec we do have' do + gemspecs = described_class.new(dir_path, preferences: preferences) + specs = gemspecs.resolve_require('backport') + backport = specs.find { |spec| spec.name == 'backport' } + + expect(backport.version.to_s).to eq('1.2.0') + end + end - expect(backport.version.to_s).to eq(version) + context 'with a gem preference already set to the version we use' do + let(:version) { Gem::Specification.find_by_name('backport').version.to_s } + + let(:preferences) do + [ + Gem::Specification.new.tap do |spec| + spec.name = 'backport' + spec.version = version + end + ] + end + + it 'returns the gemspec we do have' do + gemspecs = described_class.new(dir_path, preferences: preferences) + specs = gemspecs.resolve_require('backport') + backport = specs.find { |spec| spec.name == 'backport' } + + expect(backport.version.to_s).to eq(version) + end end end end From a3bf5ddeaad56d416e9b97314d5b716f869de1d6 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 24 Jul 2025 15:33:23 -0400 Subject: [PATCH 346/561] More spec coverage --- lib/solargraph/api_map.rb | 13 ++--- lib/solargraph/api_map/store.rb | 2 +- spec/api_map/api_map_method_spec.rb | 13 +++++ spec/shell_spec.rb | 42 ++++++++------- .../gemspecs_fetch_dependencies_spec.rb | 53 +++++++++++++++++++ 5 files changed, 98 insertions(+), 25 deletions(-) create mode 100644 spec/api_map/api_map_method_spec.rb create mode 100644 spec/workspace/gemspecs_fetch_dependencies_spec.rb diff --git a/lib/solargraph/api_map.rb b/lib/solargraph/api_map.rb index 28ff32c10..3cf79e48e 100755 --- a/lib/solargraph/api_map.rb +++ b/lib/solargraph/api_map.rb @@ -135,6 +135,7 @@ def core_pins # @param name [String] # @return [YARD::Tags::MacroDirective, nil] + # @sg-ignore Solargraph::ApiMap#named_macro return type could not be inferred def named_macro name store.named_macros[name] end @@ -192,12 +193,7 @@ def cache_all_for_doc_map! out # @param rebuild [Boolean] # @return [void] def cache_all_for_workspace! out, rebuild: false - workspace.cache_all_for_workspace!(out, rebuild: rebuild) - end - - # @return [Workspace] - def workspace - @doc_map&.workspace + workspace&.cache_all_for_workspace!(out, rebuild: rebuild) end # @param name [String] @@ -981,6 +977,11 @@ def resolve_method_alias pin private + # @return [Workspace, nil] + def workspace + @doc_map&.workspace + end + # @param namespace_pin [Pin::Namespace] # @param rooted_type [ComplexType] # @param pins [Enumerable] diff --git a/lib/solargraph/api_map/store.rb b/lib/solargraph/api_map/store.rb index 830995b3d..d3c8fc911 100644 --- a/lib/solargraph/api_map/store.rb +++ b/lib/solargraph/api_map/store.rb @@ -116,7 +116,7 @@ def get_instance_variables(fqns, scope = :instance) end # @param fqns [String] - # @return [Enumerable] + # @return [Enumerable] def get_class_variables(fqns) namespace_children(fqns).select { |pin| pin.is_a?(Pin::ClassVariable)} end diff --git a/spec/api_map/api_map_method_spec.rb b/spec/api_map/api_map_method_spec.rb new file mode 100644 index 000000000..0978569ee --- /dev/null +++ b/spec/api_map/api_map_method_spec.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +describe Solargraph::ApiMap do + describe 'cache_all_for_workspace!' do + context 'with no workspace' do + subject(:api_map) { described_class.new } + + it 'ignores the request' do + expect { api_map.cache_all_for_workspace!(nil, rebuild: false) }.not_to raise_error + end + end + end +end diff --git a/spec/shell_spec.rb b/spec/shell_spec.rb index d20526175..cebd76fa6 100644 --- a/spec/shell_spec.rb +++ b/spec/shell_spec.rb @@ -72,21 +72,39 @@ def capture_stdout &block end end - describe 'gem' do - it 'has a well set up test enviornment' do + describe 'typecheck' do + it 'typechecks without erroring out' do + output = bundle_exec('solargraph', 'typecheck', 'Gemfile', '--level=normal') + + expect(output).to include('Typecheck finished in') + end + end + + describe 'gems' do + it 'caches core without erroring out' do + expect { bundle_exec('solargraph', 'cache', 'core') }.not_to raise_error + end + + it 'has a well set up test environment' do output = bundle_exec('bundle', 'list') expect(output).to include('language_server-protocol') end - it 'caches without erroring out' do - output = bundle_exec('solargraph', 'gem', 'solargraph') + it 'caches all without erroring out' do + output = bundle_exec('solargraph', 'gems') + + expect(output).to include('Documentation cached for all') + end + + it 'caches single gem without erroring out' do + output = bundle_exec('solargraph', 'gems', 'solargraph') expect(output).to include('Caching these gems') end it 'gives sensible error for gem that does not exist' do - output = bundle_exec('solargraph', 'gem', 'solargraph123') + output = bundle_exec('solargraph', 'gems', 'solargraph123') expect(output).to include('Caching these gems') end @@ -110,23 +128,11 @@ def capture_stdout &block end end - describe 'gems' do + describe 'cache' do it 'caches a stdlib gem without erroring out' do expect { bundle_exec('solargraph', 'cache', 'stringio') }.not_to raise_error end - it 'caches core without erroring out' do - expect { bundle_exec('solargraph', 'cache', 'core') }.not_to raise_error - end - - it 'caches all without erroring out' do - output = bundle_exec('solargraph', 'gems') - - expect(output).to include('Documentation cached for all') - end - end - - describe 'cache' do it 'caches without erroring out' do output = bundle_exec('solargraph', 'cache', 'solargraph') diff --git a/spec/workspace/gemspecs_fetch_dependencies_spec.rb b/spec/workspace/gemspecs_fetch_dependencies_spec.rb new file mode 100644 index 000000000..0596d0f12 --- /dev/null +++ b/spec/workspace/gemspecs_fetch_dependencies_spec.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +require 'fileutils' +require 'tmpdir' +require 'rubygems/commands/install_command' + +describe Solargraph::Workspace::Gemspecs, '#fetch_dependencies' do + subject(:deps) { gemspecs.fetch_dependencies(gemspec) } + + let(:gemspecs) { described_class.new(dir_path) } + let(:dir_path) { Dir.pwd } + + context 'with a Bundler::LazySpecification in bundle' do + let(:gemspec) do + Bundler::LazySpecification.new('solargraph', nil, nil) + end + + it 'finds a known dependency' do + expect(deps.map(&:name)).to include('backport') + end + end + + context 'with external bundle' do + let(:dir_path) { File.realpath(Dir.mktmpdir).to_s } + + let(:gemspec) do + Bundler::LazySpecification.new('undercover', nil, nil) + end + + before do + # write out Gemfile + File.write(File.join(dir_path, 'Gemfile'), <<~GEMFILE) + source 'https://rubygems.org' + gem 'undercover' + GEMFILE + + # run bundle install + output, status = Solargraph.with_clean_env do + Open3.capture2e('bundle install --verbose', chdir: dir_path) + end + raise "Failure installing bundle: #{output}" unless status.success? + + # ensure Gemfile.lock exists + unless File.exist?(File.join(dir_path, 'Gemfile.lock')) + raise "Gemfile.lock not found after bundle install in #{dir_path}" + end + end + + it 'finds dependencies' do + expect(deps.map(&:name)).to include('ast') + end + end +end From 3eb52e60c492c75f9b112cac357b9a2bf506f2b1 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 24 Jul 2025 15:46:54 -0400 Subject: [PATCH 347/561] Bring back workspace method --- lib/solargraph/api_map.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/solargraph/api_map.rb b/lib/solargraph/api_map.rb index 3cf79e48e..66854781a 100755 --- a/lib/solargraph/api_map.rb +++ b/lib/solargraph/api_map.rb @@ -975,13 +975,13 @@ def resolve_method_alias pin include Logging - private - # @return [Workspace, nil] def workspace @doc_map&.workspace end + private + # @param namespace_pin [Pin::Namespace] # @param rooted_type [ComplexType] # @param pins [Enumerable] From f3d3c93ef60a7a70d5bbe062e1416183d8ad88b6 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 24 Jul 2025 15:54:55 -0400 Subject: [PATCH 348/561] Bring back workspace method --- lib/solargraph/api_map.rb | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/lib/solargraph/api_map.rb b/lib/solargraph/api_map.rb index 66854781a..e62e45b29 100755 --- a/lib/solargraph/api_map.rb +++ b/lib/solargraph/api_map.rb @@ -700,6 +700,11 @@ def resolve_method_aliases pins, visibility = [:public, :private, :protected] GemPins.combine_method_pins_by_path(with_resolved_aliases) end + # @return [Workspace, nil] + def workspace + @doc_map&.workspace + end + private # A hash of source maps with filename keys. @@ -975,13 +980,6 @@ def resolve_method_alias pin include Logging - # @return [Workspace, nil] - def workspace - @doc_map&.workspace - end - - private - # @param namespace_pin [Pin::Namespace] # @param rooted_type [ComplexType] # @param pins [Enumerable] From 8c4368c4f2fecce7398b67ee917f52ab35e4ee7b Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 24 Jul 2025 16:03:36 -0400 Subject: [PATCH 349/561] Add specs --- lib/solargraph/shell.rb | 4 ++-- spec/api_map/api_map_method_spec.rb | 12 ++++++++++++ spec/shell_spec.rb | 7 +++++++ 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/lib/solargraph/shell.rb b/lib/solargraph/shell.rb index b303c8a5c..00d9a389c 100755 --- a/lib/solargraph/shell.rb +++ b/lib/solargraph/shell.rb @@ -151,12 +151,12 @@ def uncache *gems workspace = Workspace.new('.') gems.each do |gem| if gem == 'core' - PinCache.uncache_core + PinCache.uncache_core(out: $stdout) next end if gem == 'stdlib' - PinCache.uncache_stdlib + PinCache.uncache_stdlib(out: $stdout) next end diff --git a/spec/api_map/api_map_method_spec.rb b/spec/api_map/api_map_method_spec.rb index 0978569ee..e345ad2a2 100644 --- a/spec/api_map/api_map_method_spec.rb +++ b/spec/api_map/api_map_method_spec.rb @@ -2,6 +2,18 @@ describe Solargraph::ApiMap do describe 'cache_all_for_workspace!' do + context 'with workspace' do + subject(:api_map) { described_class.load(Dir.pwd) } + + let(:out) { StringIO.new } + + it 'processes the request' do + api_map.cache_all_for_workspace!(out, rebuild: false) + + expect(out.string).to include('Documentation cached') + end + end + context 'with no workspace' do subject(:api_map) { described_class.new } diff --git a/spec/shell_spec.rb b/spec/shell_spec.rb index cebd76fa6..53dd49090 100644 --- a/spec/shell_spec.rb +++ b/spec/shell_spec.rb @@ -78,6 +78,13 @@ def capture_stdout &block expect(output).to include('Typecheck finished in') end + + it 'caches a gem if needed before typechecking' do + bundle_exec('solargraph', 'uncache', 'core') + output = bundle_exec('solargraph', 'typecheck', 'Gemfile', '--level=normal') + + expect(output).to include('Caching ') + end end describe 'gems' do From 741f4e3a6001c9e58e0dca3071e4c81cb322eb38 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 24 Jul 2025 16:20:10 -0400 Subject: [PATCH 350/561] More specs --- spec/api_map/api_map_method_spec.rb | 33 +++++++++++++++++++++++++++++ spec/shell_spec.rb | 24 +++++++++++++++------ 2 files changed, 51 insertions(+), 6 deletions(-) diff --git a/spec/api_map/api_map_method_spec.rb b/spec/api_map/api_map_method_spec.rb index e345ad2a2..c2382001e 100644 --- a/spec/api_map/api_map_method_spec.rb +++ b/spec/api_map/api_map_method_spec.rb @@ -22,4 +22,37 @@ end end end + + describe 'cache_gem' do + context 'with no workspace' do + subject(:api_map) { described_class.new } + + let(:out) { StringIO.new } + + it 'ignores the request' do + expect { api_map.cache_gem('backport', out: out) }.not_to raise_error + end + end + + context 'with workspace' do + subject(:api_map) { described_class.load(Dir.pwd) } + + let(:out) { StringIO.new } + + it 'processes the request' do + backport = Gem::Specification.find_by_name('backport') + expect { api_map.cache_gem(backport, out: out) }.not_to raise_error + end + end + end + + describe 'get_method_stack' do + let(:out) { StringIO.new } + let(:api_map) { described_class.load_with_cache(Dir.pwd, out) } + let(:method_stack) { api_map.get_method_stack('YAML', 'safe_load', scope: :class) } + + it 'handles the YAML gem aliased to Psych' do + expect(method_stack).not_to be_empty + end + end end diff --git a/spec/shell_spec.rb b/spec/shell_spec.rb index 53dd49090..ea2cc7d82 100644 --- a/spec/shell_spec.rb +++ b/spec/shell_spec.rb @@ -116,18 +116,30 @@ def capture_stdout &block expect(output).to include('Caching these gems') end - it 'caches a YARD-using gem and loads pins' do # rubocop:disable RSpec/MultipleExpectations + it 'caches all gems as needed' do shell = described_class.new - output = capture_stdout do + _output = capture_stdout do shell.uncache('backport') end - expect(output).to include('Clearing pin cache in') - output = capture_stdout do - shell.gems('backport') + _output = capture_stdout do + shell.gems end - expect(output).to include('Caching YARD pins for gem backport') + api_map = Solargraph::ApiMap.load(Dir.pwd) + methods = api_map.get_method_stack('Backport::Adapter', 'remote') + expect(methods.first.return_type.tag).to eq('Hash{Symbol => String, Integer}') + end + + it 'caches a YARD-using gem and loads pins' do + shell = described_class.new + _output = capture_stdout do + shell.uncache('backport') + end + + _output = capture_stdout do + shell.gems('backport') + end api_map = Solargraph::ApiMap.load(Dir.pwd) methods = api_map.get_method_stack('Backport::Adapter', 'remote') From 4897e755834287ec759bdeaf5535e4019e9532c9 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 24 Jul 2025 16:42:50 -0400 Subject: [PATCH 351/561] More specs --- lib/solargraph/doc_map.rb | 2 +- spec/api_map/api_map_method_spec.rb | 14 +++++++--- spec/shell_spec.rb | 42 ++++++++++++++++++++++++----- 3 files changed, 48 insertions(+), 10 deletions(-) diff --git a/lib/solargraph/doc_map.rb b/lib/solargraph/doc_map.rb index e7a21e4d9..17b36f936 100644 --- a/lib/solargraph/doc_map.rb +++ b/lib/solargraph/doc_map.rb @@ -64,7 +64,7 @@ def cache_doc_map_gems! out "Caching pins for gems: #{gem_desc}" end end - PinCache.cache_core unless PinCache.core? + PinCache.cache_core(out: out) unless PinCache.core? load_serialized_gem_pins(out: out) time = Benchmark.measure do uncached_gemspecs.each do |gemspec| diff --git a/spec/api_map/api_map_method_spec.rb b/spec/api_map/api_map_method_spec.rb index c2382001e..ea95ace3a 100644 --- a/spec/api_map/api_map_method_spec.rb +++ b/spec/api_map/api_map_method_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true describe Solargraph::ApiMap do - describe 'cache_all_for_workspace!' do + describe '#cache_all_for_workspace!' do context 'with workspace' do subject(:api_map) { described_class.load(Dir.pwd) } @@ -23,7 +23,7 @@ end end - describe 'cache_gem' do + describe '#cache_gem' do context 'with no workspace' do subject(:api_map) { described_class.new } @@ -46,7 +46,15 @@ end end - describe 'get_method_stack' do + describe '.load_with_cache' do + let(:out) { StringIO.new } + + it 'loads the API map with cache' do + expect(out.string).to include('Documentation cached for all') + end + end + + describe '#get_method_stack' do let(:out) { StringIO.new } let(:api_map) { described_class.load_with_cache(Dir.pwd, out) } let(:method_stack) { api_map.get_method_stack('YAML', 'safe_load', scope: :class) } diff --git a/spec/shell_spec.rb b/spec/shell_spec.rb index ea2cc7d82..d69f65d33 100644 --- a/spec/shell_spec.rb +++ b/spec/shell_spec.rb @@ -4,6 +4,8 @@ require 'open3' describe Solargraph::Shell do + let(:shell) { described_class.new } + let(:temp_dir) { Dir.mktmpdir } before do @@ -47,17 +49,19 @@ def bundle_exec(*cmd) describe 'uncache' do it 'uncaches without erroring out' do - output = bundle_exec('solargraph', 'uncache', 'solargraph') + output = capture_stdout do + shell.uncache('solargraph') + end expect(output).to include('Clearing pin cache in') end it 'uncaches stdlib without erroring out' do - expect { bundle_exec('solargraph', 'uncache', 'stdlib') }.not_to raise_error + expect { shell.uncache('stdlib') }.not_to raise_error end it 'uncaches core without erroring out' do - expect { bundle_exec('solargraph', 'uncache', 'core') }.not_to raise_error + expect { shell.uncache('core') }.not_to raise_error end end @@ -72,16 +76,42 @@ def capture_stdout &block end end + def capture_both &block + original_stdout = $stdout + original_stderr = $stderr + stringio = StringIO.new + $stdout = stringio + $stderr = stringio + begin + block.call + stringio.string + ensure + $stdout = original_stdout + $stderr = original_stderr + end + end + describe 'typecheck' do it 'typechecks without erroring out' do - output = bundle_exec('solargraph', 'typecheck', 'Gemfile', '--level=normal') + output = capture_stdout do + old_options = shell.options + shell.options = { level: 'normal', directory: '.', **old_options } + shell.typecheck('Gemfile') + end expect(output).to include('Typecheck finished in') end it 'caches a gem if needed before typechecking' do - bundle_exec('solargraph', 'uncache', 'core') - output = bundle_exec('solargraph', 'typecheck', 'Gemfile', '--level=normal') + capture_stdout do + shell.uncache('core') + end + + output = capture_both do + old_options = shell.options + shell.options = { level: 'normal', directory: '.', **old_options } + shell.typecheck('Gemfile') + end expect(output).to include('Caching ') end From 5f300019aec7b5715b61450923aa0b2b28d18be8 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 24 Jul 2025 17:03:13 -0400 Subject: [PATCH 352/561] Ensure uncaching clears out in-memory cache as well Useful for testing and probably also for LSP control --- lib/solargraph/api_map.rb | 6 ++++++ lib/solargraph/pin_cache.rb | 10 +++++++++- spec/shell_spec.rb | 2 +- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/lib/solargraph/api_map.rb b/lib/solargraph/api_map.rb index e62e45b29..482c0de55 100755 --- a/lib/solargraph/api_map.rb +++ b/lib/solargraph/api_map.rb @@ -33,6 +33,12 @@ def initialize pins: [] index pins end + # @param out [IO, nil] output stream for logging + # @return [void] + def self.reset_core out: nil + @@core_map = RbsMap::CoreMap.new + end + # # This is a mutable object, which is cached in the Chain class - # if you add any fields which change the results of calls (not diff --git a/lib/solargraph/pin_cache.rb b/lib/solargraph/pin_cache.rb index efbf25622..5067ecd23 100644 --- a/lib/solargraph/pin_cache.rb +++ b/lib/solargraph/pin_cache.rb @@ -124,9 +124,15 @@ def deserialize_combined_pin_cache(gemspec) # @return [void] def uncache_gem(gemspec, out: nil) PinCache.uncache(yardoc_path(gemspec), out: out) - uncache_by_prefix(rbs_collection_pins_path_prefix(gemspec), out: out) + yard_pins_in_memory.delete([gemspec.name, gemspec.version]) PinCache.uncache(yard_gem_path(gemspec), out: out) + + rbs_version_cache_key = lookup_rbs_version_cache_key(gemspec) + uncache_by_prefix(rbs_collection_pins_path_prefix(gemspec), out: out) + rbs_collection_pins_in_memory.delete([gemspec.name, gemspec.version, rbs_version_cache_key]) + uncache_by_prefix(combined_path_prefix(gemspec), out: out) + combined_pins_in_memory.delete([gemspec.name, gemspec.version, rbs_version_cache_key]) end # @param gemspec [Gem::Specification, Bundler::LazySpecification] @@ -496,6 +502,8 @@ def uncache *path_segments, out: nil # @return [void] def uncache_core(out: nil) uncache(core_path, out: out) + # ApiMap keep this in memory + ApiMap.reset_core(out: out) end # @param out [IO, nil] diff --git a/spec/shell_spec.rb b/spec/shell_spec.rb index d69f65d33..b40618b62 100644 --- a/spec/shell_spec.rb +++ b/spec/shell_spec.rb @@ -84,11 +84,11 @@ def capture_both &block $stderr = stringio begin block.call - stringio.string ensure $stdout = original_stdout $stderr = original_stderr end + stringio.string end describe 'typecheck' do From c805d58081851fb1c5966782e6324fada26e5989 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 24 Jul 2025 17:33:18 -0400 Subject: [PATCH 353/561] Fix spec --- lib/solargraph/api_map.rb | 3 ++- lib/solargraph/shell.rb | 2 +- spec/api_map/api_map_method_spec.rb | 10 +++++++--- spec/shell_spec.rb | 26 -------------------------- spec/spec_helper.rb | 26 ++++++++++++++++++++++++++ 5 files changed, 36 insertions(+), 31 deletions(-) diff --git a/lib/solargraph/api_map.rb b/lib/solargraph/api_map.rb index 482c0de55..75ce6cc73 100755 --- a/lib/solargraph/api_map.rb +++ b/lib/solargraph/api_map.rb @@ -178,6 +178,7 @@ def clip_at filename, position # Create an ApiMap with a workspace in the specified directory. # # @param directory [String] + # # @return [ApiMap] def self.load directory api_map = new @@ -233,7 +234,7 @@ class << self # @param directory [String] workspace directory # @param out [IO] The output stream for messages # @return [ApiMap] - def self.load_with_cache directory, out + def self.load_with_cache directory, out: $stderr api_map = load(directory) if api_map.uncached_gemspecs.empty? logger.info { "All gems cached for #{directory}" } diff --git a/lib/solargraph/shell.rb b/lib/solargraph/shell.rb index 00d9a389c..b28189a91 100755 --- a/lib/solargraph/shell.rb +++ b/lib/solargraph/shell.rb @@ -183,7 +183,7 @@ def reporters # @return [void] def typecheck *files directory = File.realpath(options[:directory]) - api_map = Solargraph::ApiMap.load_with_cache(directory, $stdout) + api_map = Solargraph::ApiMap.load_with_cache(directory, out: $stdout) probcount = 0 if files.empty? files = api_map.source_maps.map(&:filename) diff --git a/spec/api_map/api_map_method_spec.rb b/spec/api_map/api_map_method_spec.rb index ea95ace3a..f476778a0 100644 --- a/spec/api_map/api_map_method_spec.rb +++ b/spec/api_map/api_map_method_spec.rb @@ -47,10 +47,14 @@ end describe '.load_with_cache' do - let(:out) { StringIO.new } - it 'loads the API map with cache' do - expect(out.string).to include('Documentation cached for all') + Solargraph::PinCache.uncache_core + + output = capture_both do + described_class.load_with_cache(Dir.pwd) + end + + expect(output).to include('aching RBS pins for Ruby core') end end diff --git a/spec/shell_spec.rb b/spec/shell_spec.rb index b40618b62..90a83545d 100644 --- a/spec/shell_spec.rb +++ b/spec/shell_spec.rb @@ -65,32 +65,6 @@ def bundle_exec(*cmd) end end - def capture_stdout &block - original_stdout = $stdout - $stdout = StringIO.new - begin - block.call - $stdout.string - ensure - $stdout = original_stdout - end - end - - def capture_both &block - original_stdout = $stdout - original_stderr = $stderr - stringio = StringIO.new - $stdout = stringio - $stderr = stringio - begin - block.call - ensure - $stdout = original_stdout - $stderr = original_stderr - end - stringio.string - end - describe 'typecheck' do it 'typechecks without erroring out' do output = capture_stdout do diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index a9ea4a15a..fdf179ede 100755 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -31,3 +31,29 @@ def with_env_var(name, value) ENV[name] = old_value # Restore the old value end end + +def capture_stdout &block + original_stdout = $stdout + $stdout = StringIO.new + begin + block.call + $stdout.string + ensure + $stdout = original_stdout + end +end + +def capture_both &block + original_stdout = $stdout + original_stderr = $stderr + stringio = StringIO.new + $stdout = stringio + $stderr = stringio + begin + block.call + ensure + $stdout = original_stdout + $stderr = original_stderr + end + stringio.string +end From fb35deba17d1d7f9f71a1b89c0405100b15a25b3 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 24 Jul 2025 17:47:10 -0400 Subject: [PATCH 354/561] Fix specs --- lib/solargraph/shell.rb | 2 +- spec/api_map/api_map_method_spec.rb | 2 +- spec/api_map_spec.rb | 6 +++--- spec/type_checker/levels/strict_spec.rb | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/solargraph/shell.rb b/lib/solargraph/shell.rb index b28189a91..71f02f88f 100755 --- a/lib/solargraph/shell.rb +++ b/lib/solargraph/shell.rb @@ -227,7 +227,7 @@ def scan # @type [Solargraph::ApiMap, nil] api_map = nil time = Benchmark.measure { - api_map = Solargraph::ApiMap.load_with_cache(directory, $stdout) + api_map = Solargraph::ApiMap.load_with_cache(directory, out: $stdout) api_map.pins.each do |pin| begin puts pin_description(pin) if options[:verbose] diff --git a/spec/api_map/api_map_method_spec.rb b/spec/api_map/api_map_method_spec.rb index f476778a0..8a272b109 100644 --- a/spec/api_map/api_map_method_spec.rb +++ b/spec/api_map/api_map_method_spec.rb @@ -60,7 +60,7 @@ describe '#get_method_stack' do let(:out) { StringIO.new } - let(:api_map) { described_class.load_with_cache(Dir.pwd, out) } + let(:api_map) { described_class.load_with_cache(Dir.pwd, out: out) } let(:method_stack) { api_map.get_method_stack('YAML', 'safe_load', scope: :class) } it 'handles the YAML gem aliased to Psych' do diff --git a/spec/api_map_spec.rb b/spec/api_map_spec.rb index 4d7077ccd..185b30be7 100755 --- a/spec/api_map_spec.rb +++ b/spec/api_map_spec.rb @@ -6,7 +6,7 @@ # avoid performance hit of doing this many times # rubocop:disable RSpec/InstanceVariable before :all do # rubocop:disable RSpec/BeforeAfterAll - @api_map = described_class.load_with_cache(Dir.pwd, nil) + @api_map = described_class.load_with_cache(Dir.pwd, out: nil) end it 'returns core methods' do @@ -21,7 +21,7 @@ end it 'automatically caches core' do - api_map = described_class.load_with_cache(Dir.pwd, nil) + api_map = described_class.load_with_cache(Dir.pwd, out: nil) pins = api_map.get_methods('String') expect(pins.map(&:path)).to include('String#upcase') end @@ -34,7 +34,7 @@ end it 'automatically caches gems' do - api_map = described_class.load_with_cache(Dir.pwd, nil) + api_map = described_class.load_with_cache(Dir.pwd, out: nil) pins = api_map.get_methods('RuboCop::Cop::Base') expect(pins.map(&:path)).to include('RuboCop::Cop::Base#active_support_extensions_enabled?') end diff --git a/spec/type_checker/levels/strict_spec.rb b/spec/type_checker/levels/strict_spec.rb index 13c20e85f..c92a76874 100644 --- a/spec/type_checker/levels/strict_spec.rb +++ b/spec/type_checker/levels/strict_spec.rb @@ -59,7 +59,7 @@ def bar(a); end require 'kramdown-parser-gfm' Kramdown::Parser::GFM.undefined_call ), 'test.rb') - api_map = Solargraph::ApiMap.load_with_cache('.', nil) + api_map = Solargraph::ApiMap.load_with_cache('.', out: nil) api_map.catalog Solargraph::Bench.new(source_maps: [source_map], external_requires: ['kramdown-parser-gfm']) checker = Solargraph::TypeChecker.new('test.rb', api_map: api_map, level: :strict) expect(checker.problems).to be_empty From 95b74be60b00517b19c7ebec478130eaf84b6248 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 24 Jul 2025 17:54:20 -0400 Subject: [PATCH 355/561] Coverage fixes --- spec/shell_spec.rb | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/spec/shell_spec.rb b/spec/shell_spec.rb index 90a83545d..d506f72ac 100644 --- a/spec/shell_spec.rb +++ b/spec/shell_spec.rb @@ -50,7 +50,7 @@ def bundle_exec(*cmd) describe 'uncache' do it 'uncaches without erroring out' do output = capture_stdout do - shell.uncache('solargraph') + shell.uncache('backport') end expect(output).to include('Clearing pin cache in') @@ -93,7 +93,7 @@ def bundle_exec(*cmd) describe 'gems' do it 'caches core without erroring out' do - expect { bundle_exec('solargraph', 'cache', 'core') }.not_to raise_error + expect { shell.cache('core') }.not_to raise_error end it 'has a well set up test environment' do @@ -109,15 +109,23 @@ def bundle_exec(*cmd) end it 'caches single gem without erroring out' do - output = bundle_exec('solargraph', 'gems', 'solargraph') + capture_both do + shell.uncache('backport') + end + + output = capture_both do + shell.gems('backport') + end - expect(output).to include('Caching these gems') + expect(output).to include('Caching').and include('backport') end it 'gives sensible error for gem that does not exist' do - output = bundle_exec('solargraph', 'gems', 'solargraph123') + output = capture_both do + shell.gems('solargraph123') + end - expect(output).to include('Caching these gems') + expect(output).to include("Gem 'solargraph123' not found") end it 'caches all gems as needed' do @@ -153,13 +161,19 @@ def bundle_exec(*cmd) describe 'cache' do it 'caches a stdlib gem without erroring out' do - expect { bundle_exec('solargraph', 'cache', 'stringio') }.not_to raise_error + expect { shell.cache('stringio') }.not_to raise_error end - it 'caches without erroring out' do - output = bundle_exec('solargraph', 'cache', 'solargraph') + it 'caches gem without erroring out' do + _output = capture_stdout do + shell.uncache('backport') + end + + output = capture_both do + shell.cache('backport') + end - expect(output).to include('Caching these gems') + expect(output).to include('Caching').and include('backport') end end end From d99b44f723bf47f359b1b57073a9678954aa7421 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 24 Jul 2025 17:59:51 -0400 Subject: [PATCH 356/561] Coverage fixes --- spec/shell_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/shell_spec.rb b/spec/shell_spec.rb index d506f72ac..52dc79ed1 100644 --- a/spec/shell_spec.rb +++ b/spec/shell_spec.rb @@ -78,7 +78,7 @@ def bundle_exec(*cmd) it 'caches a gem if needed before typechecking' do capture_stdout do - shell.uncache('core') + shell.uncache('backport') end output = capture_both do @@ -87,7 +87,7 @@ def bundle_exec(*cmd) shell.typecheck('Gemfile') end - expect(output).to include('Caching ') + expect(output).to include('Caching ').and include('backport') end end From 8dc4a6783a4e3f7ef182331952f770a443f664b0 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 24 Jul 2025 18:11:34 -0400 Subject: [PATCH 357/561] Fix flakey spec --- lib/solargraph/pin_cache.rb | 2 +- lib/solargraph/shell.rb | 4 ++-- spec/shell_spec.rb | 5 ++--- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/solargraph/pin_cache.rb b/lib/solargraph/pin_cache.rb index 5067ecd23..b71a16ff5 100644 --- a/lib/solargraph/pin_cache.rb +++ b/lib/solargraph/pin_cache.rb @@ -132,7 +132,7 @@ def uncache_gem(gemspec, out: nil) rbs_collection_pins_in_memory.delete([gemspec.name, gemspec.version, rbs_version_cache_key]) uncache_by_prefix(combined_path_prefix(gemspec), out: out) - combined_pins_in_memory.delete([gemspec.name, gemspec.version, rbs_version_cache_key]) + combined_pins_in_memory.delete([gemspec.name, gemspec.version]) end # @param gemspec [Gem::Specification, Bundler::LazySpecification] diff --git a/lib/solargraph/shell.rb b/lib/solargraph/shell.rb index 71f02f88f..22ae1e43a 100755 --- a/lib/solargraph/shell.rb +++ b/lib/solargraph/shell.rb @@ -118,7 +118,7 @@ def gems *names if names.empty? api_map.cache_all_for_workspace!($stdout, rebuild: options.rebuild) else - STDERR.puts("Caching these gems: #{names}") + $stderr.puts("Caching these gems: #{names}") names.each do |name| if name == 'core' PinCache.cache_core(out: $stdout) @@ -134,7 +134,7 @@ def gems *names rescue Gem::MissingSpecError warn "Gem '#{name}' not found" end - STDERR.puts "Documentation cached for #{names.count} gems." + $stderr.puts "Documentation cached for #{names.count} gems." end end diff --git a/spec/shell_spec.rb b/spec/shell_spec.rb index 52dc79ed1..4bc9cacfc 100644 --- a/spec/shell_spec.rb +++ b/spec/shell_spec.rb @@ -77,13 +77,12 @@ def bundle_exec(*cmd) end it 'caches a gem if needed before typechecking' do - capture_stdout do + capture_both do shell.uncache('backport') end output = capture_both do - old_options = shell.options - shell.options = { level: 'normal', directory: '.', **old_options } + shell.options = { level: 'normal', directory: Dir.pwd } shell.typecheck('Gemfile') end From 28f164b49d38bef2d344b4bc3844ca2723bbb3bb Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 25 Jul 2025 07:17:08 -0400 Subject: [PATCH 358/561] Drop unused method --- lib/solargraph/doc_map.rb | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib/solargraph/doc_map.rb b/lib/solargraph/doc_map.rb index 17b36f936..94a3380e1 100644 --- a/lib/solargraph/doc_map.rb +++ b/lib/solargraph/doc_map.rb @@ -45,11 +45,6 @@ def pin_cache @pin_cache ||= workspace.fresh_pincache end - # @return [Array] - def yard_plugins - global_environ.yard_plugins - end - def any_uncached? uncached_gemspecs.any? end From d1ee3ce1c771734e752abbad87c13695845f9909 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 25 Jul 2025 08:50:39 -0400 Subject: [PATCH 359/561] Improve coverage, fix minor bug that happens in debug logging --- lib/solargraph/api_map/index.rb | 12 ++++--- lib/solargraph/pin/callable.rb | 15 ++++++++ lib/solargraph/pin/parameter.rb | 7 ++++ spec/api_map/index_spec.rb | 64 +++++++++++++++++++++++++++++++++ 4 files changed, 93 insertions(+), 5 deletions(-) create mode 100644 spec/api_map/index_spec.rb diff --git a/lib/solargraph/api_map/index.rb b/lib/solargraph/api_map/index.rb index 6577ad188..f2fc59052 100644 --- a/lib/solargraph/api_map/index.rb +++ b/lib/solargraph/api_map/index.rb @@ -154,10 +154,13 @@ def map_overrides ovr.tags.each do |tag| pin.docstring.add_tag(tag) redefine_return_type pin, tag - if new_pin - new_pin.docstring.add_tag(tag) - redefine_return_type new_pin, tag - end + pin.reset_generated! + + next unless new_pin + + new_pin.docstring.add_tag(tag) + redefine_return_type new_pin, tag + new_pin.reset_generated! end end end @@ -174,7 +177,6 @@ def redefine_return_type pin, tag pin.signatures.each do |sig| sig.instance_variable_set(:@return_type, ComplexType.try_parse(tag.type)) end - pin.reset_generated! end end end diff --git a/lib/solargraph/pin/callable.rb b/lib/solargraph/pin/callable.rb index 20d2301eb..0f148d851 100644 --- a/lib/solargraph/pin/callable.rb +++ b/lib/solargraph/pin/callable.rb @@ -21,10 +21,13 @@ def initialize block: nil, return_type: nil, parameters: [], **splat @parameters = parameters end + # @return [String] def method_namespace closure.namespace end + # @param other [self] + # @return [Solargraph::Pin::Signature, nil] def combine_blocks(other) if block.nil? other.block @@ -57,8 +60,11 @@ def generics [] end + # @param other [self] + # @return [::Array] def choose_parameters(other) raise "Trying to combine two pins with different arities - \nself =#{inspect}, \nother=#{other.inspect}, \n\n self.arity=#{self.arity}, \nother.arity=#{other.arity}" if other.arity != arity + # @sg-ignore Wrong argument type for Array#zip: arg expected _Each, received Array parameters.zip(other.parameters).map do |param, other_param| if param.nil? && other_param.block? other_param @@ -70,6 +76,7 @@ def choose_parameters(other) end end + # @return [Array] def blockless_parameters if parameters.last&.block? parameters[0..-2] @@ -78,6 +85,7 @@ def blockless_parameters end end + # @return [Array] def arity [generics, blockless_parameters.map(&:arity_decl), block&.arity] end @@ -125,6 +133,7 @@ def typify api_map end end + # @return [String] def method_name raise "closure was nil in #{self.inspect}" if closure.nil? @method_name ||= closure.name @@ -197,6 +206,12 @@ def arity_matches? arguments, with_block true end + def reset_generated! + super + @parameters.each(&:reset_generated!) + end + + # @return [Integer] def mandatory_positional_param_count parameters.count(&:arg?) end diff --git a/lib/solargraph/pin/parameter.rb b/lib/solargraph/pin/parameter.rb index bc802b748..f11d91628 100644 --- a/lib/solargraph/pin/parameter.rb +++ b/lib/solargraph/pin/parameter.rb @@ -35,6 +35,7 @@ def combine_with(other, attrs={}) presence: choose(other, :presence), asgn_code: choose(other, :asgn_code), }.merge(attrs) + # @sg-ignore Wrong argument type for Solargraph::Pin::LocalVariable#combine_with: other expected Solargraph::Pin::LocalVariable, received self super(other, new_attrs) end @@ -43,6 +44,7 @@ def keyword? end def kwrestarg? + # @sg-ignore Unresolved call to type decl == :kwrestarg || (assignment && [:HASH, :hash].include?(assignment.type)) end @@ -125,6 +127,11 @@ def full end end + def reset_generated! + super + @return_type = nil if @return_type&.undefined? + end + # @return [ComplexType] def return_type if @return_type.nil? diff --git a/spec/api_map/index_spec.rb b/spec/api_map/index_spec.rb new file mode 100644 index 000000000..8afb74759 --- /dev/null +++ b/spec/api_map/index_spec.rb @@ -0,0 +1,64 @@ +# frozen_string_literal: true + +describe Solargraph::ApiMap::Index do + subject(:output_pins) { described_class.new(input_pins).pins } + + describe '#map_overrides' do + let(:foo_class) do + Solargraph::Pin::Namespace.new(name: 'Foo') + end + + let(:foo_initialize) do + init = Solargraph::Pin::Method.new(name: 'initialize', + scope: :instance, + parameters: [], + closure: foo_class) + # no return type specified + param = Solargraph::Pin::Parameter.new(name: 'bar', + closure: init) + init.parameters << param + init + end + + let(:foo_new) do + init = Solargraph::Pin::Method.new(name: 'new', + scope: :class, + parameters: [], + closure: foo_class) + # no return type specified + param = Solargraph::Pin::Parameter.new(name: 'bar', + closure: init) + init.parameters << param + init + end + + let(:foo_override) do + Solargraph::Pin::Reference::Override.from_comment('Foo#initialize', + '@param [String] bar') + end + + let(:input_pins) do + [ + foo_initialize, + foo_new, + foo_override + ] + end + + it 'has a docstring to process on override' do + expect(foo_override.docstring.tags).to be_empty + end + + it 'overrides .new method' do + method_pin = output_pins.find { |pin| pin.path == 'Foo.new' } + first_parameter = method_pin.parameters.first + expect(first_parameter.return_type.tag).to eq('String') + end + + it 'overrides #initialize method in signature' do + method_pin = output_pins.find { |pin| pin.path == 'Foo#initialize' } + first_parameter = method_pin.parameters.first + expect(first_parameter.return_type.tag).to eq('String') + end + end +end From 059664080ec9133b34929fe8267acfe3745104f3 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 25 Jul 2025 20:20:35 -0400 Subject: [PATCH 360/561] Add another spec --- spec/shell_spec.rb | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/spec/shell_spec.rb b/spec/shell_spec.rb index 4bc9cacfc..fb6000495 100644 --- a/spec/shell_spec.rb +++ b/spec/shell_spec.rb @@ -88,10 +88,34 @@ def bundle_exec(*cmd) expect(output).to include('Caching ').and include('backport') end + + it 'logs if it takes a certain amount of time to cache gems' do + capture_both do + shell.uncache('backport') + end + + allow_any_instance_of(Solargraph::PinCache) # rubocop:disable RSpec/AnyInstance + .to receive(:cache_gem) + .and_wrap_original do |original, *args, **kwargs| + sleep(0.5) # simulate a slow cache + original.call(*args, **kwargs) + end + + output = capture_both do + shell.options = { level: 'normal', directory: Dir.pwd } + shell.typecheck('Gemfile') + end + + expect(output).to include(' built in ').and include(' ms') + end end describe 'gems' do it 'caches core without erroring out' do + capture_both do + shell.uncache('core') + end + expect { shell.cache('core') }.not_to raise_error end From 93db9a846dff72c578a8edaff46d76f6bb123201 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 25 Jul 2025 20:24:24 -0400 Subject: [PATCH 361/561] Add another spec --- spec/shell_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/shell_spec.rb b/spec/shell_spec.rb index fb6000495..7c087fcb3 100644 --- a/spec/shell_spec.rb +++ b/spec/shell_spec.rb @@ -106,7 +106,7 @@ def bundle_exec(*cmd) shell.typecheck('Gemfile') end - expect(output).to include(' built in ').and include(' ms') + expect(output).to include('Built ').and include(' ms') end end From b00ed5a74344233ff3f2ed76e9ade284eab52b7c Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 26 Jul 2025 09:19:13 -0400 Subject: [PATCH 362/561] More tests, drop dead code --- lib/solargraph/api_map.rb | 5 ++--- lib/solargraph/doc_map.rb | 6 +----- lib/solargraph/shell.rb | 4 ++-- spec/shell_spec.rb | 3 ++- 4 files changed, 7 insertions(+), 11 deletions(-) diff --git a/lib/solargraph/api_map.rb b/lib/solargraph/api_map.rb index 75ce6cc73..261edae52 100755 --- a/lib/solargraph/api_map.rb +++ b/lib/solargraph/api_map.rb @@ -213,11 +213,10 @@ def find_gem name, version = nil # @param gemspec [Gem::Specification] # @param rebuild [Boolean] - # @param only_if_used [Boolean] # @param out [IO, nil] # @return [void] - def cache_gem gemspec, rebuild: false, only_if_used: false, out: nil - @doc_map&.cache(gemspec, rebuild: rebuild, out: out, only_if_used: only_if_used) + def cache_gem gemspec, rebuild: false, out: nil + @doc_map&.cache(gemspec, rebuild: rebuild, out: out) end class << self diff --git a/lib/solargraph/doc_map.rb b/lib/solargraph/doc_map.rb index 94a3380e1..1dd524098 100644 --- a/lib/solargraph/doc_map.rb +++ b/lib/solargraph/doc_map.rb @@ -59,7 +59,6 @@ def cache_doc_map_gems! out "Caching pins for gems: #{gem_desc}" end end - PinCache.cache_core(out: out) unless PinCache.core? load_serialized_gem_pins(out: out) time = Benchmark.measure do uncached_gemspecs.each do |gemspec| @@ -87,13 +86,10 @@ def dependencies # # @param gemspec [Gem::Specification] # @param rebuild [Boolean] whether to rebuild the pins even if they are cached - # @param only_if_used [Boolean] # @param out [IO, nil] output stream for logging # # @return [void] - def cache gemspec, rebuild: false, only_if_used: false, out: nil - return if only_if_used && !uncached_gemspecs.include?(gemspec) - + def cache gemspec, rebuild: false, out: nil pin_cache.cache_gem(gemspec: gemspec, rebuild: rebuild, out: out) diff --git a/lib/solargraph/shell.rb b/lib/solargraph/shell.rb index 22ae1e43a..03ce59410 100755 --- a/lib/solargraph/shell.rb +++ b/lib/solargraph/shell.rb @@ -116,7 +116,7 @@ def cache gem, version = nil def gems *names api_map = ApiMap.load('.') if names.empty? - api_map.cache_all_for_workspace!($stdout, rebuild: options.rebuild) + api_map.cache_all_for_workspace!($stdout, rebuild: options[:rebuild]) else $stderr.puts("Caching these gems: #{names}") names.each do |name| @@ -129,7 +129,7 @@ def gems *names if gemspec.nil? warn "Gem '#{name}' not found" else - api_map.cache_gem(gemspec, rebuild: options.rebuild, out: $stdout) + api_map.cache_gem(gemspec, rebuild: options[:rebuild], out: $stdout) end rescue Gem::MissingSpecError warn "Gem '#{name}' not found" diff --git a/spec/shell_spec.rb b/spec/shell_spec.rb index 7c087fcb3..46cfd73f0 100644 --- a/spec/shell_spec.rb +++ b/spec/shell_spec.rb @@ -173,7 +173,8 @@ def bundle_exec(*cmd) end _output = capture_stdout do - shell.gems('backport') + shell.options = { rebuild: true } + shell.gems('backport', 'thor') end api_map = Solargraph::ApiMap.load(Dir.pwd) From a6f9337debb775285c7f7d225f4fbead17714f41 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 26 Jul 2025 10:02:28 -0400 Subject: [PATCH 363/561] More tests, drop incorrect code --- lib/solargraph/doc_map.rb | 2 -- spec/doc_map_spec.rb | 33 ++++++++++++++++++++++++--------- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/lib/solargraph/doc_map.rb b/lib/solargraph/doc_map.rb index 1dd524098..9e373953b 100644 --- a/lib/solargraph/doc_map.rb +++ b/lib/solargraph/doc_map.rb @@ -118,12 +118,10 @@ def load_serialized_gem_pins out: $stderr # this will load from disk if needed; no need to manage # uncached_gemspecs to trigger that later stdlib_name_guess = path.split('/').first - # @todo this results in pins being generated in real time, not in advance with solargrpah gems rbs_pins = pin_cache.cache_stdlib_rbs_map stdlib_name_guess if stdlib_name_guess @pins.concat rbs_pins if rbs_pins end - logger.debug { "DocMap#load_serialized_gem_pins: Combining pins..." } existing_pin_count = pins.length time = Benchmark.measure do gemspecs.each do |gemspec| diff --git a/spec/doc_map_spec.rb b/spec/doc_map_spec.rb index a6f52d033..823db14d7 100644 --- a/spec/doc_map_spec.rb +++ b/spec/doc_map_spec.rb @@ -1,12 +1,14 @@ # frozen_string_literal: true require 'bundler' +require 'benchmark' describe Solargraph::DocMap do subject(:doc_map) do - Solargraph::DocMap.new(requires, workspace, out: nil) + Solargraph::DocMap.new(requires, workspace, out: out) end + let(:out) { StringIO.new } let(:pre_cache) { true } let(:requires) { [] } @@ -41,23 +43,37 @@ end end - context 'with an uncached but valid gemspec' do - let(:uncached_gemspec) do - Gem::Specification.new('uncached_gem', '1.0.0') + context 'when deserialization takes a while' do + let(:pre_cache) { false } + let(:requires) { ['backport'] } + + before do + # proxy this method to simulate a long-running deserialization + allow(Benchmark).to receive(:measure) do |&block| + block.call + 5.0 + end + end + + it 'logs timing' do + doc_map + expect(out.string).to include('Deserialized ').and include(' gem pins ').and include(' ms') end + end + + context 'with an uncached but valid gemspec' do let(:requires) { ['uncached_gem'] } let(:pre_cache) { false } let(:workspace) { instance_double(Solargraph::Workspace) } - before do + it 'tracks uncached_gemspecs' do pincache = instance_double(Solargraph::PinCache) + uncached_gemspec = Gem::Specification.new('uncached_gem', '1.0.0') allow(workspace).to receive(:resolve_require).with('uncached_gem').and_return([uncached_gemspec]) allow(workspace).to receive(:fetch_dependencies).with(uncached_gemspec).and_return([]) allow(workspace).to receive(:fresh_pincache).and_return(pincache) allow(pincache).to receive(:deserialize_combined_pin_cache).with(uncached_gemspec).and_return(nil) - end - it 'tracks uncached_gemspecs' do expect(doc_map.uncached_gemspecs).to eq([uncached_gemspec]) end end @@ -100,10 +116,9 @@ context 'with a require that has dependencies' do let(:requires) { ['rspec'] } - let(:example_dependency) { 'rspec-core' } it 'collects dependencies' do - expect(doc_map.dependencies.map(&:name)).to include(example_dependency) + expect(doc_map.dependencies.map(&:name)).to include('rspec-core') end end end From ac7c80c18cb776ef88e774728852f14412395c33 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 26 Jul 2025 13:36:02 -0400 Subject: [PATCH 364/561] More tests, drop dead code --- lib/solargraph/gem_pins.rb | 7 - spec/gem_pins_spec.rb | 59 +++++- spec/language_server/protocol_spec.rb | 2 +- spec/library_spec.rb | 250 ++++++++++++++------------ 4 files changed, 190 insertions(+), 128 deletions(-) diff --git a/lib/solargraph/gem_pins.rb b/lib/solargraph/gem_pins.rb index dead29c34..6efe3628c 100644 --- a/lib/solargraph/gem_pins.rb +++ b/lib/solargraph/gem_pins.rb @@ -49,13 +49,6 @@ def self.combine(yard_pins, rbs_pins) rbs_pin = rbs_api_map.get_path_pins(yard_pin.path).filter { |pin| pin.is_a? Pin::Method }.first next yard_pin unless rbs_pin && yard_pin.class == Pin::Method - unless rbs_pin - logger.debug do - "GemPins.combine: No rbs pin for #{yard_pin.path} - using YARD's '#{yard_pin.inspect} (return_type=#{yard_pin.return_type}; signatures=#{yard_pin.signatures})" - end - next yard_pin - end - out = combine_method_pins(rbs_pin, yard_pin) logger.debug do "GemPins.combine: Combining yard.path=#{yard_pin.path} - rbs=#{rbs_pin.inspect} with yard=#{yard_pin.inspect} into #{out}" diff --git a/spec/gem_pins_spec.rb b/spec/gem_pins_spec.rb index 093aa4cc1..ceb7fc346 100644 --- a/spec/gem_pins_spec.rb +++ b/spec/gem_pins_spec.rb @@ -1,13 +1,56 @@ # frozen_string_literal: true describe Solargraph::GemPins do - it 'can merge YARD and RBS' do - workspace = Solargraph::Workspace.new(Dir.pwd) - doc_map = Solargraph::DocMap.new(['rbs'], workspace, out: nil) - doc_map.cache_doc_map_gems!(nil) - - core_root = doc_map.pins.find { |pin| pin.path == 'RBS::EnvironmentLoader#core_root' } - expect(core_root.return_type.to_s).to eq('Pathname, nil') - expect(core_root.location.filename).to end_with('environment_loader.rb') + let(:workspace) { Solargraph::Workspace.new(Dir.pwd) } + let(:doc_map) { Solargraph::DocMap.new(requires, workspace, out: nil) } + let(:pin) { doc_map.pins.find { |pin| pin.path == path } } + + before do + doc_map.cache_doc_map_gems!(STDERR) # rubocop:disable Style/GlobalStdStream + end + + context 'with a combined method pin' do + let(:path) { 'RBS::EnvironmentLoader#core_root' } + let(:requires) { ['rbs'] } + + it 'can merge YARD and RBS' do + expect(pin.source).to eq(:combined) + end + + it 'finds types from RBS' do + expect(pin.return_type.to_s).to eq('Pathname, nil') + end + + it 'finds locations from YARD' do + expect(pin.location.filename).to end_with('environment_loader.rb') + end + end + + context 'with a YARD-only pin' do + let(:requires) { ['rake'] } + let(:path) { 'Rake::Task#prerequisites' } + + before do + workspace = doc_map.workspace + gemspecs = workspace.gemspecs + gemspec = gemspecs.find_gem('rake') + workspace.uncache_gem(gemspec) + end + + it 'found a pin' do + expect(pin.source).not_to be_nil + end + + it 'can merge YARD and RBS' do + expect(pin.source).to eq(:yardoc) + end + + it 'does not find types from YARD in this case' do + expect(pin.return_type.to_s).to eq('undefined') + end + + it 'finds locations from YARD' do + expect(pin.location.filename).to end_with('task.rb') + end end end diff --git a/spec/language_server/protocol_spec.rb b/spec/language_server/protocol_spec.rb index e88fb9c05..0b54729b5 100644 --- a/spec/language_server/protocol_spec.rb +++ b/spec/language_server/protocol_spec.rb @@ -34,7 +34,7 @@ def stop end end -describe Protocol do +describe Protocol, order: :defined do before :all do @protocol = Protocol.new(Solargraph::LanguageServer::Host.new) end diff --git a/spec/library_spec.rb b/spec/library_spec.rb index bd7cc25a0..d080d12d8 100644 --- a/spec/library_spec.rb +++ b/spec/library_spec.rb @@ -125,10 +125,11 @@ def bar expect(pins.map(&:path)).to include('Foo#bar') end - it "collects references to an instance method symbol" do - workspace = Solargraph::Workspace.new('*') - library = Solargraph::Library.new(workspace) - src1 = Solargraph::Source.load_string(%( + describe '#references_from' do + it "collects references to an instance method symbol" do + workspace = Solargraph::Workspace.new('*') + library = Solargraph::Library.new(workspace) + src1 = Solargraph::Source.load_string(%( class Foo def bar end @@ -136,8 +137,8 @@ def bar Foo.new.bar ), 'file1.rb', 0) - library.merge src1 - src2 = Solargraph::Source.load_string(%( + library.merge src1 + src2 = Solargraph::Source.load_string(%( foo = Foo.new foo.bar class Other @@ -145,17 +146,17 @@ def bar; end end Other.new.bar ), 'file2.rb', 0) - library.merge src2 - library.catalog - locs = library.references_from('file2.rb', 2, 11) - expect(locs.length).to eq(3) - expect(locs.select{|l| l.filename == 'file2.rb' && l.range.start.line == 6}).to be_empty - end + library.merge src2 + library.catalog + locs = library.references_from('file2.rb', 2, 11) + expect(locs.length).to eq(3) + expect(locs.select{|l| l.filename == 'file2.rb' && l.range.start.line == 6}).to be_empty + end - it "collects references to a class method symbol" do - workspace = Solargraph::Workspace.new('*') - library = Solargraph::Library.new(workspace) - src1 = Solargraph::Source.load_string(%( + it "collects references to a class method symbol" do + workspace = Solargraph::Workspace.new('*') + library = Solargraph::Library.new(workspace) + src1 = Solargraph::Source.load_string(%( class Foo def self.bar end @@ -167,8 +168,8 @@ def bar Foo.bar Foo.new.bar ), 'file1.rb', 0) - library.merge src1 - src2 = Solargraph::Source.load_string(%( + library.merge src1 + src2 = Solargraph::Source.load_string(%( Foo.bar Foo.new.bar class Other @@ -178,48 +179,48 @@ def bar; end Other.bar Other.new.bar ), 'file2.rb', 0) - library.merge src2 - library.catalog - locs = library.references_from('file2.rb', 1, 11) - expect(locs.length).to eq(3) - expect(locs.select{|l| l.filename == 'file1.rb' && l.range.start.line == 2}).not_to be_empty - expect(locs.select{|l| l.filename == 'file1.rb' && l.range.start.line == 9}).not_to be_empty - expect(locs.select{|l| l.filename == 'file2.rb' && l.range.start.line == 1}).not_to be_empty - end + library.merge src2 + library.catalog + locs = library.references_from('file2.rb', 1, 11) + expect(locs.length).to eq(3) + expect(locs.select{|l| l.filename == 'file1.rb' && l.range.start.line == 2}).not_to be_empty + expect(locs.select{|l| l.filename == 'file1.rb' && l.range.start.line == 9}).not_to be_empty + expect(locs.select{|l| l.filename == 'file2.rb' && l.range.start.line == 1}).not_to be_empty + end - it "collects stripped references to constant symbols" do - workspace = Solargraph::Workspace.new('*') - library = Solargraph::Library.new(workspace) - src1 = Solargraph::Source.load_string(%( + it "collects stripped references to constant symbols" do + workspace = Solargraph::Workspace.new('*') + library = Solargraph::Library.new(workspace) + src1 = Solargraph::Source.load_string(%( class Foo def bar end end Foo.new.bar ), 'file1.rb', 0) - library.merge src1 - src2 = Solargraph::Source.load_string(%( + library.merge src1 + src2 = Solargraph::Source.load_string(%( class Other foo = Foo.new foo.bar end ), 'file2.rb', 0) - library.merge src2 - library.catalog - locs = library.references_from('file1.rb', 1, 12, strip: true) - expect(locs.length).to eq(3) - locs.each do |l| - code = library.read_text(l.filename) - o1 = Solargraph::Position.to_offset(code, l.range.start) - o2 = Solargraph::Position.to_offset(code, l.range.ending) - expect(code[o1..o2-1]).to eq('Foo') + library.merge src2 + library.catalog + locs = library.references_from('file1.rb', 1, 12, strip: true) + expect(locs.length).to eq(3) + locs.each do |l| + code = library.read_text(l.filename) + o1 = Solargraph::Position.to_offset(code, l.range.start) + o2 = Solargraph::Position.to_offset(code, l.range.ending) + expect(code[o1..o2-1]).to eq('Foo') + end end - end - it 'rejects new references from different classes' do - workspace = Solargraph::Workspace.new('*') - library = Solargraph::Library.new(workspace) - source = Solargraph::Source.load_string(%( + it 'rejects new references from different classes' do + workspace = Solargraph::Workspace.new('*') + library = Solargraph::Library.new(workspace) + source = Solargraph::Source.load_string(%( class Foo def bar end @@ -227,106 +228,131 @@ def bar Foo.new Array.new ), 'test.rb') - library.merge source - library.catalog - foo_new_locs = library.references_from('test.rb', 5, 10) - expect(foo_new_locs).to eq([Solargraph::Location.new('test.rb', Solargraph::Range.from_to(5, 10, 5, 13))]) - obj_new_locs = library.references_from('test.rb', 6, 12) - expect(obj_new_locs).to eq([Solargraph::Location.new('test.rb', Solargraph::Range.from_to(6, 12, 6, 15))]) - end + library.merge source + library.catalog + foo_new_locs = library.references_from('test.rb', 5, 10) + expect(foo_new_locs).to eq([Solargraph::Location.new('test.rb', Solargraph::Range.from_to(5, 10, 5, 13))]) + obj_new_locs = library.references_from('test.rb', 6, 12) + expect(obj_new_locs).to eq([Solargraph::Location.new('test.rb', Solargraph::Range.from_to(6, 12, 6, 15))]) + end - it "searches the core for queries" do - library = Solargraph::Library.new - result = library.search('String') - expect(result).not_to be_empty - end + it "searches the core for queries" do + library = Solargraph::Library.new + result = library.search('String') + expect(result).not_to be_empty + end - it "returns YARD documentation from the core" do - library = Solargraph::Library.new - api_map, result = library.document('String') - expect(result).not_to be_empty - expect(result.first).to be_a(Solargraph::Pin::Base) - end + it "returns YARD documentation from the core" do + library = Solargraph::Library.new + api_map, result = library.document('String') + expect(result).not_to be_empty + expect(result.first).to be_a(Solargraph::Pin::Base) + end - it "returns YARD documentation from sources" do - library = Solargraph::Library.new - src = Solargraph::Source.load_string(%( + it "returns YARD documentation from sources" do + library = Solargraph::Library.new + src = Solargraph::Source.load_string(%( class Foo # My bar method def bar; end end ), 'test.rb', 0) - library.attach src - api_map, result = library.document('Foo#bar') - expect(result).not_to be_empty - expect(result.first).to be_a(Solargraph::Pin::Base) - end + library.attach src + api_map, result = library.document('Foo#bar') + expect(result).not_to be_empty + expect(result.first).to be_a(Solargraph::Pin::Base) + end - it "synchronizes sources from updaters" do - library = Solargraph::Library.new - src = Solargraph::Source.load_string(%( + it "synchronizes sources from updaters" do + library = Solargraph::Library.new + src = Solargraph::Source.load_string(%( class Foo end ), 'test.rb', 1) - library.attach src - repl = %( + library.attach src + repl = %( class Foo def bar; end end ) - updater = Solargraph::Source::Updater.new( - 'test.rb', - 2, - [Solargraph::Source::Change.new(nil, repl)] - ) - library.attach src.synchronize(updater) - expect(library.current.code).to eq(repl) - end + updater = Solargraph::Source::Updater.new( + 'test.rb', + 2, + [Solargraph::Source::Change.new(nil, repl)] + ) + library.attach src.synchronize(updater) + expect(library.current.code).to eq(repl) + end - it "finds unique references" do - library = Solargraph::Library.new(Solargraph::Workspace.new('*')) - src1 = Solargraph::Source.load_string(%( + it "finds unique references" do + library = Solargraph::Library.new(Solargraph::Workspace.new('*')) + src1 = Solargraph::Source.load_string(%( class Foo end ), 'src1.rb', 1) - library.merge src1 - src2 = Solargraph::Source.load_string(%( + library.merge src1 + src2 = Solargraph::Source.load_string(%( foo = Foo.new ), 'src2.rb', 1) - library.merge src2 - library.catalog - refs = library.references_from('src2.rb', 1, 12) - expect(refs.length).to eq(2) - end + library.merge src2 + library.catalog + refs = library.references_from('src2.rb', 1, 12) + expect(refs.length).to eq(2) + end - it "includes method parameters in references" do - library = Solargraph::Library.new(Solargraph::Workspace.new('*')) - source = Solargraph::Source.load_string(%( + it "includes method parameters in references" do + library = Solargraph::Library.new(Solargraph::Workspace.new('*')) + source = Solargraph::Source.load_string(%( class Foo def bar(baz) baz.upcase end end ), 'test.rb', 1) - library.attach source - from_def = library.references_from('test.rb', 2, 16) - expect(from_def.length).to eq(2) - from_ref = library.references_from('test.rb', 3, 10) - expect(from_ref.length).to eq(2) - end + library.attach source + from_def = library.references_from('test.rb', 2, 16) + expect(from_def.length).to eq(2) + from_ref = library.references_from('test.rb', 3, 10) + expect(from_ref.length).to eq(2) + end - it "includes block parameters in references" do - library = Solargraph::Library.new(Solargraph::Workspace.new('*')) - source = Solargraph::Source.load_string(%( + it "lies about names when client can't handle the truth" do + library = Solargraph::Library.new(Solargraph::Workspace.new('*')) + source = Solargraph::Source.load_string(%( + class Foo + def 🤦🏻foo♀️; 123; end + end + ), 'test.rb', 1) + library.attach source + from_def = library.references_from('test.rb', 2, 16, strip: true) + expect(from_def.first.range.start.column).to eq(14) + end + + it "tells the truth about names when client can handle the truth" do + library = Solargraph::Library.new(Solargraph::Workspace.new('*')) + source = Solargraph::Source.load_string(%( + class Foo + def 🤦🏻foo♀️; 123; end + end + ), 'test.rb', 1) + library.attach source + from_def = library.references_from('test.rb', 2, 16, strip: false) + expect(from_def.first.range.start.column).to eq(12) + end + + it "includes block parameters in references" do + library = Solargraph::Library.new(Solargraph::Workspace.new('*')) + source = Solargraph::Source.load_string(%( 100.times do |foo| puts foo end ), 'test.rb', 1) - library.attach source - from_def = library.references_from('test.rb', 1, 20) - expect(from_def.length).to eq(2) - from_ref = library.references_from('test.rb', 2, 13) - expect(from_ref.length).to eq(2) + library.attach source + from_def = library.references_from('test.rb', 1, 20) + expect(from_def.length).to eq(2) + from_ref = library.references_from('test.rb', 2, 13) + expect(from_ref.length).to eq(2) + end end it 'defines YARD tags' do From 817d9bf2f6d5637c68a50995e33472a16d19175b Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 26 Jul 2025 13:36:37 -0400 Subject: [PATCH 365/561] Handle a location sorting case --- lib/solargraph/location.rb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/solargraph/location.rb b/lib/solargraph/location.rb index 74d1318df..13ce4c560 100644 --- a/lib/solargraph/location.rb +++ b/lib/solargraph/location.rb @@ -7,13 +7,13 @@ module Solargraph class Location include Equality - # @return [String] + # @return [String, nil] attr_reader :filename # @return [Solargraph::Range] attr_reader :range - # @param filename [String] + # @param filename [String, nil] # @param range [Solargraph::Range] def initialize filename, range @filename = filename @@ -25,11 +25,14 @@ def initialize filename, range [filename, range] end + # @param other [self] def <=>(other) return nil unless other.is_a?(Location) if filename == other.filename range <=> other.range else + return -1 if filename.nil? + return 1 if other.filename.nil? filename <=> other.filename end end @@ -60,6 +63,7 @@ def to_hash end # @param node [Parser::AST::Node, nil] + # @return [self] def self.from_node(node) return nil if node.nil? || node.loc.nil? range = Range.from_node(node) From 085240430d5dfc652994acef9a1de12a70213946 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 26 Jul 2025 13:40:32 -0400 Subject: [PATCH 366/561] Loosen expectations a bit based on a trial run of settings --- .gitignore | 1 + .overcommit.yml | 11 ++++------- spec/spec_helper.rb | 8 +++++++- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index fc09c2fea..e10c43432 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ doc coverage /tmp/ +/rspec-examples.txt \ No newline at end of file diff --git a/.overcommit.yml b/.overcommit.yml index c15ded82f..dea53a923 100644 --- a/.overcommit.yml +++ b/.overcommit.yml @@ -41,9 +41,10 @@ PreCommit: enabled: false ALL: - # if you make non-trivial changes to a file, please leave it - # better than you found it - problem_on_unmodified_line: fail + # if you uncomment the next line locally, and you make changes to + # a file, overcommit will have you make the entire file pass + # + # problem_on_unmodified_line: fail on_warn: fail exclude: - lib/solargraph/rails/annotations/**/* @@ -51,10 +52,6 @@ PreCommit: - ".bundle/**/*" - 'core.*' -PrePush: - RSpec: - enabled: true - verify_signatures: false # diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index da7a6280e..d2fa1f334 100755 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -10,9 +10,15 @@ SimpleCov.start do add_filter(%r{^/spec/}) add_filter('/Rakefile') - enable_coverage(:branch) + # off by default - feel free to set if you'd like undercover to + # hold you to a thorough set of specs + enable_coverage(:branch) if ENV['SOLARGRAPH_BRANCH_COVERAGE'] end end +RSpec.configure do |c| + # Allow use of --only-failures with rspec, handy for local development + c.example_status_persistence_file_path = 'rspec-examples.txt' +end require 'solargraph' # Suppress logger output in specs (if possible) Solargraph::Logging.logger.reopen(File::NULL) if Solargraph::Logging.logger.respond_to?(:reopen) From 596dc34ba73cd647f7897e588ffbf00b090898df Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 26 Jul 2025 13:40:54 -0400 Subject: [PATCH 367/561] Run overcommit hooks under bundler --- .overcommit.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.overcommit.yml b/.overcommit.yml index dea53a923..bbcd92ac6 100644 --- a/.overcommit.yml +++ b/.overcommit.yml @@ -66,3 +66,5 @@ verify_signatures: false # # IndexTags: # enabled: true # Generate a tags file with `ctags` each time HEAD changes + +gemfile: Gemfile From bfc11212ae98d63b02615ea7bc56b8ed0896acb4 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 26 Jul 2025 14:04:44 -0400 Subject: [PATCH 368/561] Fix some new RuboCop issues, fix policy adjustment in overcommit --- .overcommit.yml | 6 +++--- lib/solargraph/parser/parser_gem/node_chainer.rb | 1 + lib/solargraph/pin/base_variable.rb | 2 +- spec/language_server/transport/adapter_spec.rb | 1 + spec/pin/documenting_spec.rb | 1 + 5 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.overcommit.yml b/.overcommit.yml index bbcd92ac6..fdbc8152d 100644 --- a/.overcommit.yml +++ b/.overcommit.yml @@ -41,11 +41,11 @@ PreCommit: enabled: false ALL: - # if you uncomment the next line locally, and you make changes to + # if you change the next line to 'report', and you make changes to # a file, overcommit will have you make the entire file pass # - # problem_on_unmodified_line: fail - on_warn: fail + problem_on_unmodified_line: warn + # on_warn: fail exclude: - lib/solargraph/rails/annotations/**/* - vendor/**/* diff --git a/lib/solargraph/parser/parser_gem/node_chainer.rb b/lib/solargraph/parser/parser_gem/node_chainer.rb index 32bb186dd..646e753d5 100644 --- a/lib/solargraph/parser/parser_gem/node_chainer.rb +++ b/lib/solargraph/parser/parser_gem/node_chainer.rb @@ -7,6 +7,7 @@ module ParserGem # class NodeChainer include NodeMethods + Chain = Source::Chain # @param node [Parser::AST::Node] diff --git a/lib/solargraph/pin/base_variable.rb b/lib/solargraph/pin/base_variable.rb index 15cdd918f..cef3f44cb 100644 --- a/lib/solargraph/pin/base_variable.rb +++ b/lib/solargraph/pin/base_variable.rb @@ -3,8 +3,8 @@ module Solargraph module Pin class BaseVariable < Base - include Solargraph::Parser::NodeMethods # include Solargraph::Source::NodeMethods + include Solargraph::Parser::NodeMethods # @return [Parser::AST::Node, nil] attr_reader :assignment diff --git a/spec/language_server/transport/adapter_spec.rb b/spec/language_server/transport/adapter_spec.rb index cf07b2841..3d30dd4e6 100644 --- a/spec/language_server/transport/adapter_spec.rb +++ b/spec/language_server/transport/adapter_spec.rb @@ -1,5 +1,6 @@ class AdapterTester include Solargraph::LanguageServer::Transport::Adapter + attr_reader :host def write data diff --git a/spec/pin/documenting_spec.rb b/spec/pin/documenting_spec.rb index 9b71d220d..1fdf9b8de 100644 --- a/spec/pin/documenting_spec.rb +++ b/spec/pin/documenting_spec.rb @@ -2,6 +2,7 @@ let(:object) { Class.new do include Solargraph::Pin::Documenting + attr_accessor :docstring end.new } From 50f9228318cff0293568cd0f2d613ace4ca3205d Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 26 Jul 2025 14:08:42 -0400 Subject: [PATCH 369/561] Add some @sg-ignores --- lib/solargraph/pin/base.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/solargraph/pin/base.rb b/lib/solargraph/pin/base.rb index cdd6a5ace..1fc0f3562 100644 --- a/lib/solargraph/pin/base.rb +++ b/lib/solargraph/pin/base.rb @@ -67,6 +67,7 @@ def assert_location_provided # @return [self] def combine_with(other, attrs={}) raise "tried to combine #{other.class} with #{self.class}" unless other.class == self.class + type_location = choose(other, :type_location) location = choose(other, :location) combined_name = combine_name(other) @@ -300,6 +301,7 @@ def choose_pin_attr_with_same_name(other, attr) # @param other [self] # @param attr [::Symbol] + # @sg-ignore Missing @return tag for Solargraph::Pin::Base#choose_pin_attr # @return [undefined] def choose_pin_attr(other, attr) # @type [Pin::Base, nil] @@ -312,6 +314,7 @@ def choose_pin_attr(other, attr) return val1 end # arbitrary way of choosing a pin + # @sg-ignore Unresolved call to _1 [val1, val2].compact.min_by { _1.best_location.to_s } end From fb022d60796950f54d600b844bbfcd818be96092 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 26 Jul 2025 14:12:03 -0400 Subject: [PATCH 370/561] Update todo file with new RuboCop --- .rubocop_todo.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 32b259f2f..815848640 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,6 +1,6 @@ # This configuration was generated by # `rubocop --auto-gen-config --no-exclude-limit --no-auto-gen-timestamp` -# using RuboCop version 1.78.0. +# using RuboCop version 1.79.0. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new @@ -653,8 +653,9 @@ Lint/RedundantRequireStatement: # Offense count: 2 # This cop supports unsafe autocorrection (--autocorrect-all). -# Configuration parameters: AllowedMethods. +# Configuration parameters: AllowedMethods, InferNonNilReceiver, AdditionalNilMethods. # AllowedMethods: instance_of?, kind_of?, is_a?, eql?, respond_to?, equal? +# AdditionalNilMethods: present?, blank?, try, try! Lint/RedundantSafeNavigation: Exclude: - 'lib/solargraph/api_map/source_to_yard.rb' @@ -754,7 +755,7 @@ Lint/UselessAccessModifier: Exclude: - 'lib/solargraph/api_map.rb' -# Offense count: 40 +# Offense count: 41 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AutoCorrect. Lint/UselessAssignment: From a6d405aeba1aad7d072fdf357c76eae4c9ce6f49 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 26 Jul 2025 14:15:49 -0400 Subject: [PATCH 371/561] Ratchet Rubocop todo file --- .rubocop_todo.yml | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 64bb00759..5c85be76c 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -737,13 +737,6 @@ Lint/UnusedMethodArgument: - 'lib/solargraph/source/chain/variable.rb' - 'lib/solargraph/source/chain/z_super.rb' -# Offense count: 1 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AutoCorrect, ContextCreatingMethods, MethodCreatingMethods. -Lint/UselessAccessModifier: - Exclude: - - 'lib/solargraph/api_map.rb' - # Offense count: 41 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AutoCorrect. @@ -1233,7 +1226,7 @@ RSpec/MissingExampleGroupArgument: Exclude: - 'spec/diagnostics/rubocop_helpers_spec.rb' -# Offense count: 480 +# Offense count: 479 # Configuration parameters: Max. RSpec/MultipleExpectations: Exclude: @@ -1248,7 +1241,6 @@ RSpec/MultipleExpectations: - 'spec/diagnostics/type_check_spec.rb' - 'spec/diagnostics/update_errors_spec.rb' - 'spec/diagnostics_spec.rb' - - 'spec/gem_pins_spec.rb' - 'spec/language_server/host/message_worker_spec.rb' - 'spec/language_server/host_spec.rb' - 'spec/language_server/message/completion_item/resolve_spec.rb' @@ -1924,7 +1916,7 @@ Style/FrozenStringLiteralComment: - 'spec/yard_map/mapper/to_method_spec.rb' - 'spec/yard_map/mapper_spec.rb' -# Offense count: 11 +# Offense count: 9 # This cop supports unsafe autocorrection (--autocorrect-all). Style/GlobalStdStream: Exclude: @@ -2480,7 +2472,6 @@ Style/StringLiterals: - 'lib/solargraph/complex_type.rb' - 'lib/solargraph/complex_type/unique_type.rb' - 'lib/solargraph/convention/struct_definition.rb' - - 'lib/solargraph/doc_map.rb' - 'lib/solargraph/language_server/host.rb' - 'lib/solargraph/language_server/message/extended/document_gems.rb' - 'lib/solargraph/language_server/message/extended/download_core.rb' From b66ef0d4fc62a02e20e57b1bd0fb1ec22037113c Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 26 Jul 2025 14:29:19 -0400 Subject: [PATCH 372/561] Ratchet a couple of Rubocop todo categories --- .rubocop_todo.yml | 7 ++-- lib/solargraph/pin_cache.rb | 68 ++++++++++++++++++------------------- lib/solargraph/shell.rb | 22 ++++++------ 3 files changed, 47 insertions(+), 50 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 5c85be76c..9e024b576 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -2048,7 +2048,7 @@ Style/MapToSet: - 'lib/solargraph/library.rb' - 'spec/source_map/clip_spec.rb' -# Offense count: 197 +# Offense count: 164 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: require_parentheses, require_no_parentheses, require_no_parentheses_except_multiline @@ -2091,11 +2091,9 @@ Style/MethodDefParentheses: - 'lib/solargraph/pin/local_variable.rb' - 'lib/solargraph/pin/method.rb' - 'lib/solargraph/pin/parameter.rb' - - 'lib/solargraph/pin_cache.rb' - 'lib/solargraph/position.rb' - 'lib/solargraph/range.rb' - 'lib/solargraph/rbs_map/conversions.rb' - - 'lib/solargraph/shell.rb' - 'lib/solargraph/source.rb' - 'lib/solargraph/source/chain/call.rb' - 'lib/solargraph/source/chain/constant.rb' @@ -2460,7 +2458,7 @@ Style/StringConcatenation: - 'lib/solargraph/pin/namespace.rb' - 'solargraph.gemspec' -# Offense count: 612 +# Offense count: 604 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline. # SupportedStyles: single_quotes, double_quotes @@ -2486,7 +2484,6 @@ Style/StringLiterals: - 'lib/solargraph/pin/parameter.rb' - 'lib/solargraph/rbs_map/conversions.rb' - 'lib/solargraph/server_methods.rb' - - 'lib/solargraph/shell.rb' - 'lib/solargraph/workspace.rb' - 'lib/solargraph/yard_map/mapper/to_method.rb' - 'lib/solargraph/yard_tags.rb' diff --git a/lib/solargraph/pin_cache.rb b/lib/solargraph/pin_cache.rb index b71a16ff5..1a506ef9e 100644 --- a/lib/solargraph/pin_cache.rb +++ b/lib/solargraph/pin_cache.rb @@ -12,9 +12,9 @@ class PinCache # @param rbs_collection_config_path [String, nil] # @param directory [String, nil] # @param yard_plugins [Array] - def initialize(rbs_collection_path:, rbs_collection_config_path:, + def initialize rbs_collection_path:, rbs_collection_config_path:, directory:, - yard_plugins:) + yard_plugins: @rbs_collection_path = rbs_collection_path @rbs_collection_config_path = rbs_collection_config_path @directory = directory @@ -22,7 +22,7 @@ def initialize(rbs_collection_path:, rbs_collection_config_path:, end # @param gemspec [Gem::Specification, Bundler::LazySpecification] - def cached?(gemspec) + def cached? gemspec rbs_version_cache_key = lookup_rbs_version_cache_key(gemspec) combined_gem?(gemspec, rbs_version_cache_key) end @@ -31,7 +31,7 @@ def cached?(gemspec) # @param rebuild [Boolean] whether to rebuild the cache regardless of whether it already exists # @param out [IO, nil] output stream for logging # @return [void] - def cache_gem(gemspec:, rebuild: false, out: nil) + def cache_gem gemspec:, rebuild: false, out: nil rbs_version_cache_key = lookup_rbs_version_cache_key(gemspec) build_yard, build_rbs_collection, build_combined = @@ -51,7 +51,7 @@ def cache_gem(gemspec:, rebuild: false, out: nil) # @param gemspec [Gem::Specification, Bundler::LazySpecification] # @param rbs_version_cache_key [String] - def suppress_yard_cache?(gemspec, rbs_version_cache_key) + def suppress_yard_cache? gemspec, rbs_version_cache_key if gemspec.name == 'parser' && rbs_version_cache_key != RbsMap::CACHE_KEY_UNRESOLVED # parser takes forever to build YARD pins, but has excellent RBS collection pins return true @@ -62,7 +62,7 @@ def suppress_yard_cache?(gemspec, rbs_version_cache_key) # @param out [IO, nil] output stream for logging # # @return [void] - def cache_all_stdlibs(out: $stderr) + def cache_all_stdlibs out: $stderr possible_stdlibs.each do |stdlib| RbsMap::StdlibMap.new(stdlib, out: out) end @@ -88,7 +88,7 @@ def cache_stdlib_rbs_map path # @param gemspec [Gem::Specification, Bundler::LazySpecification] # # @return [String] - def lookup_rbs_version_cache_key(gemspec) + def lookup_rbs_version_cache_key gemspec rbs_map = RbsMap.from_gemspec(gemspec, rbs_collection_path, rbs_collection_config_path) rbs_map.cache_key end @@ -98,14 +98,14 @@ def lookup_rbs_version_cache_key(gemspec) # @param yard_pins [Array] # @param rbs_collection_pins [Array] # @return [void] - def cache_combined_pins(gemspec, rbs_version_cache_key, yard_pins, rbs_collection_pins) + def cache_combined_pins gemspec, rbs_version_cache_key, yard_pins, rbs_collection_pins combined_pins = GemPins.combine(yard_pins, rbs_collection_pins) serialize_combined_gem(gemspec, rbs_version_cache_key, combined_pins) end # @param gemspec [Gem::Specification] # @return [Array] - def deserialize_combined_pin_cache(gemspec) + def deserialize_combined_pin_cache gemspec unless combined_pins_in_memory[[gemspec.name, gemspec.version]].nil? return combined_pins_in_memory[[gemspec.name, gemspec.version]] end @@ -122,7 +122,7 @@ def deserialize_combined_pin_cache(gemspec) # @param gemspec [Gem::Specification] # @param out [IO, nil] # @return [void] - def uncache_gem(gemspec, out: nil) + def uncache_gem gemspec, out: nil PinCache.uncache(yardoc_path(gemspec), out: out) yard_pins_in_memory.delete([gemspec.name, gemspec.version]) PinCache.uncache(yard_gem_path(gemspec), out: out) @@ -136,7 +136,7 @@ def uncache_gem(gemspec, out: nil) end # @param gemspec [Gem::Specification, Bundler::LazySpecification] - def yardoc_processing?(gemspec) + def yardoc_processing? gemspec Yardoc.processing?(yardoc_path(gemspec)) end @@ -163,7 +163,7 @@ def possible_stdlibs # # @return [Array(Boolean, Boolean, Boolean)] whether to build YARD # pins, RBS collection pins, and combined pins - def calculate_build_needs(gemspec, rebuild:, rbs_version_cache_key:) + def calculate_build_needs gemspec, rebuild:, rbs_version_cache_key: if rebuild build_yard = true build_rbs_collection = true @@ -187,12 +187,12 @@ def calculate_build_needs(gemspec, rebuild:, rbs_version_cache_key:) # @param out [IO, nil] # # @return [void] - def build_combine_and_cache(gemspec, + def build_combine_and_cache gemspec, rbs_version_cache_key, build_yard:, build_rbs_collection:, build_combined:, - out:) + out: log_cache_info(gemspec, rbs_version_cache_key, build_yard: build_yard, build_rbs_collection: build_rbs_collection, @@ -214,12 +214,12 @@ def build_combine_and_cache(gemspec, # @param out [IO, nil] # # @return [void] - def log_cache_info(gemspec, + def log_cache_info gemspec, rbs_version_cache_key, build_yard:, build_rbs_collection:, build_combined:, - out:) + out: type = [] type << 'YARD' if build_yard rbs_source_desc = RbsMap.rbs_source_desc(rbs_version_cache_key) @@ -237,7 +237,7 @@ def log_cache_info(gemspec, # @param gemspec [Gem::Specification, Bundler::LazySpecification] # @param out [IO, nil] # @return [Array] - def cache_yard_pins(gemspec, out) + def cache_yard_pins gemspec, out gem_yardoc_path = yardoc_path(gemspec) Yardoc.build_docs(gem_yardoc_path, yard_plugins, gemspec) unless Yardoc.docs_built?(gem_yardoc_path) pins = Yardoc.build_pins(gem_yardoc_path, gemspec, out: out) @@ -264,7 +264,7 @@ def combined_pins_in_memory # @param gemspec [Gem::Specification] # @param _out [IO, nil] # @return [Array] - def cache_rbs_collection_pins(gemspec, _out) + def cache_rbs_collection_pins gemspec, _out rbs_map = RbsMap.from_gemspec(gemspec, rbs_collection_path, rbs_collection_config_path) pins = rbs_map.pins rbs_version_cache_key = rbs_map.cache_key @@ -340,39 +340,39 @@ def yard_gem_path gemspec # @param gemspec [Gem::Specification] # @return [Array] - def load_yard_gem(gemspec) + def load_yard_gem gemspec PinCache.load(yard_gem_path(gemspec)) end # @param gemspec [Gem::Specification] # @param pins [Array] # @return [void] - def serialize_yard_gem(gemspec, pins) + def serialize_yard_gem gemspec, pins PinCache.save(yard_gem_path(gemspec), pins) end # @param gemspec [Gem::Specification] # @return [Boolean] - def yard_gem?(gemspec) + def yard_gem? gemspec exist?(yard_gem_path(gemspec)) end # @param gemspec [Gem::Specification] # @return [Boolean] - def yardoc?(gemspec) + def yardoc? gemspec exist?(yardoc_path(gemspec)) end # @param gemspec [Gem::Specification] # @param hash [String, nil] # @return [String] - def rbs_collection_pins_path(gemspec, hash) + def rbs_collection_pins_path gemspec, hash rbs_collection_pins_path_prefix(gemspec) + "#{gemspec.name}-#{gemspec.version}-#{hash || 0}.ser" end # @param gemspec [Gem::Specification] # @return [String] - def rbs_collection_pins_path_prefix(gemspec) + def rbs_collection_pins_path_prefix gemspec File.join(PinCache.work_dir, 'rbs', "#{gemspec.name}-#{gemspec.version}-") end @@ -380,7 +380,7 @@ def rbs_collection_pins_path_prefix(gemspec) # @param hash [String] # # @return [Array] - def load_rbs_collection_pins(gemspec, hash) + def load_rbs_collection_pins gemspec, hash PinCache.load(rbs_collection_pins_path(gemspec, hash)) end @@ -388,20 +388,20 @@ def load_rbs_collection_pins(gemspec, hash) # @param hash [String, nil] # @param pins [Array]n # @return [void] - def serialize_rbs_collection_pins(gemspec, hash, pins) + def serialize_rbs_collection_pins gemspec, hash, pins PinCache.save(rbs_collection_pins_path(gemspec, hash), pins) end # @param gemspec [Gem::Specification] # @param hash [String, nil] # @return [String] - def combined_path(gemspec, hash) + def combined_path gemspec, hash File.join(combined_path_prefix(gemspec) + "-#{hash || 0}.ser") end # @param gemspec [Gem::Specification] # @return [String] - def combined_path_prefix(gemspec) + def combined_path_prefix gemspec File.join(PinCache.work_dir, 'combined', yard_plugins.sort.join('-'), "#{gemspec.name}-#{gemspec.version}") end @@ -409,13 +409,13 @@ def combined_path_prefix(gemspec) # @param hash [String, nil] # @param pins [Array] # @return [void] - def serialize_combined_gem(gemspec, hash, pins) + def serialize_combined_gem gemspec, hash, pins PinCache.save(combined_path(gemspec, hash), pins) end # @param gemspec [Gem::Specification, Bundler::LazySpecification] # @param hash [String] - def combined_gem?(gemspec, hash) + def combined_gem? gemspec, hash exist?(combined_path(gemspec, hash)) end @@ -428,7 +428,7 @@ def load_combined_gem gemspec, hash # @param gemspec [Gem::Specification] # @param hash [String] - def rbs_collection_pins?(gemspec, hash) + def rbs_collection_pins? gemspec, hash exist?(rbs_collection_pins_path(gemspec, hash)) end @@ -500,7 +500,7 @@ def uncache *path_segments, out: nil # @param out [IO, nil] # @return [void] - def uncache_core(out: nil) + def uncache_core out: nil uncache(core_path, out: out) # ApiMap keep this in memory ApiMap.reset_core(out: out) @@ -508,7 +508,7 @@ def uncache_core(out: nil) # @param out [IO, nil] # @return [void] - def uncache_stdlib(out: nil) + def uncache_stdlib out: nil uncache(stdlib_path, out: out) end @@ -559,7 +559,7 @@ def core? # @param out [IO, nil] # @return [Array] - def cache_core(out: $stderr) + def cache_core out: $stderr RbsMap::CoreMap.new.cache_core(out: out) end diff --git a/lib/solargraph/shell.rb b/lib/solargraph/shell.rb index 03ce59410..f8b98359a 100755 --- a/lib/solargraph/shell.rb +++ b/lib/solargraph/shell.rb @@ -16,7 +16,7 @@ def self.exit_on_failure? map %w[--version -v] => :version - desc "--version, -v", "Print the version" + desc '--version, -v', 'Print the version' # @return [void] def version puts Solargraph::VERSION @@ -31,10 +31,10 @@ def socket port = options[:port] port = available_port if port.zero? Backport.run do - Signal.trap("INT") do + Signal.trap('INT') do Backport.stop end - Signal.trap("TERM") do + Signal.trap('TERM') do Backport.stop end # @sg-ignore https://github.com/castwide/backport/pull/5 @@ -48,10 +48,10 @@ def socket def stdio require 'backport' Backport.run do - Signal.trap("INT") do + Signal.trap('INT') do Backport.stop end - Signal.trap("TERM") do + Signal.trap('TERM') do Backport.stop end # @sg-ignore https://github.com/castwide/backport/pull/5 @@ -64,7 +64,7 @@ def stdio option :extensions, type: :boolean, aliases: :e, desc: 'Add installed extensions', default: true # @param directory [String] # @return [void] - def config(directory = '.') + def config directory = '.' matches = [] if options[:extensions] # @sg-ignore @@ -84,7 +84,7 @@ def config(directory = '.') File.open(File.join(directory, '.solargraph.yml'), 'w') do |file| file.puts conf.to_yaml end - STDOUT.puts "Configuration file initialized." + STDOUT.puts 'Configuration file initialized.' end desc 'clear', 'Delete all cached documentation' @@ -93,7 +93,7 @@ def config(directory = '.') ) # @return [void] def clear - puts "Deleting all cached documentation (gems, core and stdlib)" + puts 'Deleting all cached documentation (gems, core and stdlib)' Solargraph::PinCache.clear end map 'clear-cache' => :clear @@ -138,7 +138,7 @@ def gems *names end end - desc 'uncache GEM [...GEM]', "Delete specific cached gem documentation" + desc 'uncache GEM [...GEM]', 'Delete specific cached gem documentation' long_desc %( Specify one or more gem names to clear. 'core' or 'stdlib' may also be specified to clear cached system documentation. @@ -274,7 +274,7 @@ def pin_description pin # @param type [ComplexType] # @return [void] - def print_type(type) + def print_type type if options[:rbs] puts type.to_rbs else @@ -284,7 +284,7 @@ def print_type(type) # @param pin [Solargraph::Pin::Base] # @return [void] - def print_pin(pin) + def print_pin pin if options[:rbs] puts pin.to_rbs else From c19d3d548f903641a648598e8fcf5888257c849f Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 26 Jul 2025 14:33:26 -0400 Subject: [PATCH 373/561] Ratchet a couple of Rubocop todo categories --- .rubocop_todo.yml | 3 +-- spec/doc_map_spec.rb | 6 +++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 9e024b576..c4d0e4686 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1027,7 +1027,7 @@ RSpec/DescribeClass: - 'spec/complex_type_spec.rb' - 'spec/source_map/node_processor_spec.rb' -# Offense count: 388 +# Offense count: 387 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: SkipBlocks, EnforcedStyle, OnlyStaticConstants. # SupportedStyles: described_class, explicit @@ -1043,7 +1043,6 @@ RSpec/DescribedClass: - 'spec/diagnostics/type_check_spec.rb' - 'spec/diagnostics/update_errors_spec.rb' - 'spec/diagnostics_spec.rb' - - 'spec/doc_map_spec.rb' - 'spec/language_server/host/diagnoser_spec.rb' - 'spec/language_server/host/dispatch_spec.rb' - 'spec/language_server/host/message_worker_spec.rb' diff --git a/spec/doc_map_spec.rb b/spec/doc_map_spec.rb index 823db14d7..c935c0c58 100644 --- a/spec/doc_map_spec.rb +++ b/spec/doc_map_spec.rb @@ -5,7 +5,7 @@ describe Solargraph::DocMap do subject(:doc_map) do - Solargraph::DocMap.new(requires, workspace, out: out) + described_class.new(requires, workspace, out: out) end let(:out) { StringIO.new } @@ -16,7 +16,7 @@ Solargraph::Workspace.new(Dir.pwd) end - let(:plain_doc_map) { Solargraph::DocMap.new([], workspace, out: nil) } + let(:plain_doc_map) { described_class.new([], workspace, out: nil) } before do doc_map.cache_doc_map_gems!(nil) if pre_cache @@ -80,7 +80,7 @@ context 'with require as bundle/require' do it 'imports all gems when bundler/require used' do - doc_map_with_bundler_require = Solargraph::DocMap.new(['bundler/require'], workspace, out: nil) + doc_map_with_bundler_require = described_class.new(['bundler/require'], workspace, out: nil) doc_map_with_bundler_require.cache_doc_map_gems!(nil) expect(doc_map_with_bundler_require.pins.length - plain_doc_map.pins.length).to be_positive end From 59053b133f46a3fe100d53b679c67ba0049e37dc Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 26 Jul 2025 14:33:50 -0400 Subject: [PATCH 374/561] Disable a RuboCop check --- .rubocop.yml | 4 ++++ .rubocop_todo.yml | 10 ---------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index e51b2f834..a73324db2 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -35,6 +35,10 @@ Metrics/ParameterLists: CountKeywordArgs: false +# we tend to use @@ and the risk doesn't seem high +Style/ClassVars: + Enabled: false + # # Set a relaxed standard on harder-to-address item for ease of # contribution - if you are good at refactoring, we welcome PRs to diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 815848640..aa00a82d0 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1688,16 +1688,6 @@ Style/ClassEqualityComparison: - 'lib/solargraph/gem_pins.rb' - 'lib/solargraph/pin/base.rb' -# Offense count: 13 -Style/ClassVars: - Exclude: - - 'lib/solargraph/api_map.rb' - - 'lib/solargraph/convention.rb' - - 'lib/solargraph/logging.rb' - - 'lib/solargraph/parser/node_processor.rb' - - 'lib/solargraph/rbs_map.rb' - - 'lib/solargraph/source/chain.rb' - # Offense count: 2 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: AllowedReceivers. From 2564549fdfc21a30869e71871f0f86495a8da966 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 26 Jul 2025 14:36:20 -0400 Subject: [PATCH 375/561] Drop rubocop:disable lines --- lib/solargraph/workspace/gemspecs.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/solargraph/workspace/gemspecs.rb b/lib/solargraph/workspace/gemspecs.rb index b07166db5..80f200a2a 100644 --- a/lib/solargraph/workspace/gemspecs.rb +++ b/lib/solargraph/workspace/gemspecs.rb @@ -182,12 +182,12 @@ def to_gem_specification specish specish.version end else - @@warned_on_gem_type ||= # rubocop:disable Style/ClassVars + @@warned_on_gem_type ||= false unless @@warned_on_gem_type logger.warn 'Unexpected type while resolving gem: ' \ "#{specish.class}" - @@warned_on_gem_type = true # rubocop:disable Style/ClassVars + @@warned_on_gem_type = true end nil end From c6f52066b5f868d1c1e8bd68c4535a0750d1df29 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 26 Jul 2025 15:09:16 -0400 Subject: [PATCH 376/561] Restore removed code --- lib/solargraph/gem_pins.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/solargraph/gem_pins.rb b/lib/solargraph/gem_pins.rb index 6efe3628c..b6ac5c6ce 100644 --- a/lib/solargraph/gem_pins.rb +++ b/lib/solargraph/gem_pins.rb @@ -49,6 +49,11 @@ def self.combine(yard_pins, rbs_pins) rbs_pin = rbs_api_map.get_path_pins(yard_pin.path).filter { |pin| pin.is_a? Pin::Method }.first next yard_pin unless rbs_pin && yard_pin.class == Pin::Method + unless rbs_pin + logger.debug { "GemPins.combine: No rbs pin for #{yard_pin.path} - using YARD's '#{yard_pin.inspect} (return_type=#{yard_pin.return_type}; signatures=#{yard_pin.signatures})" } + next yard_pin + end + out = combine_method_pins(rbs_pin, yard_pin) logger.debug do "GemPins.combine: Combining yard.path=#{yard_pin.path} - rbs=#{rbs_pin.inspect} with yard=#{yard_pin.inspect} into #{out}" From c2807b18ad0ac43dad5e3eab5adee49f21dc9889 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 28 Jul 2025 07:21:47 -0400 Subject: [PATCH 377/561] Fix reference --- lib/solargraph/workspace/gemspecs.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/solargraph/workspace/gemspecs.rb b/lib/solargraph/workspace/gemspecs.rb index 80f200a2a..170fe0c06 100644 --- a/lib/solargraph/workspace/gemspecs.rb +++ b/lib/solargraph/workspace/gemspecs.rb @@ -118,7 +118,7 @@ def fetch_dependencies gemspec # Can happen on system gems in Ruby 3.0, it seems: # # https://github.com/castwide/solargraph/actions/runs/16480452864/job/46593077954?pr=1006 - log.debug do + logger.debug do "Skipping dependency #{runtime_dep.name} for #{gemspec.name} due to NoMethodError: #{e.message}" end From 3a43053053c163ecb1abb2c1d48b6799572b655c Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 28 Jul 2025 08:25:18 -0400 Subject: [PATCH 378/561] Fix Ruby 3.0+RBS 3.6.1 yaml issue --- lib/solargraph/doc_map.rb | 9 +++++++-- lib/solargraph/rbs_map/stdlib_map.rb | 4 ++-- lib/solargraph/workspace.rb | 7 +++++++ lib/solargraph/workspace/gemspecs.rb | 8 ++++++++ 4 files changed, 24 insertions(+), 4 deletions(-) diff --git a/lib/solargraph/doc_map.rb b/lib/solargraph/doc_map.rb index 9e373953b..cfa84712f 100644 --- a/lib/solargraph/doc_map.rb +++ b/lib/solargraph/doc_map.rb @@ -118,8 +118,13 @@ def load_serialized_gem_pins out: $stderr # this will load from disk if needed; no need to manage # uncached_gemspecs to trigger that later stdlib_name_guess = path.split('/').first - rbs_pins = pin_cache.cache_stdlib_rbs_map stdlib_name_guess if stdlib_name_guess - @pins.concat rbs_pins if rbs_pins + + # try to resolve the stdlib name + deps = workspace.stdlib_dependencies(stdlib_name_guess) || [] + [stdlib_name_guess, *deps].each do |potential_stdlib_name| + rbs_pins = pin_cache.cache_stdlib_rbs_map potential_stdlib_name + @pins.concat rbs_pins if rbs_pins + end end existing_pin_count = pins.length diff --git a/lib/solargraph/rbs_map/stdlib_map.rb b/lib/solargraph/rbs_map/stdlib_map.rb index bd3fd5a99..3851307f3 100644 --- a/lib/solargraph/rbs_map/stdlib_map.rb +++ b/lib/solargraph/rbs_map/stdlib_map.rb @@ -44,9 +44,9 @@ def self.source end # @param name [String] - # @param version [String] + # @param version [String, nil] # @return [Array String}>, nil] - def self.stdlib_dependencies name, version + def self.stdlib_dependencies name, version = nil if source.has?(name, version) source.dependencies_of(name, version) else diff --git a/lib/solargraph/workspace.rb b/lib/solargraph/workspace.rb index ab788be06..dbc6f892c 100644 --- a/lib/solargraph/workspace.rb +++ b/lib/solargraph/workspace.rb @@ -66,6 +66,13 @@ def resolve_require require gemspecs.resolve_require(require) end + # @param stdlib_name [String] + # + # @return [Array] + def stdlib_dependencies stdlib_name + gemspecs.stdlib_dependencies(stdlib_name) + end + # @return [Environ] def global_environ # empty docmap, since the result needs to work in any possible diff --git a/lib/solargraph/workspace/gemspecs.rb b/lib/solargraph/workspace/gemspecs.rb index 170fe0c06..270b063eb 100644 --- a/lib/solargraph/workspace/gemspecs.rb +++ b/lib/solargraph/workspace/gemspecs.rb @@ -86,6 +86,14 @@ def resolve_require require nil end + # @param stdlib_name [String] + # + # @return [Array] + def stdlib_dependencies stdlib_name + deps = RbsMap::StdlibMap.stdlib_dependencies(stdlib_name, nil) || [] + deps.map { |dep| dep['name'] } + end + # @param name [String] # @param version [String, nil] # @param out [IO, nil] output stream for logging From a481c8083345289b32dd4af4e204fc814235a25c Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 28 Jul 2025 08:45:17 -0400 Subject: [PATCH 379/561] Test Thor.desc lookup, fix rbs_collection_pins_path issue --- lib/solargraph/pin_cache.rb | 2 +- spec/api_map/api_map_method_spec.rb | 17 ++++++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/lib/solargraph/pin_cache.rb b/lib/solargraph/pin_cache.rb index 1a506ef9e..77fa3c500 100644 --- a/lib/solargraph/pin_cache.rb +++ b/lib/solargraph/pin_cache.rb @@ -367,7 +367,7 @@ def yardoc? gemspec # @param hash [String, nil] # @return [String] def rbs_collection_pins_path gemspec, hash - rbs_collection_pins_path_prefix(gemspec) + "#{gemspec.name}-#{gemspec.version}-#{hash || 0}.ser" + rbs_collection_pins_path_prefix(gemspec) + "#{hash || 0}.ser" end # @param gemspec [Gem::Specification] diff --git a/spec/api_map/api_map_method_spec.rb b/spec/api_map/api_map_method_spec.rb index 8a272b109..91899a40c 100644 --- a/spec/api_map/api_map_method_spec.rb +++ b/spec/api_map/api_map_method_spec.rb @@ -61,10 +61,21 @@ describe '#get_method_stack' do let(:out) { StringIO.new } let(:api_map) { described_class.load_with_cache(Dir.pwd, out: out) } - let(:method_stack) { api_map.get_method_stack('YAML', 'safe_load', scope: :class) } - it 'handles the YAML gem aliased to Psych' do - expect(method_stack).not_to be_empty + context 'with stdlib that has vital dependencies' do + let(:method_stack) { api_map.get_method_stack('YAML', 'safe_load', scope: :class) } + + it 'handles the YAML gem aliased to Psych' do + expect(method_stack).not_to be_empty + end + end + + context 'with thor' do + let(:method_stack) { api_map.get_method_stack('Thor', 'desc', scope: :class) } + + it 'handles finding Thor.desc' do + expect(method_stack).not_to be_empty + end end end end From c03266ef416040b4e04a7ffce8c75a3482d2f121 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 28 Jul 2025 10:11:42 -0400 Subject: [PATCH 380/561] Work around an overcommit glitch --- lib/solargraph/yardoc.rb | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/lib/solargraph/yardoc.rb b/lib/solargraph/yardoc.rb index 9701018cf..96e39816e 100644 --- a/lib/solargraph/yardoc.rb +++ b/lib/solargraph/yardoc.rb @@ -23,7 +23,7 @@ def build_docs gem_yardoc_path, yard_plugins, gemspec # @todo set these up to run in parallel # # @sg-ignore - stdout_and_stderr_str, status = Open3.capture2e(cmd, chdir: gemspec.gem_dir) + stdout_and_stderr_str, status = Open3.capture2e(current_bundle_env_tweaks, cmd, chdir: gemspec.gem_dir) return if status.success? Solargraph.logger.warn { "YARD failed running #{cmd.inspect} in #{gemspec.gem_dir}" } Solargraph.logger.info stdout_and_stderr_str @@ -64,5 +64,22 @@ def load! gem_yardoc_path YARD::Registry.load! gem_yardoc_path YARD::Registry.all end + + # If the BUNDLE_GEMFILE environment variable is set, we need to + # make sure it's an absolute path, as we'll be changing + # directories. + # + # 'bundle exec' sets an absolute path here, but at least the + # overcommit gem does not, breaking on-the-fly documention with a + # spawned yardoc command from our current bundle + # + # @return [Hash{String => String}] a hash of environment variables to override + def current_bundle_env_tweaks + tweaks = {} + if ENV['BUNDLE_GEMFILE'] && !ENV['BUNDLE_GEMFILE'].empty? + tweaks['BUNDLE_GEMFILE'] = File.expand_path(ENV['BUNDLE_GEMFILE']) + end + tweaks + end end end From ed5381fd13b669b5a79556d775e67a28d9021385 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 28 Jul 2025 10:48:06 -0400 Subject: [PATCH 381/561] Drop offense counts for easier mitigation --- .github/workflows/linting.yml | 4 +- .rubocop_todo.yml | 215 +--------------------------------- 2 files changed, 3 insertions(+), 216 deletions(-) diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index fbd80a490..2a5968351 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -114,11 +114,11 @@ jobs: - name: Run RuboCop against todo file run: | - bundle exec rubocop --auto-gen-config --no-exclude-limit --no-auto-gen-timestamp + bundle exec rubocop --auto-gen-config --no-exclude-limit --no-offense-counts --no-auto-gen-timestamp if [ -n "$(git status --porcelain)" ] then git status --porcelain git diff -u . - >&2 echo "Please fix deltas if bad or run 'bundle exec rubocop --auto-gen-config --no-exclude-limit --no-auto-gen-timestamp' and push up changes if good" + >&2 echo "Please fix deltas if bad or run 'bundle exec rubocop --auto-gen-config --no-exclude-limit --no-offense-counts --no-auto-gen-timestamp' and push up changes if good" exit 1 fi diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index aa00a82d0..c5865e228 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,12 +1,11 @@ # This configuration was generated by -# `rubocop --auto-gen-config --no-exclude-limit --no-auto-gen-timestamp` +# `rubocop --auto-gen-config --no-exclude-limit --no-offense-counts --no-auto-gen-timestamp` # using RuboCop version 1.79.0. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. -# Offense count: 19 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: Include. # Include: **/*.gemspec @@ -14,7 +13,6 @@ Gemspec/AddRuntimeDependency: Exclude: - 'solargraph.gemspec' -# Offense count: 2 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: Severity, Include. # Include: **/*.gemspec @@ -23,7 +21,6 @@ Gemspec/DeprecatedAttributeAssignment: - 'solargraph.gemspec' - 'spec/fixtures/rdoc-lib/rdoc-lib.gemspec' -# Offense count: 13 # Configuration parameters: EnforcedStyle, AllowedGems, Include. # SupportedStyles: Gemfile, gems.rb, gemspec # Include: **/*.gemspec, **/Gemfile, **/gems.rb @@ -31,7 +28,6 @@ Gemspec/DevelopmentDependencies: Exclude: - 'solargraph.gemspec' -# Offense count: 1 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: TreatCommentsAsGroupSeparators, ConsiderPunctuation, Include. # Include: **/*.gemspec @@ -39,7 +35,6 @@ Gemspec/OrderedDependencies: Exclude: - 'solargraph.gemspec' -# Offense count: 3 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: Severity, Include. # Include: **/*.gemspec @@ -49,7 +44,6 @@ Gemspec/RequireMFA: - 'spec/fixtures/rdoc-lib/rdoc-lib.gemspec' - 'spec/fixtures/rubocop-custom-version/specifications/rubocop-0.0.0.gemspec' -# Offense count: 3 # Configuration parameters: Severity, Include. # Include: **/*.gemspec Gemspec/RequiredRubyVersion: @@ -58,7 +52,6 @@ Gemspec/RequiredRubyVersion: - 'spec/fixtures/rubocop-custom-version/specifications/rubocop-0.0.0.gemspec' - 'spec/fixtures/vendored/vendor/do_not_use.gemspec' -# Offense count: 8 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, IndentationWidth. # SupportedStyles: with_first_argument, with_fixed_indentation @@ -68,7 +61,6 @@ Layout/ArgumentAlignment: - 'lib/solargraph/pin/callable.rb' - 'spec/source/source_chainer_spec.rb' -# Offense count: 1 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyleAlignWith. # SupportedStylesAlignWith: either, start_of_block, start_of_line @@ -76,14 +68,12 @@ Layout/BlockAlignment: Exclude: - 'spec/source_map/mapper_spec.rb' -# Offense count: 2 # This cop supports safe autocorrection (--autocorrect). Layout/ClosingHeredocIndentation: Exclude: - 'spec/diagnostics/rubocop_spec.rb' - 'spec/rbs_map/conversions_spec.rb' -# Offense count: 8 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowForAlignment. Layout/CommentIndentation: @@ -93,7 +83,6 @@ Layout/CommentIndentation: - 'lib/solargraph/parser/parser_gem/node_methods.rb' - 'lib/solargraph/source_map/mapper.rb' -# Offense count: 23 # This cop supports safe autocorrection (--autocorrect). Layout/ElseAlignment: Exclude: @@ -115,7 +104,6 @@ Layout/ElseAlignment: - 'lib/solargraph/type_checker/rules.rb' - 'lib/solargraph/yard_map/mapper.rb' -# Offense count: 3 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EmptyLineBetweenMethodDefs, EmptyLineBetweenClassDefs, EmptyLineBetweenModuleDefs, DefLikeMacros, AllowAdjacentOneLineDefs, NumberOfEmptyLines. Layout/EmptyLineBetweenDefs: @@ -124,7 +112,6 @@ Layout/EmptyLineBetweenDefs: - 'lib/solargraph/language_server/message/initialize.rb' - 'lib/solargraph/pin/delegated_method.rb' -# Offense count: 13 # This cop supports safe autocorrection (--autocorrect). Layout/EmptyLines: Exclude: @@ -142,7 +129,6 @@ Layout/EmptyLines: - 'spec/pin/symbol_spec.rb' - 'spec/type_checker/levels/strict_spec.rb' -# Offense count: 1 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: empty_lines, empty_lines_except_namespace, empty_lines_special, no_empty_lines, beginning_only, ending_only @@ -150,7 +136,6 @@ Layout/EmptyLinesAroundClassBody: Exclude: - 'lib/solargraph/rbs_map/core_map.rb' -# Offense count: 1 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: empty_lines, empty_lines_except_namespace, empty_lines_special, no_empty_lines @@ -158,7 +143,6 @@ Layout/EmptyLinesAroundModuleBody: Exclude: - 'lib/solargraph/api_map/source_to_yard.rb' -# Offense count: 22 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyleAlignWith, Severity. # SupportedStylesAlignWith: keyword, variable, start_of_line @@ -182,7 +166,6 @@ Layout/EndAlignment: - 'lib/solargraph/type_checker/rules.rb' - 'lib/solargraph/yard_map/mapper.rb' -# Offense count: 4 # Configuration parameters: EnforcedStyle. # SupportedStyles: native, lf, crlf Layout/EndOfLine: @@ -192,7 +175,6 @@ Layout/EndOfLine: - 'lib/solargraph/source/encoding_fixes.rb' - 'solargraph.gemspec' -# Offense count: 7 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowForAlignment, AllowBeforeTrailingComments, ForceEqualSignAlignment. Layout/ExtraSpacing: @@ -204,7 +186,6 @@ Layout/ExtraSpacing: - 'lib/solargraph/type_checker.rb' - 'spec/spec_helper.rb' -# Offense count: 2 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, IndentationWidth. # SupportedStyles: consistent, consistent_relative_to_receiver, special_for_inner_method_call, special_for_inner_method_call_in_parentheses @@ -213,7 +194,6 @@ Layout/FirstArgumentIndentation: - 'lib/solargraph/parser/parser_gem/node_processors/args_node.rb' - 'spec/source/source_chainer_spec.rb' -# Offense count: 18 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, IndentationWidth. # SupportedStyles: special_inside_parentheses, consistent, align_brackets @@ -225,7 +205,6 @@ Layout/FirstArrayElementIndentation: - 'spec/source/source_chainer_spec.rb' - 'spec/source_spec.rb' -# Offense count: 65 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, IndentationWidth. # SupportedStyles: special_inside_parentheses, consistent, align_braces @@ -247,7 +226,6 @@ Layout/FirstHashElementIndentation: - 'spec/language_server/message/text_document/type_definition_spec.rb' - 'spec/language_server/message/workspace/did_change_watched_files_spec.rb' -# Offense count: 4 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowMultipleStyles, EnforcedHashRocketStyle, EnforcedColonStyle, EnforcedLastArgumentHashStyle. # SupportedHashRocketStyles: key, separator, table @@ -258,7 +236,6 @@ Layout/HashAlignment: - 'lib/solargraph/convention/struct_definition.rb' - 'lib/solargraph/workspace/config.rb' -# Offense count: 3 # This cop supports safe autocorrection (--autocorrect). Layout/HeredocIndentation: Exclude: @@ -266,7 +243,6 @@ Layout/HeredocIndentation: - 'spec/rbs_map/conversions_spec.rb' - 'spec/yard_map/mapper/to_method_spec.rb' -# Offense count: 24 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: Width, AllowedPatterns. Layout/IndentationWidth: @@ -291,7 +267,6 @@ Layout/IndentationWidth: - 'spec/api_map/config_spec.rb' - 'spec/source_map/mapper_spec.rb' -# Offense count: 5 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowDoxygenCommentStyle, AllowGemfileRubyComment, AllowRBSInlineAnnotation, AllowSteepAnnotation. Layout/LeadingCommentSpace: @@ -301,7 +276,6 @@ Layout/LeadingCommentSpace: - 'lib/solargraph/source/chain/call.rb' - 'lib/solargraph/source_map/clip.rb' -# Offense count: 1 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: space, no_space @@ -309,7 +283,6 @@ Layout/LineContinuationSpacing: Exclude: - 'lib/solargraph/diagnostics/rubocop_helpers.rb' -# Offense count: 3 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: symmetrical, new_line, same_line @@ -318,7 +291,6 @@ Layout/MultilineMethodCallBraceLayout: - 'lib/solargraph/parser/parser_gem/node_processors/send_node.rb' - 'spec/source/source_chainer_spec.rb' -# Offense count: 12 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, IndentationWidth. # SupportedStyles: aligned, indented, indented_relative_to_receiver @@ -332,7 +304,6 @@ Layout/MultilineMethodCallIndentation: - 'lib/solargraph/pin/search.rb' - 'lib/solargraph/type_checker.rb' -# Offense count: 6 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, IndentationWidth. # SupportedStyles: aligned, indented @@ -342,13 +313,11 @@ Layout/MultilineOperationIndentation: - 'lib/solargraph/language_server/host/dispatch.rb' - 'lib/solargraph/source.rb' -# Offense count: 6 # This cop supports safe autocorrection (--autocorrect). Layout/SpaceAfterComma: Exclude: - 'spec/source/cursor_spec.rb' -# Offense count: 6 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: space, no_space @@ -361,13 +330,11 @@ Layout/SpaceAroundEqualsInParameterDefault: - 'lib/solargraph/pin/local_variable.rb' - 'lib/solargraph/pin/parameter.rb' -# Offense count: 1 # This cop supports safe autocorrection (--autocorrect). Layout/SpaceAroundKeyword: Exclude: - 'spec/rbs_map/conversions_spec.rb' -# Offense count: 28 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowForAlignment, EnforcedStyleForExponentOperator, EnforcedStyleForRationalLiterals. # SupportedStylesForExponentOperator: space, no_space @@ -386,7 +353,6 @@ Layout/SpaceAroundOperators: - 'spec/library_spec.rb' - 'spec/yard_map/mapper_spec.rb' -# Offense count: 105 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces. # SupportedStyles: space, no_space @@ -425,13 +391,11 @@ Layout/SpaceBeforeBlockBraces: - 'spec/source_map_spec.rb' - 'spec/source_spec.rb' -# Offense count: 1 # This cop supports safe autocorrection (--autocorrect). Layout/SpaceBeforeComma: Exclude: - 'spec/source/cursor_spec.rb' -# Offense count: 177 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces, SpaceBeforeBlockParameters. # SupportedStyles: space, no_space @@ -460,7 +424,6 @@ Layout/SpaceInsideBlockBraces: - 'spec/source_map/mapper_spec.rb' - 'spec/source_spec.rb' -# Offense count: 10 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces. # SupportedStyles: space, no_space, compact @@ -473,7 +436,6 @@ Layout/SpaceInsideHashLiteralBraces: - 'spec/language_server/host/message_worker_spec.rb' - 'spec/language_server/message/extended/check_gem_version_spec.rb' -# Offense count: 2 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: space, compact, no_space @@ -482,7 +444,6 @@ Layout/SpaceInsideParens: - 'lib/solargraph/pin/namespace.rb' - 'lib/solargraph/source_map.rb' -# Offense count: 1 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: final_newline, final_blank_line @@ -490,7 +451,6 @@ Layout/TrailingEmptyLines: Exclude: - 'spec/convention/struct_definition_spec.rb' -# Offense count: 5 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowInHeredoc. Layout/TrailingWhitespace: @@ -500,7 +460,6 @@ Layout/TrailingWhitespace: - 'spec/convention/struct_definition_spec.rb' - 'spec/convention_spec.rb' -# Offense count: 2 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowedMethods, AllowedPatterns. Lint/AmbiguousBlockAssociation: @@ -508,7 +467,6 @@ Lint/AmbiguousBlockAssociation: - 'lib/solargraph/api_map.rb' - 'lib/solargraph/language_server/host.rb' -# Offense count: 15 # This cop supports safe autocorrection (--autocorrect). Lint/AmbiguousOperator: Exclude: @@ -519,14 +477,12 @@ Lint/AmbiguousOperator: - 'lib/solargraph/pin/constant.rb' - 'lib/solargraph/pin/method.rb' -# Offense count: 5 # This cop supports safe autocorrection (--autocorrect). Lint/AmbiguousOperatorPrecedence: Exclude: - 'lib/solargraph/pin/method.rb' - 'lib/solargraph/source.rb' -# Offense count: 18 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: RequireParenthesesForMethodChains. Lint/AmbiguousRange: @@ -540,19 +496,16 @@ Lint/AmbiguousRange: - 'lib/solargraph/source_map/clip.rb' - 'spec/library_spec.rb' -# Offense count: 1 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: AllowSafeAssignment. Lint/AssignmentInCondition: Exclude: - 'lib/solargraph/library.rb' -# Offense count: 3 Lint/BinaryOperatorWithIdenticalOperands: Exclude: - 'lib/solargraph/api_map/source_to_yard.rb' -# Offense count: 5 # This cop supports unsafe autocorrection (--autocorrect-all). Lint/BooleanSymbol: Exclude: @@ -560,14 +513,12 @@ Lint/BooleanSymbol: - 'lib/solargraph/parser/parser_gem/node_methods.rb' - 'lib/solargraph/source/chain/literal.rb' -# Offense count: 1 # Configuration parameters: AllowedMethods. # AllowedMethods: enums Lint/ConstantDefinitionInBlock: Exclude: - 'spec/complex_type_spec.rb' -# Offense count: 8 # Configuration parameters: IgnoreLiteralBranches, IgnoreConstantBranches, IgnoreDuplicateElseBranch. Lint/DuplicateBranch: Exclude: @@ -576,7 +527,6 @@ Lint/DuplicateBranch: - 'lib/solargraph/pin/base.rb' - 'lib/solargraph/rbs_map/conversions.rb' -# Offense count: 9 Lint/DuplicateMethods: Exclude: - 'lib/solargraph/complex_type.rb' @@ -588,13 +538,11 @@ Lint/DuplicateMethods: - 'lib/solargraph/rbs_map/core_map.rb' - 'lib/solargraph/source/chain/link.rb' -# Offense count: 1 # Configuration parameters: AllowComments, AllowEmptyLambdas. Lint/EmptyBlock: Exclude: - 'spec/convention_spec.rb' -# Offense count: 6 # Configuration parameters: AllowComments. Lint/EmptyClass: Exclude: @@ -605,13 +553,11 @@ Lint/EmptyClass: - 'spec/fixtures/workspace_folders/folder1/app.rb' - 'spec/fixtures/workspace_folders/folder2/app.rb' -# Offense count: 1 # Configuration parameters: AllowComments. Lint/EmptyFile: Exclude: - 'spec/fixtures/vendored/vendor/do_not_use.gemspec' -# Offense count: 6 # This cop supports unsafe autocorrection (--autocorrect-all). Lint/InterpolationCheck: Exclude: @@ -620,7 +566,6 @@ Lint/InterpolationCheck: - 'spec/source/chain_spec.rb' - 'spec/source/cursor_spec.rb' -# Offense count: 5 # Configuration parameters: AllowedParentClasses. Lint/MissingSuper: Exclude: @@ -630,13 +575,11 @@ Lint/MissingSuper: - 'lib/solargraph/source/chain/literal.rb' - 'lib/solargraph/source/chain/or.rb' -# Offense count: 2 # This cop supports unsafe autocorrection (--autocorrect-all). Lint/NonAtomicFileOperation: Exclude: - 'spec/diagnostics/rubocop_spec.rb' -# Offense count: 4 # This cop supports safe autocorrection (--autocorrect). Lint/ParenthesesAsGroupedExpression: Exclude: @@ -645,13 +588,11 @@ Lint/ParenthesesAsGroupedExpression: - 'spec/language_server/host_spec.rb' - 'spec/source_map/clip_spec.rb' -# Offense count: 1 # This cop supports safe autocorrection (--autocorrect). Lint/RedundantRequireStatement: Exclude: - 'spec/language_server/protocol_spec.rb' -# Offense count: 2 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: AllowedMethods, InferNonNilReceiver, AdditionalNilMethods. # AllowedMethods: instance_of?, kind_of?, is_a?, eql?, respond_to?, equal? @@ -661,7 +602,6 @@ Lint/RedundantSafeNavigation: - 'lib/solargraph/api_map/source_to_yard.rb' - 'lib/solargraph/rbs_map.rb' -# Offense count: 6 # This cop supports safe autocorrection (--autocorrect). Lint/RedundantStringCoercion: Exclude: @@ -671,19 +611,16 @@ Lint/RedundantStringCoercion: - 'lib/solargraph/pin/namespace.rb' - 'lib/solargraph/rbs_map/conversions.rb' -# Offense count: 1 # This cop supports safe autocorrection (--autocorrect). Lint/RedundantWithIndex: Exclude: - 'lib/solargraph/language_server/message/completion_item/resolve.rb' -# Offense count: 1 # Configuration parameters: AllowComments, AllowNil. Lint/SuppressedException: Exclude: - 'Rakefile' -# Offense count: 5 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: strict, consistent @@ -691,25 +628,21 @@ Lint/SymbolConversion: Exclude: - 'lib/solargraph/pin/base.rb' -# Offense count: 1 # Configuration parameters: AllowKeywordBlockArguments. Lint/UnderscorePrefixedVariableName: Exclude: - 'lib/solargraph/library.rb' -# Offense count: 2 # Configuration parameters: Methods. Lint/UnexpectedBlockArity: Exclude: - 'lib/solargraph/language_server/message/completion_item/resolve.rb' - 'lib/solargraph/type_checker.rb' -# Offense count: 1 Lint/UnmodifiedReduceAccumulator: Exclude: - 'lib/solargraph/pin/method.rb' -# Offense count: 4 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AutoCorrect, IgnoreEmptyBlocks, AllowUnusedKeywordArguments. Lint/UnusedBlockArgument: @@ -718,7 +651,6 @@ Lint/UnusedBlockArgument: - 'lib/solargraph/logging.rb' - 'spec/language_server/transport/data_reader_spec.rb' -# Offense count: 38 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AutoCorrect, AllowUnusedKeywordArguments, IgnoreEmptyMethods, IgnoreNotImplementedMethods, NotImplementedExceptions. # NotImplementedExceptions: NotImplementedError @@ -748,14 +680,12 @@ Lint/UnusedMethodArgument: - 'lib/solargraph/source/chain/variable.rb' - 'lib/solargraph/source/chain/z_super.rb' -# Offense count: 1 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AutoCorrect, ContextCreatingMethods, MethodCreatingMethods. Lint/UselessAccessModifier: Exclude: - 'lib/solargraph/api_map.rb' -# Offense count: 41 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AutoCorrect. Lint/UselessAssignment: @@ -784,19 +714,16 @@ Lint/UselessAssignment: - 'spec/source/chain/call_spec.rb' - 'spec/source_map/mapper_spec.rb' -# Offense count: 2 Lint/UselessConstantScoping: Exclude: - 'lib/solargraph/rbs_map/conversions.rb' -# Offense count: 1 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: AutoCorrect. Lint/UselessMethodDefinition: Exclude: - 'lib/solargraph/pin/signature.rb' -# Offense count: 22 # Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes, Max. Metrics/AbcSize: Exclude: @@ -818,7 +745,6 @@ Metrics/AbcSize: - 'lib/solargraph/source_map/mapper.rb' - 'lib/solargraph/type_checker.rb' -# Offense count: 12 # Configuration parameters: CountComments, Max, CountAsOne, AllowedMethods, AllowedPatterns, inherit_mode. # AllowedMethods: refine Metrics/BlockLength: @@ -830,7 +756,6 @@ Metrics/BlockLength: - 'lib/solargraph/source/chain/call.rb' - 'lib/solargraph/type_checker.rb' -# Offense count: 13 # Configuration parameters: CountBlocks, CountModifierForms, Max. Metrics/BlockNesting: Exclude: @@ -838,7 +763,6 @@ Metrics/BlockNesting: - 'lib/solargraph/source_map/clip.rb' - 'lib/solargraph/type_checker.rb' -# Offense count: 4 # Configuration parameters: CountComments, Max, CountAsOne. Metrics/ClassLength: Exclude: @@ -847,7 +771,6 @@ Metrics/ClassLength: - 'lib/solargraph/rbs_map/conversions.rb' - 'lib/solargraph/type_checker.rb' -# Offense count: 11 # Configuration parameters: AllowedMethods, AllowedPatterns, Max. Metrics/CyclomaticComplexity: Exclude: @@ -861,7 +784,6 @@ Metrics/CyclomaticComplexity: - 'lib/solargraph/source_map/mapper.rb' - 'lib/solargraph/type_checker.rb' -# Offense count: 7 # Configuration parameters: CountComments, Max, CountAsOne, AllowedMethods, AllowedPatterns. Metrics/MethodLength: Exclude: @@ -873,7 +795,6 @@ Metrics/MethodLength: - 'lib/solargraph/source_map/mapper.rb' - 'lib/solargraph/type_checker.rb' -# Offense count: 4 # Configuration parameters: CountComments, Max, CountAsOne. Metrics/ModuleLength: Exclude: @@ -881,7 +802,6 @@ Metrics/ModuleLength: - 'lib/solargraph/parser/parser_gem/node_methods.rb' - 'lib/solargraph/pin_cache.rb' -# Offense count: 6 # Configuration parameters: Max, CountKeywordArgs, MaxOptionalParameters. Metrics/ParameterLists: Exclude: @@ -891,7 +811,6 @@ Metrics/ParameterLists: - 'lib/solargraph/yard_map/mapper/to_method.rb' - 'lib/solargraph/yard_map/to_method.rb' -# Offense count: 9 # Configuration parameters: AllowedMethods, AllowedPatterns, Max. Metrics/PerceivedComplexity: Exclude: @@ -903,27 +822,23 @@ Metrics/PerceivedComplexity: - 'lib/solargraph/source_map/mapper.rb' - 'lib/solargraph/type_checker.rb' -# Offense count: 5 Naming/AccessorMethodName: Exclude: - 'lib/solargraph/api_map.rb' - 'lib/solargraph/api_map/store.rb' - 'lib/solargraph/language_server/message/base.rb' -# Offense count: 1 # Configuration parameters: AsciiConstants. Naming/AsciiIdentifiers: Exclude: - 'spec/fixtures/unicode.rb' -# Offense count: 1 # Configuration parameters: ForbiddenDelimiters. # ForbiddenDelimiters: (?i-mx:(^|\s)(EO[A-Z]{1}|END)(\s|$)) Naming/HeredocDelimiterNaming: Exclude: - 'spec/yard_map/mapper/to_method_spec.rb' -# Offense count: 9 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: EnforcedStyleForLeadingUnderscores. # SupportedStylesForLeadingUnderscores: disallowed, required, optional @@ -937,7 +852,6 @@ Naming/MemoizedInstanceVariableName: - 'lib/solargraph/rbs_map.rb' - 'lib/solargraph/workspace.rb' -# Offense count: 16 # Configuration parameters: MinNameLength, AllowNamesEndingInNumbers, AllowedNames, ForbiddenNames. # AllowedNames: as, at, by, cc, db, id, if, in, io, ip, of, on, os, pp, to Naming/MethodParameterName: @@ -949,7 +863,6 @@ Naming/MethodParameterName: - 'lib/solargraph/yard_map/mapper/to_method.rb' - 'lib/solargraph/yard_map/to_method.rb' -# Offense count: 14 # Configuration parameters: Mode, AllowedMethods, AllowedPatterns, AllowBangMethods, WaywardPredicates. # AllowedMethods: call # WaywardPredicates: nonzero? @@ -967,7 +880,6 @@ Naming/PredicateMethod: - 'lib/solargraph/pin/local_variable.rb' - 'lib/solargraph/workspace.rb' -# Offense count: 6 # Configuration parameters: NamePrefix, ForbiddenPrefixes, AllowedMethods, MethodDefinitionMacros, UseSorbetSigs. # NamePrefix: is_, has_, have_, does_ # ForbiddenPrefixes: is_, has_, have_, does_ @@ -982,34 +894,29 @@ Naming/PredicatePrefix: - 'lib/solargraph/pin_cache.rb' - 'lib/solargraph/workspace.rb' -# Offense count: 1 # Configuration parameters: EnforcedStyle, AllowedIdentifiers, AllowedPatterns, ForbiddenIdentifiers, ForbiddenPatterns. # SupportedStyles: snake_case, camelCase Naming/VariableName: Exclude: - 'spec/fixtures/unicode.rb' -# Offense count: 3 RSpec/Be: Exclude: - 'spec/rbs_map/stdlib_map_spec.rb' - 'spec/rbs_map_spec.rb' - 'spec/source/source_chainer_spec.rb' -# Offense count: 3 # This cop supports unsafe autocorrection (--autocorrect-all). RSpec/BeEq: Exclude: - 'spec/complex_type_spec.rb' - 'spec/pin/method_spec.rb' -# Offense count: 2 # This cop supports unsafe autocorrection (--autocorrect-all). RSpec/BeEql: Exclude: - 'spec/convention/struct_definition_spec.rb' -# Offense count: 7 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: be, be_nil @@ -1018,7 +925,6 @@ RSpec/BeNil: - 'spec/api_map/source_to_yard_spec.rb' - 'spec/language_server/host_spec.rb' -# Offense count: 5 RSpec/BeforeAfterAll: Exclude: - '**/spec/spec_helper.rb' @@ -1029,7 +935,6 @@ RSpec/BeforeAfterAll: - 'spec/language_server/host/dispatch_spec.rb' - 'spec/language_server/protocol_spec.rb' -# Offense count: 25 # Configuration parameters: Prefixes, AllowedPatterns. # Prefixes: when, with, without RSpec/ContextWording: @@ -1044,7 +949,6 @@ RSpec/ContextWording: - 'spec/type_checker/levels/strong_spec.rb' - 'spec/type_checker/levels/typed_spec.rb' -# Offense count: 2 # Configuration parameters: IgnoredMetadata. RSpec/DescribeClass: Exclude: @@ -1056,7 +960,6 @@ RSpec/DescribeClass: - 'spec/complex_type_spec.rb' - 'spec/source_map/node_processor_spec.rb' -# Offense count: 412 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: SkipBlocks, EnforcedStyle, OnlyStaticConstants. # SupportedStyles: described_class, explicit @@ -1133,20 +1036,17 @@ RSpec/DescribedClass: - 'spec/yard_map/mapper/to_method_spec.rb' - 'spec/yard_map/mapper_spec.rb' -# Offense count: 1 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: AutoCorrect. RSpec/EmptyExampleGroup: Exclude: - 'spec/convention_spec.rb' -# Offense count: 1 # This cop supports safe autocorrection (--autocorrect). RSpec/EmptyLineAfterFinalLet: Exclude: - 'spec/workspace/config_spec.rb' -# Offense count: 89 # Configuration parameters: Max, CountAsOne. RSpec/ExampleLength: Exclude: @@ -1177,7 +1077,6 @@ RSpec/ExampleLength: - 'spec/type_checker/levels/strict_spec.rb' - 'spec/type_checker_spec.rb' -# Offense count: 10 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: CustomTransform, IgnoredWords, DisallowedExamples. # DisallowedExamples: works @@ -1187,14 +1086,12 @@ RSpec/ExampleWording: - 'spec/pin/base_spec.rb' - 'spec/pin/method_spec.rb' -# Offense count: 2 # This cop supports safe autocorrection (--autocorrect). RSpec/ExcessiveDocstringSpacing: Exclude: - 'spec/rbs_map/conversions_spec.rb' - 'spec/source/chain/call_spec.rb' -# Offense count: 5 # This cop supports safe autocorrection (--autocorrect). RSpec/ExpectActual: Exclude: @@ -1202,7 +1099,6 @@ RSpec/ExpectActual: - 'spec/rbs_map/stdlib_map_spec.rb' - 'spec/source_map/mapper_spec.rb' -# Offense count: 13 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: implicit, each, example @@ -1217,14 +1113,12 @@ RSpec/HookArgument: - 'spec/workspace/config_spec.rb' - 'spec/workspace_spec.rb' -# Offense count: 2 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: . # SupportedStyles: is_expected, should RSpec/ImplicitExpect: EnforcedStyle: should -# Offense count: 247 # Configuration parameters: AssignmentOnly. RSpec/InstanceVariable: Exclude: @@ -1235,36 +1129,30 @@ RSpec/InstanceVariable: - 'spec/language_server/host_spec.rb' - 'spec/language_server/protocol_spec.rb' -# Offense count: 1 # This cop supports safe autocorrection (--autocorrect). RSpec/LeadingSubject: Exclude: - 'spec/rbs_map/conversions_spec.rb' -# Offense count: 1 RSpec/LeakyConstantDeclaration: Exclude: - 'spec/complex_type_spec.rb' -# Offense count: 3 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AutoCorrect. RSpec/LetBeforeExamples: Exclude: - 'spec/complex_type_spec.rb' -# Offense count: 3 # Configuration parameters: . # SupportedStyles: have_received, receive RSpec/MessageSpies: EnforcedStyle: receive -# Offense count: 2 RSpec/MissingExampleGroupArgument: Exclude: - 'spec/diagnostics/rubocop_helpers_spec.rb' -# Offense count: 481 # Configuration parameters: Max. RSpec/MultipleExpectations: Exclude: @@ -1331,13 +1219,11 @@ RSpec/MultipleExpectations: - 'spec/yard_map/mapper/to_method_spec.rb' - 'spec/yard_map/mapper_spec.rb' -# Offense count: 1 # Configuration parameters: Max, AllowedGroups. RSpec/NestedGroups: Exclude: - 'spec/complex_type_spec.rb' -# Offense count: 8 # Configuration parameters: AllowedPatterns. # AllowedPatterns: ^expect_, ^assert_ RSpec/NoExpectationExample: @@ -1350,7 +1236,6 @@ RSpec/NoExpectationExample: - 'spec/type_checker/checks_spec.rb' - 'spec/type_checker/levels/typed_spec.rb' -# Offense count: 2 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: not_to, to_not @@ -1359,7 +1244,6 @@ RSpec/NotToNot: - 'spec/api_map_spec.rb' - 'spec/rbs_map/core_map_spec.rb' -# Offense count: 28 RSpec/PendingWithoutReason: Exclude: - 'spec/api_map_spec.rb' @@ -1374,7 +1258,6 @@ RSpec/PendingWithoutReason: - 'spec/type_checker/levels/strict_spec.rb' - 'spec/yard_map/mapper/to_method_spec.rb' -# Offense count: 3 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: Strict, EnforcedStyle, AllowedExplicitMatchers. # SupportedStyles: inflected, explicit @@ -1383,18 +1266,15 @@ RSpec/PredicateMatcher: - 'spec/language_server/message/workspace/did_change_configuration_spec.rb' - 'spec/source_spec.rb' -# Offense count: 3 # This cop supports unsafe autocorrection (--autocorrect-all). RSpec/ReceiveMessages: Exclude: - 'spec/language_server/host_spec.rb' -# Offense count: 1 RSpec/RemoveConst: Exclude: - 'spec/diagnostics/rubocop_helpers_spec.rb' -# Offense count: 50 RSpec/RepeatedDescription: Exclude: - 'spec/api_map_spec.rb' @@ -1407,7 +1287,6 @@ RSpec/RepeatedDescription: - 'spec/type_checker/levels/normal_spec.rb' - 'spec/type_checker/levels/strict_spec.rb' -# Offense count: 30 RSpec/RepeatedExample: Exclude: - 'spec/api_map_spec.rb' @@ -1416,14 +1295,12 @@ RSpec/RepeatedExample: - 'spec/source_map/clip_spec.rb' - 'spec/type_checker/levels/strict_spec.rb' -# Offense count: 2 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AutoCorrect. RSpec/ScatteredLet: Exclude: - 'spec/complex_type_spec.rb' -# Offense count: 89 # Configuration parameters: Include, CustomTransform, IgnoreMethods, IgnoreMetadata. # Include: **/*_spec.rb RSpec/SpecFilePathFormat: @@ -1519,12 +1396,10 @@ RSpec/SpecFilePathFormat: - 'spec/yard_map/mapper/to_method_spec.rb' - 'spec/yard_map/mapper_spec.rb' -# Offense count: 1 RSpec/StubbedMock: Exclude: - 'spec/language_server/host/message_worker_spec.rb' -# Offense count: 23 # Configuration parameters: IgnoreNameless, IgnoreSymbolicNames. RSpec/VerifiedDoubles: Exclude: @@ -1542,12 +1417,10 @@ RSpec/VerifiedDoubles: - 'spec/source/source_chainer_spec.rb' - 'spec/workspace_spec.rb' -# Offense count: 1 Security/MarshalLoad: Exclude: - 'lib/solargraph/pin_cache.rb' -# Offense count: 13 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: EnforcedStyle, AllowModifiersOnSymbols, AllowModifiersOnAttrs, AllowModifiersOnAliasMethod. # SupportedStyles: inline, group @@ -1567,7 +1440,6 @@ Style/AccessModifierDeclarations: - 'lib/solargraph/source/chain/link.rb' - 'lib/solargraph/source/chain/literal.rb' -# Offense count: 13 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: separated, grouped @@ -1579,7 +1451,6 @@ Style/AccessorGrouping: - 'lib/solargraph/pin/method.rb' - 'lib/solargraph/rbs_map.rb' -# Offense count: 38 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: EnforcedStyle. # SupportedStyles: always, conditionals @@ -1600,7 +1471,6 @@ Style/AndOr: - 'lib/solargraph/workspace.rb' - 'lib/solargraph/yard_map/mapper.rb' -# Offense count: 4 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowOnlyRestArgument, UseAnonymousForwarding, RedundantRestArgumentNames, RedundantKeywordRestArgumentNames, RedundantBlockArgumentNames. # RedundantRestArgumentNames: args, arguments @@ -1611,7 +1481,6 @@ Style/ArgumentsForwarding: - 'lib/solargraph/api_map.rb' - 'lib/solargraph/complex_type.rb' -# Offense count: 52 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, ProceduralMethods, FunctionalMethods, AllowedMethods, AllowedPatterns, AllowBracesOnProceduralOneLiners, BracesRequiredMethods. # SupportedStyles: line_count_based, semantic, braces_for_chaining, always_braces @@ -1647,7 +1516,6 @@ Style/BlockDelimiters: - 'spec/workspace_spec.rb' - 'spec/yard_map/mapper/to_method_spec.rb' -# Offense count: 7 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: MinBranchesCount. Style/CaseLikeIf: @@ -1659,7 +1527,6 @@ Style/CaseLikeIf: - 'lib/solargraph/source/source_chainer.rb' - 'lib/solargraph/yard_map/mapper.rb' -# Offense count: 11 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: EnforcedStyle, EnforcedStyleForClasses, EnforcedStyleForModules. # SupportedStyles: nested, compact @@ -1679,7 +1546,6 @@ Style/ClassAndModuleChildren: - 'lib/solargraph/language_server/message/workspace/did_change_workspace_folders.rb' - 'lib/solargraph/language_server/message/workspace/workspace_symbol.rb' -# Offense count: 5 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: AllowedMethods, AllowedPatterns. # AllowedMethods: ==, equal?, eql? @@ -1688,32 +1554,27 @@ Style/ClassEqualityComparison: - 'lib/solargraph/gem_pins.rb' - 'lib/solargraph/pin/base.rb' -# Offense count: 2 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: AllowedReceivers. Style/CollectionCompact: Exclude: - 'lib/solargraph/pin/constant.rb' -# Offense count: 1 # This cop supports safe autocorrection (--autocorrect). Style/ColonMethodCall: Exclude: - 'spec/type_checker_spec.rb' -# Offense count: 1 # This cop supports unsafe autocorrection (--autocorrect-all). Style/CombinableLoops: Exclude: - 'lib/solargraph/pin/parameter.rb' -# Offense count: 1 # This cop supports unsafe autocorrection (--autocorrect-all). Style/ConcatArrayLiterals: Exclude: - 'lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb' -# Offense count: 3 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, SingleLineConditionsOnly, IncludeTernaryExpressions. # SupportedStyles: assign_to_condition, assign_inside_condition @@ -1722,7 +1583,6 @@ Style/ConditionalAssignment: - 'lib/solargraph/parser/parser_gem/node_processors/defs_node.rb' - 'lib/solargraph/source/chain/call.rb' -# Offense count: 141 # Configuration parameters: AllowedConstants. Style/Documentation: Exclude: @@ -1865,13 +1725,11 @@ Style/Documentation: - 'lib/solargraph/yard_map/mapper/to_namespace.rb' - 'lib/solargraph/yard_map/to_method.rb' -# Offense count: 1 # This cop supports safe autocorrection (--autocorrect). Style/EmptyLambdaParameter: Exclude: - 'spec/rbs_map/core_map_spec.rb' -# Offense count: 5 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AutoCorrect, EnforcedStyle. # SupportedStyles: compact, expanded @@ -1883,7 +1741,6 @@ Style/EmptyMethod: - 'spec/fixtures/rdoc-lib/lib/example.rb' - 'spec/fixtures/workspace-with-gemfile/lib/thing.rb' -# Offense count: 5 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: trailing_conditional, ternary @@ -1893,13 +1750,11 @@ Style/EmptyStringInsideInterpolation: - 'lib/solargraph/pin/documenting.rb' - 'lib/solargraph/shell.rb' -# Offense count: 1 # This cop supports safe autocorrection (--autocorrect). Style/ExpandPathArguments: Exclude: - 'solargraph.gemspec' -# Offense count: 2 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowedVars, DefaultToNil. Style/FetchEnvVar: @@ -1907,7 +1762,6 @@ Style/FetchEnvVar: - 'spec/api_map/config_spec.rb' - 'spec/spec_helper.rb' -# Offense count: 2 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: EnforcedStyle. # SupportedStyles: left_coerce, right_coerce, single_coerce, fdiv @@ -1916,7 +1770,6 @@ Style/FloatDivision: - 'lib/solargraph/library.rb' - 'lib/solargraph/pin/search.rb' -# Offense count: 127 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: EnforcedStyle. # SupportedStyles: always, always_true, never @@ -2051,7 +1904,6 @@ Style/FrozenStringLiteralComment: - 'spec/yard_map/mapper/to_method_spec.rb' - 'spec/yard_map/mapper_spec.rb' -# Offense count: 11 # This cop supports unsafe autocorrection (--autocorrect-all). Style/GlobalStdStream: Exclude: @@ -2060,7 +1912,6 @@ Style/GlobalStdStream: - 'lib/solargraph/shell.rb' - 'spec/logging_spec.rb' -# Offense count: 12 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: MinBodyLength, AllowConsecutiveConditionals. Style/GuardClause: @@ -2074,14 +1925,12 @@ Style/GuardClause: - 'lib/solargraph/source.rb' - 'lib/solargraph/workspace.rb' -# Offense count: 2 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: AllowSplatArgument. Style/HashConversion: Exclude: - 'lib/solargraph/doc_map.rb' -# Offense count: 2 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: AllowedReceivers. # AllowedReceivers: Thread.current @@ -2090,7 +1939,6 @@ Style/HashEachMethods: - 'lib/solargraph/library.rb' - 'lib/solargraph/source.rb' -# Offense count: 25 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, EnforcedShorthandSyntax, UseHashRocketsWithSymbolValues, PreferHashRocketsForNonAlnumEndingSymbols. # SupportedStyles: ruby19, hash_rockets, no_mixed_keys, ruby19_no_mixed_keys @@ -2101,14 +1949,12 @@ Style/HashSyntax: - 'spec/source/cursor_spec.rb' - 'spec/source/source_chainer_spec.rb' -# Offense count: 6 # This cop supports unsafe autocorrection (--autocorrect-all). Style/IdenticalConditionalBranches: Exclude: - 'lib/solargraph/library.rb' - 'lib/solargraph/type_checker.rb' -# Offense count: 7 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowIfModifier. Style/IfInsideElse: @@ -2120,7 +1966,6 @@ Style/IfInsideElse: - 'lib/solargraph/source_map/clip.rb' - 'lib/solargraph/type_checker.rb' -# Offense count: 63 # This cop supports safe autocorrection (--autocorrect). Style/IfUnlessModifier: Exclude: @@ -2160,7 +2005,6 @@ Style/IfUnlessModifier: - 'lib/solargraph/yard_map/helpers.rb' - 'lib/solargraph/yard_map/mapper/to_method.rb' -# Offense count: 2 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: call, braces @@ -2168,7 +2012,6 @@ Style/LambdaCall: Exclude: - 'lib/solargraph/library.rb' -# Offense count: 3 # This cop supports unsafe autocorrection (--autocorrect-all). Style/MapIntoArray: Exclude: @@ -2176,20 +2019,17 @@ Style/MapIntoArray: - 'lib/solargraph/parser/parser_gem/node_chainer.rb' - 'lib/solargraph/type_checker/param_def.rb' -# Offense count: 1 # This cop supports unsafe autocorrection (--autocorrect-all). Style/MapToHash: Exclude: - 'lib/solargraph/bench.rb' -# Offense count: 2 # This cop supports unsafe autocorrection (--autocorrect-all). Style/MapToSet: Exclude: - 'lib/solargraph/library.rb' - 'spec/source_map/clip_spec.rb' -# Offense count: 203 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: require_parentheses, require_no_parentheses, require_no_parentheses_except_multiline @@ -2256,24 +2096,20 @@ Style/MethodDefParentheses: - 'spec/type_checker/levels/strong_spec.rb' - 'spec/type_checker/levels/typed_spec.rb' -# Offense count: 1 Style/MultilineBlockChain: Exclude: - 'lib/solargraph/pin/search.rb' -# Offense count: 1 # This cop supports safe autocorrection (--autocorrect). Style/MultilineIfModifier: Exclude: - 'lib/solargraph/pin/callable.rb' -# Offense count: 1 # This cop supports safe autocorrection (--autocorrect). Style/MultilineTernaryOperator: Exclude: - 'lib/solargraph/language_server/host.rb' -# Offense count: 20 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowMethodComparison, ComparisonsThreshold. Style/MultipleComparison: @@ -2288,7 +2124,6 @@ Style/MultipleComparison: - 'lib/solargraph/source.rb' - 'lib/solargraph/type_checker.rb' -# Offense count: 17 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: EnforcedStyle. # SupportedStyles: literals, strict @@ -2302,7 +2137,6 @@ Style/MutableConstant: - 'lib/solargraph/yard_map/mapper/to_method.rb' - 'spec/complex_type_spec.rb' -# Offense count: 1 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: both, prefix, postfix @@ -2310,7 +2144,6 @@ Style/NegatedIf: Exclude: - 'lib/solargraph/language_server/host/diagnoser.rb' -# Offense count: 6 # This cop supports safe autocorrection (--autocorrect). Style/NegatedIfElseCondition: Exclude: @@ -2320,7 +2153,6 @@ Style/NegatedIfElseCondition: - 'lib/solargraph/shell.rb' - 'lib/solargraph/type_checker.rb' -# Offense count: 1 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowedMethods. # AllowedMethods: be, be_a, be_an, be_between, be_falsey, be_kind_of, be_instance_of, be_truthy, be_within, eq, eql, end_with, include, match, raise_error, respond_to, start_with @@ -2328,14 +2160,12 @@ Style/NestedParenthesizedCalls: Exclude: - 'lib/solargraph/type_checker.rb' -# Offense count: 2 # This cop supports safe autocorrection (--autocorrect). Style/NestedTernaryOperator: Exclude: - 'lib/solargraph/pin/conversions.rb' - 'lib/solargraph/pin/method.rb' -# Offense count: 5 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, MinBodyLength, AllowConsecutiveConditionals. # SupportedStyles: skip_modifier_ifs, always @@ -2346,7 +2176,6 @@ Style/Next: - 'lib/solargraph/source_map/clip.rb' - 'lib/solargraph/type_checker/checks.rb' -# Offense count: 11 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: MinDigits, Strict, AllowedNumbers, AllowedPatterns. Style/NumericLiterals: @@ -2354,7 +2183,6 @@ Style/NumericLiterals: - 'lib/solargraph/language_server/error_codes.rb' - 'spec/language_server/protocol_spec.rb' -# Offense count: 31 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: EnforcedStyle, AllowedMethods, AllowedPatterns. # SupportedStyles: predicate, comparison @@ -2378,12 +2206,10 @@ Style/NumericPredicate: - 'lib/solargraph/source/source_chainer.rb' - 'lib/solargraph/workspace.rb' -# Offense count: 1 Style/OpenStructUse: Exclude: - 'lib/solargraph/page.rb' -# Offense count: 8 # Configuration parameters: AllowedMethods. # AllowedMethods: respond_to_missing? Style/OptionalBooleanParameter: @@ -2396,7 +2222,6 @@ Style/OptionalBooleanParameter: - 'lib/solargraph/source/change.rb' - 'lib/solargraph/source/updater.rb' -# Offense count: 3 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowSafeAssignment, AllowInMultilineConditions. Style/ParenthesesAroundCondition: @@ -2404,7 +2229,6 @@ Style/ParenthesesAroundCondition: - 'lib/solargraph/parser/parser_gem/node_processors/send_node.rb' - 'lib/solargraph/type_checker.rb' -# Offense count: 1 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: EnforcedStyle. # SupportedStyles: short, verbose @@ -2412,21 +2236,18 @@ Style/PreferredHashMethods: Exclude: - 'lib/solargraph/language_server/message.rb' -# Offense count: 1 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: Methods. Style/RedundantArgument: Exclude: - 'lib/solargraph/source_map/mapper.rb' -# Offense count: 2 # This cop supports safe autocorrection (--autocorrect). Style/RedundantAssignment: Exclude: - 'lib/solargraph/language_server/host/dispatch.rb' - 'lib/solargraph/workspace/config.rb' -# Offense count: 8 # This cop supports safe autocorrection (--autocorrect). Style/RedundantBegin: Exclude: @@ -2437,27 +2258,23 @@ Style/RedundantBegin: - 'lib/solargraph/type_checker.rb' - 'lib/solargraph/workspace.rb' -# Offense count: 1 # This cop supports safe autocorrection (--autocorrect). Style/RedundantException: Exclude: - 'spec/language_server/host_spec.rb' -# Offense count: 2 # This cop supports safe autocorrection (--autocorrect). Style/RedundantFreeze: Exclude: - 'lib/solargraph/complex_type.rb' - 'lib/solargraph/source_map/mapper.rb' -# Offense count: 1 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: AutoCorrect, AllowComments. Style/RedundantInitialize: Exclude: - 'lib/solargraph/rbs_map/core_map.rb' -# Offense count: 3 # This cop supports unsafe autocorrection (--autocorrect-all). Style/RedundantInterpolation: Exclude: @@ -2465,7 +2282,6 @@ Style/RedundantInterpolation: - 'lib/solargraph/parser/parser_gem/node_chainer.rb' - 'lib/solargraph/source_map/mapper.rb' -# Offense count: 7 # This cop supports safe autocorrection (--autocorrect). Style/RedundantParentheses: Exclude: @@ -2476,7 +2292,6 @@ Style/RedundantParentheses: - 'lib/solargraph/source.rb' - 'lib/solargraph/type_checker.rb' -# Offense count: 6 # This cop supports safe autocorrection (--autocorrect). Style/RedundantRegexpArgument: Exclude: @@ -2486,7 +2301,6 @@ Style/RedundantRegexpArgument: - 'spec/diagnostics/rubocop_spec.rb' - 'spec/language_server/host_spec.rb' -# Offense count: 18 # This cop supports safe autocorrection (--autocorrect). Style/RedundantRegexpEscape: Exclude: @@ -2497,7 +2311,6 @@ Style/RedundantRegexpEscape: - 'lib/solargraph/source_map/clip.rb' - 'lib/solargraph/source_map/mapper.rb' -# Offense count: 9 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowMultipleReturnValues. Style/RedundantReturn: @@ -2508,7 +2321,6 @@ Style/RedundantReturn: - 'lib/solargraph/parser/parser_gem/node_methods.rb' - 'lib/solargraph/source/chain/z_super.rb' -# Offense count: 19 # This cop supports safe autocorrection (--autocorrect). Style/RedundantSelf: Exclude: @@ -2521,7 +2333,6 @@ Style/RedundantSelf: - 'lib/solargraph/source/chain.rb' - 'lib/solargraph/source/chain/link.rb' -# Offense count: 3 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, AllowInnerSlashes. # SupportedStyles: slashes, percent_r, mixed @@ -2530,7 +2341,6 @@ Style/RegexpLiteral: - 'lib/solargraph/language_server/uri_helpers.rb' - 'lib/solargraph/workspace/config.rb' -# Offense count: 1 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: implicit, explicit @@ -2538,7 +2348,6 @@ Style/RescueStandardError: Exclude: - 'lib/solargraph/pin/base.rb' -# Offense count: 17 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: ConvertCodeThatCanStartToReturnNil, AllowedMethods, MaxChainLength. # AllowedMethods: present?, blank?, presence, try, try! @@ -2557,13 +2366,11 @@ Style/SafeNavigation: - 'lib/solargraph/range.rb' - 'lib/solargraph/type_checker.rb' -# Offense count: 1 # Configuration parameters: Max. Style/SafeNavigationChainLength: Exclude: - 'lib/solargraph/doc_map.rb' -# Offense count: 40 # This cop supports unsafe autocorrection (--autocorrect-all). Style/SlicingWithRange: Exclude: @@ -2586,7 +2393,6 @@ Style/SlicingWithRange: - 'lib/solargraph/source_map/mapper.rb' - 'lib/solargraph/type_checker/checks.rb' -# Offense count: 6 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowModifier. Style/SoleNestedConditional: @@ -2598,14 +2404,12 @@ Style/SoleNestedConditional: - 'lib/solargraph/source/source_chainer.rb' - 'lib/solargraph/type_checker.rb' -# Offense count: 8 # This cop supports safe autocorrection (--autocorrect). Style/StderrPuts: Exclude: - 'lib/solargraph/pin/base.rb' - 'lib/solargraph/shell.rb' -# Offense count: 11 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: Mode. Style/StringConcatenation: @@ -2621,7 +2425,6 @@ Style/StringConcatenation: - 'lib/solargraph/pin/namespace.rb' - 'solargraph.gemspec' -# Offense count: 613 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline. # SupportedStyles: single_quotes, double_quotes @@ -2719,7 +2522,6 @@ Style/StringLiterals: - 'spec/workspace_spec.rb' - 'spec/yard_map/mapper/to_method_spec.rb' -# Offense count: 4 # This cop supports safe autocorrection (--autocorrect). Style/SuperArguments: Exclude: @@ -2728,7 +2530,6 @@ Style/SuperArguments: - 'lib/solargraph/pin/method.rb' - 'lib/solargraph/pin/signature.rb' -# Offense count: 44 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, MinSize. # SupportedStyles: percent, brackets @@ -2750,7 +2551,6 @@ Style/SymbolArray: - 'spec/parser/node_methods_spec.rb' - 'spec/source_map/mapper_spec.rb' -# Offense count: 7 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: AllowMethodsWithArguments, AllowedMethods, AllowedPatterns, AllowComments. # AllowedMethods: define_method @@ -2764,7 +2564,6 @@ Style/SymbolProc: - 'lib/solargraph/pin/callable.rb' - 'lib/solargraph/pin/closure.rb' -# Offense count: 1 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, AllowSafeAssignment. # SupportedStyles: require_parentheses, require_no_parentheses, require_parentheses_when_complex @@ -2772,7 +2571,6 @@ Style/TernaryParentheses: Exclude: - 'lib/solargraph/source_map/mapper.rb' -# Offense count: 16 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyleForMultiline. # SupportedStylesForMultiline: comma, consistent_comma, no_comma @@ -2791,7 +2589,6 @@ Style/TrailingCommaInArguments: - 'lib/solargraph/yard_map/mapper/to_method.rb' - 'lib/solargraph/yard_map/mapper/to_namespace.rb' -# Offense count: 2 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyleForMultiline. # SupportedStylesForMultiline: comma, consistent_comma, diff_comma, no_comma @@ -2800,7 +2597,6 @@ Style/TrailingCommaInArrayLiteral: - 'lib/solargraph/language_server/message/text_document/formatting.rb' - 'lib/solargraph/rbs_map/core_fills.rb' -# Offense count: 7 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyleForMultiline. # SupportedStylesForMultiline: comma, consistent_comma, diff_comma, no_comma @@ -2814,7 +2610,6 @@ Style/TrailingCommaInHashLiteral: - 'lib/solargraph/pin/parameter.rb' - 'lib/solargraph/rbs_map/conversions.rb' -# Offense count: 2 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: ExactNameMatch, AllowPredicates, AllowDSLWriters, IgnoreClassMethods, AllowedMethods. # AllowedMethods: to_ary, to_a, to_c, to_enum, to_h, to_hash, to_i, to_int, to_io, to_open, to_path, to_proc, to_r, to_regexp, to_str, to_s, to_sym @@ -2823,13 +2618,11 @@ Style/TrivialAccessors: - 'lib/solargraph/language_server/message/extended/check_gem_version.rb' - 'lib/solargraph/pin/keyword.rb' -# Offense count: 1 # This cop supports safe autocorrection (--autocorrect). Style/WhileUntilModifier: Exclude: - 'lib/solargraph/complex_type.rb' -# Offense count: 23 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, MinSize, WordRegex. # SupportedStyles: percent, brackets @@ -2848,13 +2641,11 @@ Style/WordArray: - 'spec/source_map_spec.rb' - 'spec/source_spec.rb' -# Offense count: 1 # This cop supports safe autocorrection (--autocorrect). Style/YAMLFileRead: Exclude: - 'lib/solargraph/workspace/config.rb' -# Offense count: 7 # This cop supports unsafe autocorrection (--autocorrect-all). Style/ZeroLengthPredicate: Exclude: @@ -2865,7 +2656,6 @@ Style/ZeroLengthPredicate: - 'lib/solargraph/source/chain/array.rb' - 'spec/language_server/protocol_spec.rb' -# Offense count: 1 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: long, short @@ -2873,7 +2663,6 @@ YARD/CollectionType: Exclude: - 'lib/solargraph/range.rb' -# Offense count: 60 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStylePrototypeName. # SupportedStylesPrototypeName: before, after @@ -2915,14 +2704,12 @@ YARD/MismatchName: - 'lib/solargraph/source/chain/z_super.rb' - 'lib/solargraph/type_checker.rb' -# Offense count: 4 YARD/TagTypeSyntax: Exclude: - 'lib/solargraph/language_server/host.rb' - 'lib/solargraph/parser/comment_ripper.rb' - 'lib/solargraph/type_checker.rb' -# Offense count: 195 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: Max, AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, IgnoreCopDirectives, AllowedPatterns, SplitStrings. # URISchemes: http, https From 0eeef79b615f227d9586ee0cc06a57848ff2f2e8 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 28 Jul 2025 12:36:45 -0400 Subject: [PATCH 382/561] Deal with a potential nil case from CI --- lib/solargraph/workspace/gemspecs.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/solargraph/workspace/gemspecs.rb b/lib/solargraph/workspace/gemspecs.rb index 270b063eb..289a84a1c 100644 --- a/lib/solargraph/workspace/gemspecs.rb +++ b/lib/solargraph/workspace/gemspecs.rb @@ -91,7 +91,7 @@ def resolve_require require # @return [Array] def stdlib_dependencies stdlib_name deps = RbsMap::StdlibMap.stdlib_dependencies(stdlib_name, nil) || [] - deps.map { |dep| dep['name'] } + deps.map { |dep| dep['name'] }.compact end # @param name [String] From 624ffa19b1fd5e0c9ea399fe8da0b7fe2d3663f1 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 28 Jul 2025 12:56:56 -0400 Subject: [PATCH 383/561] Deal with a potential nil case from CI --- lib/solargraph/doc_map.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/solargraph/doc_map.rb b/lib/solargraph/doc_map.rb index cfa84712f..9d869a180 100644 --- a/lib/solargraph/doc_map.rb +++ b/lib/solargraph/doc_map.rb @@ -121,7 +121,7 @@ def load_serialized_gem_pins out: $stderr # try to resolve the stdlib name deps = workspace.stdlib_dependencies(stdlib_name_guess) || [] - [stdlib_name_guess, *deps].each do |potential_stdlib_name| + [stdlib_name_guess, *deps].compact.each do |potential_stdlib_name| rbs_pins = pin_cache.cache_stdlib_rbs_map potential_stdlib_name @pins.concat rbs_pins if rbs_pins end From 53c83549a7d09817488573c8d139088f754adaa6 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 28 Jul 2025 14:25:59 -0400 Subject: [PATCH 384/561] Look for external requires before cataloging bench It seems like sync_catalog will go through the motions but not actually load pins from gems here due to passing an empty requires array to ApiMap. I'm sure those requires get pulled in eventually, but we go through at least one catalog cycle without it happening. Found while trying to test a different issue but not being able to get completions from a gem in a spec. --- lib/solargraph/library.rb | 2 +- spec/library_spec.rb | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/lib/solargraph/library.rb b/lib/solargraph/library.rb index 740d700b3..c277c0697 100644 --- a/lib/solargraph/library.rb +++ b/lib/solargraph/library.rb @@ -677,8 +677,8 @@ def sync_catalog mutex.synchronize do logger.info "Cataloging #{workspace.directory.empty? ? 'generic workspace' : workspace.directory}" - api_map.catalog bench source_map_hash.values.each { |map| find_external_requires(map) } + api_map.catalog bench logger.info "Catalog complete (#{api_map.source_maps.length} files, #{api_map.pins.length} pins)" logger.info "#{api_map.uncached_yard_gemspecs.length} uncached YARD gemspecs" logger.info "#{api_map.uncached_rbs_collection_gemspecs.length} uncached RBS collection gemspecs" diff --git a/spec/library_spec.rb b/spec/library_spec.rb index bd7cc25a0..db8382290 100644 --- a/spec/library_spec.rb +++ b/spec/library_spec.rb @@ -26,6 +26,22 @@ expect(completion.pins.map(&:name)).to include('x') end + it "returns a Completion from a gem" do + library = Solargraph::Library.new(Solargraph::Workspace.new(Dir.pwd, + Solargraph::Workspace::Config.new)) + library.attach Solargraph::Source.load_string(%( + require 'backport' + + # @param adapter [Backport::Adapter] + def foo(adapter) + adapter.remo + end + ), 'file.rb', 0) + completion = library.completions_at('file.rb', 5, 19) + expect(completion).to be_a(Solargraph::SourceMap::Completion) + expect(completion.pins.map(&:name)).to include('remote') + end + it "gets definitions from a file" do library = Solargraph::Library.new src = Solargraph::Source.load_string %( From 581cc02321f791e16721305a7036310b71466211 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 28 Jul 2025 15:03:36 -0400 Subject: [PATCH 385/561] Ensure backport is pre-cached --- spec/library_spec.rb | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/spec/library_spec.rb b/spec/library_spec.rb index db8382290..bea0f2983 100644 --- a/spec/library_spec.rb +++ b/spec/library_spec.rb @@ -26,20 +26,26 @@ expect(completion.pins.map(&:name)).to include('x') end - it "returns a Completion from a gem" do - library = Solargraph::Library.new(Solargraph::Workspace.new(Dir.pwd, - Solargraph::Workspace::Config.new)) - library.attach Solargraph::Source.load_string(%( - require 'backport' + context 'with a require from an already-cached external gem' do + before do + Solargraph::Shell.new.gems('backport') + end - # @param adapter [Backport::Adapter] - def foo(adapter) - adapter.remo - end - ), 'file.rb', 0) - completion = library.completions_at('file.rb', 5, 19) - expect(completion).to be_a(Solargraph::SourceMap::Completion) - expect(completion.pins.map(&:name)).to include('remote') + it "returns a Completion" do + library = Solargraph::Library.new(Solargraph::Workspace.new(Dir.pwd, + Solargraph::Workspace::Config.new)) + library.attach Solargraph::Source.load_string(%( + require 'backport' + + # @param adapter [Backport::Adapter] + def foo(adapter) + adapter.remo + end + ), 'file.rb', 0) + completion = library.completions_at('file.rb', 5, 19) + expect(completion).to be_a(Solargraph::SourceMap::Completion) + expect(completion.pins.map(&:name)).to include('remote') + end end it "gets definitions from a file" do From 200d198024a2685301047b8975c1958c71e8614d Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 28 Jul 2025 16:15:08 -0400 Subject: [PATCH 386/561] Only cache combined pins in memory --- lib/solargraph/pin_cache.rb | 46 +------------------------------------ 1 file changed, 1 insertion(+), 45 deletions(-) diff --git a/lib/solargraph/pin_cache.rb b/lib/solargraph/pin_cache.rb index 77fa3c500..765eb401a 100644 --- a/lib/solargraph/pin_cache.rb +++ b/lib/solargraph/pin_cache.rb @@ -106,17 +106,9 @@ def cache_combined_pins gemspec, rbs_version_cache_key, yard_pins, rbs_collectio # @param gemspec [Gem::Specification] # @return [Array] def deserialize_combined_pin_cache gemspec - unless combined_pins_in_memory[[gemspec.name, gemspec.version]].nil? - return combined_pins_in_memory[[gemspec.name, gemspec.version]] - end - rbs_version_cache_key = lookup_rbs_version_cache_key(gemspec) - cached = load_combined_gem(gemspec, rbs_version_cache_key) - if cached - logger.info { "Loaded #{cached.length} cached YARD pins from #{gemspec.name}:#{gemspec.version}" } - combined_pins_in_memory[[gemspec.name, gemspec.version]] = cached - end + load_combined_gem(gemspec, rbs_version_cache_key) end # @param gemspec [Gem::Specification] @@ -124,13 +116,8 @@ def deserialize_combined_pin_cache gemspec # @return [void] def uncache_gem gemspec, out: nil PinCache.uncache(yardoc_path(gemspec), out: out) - yard_pins_in_memory.delete([gemspec.name, gemspec.version]) PinCache.uncache(yard_gem_path(gemspec), out: out) - - rbs_version_cache_key = lookup_rbs_version_cache_key(gemspec) uncache_by_prefix(rbs_collection_pins_path_prefix(gemspec), out: out) - rbs_collection_pins_in_memory.delete([gemspec.name, gemspec.version, rbs_version_cache_key]) - uncache_by_prefix(combined_path_prefix(gemspec), out: out) combined_pins_in_memory.delete([gemspec.name, gemspec.version]) end @@ -246,16 +233,6 @@ def cache_yard_pins gemspec, out pins end - # @return [Hash{Array(String, String) => Array}] gemspec name, version - def yard_pins_in_memory - PinCache.all_yard_pins_in_memory[yard_plugins] ||= {} - end - - # @return [Hash{Array(String, String, String) => Array}] gemspec name, version and rbs version cache key - def rbs_collection_pins_in_memory - PinCache.all_rbs_collection_pins_in_memory ||= {} - end - # @return [Hash{Array(String, String, String) => Array}] def combined_pins_in_memory PinCache.all_combined_pins_in_memory[yard_plugins] ||= {} @@ -283,14 +260,8 @@ def cache_rbs_collection_pins gemspec, _out # @param gemspec [Gem::Specification] # @return [Array] def deserialize_yard_pin_cache gemspec - if yard_pins_in_memory.key?([gemspec.name, gemspec.version]) - return yard_pins_in_memory[[gemspec.name, gemspec.version]] - end - cached = load_yard_gem(gemspec) if cached - logger.info { "Loaded #{cached.length} cached YARD pins from #{gemspec.name}:#{gemspec.version}" } - yard_pins_in_memory[[gemspec.name, gemspec.version]] = cached cached else logger.debug "No YARD pin cache for #{gemspec.name}:#{gemspec.version}" @@ -302,7 +273,6 @@ def deserialize_yard_pin_cache gemspec # @param rbs_version_cache_key [String] # @return [Array, nil] def deserialize_rbs_collection_cache gemspec, rbs_version_cache_key - return if rbs_collection_pins_in_memory.key?([gemspec.name, gemspec.version, rbs_version_cache_key]) cached = load_rbs_collection_pins(gemspec, rbs_version_cache_key) if cached unless cached.empty? @@ -310,7 +280,6 @@ def deserialize_rbs_collection_cache gemspec, rbs_version_cache_key "Loaded #{cached.length} pins from RBS collection cache for #{gemspec.name}:#{gemspec.version}" end end - rbs_collection_pins_in_memory[[gemspec.name, gemspec.version, rbs_version_cache_key]] = cached cached else logger.debug "No RBS collection pin cache for #{gemspec.name} #{gemspec.version}" @@ -455,19 +424,6 @@ def uncache_by_prefix *path_segments, out: nil class << self include Logging - # @return [Hash{Array => Hash{Array(String, String) => Array}}] yard - # plugins, then gemspec name and version - def all_yard_pins_in_memory - @all_yard_pins_in_memory ||= {} - end - - # @return [Hash{Array(String, String, String) => - # Array}] gemspec name, version and rbs version - # cache key - def all_rbs_collection_pins_in_memory - @all_rbs_collection_pins_in_memory ||= {} - end - # @return [Hash{Array => Hash{Array(String, String) => # Array}}] yard plugins, then gemspec name and # version From cd1306d3b6c2b9fc8035ee0eb3e118f1a05f6d58 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 28 Jul 2025 16:15:19 -0400 Subject: [PATCH 387/561] More specs --- spec/library_spec.rb | 13 ++++++++--- spec/pin_cache_spec.rb | 52 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 3 deletions(-) create mode 100644 spec/pin_cache_spec.rb diff --git a/spec/library_spec.rb b/spec/library_spec.rb index d51974029..76128fedb 100644 --- a/spec/library_spec.rb +++ b/spec/library_spec.rb @@ -1,5 +1,6 @@ require 'tmpdir' require 'yard' +require 'timeout' describe Solargraph::Library do it "does not open created files in the workspace" do @@ -26,9 +27,9 @@ expect(completion.pins.map(&:name)).to include('x') end - context 'with a require from an already-cached external gem' do + context 'with a require from a not-yet-cached external gem' do before do - Solargraph::Shell.new.gems('backport') + Solargraph::Shell.new.uncache('backport') end it "returns a Completion" do @@ -42,7 +43,13 @@ def foo(adapter) adapter.remo end ), 'file.rb', 0) - completion = library.completions_at('file.rb', 5, 19) + completion = nil + Timeout.timeout(10) do + # give Solargraph time to cache the gem + while (completion = library.completions_at('file.rb', 5, 19)).pins.empty? + sleep 0.25 + end + end expect(completion).to be_a(Solargraph::SourceMap::Completion) expect(completion.pins.map(&:name)).to include('remote') end diff --git a/spec/pin_cache_spec.rb b/spec/pin_cache_spec.rb new file mode 100644 index 000000000..31fba5f40 --- /dev/null +++ b/spec/pin_cache_spec.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +require 'bundler' +require 'benchmark' + +describe Solargraph::PinCache do + subject(:pin_cache) do + described_class.new(rbs_collection_path: '.gem_rbs_collection', + rbs_collection_config_path: 'rbs_collection.yaml', + directory: Dir.pwd, + yard_plugins: []) + end + + describe '#possible_stdlibs' do + it 'is tolerant of less usual Ruby installations' do + stub_const('Gem::RUBYGEMS_DIR', nil) + + expect(pin_cache.possible_stdlibs).to eq([]) + end + end + + describe '#cache_gem' do + context 'with an already in-memory gem' do + let(:backport_gemspec) { Gem::Specification.find_by_name('backport') } + + before do + pin_cache.cache_gem(gemspec: backport_gemspec, out: nil) + end + + it 'does not load the gem again' do + allow(Marshal).to receive(:load).and_call_original + + pin_cache.cache_gem(gemspec: backport_gemspec, out: nil) + + expect(Marshal).not_to have_received(:load).with(anything) + end + end + + context 'with the parser gem' do + before do + Solargraph::Shell.new.uncache('parser') + allow(Solargraph::Yardoc).to receive(:build_docs) + end + + it 'chooses not to use YARD' do + parser_gemspec = Gem::Specification.find_by_name('parser') + pin_cache.cache_gem(gemspec: parser_gemspec, out: nil) + expect(Solargraph::Yardoc).not_to have_received(:build_docs) + end + end + end +end From d730fad7b2991c117edfd2ea73bb676055aa427c Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 28 Jul 2025 17:26:56 -0400 Subject: [PATCH 388/561] Revert accidental formatting change --- lib/solargraph/library.rb | 2 +- lib/solargraph/pin_cache.rb | 28 +++++++++++----------------- 2 files changed, 12 insertions(+), 18 deletions(-) diff --git a/lib/solargraph/library.rb b/lib/solargraph/library.rb index 79d683262..688adef5f 100644 --- a/lib/solargraph/library.rb +++ b/lib/solargraph/library.rb @@ -655,7 +655,7 @@ def report_cache_progress gem_name, pending @total = pending if pending > @total finished = @total - pending pct = if @total.zero? - 0 + 0 else ((finished.to_f / @total.to_f) * 100).to_i end diff --git a/lib/solargraph/pin_cache.rb b/lib/solargraph/pin_cache.rb index 765eb401a..ed9e6b39f 100644 --- a/lib/solargraph/pin_cache.rb +++ b/lib/solargraph/pin_cache.rb @@ -271,20 +271,14 @@ def deserialize_yard_pin_cache gemspec # @param gemspec [Gem::Specification, Bundler::LazySpecification] # @param rbs_version_cache_key [String] - # @return [Array, nil] + # @return [Array] def deserialize_rbs_collection_cache gemspec, rbs_version_cache_key cached = load_rbs_collection_pins(gemspec, rbs_version_cache_key) - if cached - unless cached.empty? - logger.info do - "Loaded #{cached.length} pins from RBS collection cache for #{gemspec.name}:#{gemspec.version}" - end - end - cached - else - logger.debug "No RBS collection pin cache for #{gemspec.name} #{gemspec.version}" - nil + Solargraph.assert_or_log(:pin_cache_rbs_collection, 'Asked for non-existent rbs collection') if cached.nil? + logger.info do + "Loaded #{cached&.length} pins from RBS collection cache for #{gemspec.name}:#{gemspec.version}" end + cached end # @return [Array] @@ -308,7 +302,7 @@ def yard_gem_path gemspec end # @param gemspec [Gem::Specification] - # @return [Array] + # @return [Array, nil] def load_yard_gem gemspec PinCache.load(yard_gem_path(gemspec)) end @@ -348,14 +342,14 @@ def rbs_collection_pins_path_prefix gemspec # @param gemspec [Gem::Specification, Bundler::LazySpecification] # @param hash [String] # - # @return [Array] + # @return [Array, nil] def load_rbs_collection_pins gemspec, hash PinCache.load(rbs_collection_pins_path(gemspec, hash)) end # @param gemspec [Gem::Specification] # @param hash [String, nil] - # @param pins [Array]n + # @param pins [Array] # @return [void] def serialize_rbs_collection_pins gemspec, hash, pins PinCache.save(rbs_collection_pins_path(gemspec, hash), pins) @@ -390,7 +384,7 @@ def combined_gem? gemspec, hash # @param gemspec [Gem::Specification] # @param hash [String, nil] - # @return [Array] + # @return [Array, nil] def load_combined_gem gemspec, hash PinCache.load(combined_path(gemspec, hash)) end @@ -519,7 +513,7 @@ def cache_core out: $stderr RbsMap::CoreMap.new.cache_core(out: out) end - # @return [Array] + # @return [Array, nil] def load_core load(core_path) end @@ -542,7 +536,7 @@ def stdlib_require_path require end # @param require [String] - # @return [Array] + # @return [Array, nil] def load_stdlib_require require load(stdlib_require_path(require)) end From 0deb8cffd42c6a25fe3ba58ebe6e069fa7570732 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 28 Jul 2025 17:35:23 -0400 Subject: [PATCH 389/561] Drop unused method --- lib/solargraph/pin_cache.rb | 6 ------ 1 file changed, 6 deletions(-) diff --git a/lib/solargraph/pin_cache.rb b/lib/solargraph/pin_cache.rb index ed9e6b39f..2b3d73f88 100644 --- a/lib/solargraph/pin_cache.rb +++ b/lib/solargraph/pin_cache.rb @@ -320,12 +320,6 @@ def yard_gem? gemspec exist?(yard_gem_path(gemspec)) end - # @param gemspec [Gem::Specification] - # @return [Boolean] - def yardoc? gemspec - exist?(yardoc_path(gemspec)) - end - # @param gemspec [Gem::Specification] # @param hash [String, nil] # @return [String] From a812069ffd231cfed16fb92c5fa51f3ea1828455 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 28 Jul 2025 18:54:21 -0400 Subject: [PATCH 390/561] Add RBS collection types during tests --- .github/workflows/rspec.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/rspec.yml b/.github/workflows/rspec.yml index 8d1043503..5bf5e9775 100644 --- a/.github/workflows/rspec.yml +++ b/.github/workflows/rspec.yml @@ -34,6 +34,8 @@ jobs: run: | bundle install bundle update rbs # use latest available for this Ruby version + - name: Install types + run: bundle exec rbs collection update - name: Run tests run: bundle exec rspec undercover: @@ -51,6 +53,8 @@ jobs: bundler-cache: false - name: Install gems run: bundle install + - name: Install types + run: bundle exec rbs collection update - name: Run tests run: bundle exec rspec - name: Check PR coverage From 09958f47d9f0d48634b4fa0443e2b5d9d26cc701 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 28 Jul 2025 19:24:28 -0400 Subject: [PATCH 391/561] More specs --- spec/pin_cache_spec.rb | 37 +++++++++++++++++++++++++++++++++++++ spec/rbs_map_spec.rb | 8 +++++++- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/spec/pin_cache_spec.rb b/spec/pin_cache_spec.rb index 31fba5f40..bb98af6be 100644 --- a/spec/pin_cache_spec.rb +++ b/spec/pin_cache_spec.rb @@ -45,8 +45,45 @@ it 'chooses not to use YARD' do parser_gemspec = Gem::Specification.find_by_name('parser') pin_cache.cache_gem(gemspec: parser_gemspec, out: nil) + # if this fails, you may not have run `bundle exec rbs collection update` expect(Solargraph::Yardoc).not_to have_received(:build_docs) end end + + context 'with a stdlib gem' do + let(:gem_name) { 'cgi' } + + before do + Solargraph::Shell.new.uncache(gem_name) + end + + it 'caches' do + yaml_gemspec = Gem::Specification.find_by_name(gem_name) + allow(File).to receive(:write).and_call_original + + pin_cache.cache_gem(gemspec: yaml_gemspec, out: nil) + + # match arguments with regexp using rspec-matchers syntax + expect(File).to have_received(:write).with(%r{combined/cgi-.*-stdlib.ser$}, any_args).once + end + end + + context 'with a gem packaged with its own RBS' do + let(:gem_name) { 'base64' } + + before do + Solargraph::Shell.new.uncache(gem_name) + end + + it 'caches' do + yaml_gemspec = Gem::Specification.find_by_name(gem_name) + allow(File).to receive(:write).and_call_original + + pin_cache.cache_gem(gemspec: yaml_gemspec, out: nil) + + # match arguments with regexp using rspec-matchers syntax + expect(File).to have_received(:write).with(%r{combined/base64-.*-export.ser$}, any_args).once + end + end end end diff --git a/spec/rbs_map_spec.rb b/spec/rbs_map_spec.rb index b06c975d1..a18c5d5b5 100644 --- a/spec/rbs_map_spec.rb +++ b/spec/rbs_map_spec.rb @@ -3,7 +3,13 @@ spec = Gem::Specification.find_by_name('rbs') rbs_map = Solargraph::RbsMap.from_gemspec(spec, nil, nil) pin = rbs_map.path_pin('RBS::EnvironmentLoader#add_collection') - expect(pin).to be + expect(pin).not_to be_nil + end + + it 'fails if it does not find data' do + spec = Gem::Specification.find_by_name('backport') + rbs_map = Solargraph::RbsMap.from_gemspec(spec, nil, nil) + expect(rbs_map.pins).to be_empty end it 'converts constants and aliases to correct types' do From df609a612f8f775c8892d0ce2cfde238395128b9 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 28 Jul 2025 19:32:48 -0400 Subject: [PATCH 392/561] Revert double quote changes for another PR --- lib/solargraph/shell.rb | 106 ++++++++++++++++++++-------------------- 1 file changed, 53 insertions(+), 53 deletions(-) diff --git a/lib/solargraph/shell.rb b/lib/solargraph/shell.rb index f8b98359a..c40a0894e 100755 --- a/lib/solargraph/shell.rb +++ b/lib/solargraph/shell.rb @@ -1,9 +1,9 @@ # frozen_string_literal: true -require 'benchmark' -require 'thor' -require 'yard' -require 'yaml' +require "benchmark" +require "thor" +require "yard" +require "yaml" module Solargraph class Shell < Thor @@ -16,25 +16,25 @@ def self.exit_on_failure? map %w[--version -v] => :version - desc '--version, -v', 'Print the version' + desc "--version, -v", "Print the version" # @return [void] def version puts Solargraph::VERSION end - desc 'socket', 'Run a Solargraph socket server' - option :host, type: :string, aliases: :h, desc: 'The server host', default: '127.0.0.1' - option :port, type: :numeric, aliases: :p, desc: 'The server port', default: 7658 + desc "socket", "Run a Solargraph socket server" + option :host, type: :string, aliases: :h, desc: "The server host", default: "127.0.0.1" + option :port, type: :numeric, aliases: :p, desc: "The server port", default: 7658 # @return [void] def socket - require 'backport' + require "backport" port = options[:port] port = available_port if port.zero? Backport.run do - Signal.trap('INT') do + Signal.trap("INT") do Backport.stop end - Signal.trap('TERM') do + Signal.trap("TERM") do Backport.stop end # @sg-ignore https://github.com/castwide/backport/pull/5 @@ -43,15 +43,15 @@ def socket end end - desc 'stdio', 'Run a Solargraph stdio server' + desc "stdio", "Run a Solargraph stdio server" # @return [void] def stdio - require 'backport' + require "backport" Backport.run do - Signal.trap('INT') do + Signal.trap("INT") do Backport.stop end - Signal.trap('TERM') do + Signal.trap("TERM") do Backport.stop end # @sg-ignore https://github.com/castwide/backport/pull/5 @@ -60,11 +60,11 @@ def stdio end end - desc 'config [DIRECTORY]', 'Create or overwrite a default configuration file' - option :extensions, type: :boolean, aliases: :e, desc: 'Add installed extensions', default: true + desc "config [DIRECTORY]", "Create or overwrite a default configuration file" + option :extensions, type: :boolean, aliases: :e, desc: "Add installed extensions", default: true # @param directory [String] # @return [void] - def config directory = '.' + def config directory = "." matches = [] if options[:extensions] # @sg-ignore @@ -78,84 +78,84 @@ def config directory = '.' conf = Solargraph::Workspace::Config.new.raw_data unless matches.empty? matches.each do |m| - conf['extensions'].push m + conf["extensions"].push m end end - File.open(File.join(directory, '.solargraph.yml'), 'w') do |file| + File.open(File.join(directory, ".solargraph.yml"), "w") do |file| file.puts conf.to_yaml end - STDOUT.puts 'Configuration file initialized.' + STDOUT.puts "Configuration file initialized." end - desc 'clear', 'Delete all cached documentation' + desc "clear", "Delete all cached documentation" long_desc %( This command will delete all core and gem documentation from the cache. ) # @return [void] def clear - puts 'Deleting all cached documentation (gems, core and stdlib)' + puts "Deleting all cached documentation (gems, core and stdlib)" Solargraph::PinCache.clear end - map 'clear-cache' => :clear - map 'clear-cores' => :clear + map "clear-cache" => :clear + map "clear-cores" => :clear - desc 'cache', 'Cache a gem', hide: true - option :rebuild, type: :boolean, desc: 'Rebuild existing documentation', default: false + desc "cache", "Cache a gem", hide: true + option :rebuild, type: :boolean, desc: "Rebuild existing documentation", default: false # @return [void] # @param gem [String] # @param version [String, nil] def cache gem, version = nil - gems(gem + (version ? "=#{version}" : '')) + gems(gem + (version ? "=#{version}" : "")) # " end - desc 'gems [GEM[=VERSION]]', 'Cache documentation for installed gems' - option :rebuild, type: :boolean, desc: 'Rebuild existing documentation', default: false + desc "gems [GEM[=VERSION]]", "Cache documentation for installed gems" + option :rebuild, type: :boolean, desc: "Rebuild existing documentation", default: false # @param names [Array] # @return [void] def gems *names - api_map = ApiMap.load('.') + api_map = ApiMap.load(".") if names.empty? api_map.cache_all_for_workspace!($stdout, rebuild: options[:rebuild]) else $stderr.puts("Caching these gems: #{names}") names.each do |name| - if name == 'core' + if name == "core" PinCache.cache_core(out: $stdout) next end - gemspec = api_map.find_gem(*name.split('=')) + gemspec = api_map.find_gem(*name.split("=")) if gemspec.nil? - warn "Gem '#{name}' not found" + warn "Gem "#{name}" not found" else api_map.cache_gem(gemspec, rebuild: options[:rebuild], out: $stdout) end rescue Gem::MissingSpecError - warn "Gem '#{name}' not found" + warn "Gem "#{name}" not found" end $stderr.puts "Documentation cached for #{names.count} gems." end end - desc 'uncache GEM [...GEM]', 'Delete specific cached gem documentation' + desc "uncache GEM [...GEM]", "Delete specific cached gem documentation" long_desc %( - Specify one or more gem names to clear. 'core' or 'stdlib' may + Specify one or more gem names to clear. "core" or "stdlib" may also be specified to clear cached system documentation. Documentation will be regenerated as needed. ) # @param gems [Array] # @return [void] def uncache *gems - raise ArgumentError, 'No gems specified.' if gems.empty? - workspace = Workspace.new('.') + raise ArgumentError, "No gems specified." if gems.empty? + workspace = Workspace.new(".") gems.each do |gem| - if gem == 'core' + if gem == "core" PinCache.uncache_core(out: $stdout) next end - if gem == 'stdlib' + if gem == "stdlib" PinCache.uncache_stdlib(out: $stdout) next end @@ -165,21 +165,21 @@ def uncache *gems end end - desc 'reporters', 'Get a list of diagnostics reporters' + desc "reporters", "Get a list of diagnostics reporters" # @return [void] def reporters puts Solargraph::Diagnostics.reporters end - desc 'typecheck [FILE(s)]', 'Run the type checker' + desc "typecheck [FILE(s)]", "Run the type checker" long_desc %( Perform type checking on one or more files in a workspace. Check the entire workspace if no files are specified. Type checking levels are normal, typed, strict, and strong. ) - option :level, type: :string, aliases: [:mode, :m, :l], desc: 'Type checking level', default: 'normal' - option :directory, type: :string, aliases: :d, desc: 'The workspace directory', default: '.' + option :level, type: :string, aliases: [:mode, :m, :l], desc: "Type checking level", default: "normal" + option :directory, type: :string, aliases: :d, desc: "The workspace directory", default: "." # @return [void] def typecheck *files directory = File.realpath(options[:directory]) @@ -207,20 +207,20 @@ def typecheck *files # " } puts "Typecheck finished in #{time.real} seconds." - puts "#{probcount} problem#{probcount != 1 ? 's' : ''} found#{files.length != 1 ? " in #{filecount} of #{files.length} files" : ''}." + puts "#{probcount} problem#{probcount != 1 ? "s" : ""} found#{files.length != 1 ? " in #{filecount} of #{files.length} files" : ""}." # " exit 1 if probcount > 0 end - desc 'scan', 'Test the workspace for problems' + desc "scan", "Test the workspace for problems" long_desc %( A scan loads the entire workspace to make sure that the ASTs and maps do not raise errors during analysis. It does not perform any type checking or validation; it only confirms that the analysis itself is error-free. ) - option :directory, type: :string, aliases: :d, desc: 'The workspace directory', default: '.' - option :verbose, type: :boolean, aliases: :v, desc: 'Verbose output', default: false + option :directory, type: :string, aliases: :d, desc: "The workspace directory", default: "." + option :verbose, type: :boolean, aliases: :v, desc: "Verbose output", default: false # @return [void] def scan directory = File.realpath(options[:directory]) @@ -234,7 +234,7 @@ def scan pin.typify api_map pin.probe api_map rescue StandardError => e - STDERR.puts "Error testing #{pin_description(pin)} #{pin.location ? "at #{pin.location.filename}:#{pin.location.range.start.line + 1}" : ''}" + STDERR.puts "Error testing #{pin_description(pin)} #{pin.location ? "at #{pin.location.filename}:#{pin.location.range.start.line + 1}" : ""}" STDERR.puts "[#{e.class}]: #{e.message}" STDERR.puts e.backtrace.join("\n") exit 1 @@ -244,9 +244,9 @@ def scan puts "Scanned #{directory} (#{api_map.pins.length} pins) in #{time.real} seconds." end - desc 'list', 'List the files in the workspace and the total count' - option :count, type: :boolean, aliases: :c, desc: 'Display the file count only', default: false - option :directory, type: :string, aliases: :d, desc: 'The directory to read', default: '.' + desc "list", "List the files in the workspace and the total count" + option :count, type: :boolean, aliases: :c, desc: "Display the file count only", default: false + option :directory, type: :string, aliases: :d, desc: "The directory to read", default: "." # @return [void] def list workspace = Solargraph::Workspace.new(options[:directory]) From cda38edb8dcd9d7ebd114d404dcaf9359b73e430 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 28 Jul 2025 19:35:20 -0400 Subject: [PATCH 393/561] Revert double quote changes for another PR --- lib/solargraph/shell.rb | 126 ++++++++++++++++++++-------------------- 1 file changed, 63 insertions(+), 63 deletions(-) diff --git a/lib/solargraph/shell.rb b/lib/solargraph/shell.rb index c40a0894e..047ec175f 100755 --- a/lib/solargraph/shell.rb +++ b/lib/solargraph/shell.rb @@ -22,12 +22,12 @@ def version puts Solargraph::VERSION end - desc "socket", "Run a Solargraph socket server" - option :host, type: :string, aliases: :h, desc: "The server host", default: "127.0.0.1" - option :port, type: :numeric, aliases: :p, desc: "The server port", default: 7658 + desc 'socket', 'Run a Solargraph socket server' + option :host, type: :string, aliases: :h, desc: 'The server host', default: '127.0.0.1' + option :port, type: :numeric, aliases: :p, desc: 'The server port', default: 7658 # @return [void] def socket - require "backport" + require 'backport' port = options[:port] port = available_port if port.zero? Backport.run do @@ -39,32 +39,32 @@ def socket end # @sg-ignore https://github.com/castwide/backport/pull/5 Backport.prepare_tcp_server host: options[:host], port: port, adapter: Solargraph::LanguageServer::Transport::Adapter - STDERR.puts "Solargraph is listening PORT=#{port} PID=#{Process.pid}" + STDERR.puts 'Solargraph is listening PORT=#{port} PID=#{Process.pid}' end end - desc "stdio", "Run a Solargraph stdio server" + desc 'stdio', 'Run a Solargraph stdio server' # @return [void] def stdio - require "backport" + require 'backport' Backport.run do - Signal.trap("INT") do + Signal.trap('INT') do Backport.stop end - Signal.trap("TERM") do + Signal.trap('TERM') do Backport.stop end # @sg-ignore https://github.com/castwide/backport/pull/5 Backport.prepare_stdio_server adapter: Solargraph::LanguageServer::Transport::Adapter - STDERR.puts "Solargraph is listening on stdio PID=#{Process.pid}" + STDERR.puts 'Solargraph is listening on stdio PID=#{Process.pid}' end end - desc "config [DIRECTORY]", "Create or overwrite a default configuration file" - option :extensions, type: :boolean, aliases: :e, desc: "Add installed extensions", default: true + desc 'config [DIRECTORY]', 'Create or overwrite a default configuration file' + option :extensions, type: :boolean, aliases: :e, desc: 'Add installed extensions', default: true # @param directory [String] # @return [void] - def config directory = "." + def config directory = '.' matches = [] if options[:extensions] # @sg-ignore @@ -78,84 +78,84 @@ def config directory = "." conf = Solargraph::Workspace::Config.new.raw_data unless matches.empty? matches.each do |m| - conf["extensions"].push m + conf['extensions'].push m end end - File.open(File.join(directory, ".solargraph.yml"), "w") do |file| + File.open(File.join(directory, '.solargraph.yml'), 'w') do |file| file.puts conf.to_yaml end - STDOUT.puts "Configuration file initialized." + STDOUT.puts 'Configuration file initialized.' end - desc "clear", "Delete all cached documentation" + desc 'clear', 'Delete all cached documentation' long_desc %( This command will delete all core and gem documentation from the cache. ) # @return [void] def clear - puts "Deleting all cached documentation (gems, core and stdlib)" + puts 'Deleting all cached documentation (gems, core and stdlib)' Solargraph::PinCache.clear end - map "clear-cache" => :clear - map "clear-cores" => :clear + map 'clear-cache' => :clear + map 'clear-cores' => :clear - desc "cache", "Cache a gem", hide: true - option :rebuild, type: :boolean, desc: "Rebuild existing documentation", default: false + desc 'cache', 'Cache a gem', hide: true + option :rebuild, type: :boolean, desc: 'Rebuild existing documentation', default: false # @return [void] # @param gem [String] # @param version [String, nil] def cache gem, version = nil - gems(gem + (version ? "=#{version}" : "")) - # " + gems(gem + (version ? '=#{version}' : '')) + # ' end - desc "gems [GEM[=VERSION]]", "Cache documentation for installed gems" - option :rebuild, type: :boolean, desc: "Rebuild existing documentation", default: false + desc 'gems [GEM[=VERSION]]', 'Cache documentation for installed gems' + option :rebuild, type: :boolean, desc: 'Rebuild existing documentation', default: false # @param names [Array] # @return [void] def gems *names - api_map = ApiMap.load(".") + api_map = ApiMap.load('.') if names.empty? api_map.cache_all_for_workspace!($stdout, rebuild: options[:rebuild]) else - $stderr.puts("Caching these gems: #{names}") + $stderr.puts('Caching these gems: #{names}') names.each do |name| - if name == "core" + if name == 'core' PinCache.cache_core(out: $stdout) next end - gemspec = api_map.find_gem(*name.split("=")) + gemspec = api_map.find_gem(*name.split('=')) if gemspec.nil? - warn "Gem "#{name}" not found" + warn 'Gem '#{name}' not found' else api_map.cache_gem(gemspec, rebuild: options[:rebuild], out: $stdout) end rescue Gem::MissingSpecError - warn "Gem "#{name}" not found" + warn 'Gem '#{name}' not found' end - $stderr.puts "Documentation cached for #{names.count} gems." + $stderr.puts 'Documentation cached for #{names.count} gems.' end end - desc "uncache GEM [...GEM]", "Delete specific cached gem documentation" + desc 'uncache GEM [...GEM]', 'Delete specific cached gem documentation' long_desc %( - Specify one or more gem names to clear. "core" or "stdlib" may + Specify one or more gem names to clear. 'core' or 'stdlib' may also be specified to clear cached system documentation. Documentation will be regenerated as needed. ) # @param gems [Array] # @return [void] def uncache *gems - raise ArgumentError, "No gems specified." if gems.empty? - workspace = Workspace.new(".") + raise ArgumentError, 'No gems specified.' if gems.empty? + workspace = Workspace.new('.') gems.each do |gem| - if gem == "core" + if gem == 'core' PinCache.uncache_core(out: $stdout) next end - if gem == "stdlib" + if gem == 'stdlib' PinCache.uncache_stdlib(out: $stdout) next end @@ -165,21 +165,21 @@ def uncache *gems end end - desc "reporters", "Get a list of diagnostics reporters" + desc 'reporters', 'Get a list of diagnostics reporters' # @return [void] def reporters puts Solargraph::Diagnostics.reporters end - desc "typecheck [FILE(s)]", "Run the type checker" + desc 'typecheck [FILE(s)]', 'Run the type checker' long_desc %( Perform type checking on one or more files in a workspace. Check the entire workspace if no files are specified. Type checking levels are normal, typed, strict, and strong. ) - option :level, type: :string, aliases: [:mode, :m, :l], desc: "Type checking level", default: "normal" - option :directory, type: :string, aliases: :d, desc: "The workspace directory", default: "." + option :level, type: :string, aliases: [:mode, :m, :l], desc: 'Type checking level', default: 'normal' + option :directory, type: :string, aliases: :d, desc: 'The workspace directory', default: '.' # @return [void] def typecheck *files directory = File.realpath(options[:directory]) @@ -199,28 +199,28 @@ def typecheck *files next if problems.empty? problems.sort! { |a, b| a.location.range.start.line <=> b.location.range.start.line } puts problems.map { |prob| - "#{prob.location.filename}:#{prob.location.range.start.line + 1} - #{prob.message}" - }.join("\n") + '#{prob.location.filename}:#{prob.location.range.start.line + 1} - #{prob.message}' + }.join('\n') filecount += 1 probcount += problems.length end - # " + # ' } - puts "Typecheck finished in #{time.real} seconds." - puts "#{probcount} problem#{probcount != 1 ? "s" : ""} found#{files.length != 1 ? " in #{filecount} of #{files.length} files" : ""}." - # " + puts 'Typecheck finished in #{time.real} seconds.' + puts '#{probcount} problem#{probcount != 1 ? 's' : ''} found#{files.length != 1 ? ' in #{filecount} of #{files.length} files' : ''}.' + # ' exit 1 if probcount > 0 end - desc "scan", "Test the workspace for problems" + desc 'scan', 'Test the workspace for problems' long_desc %( A scan loads the entire workspace to make sure that the ASTs and maps do not raise errors during analysis. It does not perform any type checking or validation; it only confirms that the analysis itself is error-free. ) - option :directory, type: :string, aliases: :d, desc: "The workspace directory", default: "." - option :verbose, type: :boolean, aliases: :v, desc: "Verbose output", default: false + option :directory, type: :string, aliases: :d, desc: 'The workspace directory', default: '.' + option :verbose, type: :boolean, aliases: :v, desc: 'Verbose output', default: false # @return [void] def scan directory = File.realpath(options[:directory]) @@ -234,24 +234,24 @@ def scan pin.typify api_map pin.probe api_map rescue StandardError => e - STDERR.puts "Error testing #{pin_description(pin)} #{pin.location ? "at #{pin.location.filename}:#{pin.location.range.start.line + 1}" : ""}" - STDERR.puts "[#{e.class}]: #{e.message}" - STDERR.puts e.backtrace.join("\n") + STDERR.puts 'Error testing #{pin_description(pin)} #{pin.location ? 'at #{pin.location.filename}:#{pin.location.range.start.line + 1}' : ''}' + STDERR.puts '[#{e.class}]: #{e.message}' + STDERR.puts e.backtrace.join('\n') exit 1 end end } - puts "Scanned #{directory} (#{api_map.pins.length} pins) in #{time.real} seconds." + puts 'Scanned #{directory} (#{api_map.pins.length} pins) in #{time.real} seconds.' end - desc "list", "List the files in the workspace and the total count" - option :count, type: :boolean, aliases: :c, desc: "Display the file count only", default: false - option :directory, type: :string, aliases: :d, desc: "The directory to read", default: "." + desc 'list', 'List the files in the workspace and the total count' + option :count, type: :boolean, aliases: :c, desc: 'Display the file count only', default: false + option :directory, type: :string, aliases: :d, desc: 'The directory to read', default: '.' # @return [void] def list workspace = Solargraph::Workspace.new(options[:directory]) puts workspace.filenames unless options[:count] - puts "#{workspace.filenames.length} files total." + puts '#{workspace.filenames.length} files total.' end private @@ -261,14 +261,14 @@ def list def pin_description pin desc = if pin.path.nil? || pin.path.empty? if pin.closure - "#{pin.closure.path} | #{pin.name}" + '#{pin.closure.path} | #{pin.name}' else - "#{pin.context.namespace} | #{pin.name}" + '#{pin.context.namespace} | #{pin.name}' end else pin.path end - desc += " (#{pin.location.filename} #{pin.location.range.start.line})" if pin.location + desc += ' (#{pin.location.filename} #{pin.location.range.start.line})' if pin.location desc end From da927a6f00a0b4cd8c5895d540811cee7cf26d08 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 28 Jul 2025 19:36:54 -0400 Subject: [PATCH 394/561] Revert double quote changes for another PR --- lib/solargraph/shell.rb | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/solargraph/shell.rb b/lib/solargraph/shell.rb index 047ec175f..4bc88b2df 100755 --- a/lib/solargraph/shell.rb +++ b/lib/solargraph/shell.rb @@ -1,9 +1,9 @@ # frozen_string_literal: true -require "benchmark" -require "thor" -require "yard" -require "yaml" +require 'benchmark' +require 'thor' +require 'yard' +require 'yaml' module Solargraph class Shell < Thor @@ -16,7 +16,7 @@ def self.exit_on_failure? map %w[--version -v] => :version - desc "--version, -v", "Print the version" + desc '--version, -v', 'Print the version' # @return [void] def version puts Solargraph::VERSION @@ -39,7 +39,7 @@ def socket end # @sg-ignore https://github.com/castwide/backport/pull/5 Backport.prepare_tcp_server host: options[:host], port: port, adapter: Solargraph::LanguageServer::Transport::Adapter - STDERR.puts 'Solargraph is listening PORT=#{port} PID=#{Process.pid}' + STDERR.puts "Solargraph is listening PORT=#{port} PID=#{Process.pid}" end end @@ -64,7 +64,7 @@ def stdio option :extensions, type: :boolean, aliases: :e, desc: 'Add installed extensions', default: true # @param directory [String] # @return [void] - def config directory = '.' + def config(directory = '.') matches = [] if options[:extensions] # @sg-ignore @@ -93,7 +93,7 @@ def config directory = '.' ) # @return [void] def clear - puts 'Deleting all cached documentation (gems, core and stdlib)' + puts "Deleting all cached documentation (gems, core and stdlib)" Solargraph::PinCache.clear end map 'clear-cache' => :clear From c8aa61f5f89d77d9fa5e4ab8dd2042eb340976e8 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 28 Jul 2025 19:37:57 -0400 Subject: [PATCH 395/561] Revert double quote changes for another PR --- lib/solargraph/shell.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/solargraph/shell.rb b/lib/solargraph/shell.rb index 4bc88b2df..ff44018d4 100755 --- a/lib/solargraph/shell.rb +++ b/lib/solargraph/shell.rb @@ -207,7 +207,7 @@ def typecheck *files # ' } puts 'Typecheck finished in #{time.real} seconds.' - puts '#{probcount} problem#{probcount != 1 ? 's' : ''} found#{files.length != 1 ? ' in #{filecount} of #{files.length} files' : ''}.' + puts "{probcount} problem#{probcount != 1 ? 's' : ''} found#{files.length != 1 ? ' in #{filecount} of #{files.length} files' : ''}." # ' exit 1 if probcount > 0 end @@ -234,7 +234,7 @@ def scan pin.typify api_map pin.probe api_map rescue StandardError => e - STDERR.puts 'Error testing #{pin_description(pin)} #{pin.location ? 'at #{pin.location.filename}:#{pin.location.range.start.line + 1}' : ''}' + STDERR.puts "Error testing #{pin_description(pin)} #{pin.location ? 'at #{pin.location.filename}:#{pin.location.range.start.line + 1}' : ''}" STDERR.puts '[#{e.class}]: #{e.message}' STDERR.puts e.backtrace.join('\n') exit 1 From e1665fe034c920faa19a3cbfdd906357f0568e39 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 28 Jul 2025 19:38:36 -0400 Subject: [PATCH 396/561] Revert double quote changes for another PR --- lib/solargraph/shell.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/solargraph/shell.rb b/lib/solargraph/shell.rb index ff44018d4..8a3522c38 100755 --- a/lib/solargraph/shell.rb +++ b/lib/solargraph/shell.rb @@ -127,12 +127,12 @@ def gems *names gemspec = api_map.find_gem(*name.split('=')) if gemspec.nil? - warn 'Gem '#{name}' not found' + warn "Gem '#{name}' not found" else api_map.cache_gem(gemspec, rebuild: options[:rebuild], out: $stdout) end rescue Gem::MissingSpecError - warn 'Gem '#{name}' not found' + warn "Gem '#{name}' not found" end $stderr.puts 'Documentation cached for #{names.count} gems.' end From edcfc698f88c074d1cf981f7819b5c8ee811c9a7 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 28 Jul 2025 19:42:47 -0400 Subject: [PATCH 397/561] Revert double quote changes for another PR --- lib/solargraph/shell.rb | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/solargraph/shell.rb b/lib/solargraph/shell.rb index 8a3522c38..15a4e4361 100755 --- a/lib/solargraph/shell.rb +++ b/lib/solargraph/shell.rb @@ -56,7 +56,7 @@ def stdio end # @sg-ignore https://github.com/castwide/backport/pull/5 Backport.prepare_stdio_server adapter: Solargraph::LanguageServer::Transport::Adapter - STDERR.puts 'Solargraph is listening on stdio PID=#{Process.pid}' + STDERR.puts "Solargraph is listening on stdio PID=#{Process.pid}" end end @@ -105,7 +105,7 @@ def clear # @param gem [String] # @param version [String, nil] def cache gem, version = nil - gems(gem + (version ? '=#{version}' : '')) + gems(gem + (version ? "=#{version}" : '')) # ' end @@ -118,7 +118,7 @@ def gems *names if names.empty? api_map.cache_all_for_workspace!($stdout, rebuild: options[:rebuild]) else - $stderr.puts('Caching these gems: #{names}') + $stderr.puts("Caching these gems: #{names}") names.each do |name| if name == 'core' PinCache.cache_core(out: $stdout) @@ -134,7 +134,7 @@ def gems *names rescue Gem::MissingSpecError warn "Gem '#{name}' not found" end - $stderr.puts 'Documentation cached for #{names.count} gems.' + $stderr.puts "Documentation cached for #{names.count} gems." end end @@ -199,15 +199,15 @@ def typecheck *files next if problems.empty? problems.sort! { |a, b| a.location.range.start.line <=> b.location.range.start.line } puts problems.map { |prob| - '#{prob.location.filename}:#{prob.location.range.start.line + 1} - #{prob.message}' + "#{prob.location.filename}:#{prob.location.range.start.line + 1} - #{prob.message}" }.join('\n') filecount += 1 probcount += problems.length end # ' } - puts 'Typecheck finished in #{time.real} seconds.' - puts "{probcount} problem#{probcount != 1 ? 's' : ''} found#{files.length != 1 ? ' in #{filecount} of #{files.length} files' : ''}." + puts "Typecheck finished in #{time.real} seconds." + puts "{probcount} problem#{probcount != 1 ? 's' : ''} found#{files.length != 1 ? " in #{filecount} of #{files.length} files" : ''}." # ' exit 1 if probcount > 0 end @@ -234,14 +234,14 @@ def scan pin.typify api_map pin.probe api_map rescue StandardError => e - STDERR.puts "Error testing #{pin_description(pin)} #{pin.location ? 'at #{pin.location.filename}:#{pin.location.range.start.line + 1}' : ''}" - STDERR.puts '[#{e.class}]: #{e.message}' + STDERR.puts "Error testing #{pin_description(pin)} #{pin.location ? "at #{pin.location.filename}:#{pin.location.range.start.line + 1}" : ''}" + STDERR.puts "[#{e.class}]: #{e.message}" STDERR.puts e.backtrace.join('\n') exit 1 end end } - puts 'Scanned #{directory} (#{api_map.pins.length} pins) in #{time.real} seconds.' + puts "Scanned #{directory} (#{api_map.pins.length} pins) in #{time.real} seconds." end desc 'list', 'List the files in the workspace and the total count' @@ -251,7 +251,7 @@ def scan def list workspace = Solargraph::Workspace.new(options[:directory]) puts workspace.filenames unless options[:count] - puts '#{workspace.filenames.length} files total.' + puts "#{workspace.filenames.length} files total." end private @@ -261,14 +261,14 @@ def list def pin_description pin desc = if pin.path.nil? || pin.path.empty? if pin.closure - '#{pin.closure.path} | #{pin.name}' + "#{pin.closure.path} | #{pin.name}" else - '#{pin.context.namespace} | #{pin.name}' + "#{pin.context.namespace} | #{pin.name}" end else pin.path end - desc += ' (#{pin.location.filename} #{pin.location.range.start.line})' if pin.location + desc += " (#{pin.location.filename} #{pin.location.range.start.line})" if pin.location desc end From b7af7750d8cfe16c9dcc9ec74e41fdb1957c7f51 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 28 Jul 2025 19:44:27 -0400 Subject: [PATCH 398/561] Revert double quote changes for another PR --- lib/solargraph/shell.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/solargraph/shell.rb b/lib/solargraph/shell.rb index 15a4e4361..15074f27b 100755 --- a/lib/solargraph/shell.rb +++ b/lib/solargraph/shell.rb @@ -48,10 +48,10 @@ def socket def stdio require 'backport' Backport.run do - Signal.trap('INT') do + Signal.trap("INT") do Backport.stop end - Signal.trap('TERM') do + Signal.trap("TERM") do Backport.stop end # @sg-ignore https://github.com/castwide/backport/pull/5 From 027c774cb45fa42affa9bf43c0329e6ad1fbcc6a Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 28 Jul 2025 19:44:35 -0400 Subject: [PATCH 399/561] Revert double quote changes for another PR --- lib/solargraph/shell.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/solargraph/shell.rb b/lib/solargraph/shell.rb index 15074f27b..78763030d 100755 --- a/lib/solargraph/shell.rb +++ b/lib/solargraph/shell.rb @@ -84,7 +84,7 @@ def config(directory = '.') File.open(File.join(directory, '.solargraph.yml'), 'w') do |file| file.puts conf.to_yaml end - STDOUT.puts 'Configuration file initialized.' + STDOUT.puts "Configuration file initialized." end desc 'clear', 'Delete all cached documentation' From 9c3f0b4b8d9096a6cb9e45e8c4e181294eed6cb2 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 28 Jul 2025 19:56:33 -0400 Subject: [PATCH 400/561] Revert double quote changes for another PR --- lib/solargraph/shell.rb | 38 ++++++++------------------------------ spec/shell_spec.rb | 8 ++++++++ 2 files changed, 16 insertions(+), 30 deletions(-) diff --git a/lib/solargraph/shell.rb b/lib/solargraph/shell.rb index 78763030d..fa2a343a4 100755 --- a/lib/solargraph/shell.rb +++ b/lib/solargraph/shell.rb @@ -198,9 +198,7 @@ def typecheck *files problems = checker.problems next if problems.empty? problems.sort! { |a, b| a.location.range.start.line <=> b.location.range.start.line } - puts problems.map { |prob| - "#{prob.location.filename}:#{prob.location.range.start.line + 1} - #{prob.message}" - }.join('\n') + puts problems.map { |prob| "#{prob.location.filename}:#{prob.location.range.start.line + 1} - #{prob.message}" }.join("\n") filecount += 1 probcount += problems.length end @@ -208,7 +206,7 @@ def typecheck *files } puts "Typecheck finished in #{time.real} seconds." puts "{probcount} problem#{probcount != 1 ? 's' : ''} found#{files.length != 1 ? " in #{filecount} of #{files.length} files" : ''}." - # ' + # " exit 1 if probcount > 0 end @@ -236,7 +234,7 @@ def scan rescue StandardError => e STDERR.puts "Error testing #{pin_description(pin)} #{pin.location ? "at #{pin.location.filename}:#{pin.location.range.start.line + 1}" : ''}" STDERR.puts "[#{e.class}]: #{e.message}" - STDERR.puts e.backtrace.join('\n') + STDERR.puts e.backtrace.join("\n") exit 1 end end @@ -260,36 +258,16 @@ def list # @return [String] def pin_description pin desc = if pin.path.nil? || pin.path.empty? - if pin.closure - "#{pin.closure.path} | #{pin.name}" - else - "#{pin.context.namespace} | #{pin.name}" - end + if pin.closure + "#{pin.closure.path} | #{pin.name}" + else + "#{pin.context.namespace} | #{pin.name}" + end else pin.path end desc += " (#{pin.location.filename} #{pin.location.range.start.line})" if pin.location desc end - - # @param type [ComplexType] - # @return [void] - def print_type type - if options[:rbs] - puts type.to_rbs - else - puts type.rooted_tag - end - end - - # @param pin [Solargraph::Pin::Base] - # @return [void] - def print_pin pin - if options[:rbs] - puts pin.to_rbs - else - puts pin.inspect - end - end end end diff --git a/spec/shell_spec.rb b/spec/shell_spec.rb index 46cfd73f0..5c1f26bfc 100644 --- a/spec/shell_spec.rb +++ b/spec/shell_spec.rb @@ -111,6 +111,14 @@ def bundle_exec(*cmd) end describe 'gems' do + it 'complains when gem does not exist' do + output = capture_both do + shell.gems('nonexistentgem') + end + + expect(output).to include("Gem 'nonexistentgem' not found") + end + it 'caches core without erroring out' do capture_both do shell.uncache('core') From bf740419885888e2826a737f93fe92cdffccceb7 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 29 Jul 2025 06:30:15 -0400 Subject: [PATCH 401/561] More specs --- spec/pin_cache_spec.rb | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/spec/pin_cache_spec.rb b/spec/pin_cache_spec.rb index bb98af6be..c2ff986f9 100644 --- a/spec/pin_cache_spec.rb +++ b/spec/pin_cache_spec.rb @@ -68,7 +68,7 @@ end end - context 'with a gem packaged with its own RBS' do + context 'with gem packaged with its own RBS gem' do let(:gem_name) { 'base64' } before do @@ -86,4 +86,44 @@ end end end + + describe '#uncache_gem' do + subject(:call) { pin_cache.uncache_gem(gemspec, out: out) } + + let(:out) { StringIO.new } + + before do + allow(FileUtils).to receive(:rm_rf) + end + + context 'with an already cached gem' do + let(:gemspec) { Gem::Specification.find_by_name('backport') } + + it 'deletes files' do + call + + expect(FileUtils).to have_received(:rm_rf).at_least(:once) + end + end + + context 'with a non-existent gem' do + let(:gemspec) { instance_double(Gem::Specification, name: 'nonexistent', version: '0.0.1') } + + it 'does not raise an error' do + expect { call }.not_to raise_error + end + + it 'logs a message' do + call + + expect(out.string).to include('does not exist') + end + + it 'does not delete files' do + call + + expect(FileUtils).not_to have_received(:rm_rf) + end + end + end end From bbce86b3090441f9c681c94e38b3a49b00cd4b02 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 29 Jul 2025 07:10:45 -0400 Subject: [PATCH 402/561] Add spec --- spec/shell_spec.rb | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/spec/shell_spec.rb b/spec/shell_spec.rb index 5c1f26bfc..a5263df5f 100644 --- a/spec/shell_spec.rb +++ b/spec/shell_spec.rb @@ -196,6 +196,15 @@ def bundle_exec(*cmd) expect { shell.cache('stringio') }.not_to raise_error end + context 'when gem does not exist' do + subject(:call) { shell.cache('nonexistentgem8675309') } + + it 'gives a good error message' do + # capture stderr output + expect { call }.to output(/not found/).to_stderr + end + end + it 'caches gem without erroring out' do _output = capture_stdout do shell.uncache('backport') From 81d91ed7a381f78306632805549d457f041d3bff Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 29 Jul 2025 09:41:46 -0400 Subject: [PATCH 403/561] Try to reproduce CI issue --- lib/solargraph/workspace/gemspecs.rb | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/lib/solargraph/workspace/gemspecs.rb b/lib/solargraph/workspace/gemspecs.rb index 289a84a1c..eafd521d3 100644 --- a/lib/solargraph/workspace/gemspecs.rb +++ b/lib/solargraph/workspace/gemspecs.rb @@ -121,16 +121,17 @@ def fetch_dependencies gemspec dep = gemspecs.find { |dep| dep.name == runtime_dep.name } dep ||= Gem::Specification.find_by_name(runtime_dep.name, runtime_dep.requirement) deps.merge fetch_dependencies(dep) if deps.add?(dep) - rescue NoMethodError => e - raise unless e.message.include?('identifier') && e.message.include?('lib/ruby/gems') - # Can happen on system gems in Ruby 3.0, it seems: - # - # https://github.com/castwide/solargraph/actions/runs/16480452864/job/46593077954?pr=1006 - logger.debug do - "Skipping dependency #{runtime_dep.name} for #{gemspec.name} due to NoMethodError: #{e.message}" - end - - nil +# TODO: See if this is needed +# rescue NoMethodError => e +# raise unless e.message.include?('identifier') && e.message.include?('lib/ruby/gems') +# # Can happen on system gems in Ruby 3.0, it seems: +# # +# # https://github.com/castwide/solargraph/actions/runs/16480452864/job/46593077954?pr=1006 +# logger.debug do +# "Skipping dependency #{runtime_dep.name} for #{gemspec.name} due to NoMethodError: #{e.message}" +# end + +# nil rescue Gem::MissingSpecError Solargraph.logger.warn("Gem dependency #{runtime_dep.name} #{runtime_dep.requirement} " \ "for #{gemspec.name} not found in bundle.") From 4a6ef4f9963228d851984c2fe865960728464674 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 29 Jul 2025 10:07:24 -0400 Subject: [PATCH 404/561] Disable testing against head --- .github/workflows/rspec.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/rspec.yml b/.github/workflows/rspec.yml index 5bf5e9775..3eddbabe6 100644 --- a/.github/workflows/rspec.yml +++ b/.github/workflows/rspec.yml @@ -21,7 +21,13 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - ruby-version: ['3.0', '3.1', '3.2', '3.3', '3.4', 'head'] + ruby-version: + - '3.0' + - '3.1' + - '3.2' + - '3.3' + - '3.4' + # - 'head' - see https://github.com/castwide/solargraph/issues/1022 steps: - uses: actions/checkout@v3 From 0268ef15864f770c7fc8359126568e0b10755d7e Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 29 Jul 2025 10:19:07 -0400 Subject: [PATCH 405/561] Drop dead code --- lib/solargraph/shell.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/solargraph/shell.rb b/lib/solargraph/shell.rb index fa2a343a4..c23110dd1 100755 --- a/lib/solargraph/shell.rb +++ b/lib/solargraph/shell.rb @@ -131,8 +131,6 @@ def gems *names else api_map.cache_gem(gemspec, rebuild: options[:rebuild], out: $stdout) end - rescue Gem::MissingSpecError - warn "Gem '#{name}' not found" end $stderr.puts "Documentation cached for #{names.count} gems." end From 73821eebc37f445743b4478be8d38c17cf551af1 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 29 Jul 2025 10:20:20 -0400 Subject: [PATCH 406/561] Drop unintended change --- lib/solargraph/shell.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/solargraph/shell.rb b/lib/solargraph/shell.rb index c23110dd1..ff8fb74e2 100755 --- a/lib/solargraph/shell.rb +++ b/lib/solargraph/shell.rb @@ -203,7 +203,7 @@ def typecheck *files # ' } puts "Typecheck finished in #{time.real} seconds." - puts "{probcount} problem#{probcount != 1 ? 's' : ''} found#{files.length != 1 ? " in #{filecount} of #{files.length} files" : ''}." + puts "#{probcount} problem#{probcount != 1 ? 's' : ''} found#{files.length != 1 ? " in #{filecount} of #{files.length} files" : ''}." # " exit 1 if probcount > 0 end From 8564aec8504fa581d4a8e8ba01cdd03a738a0be2 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 29 Jul 2025 10:20:53 -0400 Subject: [PATCH 407/561] Drop no-longer-needed line --- lib/solargraph/shell.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/solargraph/shell.rb b/lib/solargraph/shell.rb index ff8fb74e2..2eedca90a 100755 --- a/lib/solargraph/shell.rb +++ b/lib/solargraph/shell.rb @@ -200,7 +200,6 @@ def typecheck *files filecount += 1 probcount += problems.length end - # ' } puts "Typecheck finished in #{time.real} seconds." puts "#{probcount} problem#{probcount != 1 ? 's' : ''} found#{files.length != 1 ? " in #{filecount} of #{files.length} files" : ''}." From fb988a20729e90ce552ae3dcc12495e0cc6c51f8 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 29 Jul 2025 10:29:41 -0400 Subject: [PATCH 408/561] Try abbrev instead of cgi in spec --- spec/pin_cache_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/pin_cache_spec.rb b/spec/pin_cache_spec.rb index c2ff986f9..f98763bf2 100644 --- a/spec/pin_cache_spec.rb +++ b/spec/pin_cache_spec.rb @@ -51,7 +51,7 @@ end context 'with a stdlib gem' do - let(:gem_name) { 'cgi' } + let(:gem_name) { 'abbrev' } before do Solargraph::Shell.new.uncache(gem_name) @@ -64,7 +64,7 @@ pin_cache.cache_gem(gemspec: yaml_gemspec, out: nil) # match arguments with regexp using rspec-matchers syntax - expect(File).to have_received(:write).with(%r{combined/cgi-.*-stdlib.ser$}, any_args).once + expect(File).to have_received(:write).with(%r{combined/abbrev-.*-stdlib.ser$}, any_args).once end end From 2ea23a6ed3985abaf204569d6d38ec9a02e971ab Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 29 Jul 2025 11:24:34 -0400 Subject: [PATCH 409/561] Drop unneeded error handling --- lib/solargraph/workspace/gemspecs.rb | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/lib/solargraph/workspace/gemspecs.rb b/lib/solargraph/workspace/gemspecs.rb index eafd521d3..bce35b1b7 100644 --- a/lib/solargraph/workspace/gemspecs.rb +++ b/lib/solargraph/workspace/gemspecs.rb @@ -45,15 +45,8 @@ def resolve_require require # Determine gem name based on the require path file = "lib/#{require}.rb" - begin - # @sg-ignore - spec_with_path = Gem::Specification.find_by_path(file) - rescue Gem::MissingSpecError - logger.debug do - "Require #{require.inspect} could not be resolved to a gem via find_by_name or path of #{file}" - end - [] - end + # @sg-ignore + spec_with_path = Gem::Specification.find_by_path(file) all_gemspecs = all_gemspecs_from_bundle @@ -121,17 +114,6 @@ def fetch_dependencies gemspec dep = gemspecs.find { |dep| dep.name == runtime_dep.name } dep ||= Gem::Specification.find_by_name(runtime_dep.name, runtime_dep.requirement) deps.merge fetch_dependencies(dep) if deps.add?(dep) -# TODO: See if this is needed -# rescue NoMethodError => e -# raise unless e.message.include?('identifier') && e.message.include?('lib/ruby/gems') -# # Can happen on system gems in Ruby 3.0, it seems: -# # -# # https://github.com/castwide/solargraph/actions/runs/16480452864/job/46593077954?pr=1006 -# logger.debug do -# "Skipping dependency #{runtime_dep.name} for #{gemspec.name} due to NoMethodError: #{e.message}" -# end - -# nil rescue Gem::MissingSpecError Solargraph.logger.warn("Gem dependency #{runtime_dep.name} #{runtime_dep.requirement} " \ "for #{gemspec.name} not found in bundle.") From 594bd268e3921d4bea1c0efb1a6953a3f25173ee Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 29 Jul 2025 11:38:16 -0400 Subject: [PATCH 410/561] More specs --- spec/rbs_map_spec.rb | 7 +- spec/shell_spec.rb | 11 +++ .../gemspecs_resolve_require_spec.rb | 87 +++++++++++-------- 3 files changed, 66 insertions(+), 39 deletions(-) diff --git a/spec/rbs_map_spec.rb b/spec/rbs_map_spec.rb index a18c5d5b5..f3ca90a36 100644 --- a/spec/rbs_map_spec.rb +++ b/spec/rbs_map_spec.rb @@ -6,9 +6,14 @@ expect(pin).not_to be_nil end - it 'fails if it does not find data' do + it 'fails if it does not find data from gemspec' do spec = Gem::Specification.find_by_name('backport') rbs_map = Solargraph::RbsMap.from_gemspec(spec, nil, nil) + expect(rbs_map).not_to be_resolved + end + + it 'fails if it does not find data from name' do + rbs_map = Solargraph::RbsMap.new('lskdflaksdfjl') expect(rbs_map.pins).to be_empty end diff --git a/spec/shell_spec.rb b/spec/shell_spec.rb index a5263df5f..8a73b291b 100644 --- a/spec/shell_spec.rb +++ b/spec/shell_spec.rb @@ -65,6 +65,17 @@ def bundle_exec(*cmd) end end + describe 'scan' do + it 'scans without erroring out' do + output = capture_stdout do + shell.options = { directory: 'spec/fixtures/workspace' } + shell.scan + end + + expect(output).to include('Scanned ').and include(' seconds.') + end + end + describe 'typecheck' do it 'typechecks without erroring out' do output = capture_stdout do diff --git a/spec/workspace/gemspecs_resolve_require_spec.rb b/spec/workspace/gemspecs_resolve_require_spec.rb index d09d9d16f..6e8f69442 100644 --- a/spec/workspace/gemspecs_resolve_require_spec.rb +++ b/spec/workspace/gemspecs_resolve_require_spec.rb @@ -15,6 +15,22 @@ def find_or_install gem_name, version install_gem(gem_name, version) end + def add_bundle + # write out Gemfile + File.write(File.join(dir_path, 'Gemfile'), <<~GEMFILE) + source 'https://rubygems.org' + gem 'backport' + GEMFILE + # run bundle install + output, status = Solargraph.with_clean_env do + Open3.capture2e('bundle install --verbose', chdir: dir_path) + end + raise "Failure installing bundle: #{output}" unless status.success? + # ensure Gemfile.lock exists + return if File.exist?(File.join(dir_path, 'Gemfile.lock')) + raise "Gemfile.lock not found after bundle install in #{dir_path}" + end + def install_gem gem_name, version Bundler.with_unbundled_env do cmd = Gem::Commands::InstallCommand.new @@ -92,25 +108,8 @@ def install_gem gem_name, version end end - context 'with Gemfile' do - before do - # write out Gemfile - File.write(File.join(dir_path, 'Gemfile'), <<~GEMFILE) - source 'https://rubygems.org' - gem 'backport' - GEMFILE - - # run bundle install - output, status = Solargraph.with_clean_env do - Open3.capture2e('bundle install --verbose', chdir: dir_path) - end - raise "Failure installing bundle: #{output}" unless status.success? - - # ensure Gemfile.lock exists - unless File.exist?(File.join(dir_path, 'Gemfile.lock')) - raise "Gemfile.lock not found after bundle install in #{dir_path}" - end - end + context 'with Gemfile and Bundler.require' do + before { add_bundle } let(:require) { 'bundler/require' } @@ -121,31 +120,43 @@ def install_gem gem_name, version it 'returns gems' do expect(specs.map(&:name)).to include('backport') end + end + + context 'with Gemfile but an unknown gem' do + before { add_bundle } + let(:require) { 'unknown_gemlaksdflkdf' } + + it 'returns nil' do + expect(specs).to be_nil + end + end + + context 'with a Gemfile and a gem preference' do # find_or_install helper doesn't seem to work on older versions if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('3.1.0') - context 'with a gem preference' do - before do - find_or_install('backport', '1.0.0') - Gem::Specification.find_by_name('backport', '= 1.0.0') - end + warn 'running the cool kid tests' + before do + add_bundle + find_or_install('backport', '1.0.0') + Gem::Specification.find_by_name('backport', '= 1.0.0') + end - let(:preferences) do - [ - Gem::Specification.new.tap do |spec| - spec.name = 'backport' - spec.version = '1.0.0' - end - ] - end + let(:preferences) do + [ + Gem::Specification.new.tap do |spec| + spec.name = 'backport' + spec.version = '1.0.0' + end + ] + end - it 'returns the preferred gemspec' do - gemspecs = described_class.new(dir_path, preferences: preferences) - specs = gemspecs.resolve_require('backport') - backport = specs.find { |spec| spec.name == 'backport' } + it 'returns the preferred gemspec' do + gemspecs = described_class.new(dir_path, preferences: preferences) + specs = gemspecs.resolve_require('backport') + backport = specs.find { |spec| spec.name == 'backport' } - expect(backport.version.to_s).to eq('1.0.0') - end + expect(backport.version.to_s).to eq('1.0.0') end context 'with a gem preference that does not exist' do From d381ba757ece988453b11b848271a690e76e382c Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 29 Jul 2025 12:11:15 -0400 Subject: [PATCH 411/561] More specs --- .../gemspecs_resolve_require_spec.rb | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/spec/workspace/gemspecs_resolve_require_spec.rb b/spec/workspace/gemspecs_resolve_require_spec.rb index 6e8f69442..6329b1797 100644 --- a/spec/workspace/gemspecs_resolve_require_spec.rb +++ b/spec/workspace/gemspecs_resolve_require_spec.rb @@ -56,6 +56,27 @@ def install_gem gem_name, version end end + context 'with an unknown type from Bundler / RubyGems' do + let(:require) { 'solargraph' } + + let(:specish_objects) { [double()] } + + let(:locked_gems) { double(specs: specish_objects) } + + before do + # specish_objects = Bundler.definition.locked_gems.specs + allow(Bundler.definition).to receive(:locked_gems).and_return(locked_gems) + end + + it 'returns a single spec' do + expect(specs.size).to eq(1) + end + + it 'resolves to the right known gem' do + expect(specs.map(&:name)).to eq(['solargraph']) + end + end + context 'with a less usual require mapping' do let(:require) { 'diff/lcs' } From b534c026eb171cf93d408ce821cba24a399c76de Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 29 Jul 2025 12:19:48 -0400 Subject: [PATCH 412/561] Fix pin caching --- spec/workspace/gemspecs_resolve_require_spec.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/spec/workspace/gemspecs_resolve_require_spec.rb b/spec/workspace/gemspecs_resolve_require_spec.rb index 6329b1797..c95535eb6 100644 --- a/spec/workspace/gemspecs_resolve_require_spec.rb +++ b/spec/workspace/gemspecs_resolve_require_spec.rb @@ -59,9 +59,9 @@ def install_gem gem_name, version context 'with an unknown type from Bundler / RubyGems' do let(:require) { 'solargraph' } - let(:specish_objects) { [double()] } + let(:specish_objects) { [double] } - let(:locked_gems) { double(specs: specish_objects) } + let(:locked_gems) { double(specs: specish_objects) } # rubocop:disable RSpec/VerifiedDoubles before do # specish_objects = Bundler.definition.locked_gems.specs @@ -156,7 +156,6 @@ def install_gem gem_name, version context 'with a Gemfile and a gem preference' do # find_or_install helper doesn't seem to work on older versions if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('3.1.0') - warn 'running the cool kid tests' before do add_bundle find_or_install('backport', '1.0.0') From 17fe05f8e73d49bfd2a566dc3b06d18602405d85 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 29 Jul 2025 12:25:06 -0400 Subject: [PATCH 413/561] Rebaseline rubocop todo --- .rubocop_todo.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 171a16938..754162de0 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -248,10 +248,12 @@ Layout/IndentationWidth: - 'lib/solargraph.rb' - 'lib/solargraph/diagnostics/rubocop.rb' - 'lib/solargraph/language_server/message/extended/check_gem_version.rb' + - 'lib/solargraph/library.rb' - 'lib/solargraph/parser/parser_gem/node_methods.rb' - 'lib/solargraph/parser/parser_gem/node_processors/block_node.rb' - 'lib/solargraph/pin/method.rb' - 'lib/solargraph/pin/namespace.rb' + - 'lib/solargraph/shell.rb' - 'lib/solargraph/source/chain/call.rb' - 'lib/solargraph/source_map/clip.rb' - 'lib/solargraph/source_map/mapper.rb' @@ -881,7 +883,6 @@ Naming/VariableName: RSpec/Be: Exclude: - 'spec/rbs_map/stdlib_map_spec.rb' - - 'spec/rbs_map_spec.rb' - 'spec/source/source_chainer_spec.rb' # This cop supports unsafe autocorrection (--autocorrect-all). @@ -1777,7 +1778,6 @@ Style/GuardClause: - 'lib/solargraph/api_map.rb' - 'lib/solargraph/library.rb' - 'lib/solargraph/parser/parser_gem/node_processors/send_node.rb' - - 'lib/solargraph/pin_cache.rb' - 'lib/solargraph/range.rb' - 'lib/solargraph/rbs_map/conversions.rb' - 'lib/solargraph/source.rb' @@ -1927,6 +1927,7 @@ Style/MethodDefParentheses: - 'lib/solargraph/position.rb' - 'lib/solargraph/range.rb' - 'lib/solargraph/rbs_map/conversions.rb' + - 'lib/solargraph/shell.rb' - 'lib/solargraph/source.rb' - 'lib/solargraph/source/chain/call.rb' - 'lib/solargraph/source/chain/constant.rb' @@ -2282,6 +2283,7 @@ Style/StringLiterals: - 'lib/solargraph/pin/parameter.rb' - 'lib/solargraph/rbs_map/conversions.rb' - 'lib/solargraph/server_methods.rb' + - 'lib/solargraph/shell.rb' - 'lib/solargraph/workspace.rb' - 'lib/solargraph/yard_map/mapper/to_method.rb' - 'lib/solargraph/yard_tags.rb' From af0e537d1303c0eb202837aa8efd75eeef3fe186 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 29 Jul 2025 12:43:58 -0400 Subject: [PATCH 414/561] Try again with logger, which is formally in the gemspec as a dependency --- spec/pin_cache_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/pin_cache_spec.rb b/spec/pin_cache_spec.rb index f98763bf2..825f0facb 100644 --- a/spec/pin_cache_spec.rb +++ b/spec/pin_cache_spec.rb @@ -51,7 +51,7 @@ end context 'with a stdlib gem' do - let(:gem_name) { 'abbrev' } + let(:gem_name) { 'logger' } before do Solargraph::Shell.new.uncache(gem_name) @@ -64,7 +64,7 @@ pin_cache.cache_gem(gemspec: yaml_gemspec, out: nil) # match arguments with regexp using rspec-matchers syntax - expect(File).to have_received(:write).with(%r{combined/abbrev-.*-stdlib.ser$}, any_args).once + expect(File).to have_received(:write).with(%r{combined/logger-.*-stdlib.ser$}, any_args).once end end From 05b68679e35c86de9ca8689abec5e4a0b1fd16be Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 29 Jul 2025 12:59:00 -0400 Subject: [PATCH 415/561] Drop un-needed new code --- lib/solargraph/api_map.rb | 7 ------- lib/solargraph/api_map/store.rb | 7 +------ 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/lib/solargraph/api_map.rb b/lib/solargraph/api_map.rb index 261edae52..71169157c 100755 --- a/lib/solargraph/api_map.rb +++ b/lib/solargraph/api_map.rb @@ -256,13 +256,6 @@ def keyword_pins store.pins_by_class(Pin::Keyword) end - # An array of namespace names defined in the ApiMap. - # - # @return [Set] - def namespaces - store.namespaces - end - # True if the namespace exists. # # @param name [String] The namespace to match diff --git a/lib/solargraph/api_map/store.rb b/lib/solargraph/api_map/store.rb index d3c8fc911..413c00aa7 100644 --- a/lib/solargraph/api_map/store.rb +++ b/lib/solargraph/api_map/store.rb @@ -31,7 +31,7 @@ def update *pinsets pinsets[changed..].each_with_index do |pins, idx| @pinsets[changed + idx] = pins @indexes[changed + idx] = if pins.empty? - @indexes[changed + idx - 1] + @indexes[changed + idx - 1] else @indexes[changed + idx - 1].merge(pins) end @@ -132,11 +132,6 @@ def namespace_exists?(fqns) fqns_pins(fqns).any? end - # @return [Set] - def namespaces - index.namespaces - end - # @return [Enumerable] def namespace_pins pins_by_class(Solargraph::Pin::Namespace) From 715274fc054558e5725ea253f5470eefa7e163a1 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 29 Jul 2025 13:34:30 -0400 Subject: [PATCH 416/561] Rebaseline rubocop todo --- .rubocop_todo.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 754162de0..3f3f654e9 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -246,6 +246,7 @@ Layout/HeredocIndentation: Layout/IndentationWidth: Exclude: - 'lib/solargraph.rb' + - 'lib/solargraph/api_map/store.rb' - 'lib/solargraph/diagnostics/rubocop.rb' - 'lib/solargraph/language_server/message/extended/check_gem_version.rb' - 'lib/solargraph/library.rb' From 373063bebf784bb67fdf9548f2c4edda41e36c03 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 29 Jul 2025 13:59:58 -0400 Subject: [PATCH 417/561] Another spec --- lib/solargraph/workspace/gemspecs.rb | 2 ++ .../gemspecs_fetch_dependencies_spec.rb | 22 +++++++++++++++---- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/lib/solargraph/workspace/gemspecs.rb b/lib/solargraph/workspace/gemspecs.rb index bce35b1b7..654c2e224 100644 --- a/lib/solargraph/workspace/gemspecs.rb +++ b/lib/solargraph/workspace/gemspecs.rb @@ -273,6 +273,8 @@ def only_runtime_dependencies gemspec unless gemspec.respond_to?(:dependencies) && gemspec.respond_to?(:development_dependencies) gemspec = to_gem_specification(gemspec) end + return [] if gemspec.nil? + gemspec.dependencies - gemspec.development_dependencies end diff --git a/spec/workspace/gemspecs_fetch_dependencies_spec.rb b/spec/workspace/gemspecs_fetch_dependencies_spec.rb index 0596d0f12..85ca68207 100644 --- a/spec/workspace/gemspecs_fetch_dependencies_spec.rb +++ b/spec/workspace/gemspecs_fetch_dependencies_spec.rb @@ -24,14 +24,14 @@ let(:dir_path) { File.realpath(Dir.mktmpdir).to_s } let(:gemspec) do - Bundler::LazySpecification.new('undercover', nil, nil) + Bundler::LazySpecification.new(gem_name, nil, nil) end before do # write out Gemfile File.write(File.join(dir_path, 'Gemfile'), <<~GEMFILE) source 'https://rubygems.org' - gem 'undercover' + gem '#{gem_name}' GEMFILE # run bundle install @@ -46,8 +46,22 @@ end end - it 'finds dependencies' do - expect(deps.map(&:name)).to include('ast') + context 'with gem that exists in our bundle' do + let(:gem_name) { 'undercover' } + + it 'finds dependencies' do + expect(deps.map(&:name)).to include('ast') + end + end + + context 'with gem does not hat eists in our bundle' do + let(:gem_name) { 'activerecord' } + + it 'gives a useful message' do + dep_names = nil + output = capture_both { dep_names = deps.map(&:name) } + expect(output).to include('Please install the gem activerecord') + end end end end From 79ffb59804bc21bfc3ddfc0cbbc8caad87fac6f1 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 29 Jul 2025 14:36:24 -0400 Subject: [PATCH 418/561] Improve coverage --- .../gemspecs_resolve_require_spec.rb | 53 ++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/spec/workspace/gemspecs_resolve_require_spec.rb b/spec/workspace/gemspecs_resolve_require_spec.rb index c95535eb6..ad0c5ec90 100644 --- a/spec/workspace/gemspecs_resolve_require_spec.rb +++ b/spec/workspace/gemspecs_resolve_require_spec.rb @@ -62,10 +62,61 @@ def install_gem gem_name, version let(:specish_objects) { [double] } let(:locked_gems) { double(specs: specish_objects) } # rubocop:disable RSpec/VerifiedDoubles + let(:lockfile) { instance_double(Bundler::LockfileParser, specs: specish_objects) } + let(:definition) { instance_double(Bundler::Definition, + locked_gems: locked_gems, + lockfile: lockfile) } before do # specish_objects = Bundler.definition.locked_gems.specs - allow(Bundler.definition).to receive(:locked_gems).and_return(locked_gems) + allow(Bundler).to receive(:definition).and_return(definition) + allow(lockfile).to receive(:to_s).and_return(dir_path) + end + + it 'returns a single spec' do + expect(specs.size).to eq(1) + end + + it 'resolves to the right known gem' do + expect(specs.map(&:name)).to eq(['solargraph']) + end + end + + context 'with a Bundler::StubSpecification from Bundler / RubyGems' do + # this can happen from local gems, which is hard to test + # organically + + let(:require) { 'solargraph' } + + let(:spec_fetcher) { instance_double(Gem::SpecFetcher) } + let(:platform) { Gem::Platform::RUBY } + let(:bundler_stub_spec) { Bundler::StubSpecification.new('solargraph', '123', platform, spec_fetcher) } + let(:gem_stub_spec) { Gem::StubSpecification.new('solargraph', '123', platform, spec_fetcher) } + + let(:specish_objects) { [bundler_stub_spec] } + + let(:real_spec) { instance_double(Gem::Specification) } + + let(:locked_gems) { double(specs: specish_objects) } # rubocop:disable RSpec/VerifiedDoubles + let(:lockfile) { instance_double(Bundler::LockfileParser, specs: specish_objects) } + let(:definition) { instance_double(Bundler::Definition, + locked_gems: locked_gems, + lockfile: lockfile) } + + before do + # specish_objects = Bundler.definition.locked_gems.specs + allow(Bundler).to receive(:definition).and_return(definition) + allow(lockfile).to receive(:to_s).and_return(dir_path) + allow(bundler_stub_spec).to receive(:name).and_return('solargraph') + allow(bundler_stub_spec).to receive(:respond_to?).with(:name).and_return(true) + allow(bundler_stub_spec).to receive(:respond_to?).with(:version).and_return(true) + allow(bundler_stub_spec).to receive(:respond_to?).with(:gem_dir).and_return(false) + allow(bundler_stub_spec).to receive(:respond_to?).with(:materialize_for_installation).and_return(false) + allow(bundler_stub_spec).to receive(:stub).and_return(gem_stub_spec) + allow(gem_stub_spec).to receive(:name).and_return('solargraph') + allow(gem_stub_spec).to receive(:version).and_return('123') + allow(gem_stub_spec).to receive(:spec).and_return(real_spec) + allow(real_spec).to receive(:name).and_return('solargraph') end it 'returns a single spec' do From 70d63656347680a9c4a46040167788779332d02f Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 29 Jul 2025 14:50:53 -0400 Subject: [PATCH 419/561] Another spec --- .../gemspecs_resolve_require_spec.rb | 44 +++++++------------ 1 file changed, 17 insertions(+), 27 deletions(-) diff --git a/spec/workspace/gemspecs_resolve_require_spec.rb b/spec/workspace/gemspecs_resolve_require_spec.rb index ad0c5ec90..96cc35a77 100644 --- a/spec/workspace/gemspecs_resolve_require_spec.rb +++ b/spec/workspace/gemspecs_resolve_require_spec.rb @@ -58,17 +58,14 @@ def install_gem gem_name, version context 'with an unknown type from Bundler / RubyGems' do let(:require) { 'solargraph' } - let(:specish_objects) { [double] } - let(:locked_gems) { double(specs: specish_objects) } # rubocop:disable RSpec/VerifiedDoubles - let(:lockfile) { instance_double(Bundler::LockfileParser, specs: specish_objects) } - let(:definition) { instance_double(Bundler::Definition, - locked_gems: locked_gems, - lockfile: lockfile) } - before do - # specish_objects = Bundler.definition.locked_gems.specs + lockfile = instance_double(Pathname) + locked_gems = instance_double(Bundler::LockfileParser, specs: specish_objects) + definition = instance_double(Bundler::Definition, + locked_gems: locked_gems, + lockfile: lockfile) allow(Bundler).to receive(:definition).and_return(definition) allow(lockfile).to receive(:to_s).and_return(dir_path) end @@ -87,35 +84,28 @@ def install_gem gem_name, version # organically let(:require) { 'solargraph' } - let(:spec_fetcher) { instance_double(Gem::SpecFetcher) } - let(:platform) { Gem::Platform::RUBY } - let(:bundler_stub_spec) { Bundler::StubSpecification.new('solargraph', '123', platform, spec_fetcher) } - let(:gem_stub_spec) { Gem::StubSpecification.new('solargraph', '123', platform, spec_fetcher) } - - let(:specish_objects) { [bundler_stub_spec] } - - let(:real_spec) { instance_double(Gem::Specification) } - - let(:locked_gems) { double(specs: specish_objects) } # rubocop:disable RSpec/VerifiedDoubles - let(:lockfile) { instance_double(Bundler::LockfileParser, specs: specish_objects) } - let(:definition) { instance_double(Bundler::Definition, - locked_gems: locked_gems, - lockfile: lockfile) } before do + platform = Gem::Platform::RUBY + gem_stub_spec = Gem::StubSpecification.new('solargraph', '123', platform, spec_fetcher) + real_spec = instance_double(Gem::Specification) + bundler_stub_spec = Bundler::StubSpecification.new('solargraph', '123', platform, spec_fetcher) + specish_objects = [bundler_stub_spec] + lockfile = instance_double(Pathname) + locked_gems = instance_double(Bundler::LockfileParser, specs: specish_objects) + definition = instance_double(Bundler::Definition, + locked_gems: locked_gems, + lockfile: lockfile) # specish_objects = Bundler.definition.locked_gems.specs allow(Bundler).to receive(:definition).and_return(definition) allow(lockfile).to receive(:to_s).and_return(dir_path) - allow(bundler_stub_spec).to receive(:name).and_return('solargraph') allow(bundler_stub_spec).to receive(:respond_to?).with(:name).and_return(true) allow(bundler_stub_spec).to receive(:respond_to?).with(:version).and_return(true) allow(bundler_stub_spec).to receive(:respond_to?).with(:gem_dir).and_return(false) allow(bundler_stub_spec).to receive(:respond_to?).with(:materialize_for_installation).and_return(false) - allow(bundler_stub_spec).to receive(:stub).and_return(gem_stub_spec) - allow(gem_stub_spec).to receive(:name).and_return('solargraph') - allow(gem_stub_spec).to receive(:version).and_return('123') - allow(gem_stub_spec).to receive(:spec).and_return(real_spec) + allow(bundler_stub_spec).to receive_messages(name: 'solargraph', stub: gem_stub_spec) + allow(gem_stub_spec).to receive_messages(name: 'solargraph', version: '123', spec: real_spec) allow(real_spec).to receive(:name).and_return('solargraph') end From 17be263a2b3cfa17055a6d2fbf17b213f6ea42ae Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 29 Jul 2025 16:23:46 -0400 Subject: [PATCH 420/561] Another spec --- lib/solargraph/doc_map.rb | 7 +++-- lib/solargraph/workspace.rb | 5 ++-- lib/solargraph/workspace/gemspecs.rb | 15 +++++----- spec/doc_map_spec.rb | 2 +- .../gemspecs_fetch_dependencies_spec.rb | 30 +++++++++++++++---- 5 files changed, 40 insertions(+), 19 deletions(-) diff --git a/lib/solargraph/doc_map.rb b/lib/solargraph/doc_map.rb index 9d869a180..593d3971e 100644 --- a/lib/solargraph/doc_map.rb +++ b/lib/solargraph/doc_map.rb @@ -77,9 +77,10 @@ def unresolved_requires @unresolved_requires ||= required_gems_map.select { |_, gemspecs| gemspecs.nil? }.keys end + # @param out [IO, nil] output stream for logging # @return [Set] - def dependencies - @dependencies ||= (gemspecs.flat_map { |spec| workspace.fetch_dependencies(spec) } - gemspecs).to_set + def dependencies out: $stderr + @dependencies ||= (gemspecs.flat_map { |spec| workspace.fetch_dependencies(spec, out: out) } - gemspecs).to_set end # Cache gem documentation if needed for this doc_map @@ -112,7 +113,7 @@ def load_serialized_gem_pins out: $stderr missing_paths = Hash[without_gemspecs].keys # @sg-ignore Need support for RBS duck interfaces like _ToHash # @type [Array] - gemspecs = Hash[with_gemspecs].values.flatten.compact + dependencies.to_a + gemspecs = Hash[with_gemspecs].values.flatten.compact + dependencies(out: out).to_a missing_paths.each do |path| # this will load from disk if needed; no need to manage diff --git a/lib/solargraph/workspace.rb b/lib/solargraph/workspace.rb index dbc6f892c..fa138bf13 100644 --- a/lib/solargraph/workspace.rb +++ b/lib/solargraph/workspace.rb @@ -49,10 +49,11 @@ def config @config ||= Solargraph::Workspace::Config.new(directory) end + # @param out [IO, nil] output stream for logging # @param gemspec [Gem::Specification] # @return [Array] - def fetch_dependencies gemspec - gemspecs.fetch_dependencies(gemspec) + def fetch_dependencies gemspec, out: $stderr + gemspecs.fetch_dependencies(gemspec, out: out) end # @return [Solargraph::PinCache] diff --git a/lib/solargraph/workspace/gemspecs.rb b/lib/solargraph/workspace/gemspecs.rb index 654c2e224..5bad76d2f 100644 --- a/lib/solargraph/workspace/gemspecs.rb +++ b/lib/solargraph/workspace/gemspecs.rb @@ -103,8 +103,10 @@ def find_gem name, version = nil, out: $stderr end # @param gemspec [Gem::Specification] + # @param out[IO, nil] output stream for logging + # # @return [Array] - def fetch_dependencies gemspec + def fetch_dependencies gemspec, out: $stderr gemspecs = all_gemspecs_from_bundle # @param runtime_dep [Gem::Dependency] @@ -113,11 +115,10 @@ def fetch_dependencies gemspec Solargraph.logger.info "Adding #{runtime_dep.name} dependency for #{gemspec.name}" dep = gemspecs.find { |dep| dep.name == runtime_dep.name } dep ||= Gem::Specification.find_by_name(runtime_dep.name, runtime_dep.requirement) - deps.merge fetch_dependencies(dep) if deps.add?(dep) rescue Gem::MissingSpecError - Solargraph.logger.warn("Gem dependency #{runtime_dep.name} #{runtime_dep.requirement} " \ - "for #{gemspec.name} not found in bundle.") - nil + dep = resolve_gem_ignoring_local_bundle runtime_dep.name, out: out + ensure + deps.merge fetch_dependencies(dep, out: out) if dep && deps.add?(dep) end.to_a.compact # RBS tracks implicit dependencies, like how the YAML standard # library implies pulling in the psych library. @@ -281,11 +282,11 @@ def only_runtime_dependencies gemspec # @todo Should this be using Gem::SpecFetcher and pull them automatically? # # @param name [String] - # @param version [String] + # @param version [String, nil] # @param out [IO, nil] output stream for logging # # @return [Gem::Specification, nil] - def resolve_gem_ignoring_local_bundle name, version, out: $stderr + def resolve_gem_ignoring_local_bundle name, version = nil, out: $stderr Gem::Specification.find_by_name(name, version) rescue Gem::MissingSpecError begin diff --git a/spec/doc_map_spec.rb b/spec/doc_map_spec.rb index c935c0c58..b2c55d54f 100644 --- a/spec/doc_map_spec.rb +++ b/spec/doc_map_spec.rb @@ -70,7 +70,7 @@ pincache = instance_double(Solargraph::PinCache) uncached_gemspec = Gem::Specification.new('uncached_gem', '1.0.0') allow(workspace).to receive(:resolve_require).with('uncached_gem').and_return([uncached_gemspec]) - allow(workspace).to receive(:fetch_dependencies).with(uncached_gemspec).and_return([]) + allow(workspace).to receive(:fetch_dependencies).with(uncached_gemspec, out: out).and_return([]) allow(workspace).to receive(:fresh_pincache).and_return(pincache) allow(pincache).to receive(:deserialize_combined_pin_cache).with(uncached_gemspec).and_return(nil) diff --git a/spec/workspace/gemspecs_fetch_dependencies_spec.rb b/spec/workspace/gemspecs_fetch_dependencies_spec.rb index 85ca68207..9a21f967c 100644 --- a/spec/workspace/gemspecs_fetch_dependencies_spec.rb +++ b/spec/workspace/gemspecs_fetch_dependencies_spec.rb @@ -10,13 +10,31 @@ let(:gemspecs) { described_class.new(dir_path) } let(:dir_path) { Dir.pwd } - context 'with a Bundler::LazySpecification in bundle' do - let(:gemspec) do - Bundler::LazySpecification.new('solargraph', nil, nil) + context 'when in our bundle' do + context 'with a Bundler::LazySpecification' do + let(:gemspec) do + Bundler::LazySpecification.new('solargraph', nil, nil) + end + + it 'finds a known dependency' do + expect(deps.map(&:name)).to include('backport') + end end - it 'finds a known dependency' do - expect(deps.map(&:name)).to include('backport') + context 'with gem whose dependency does not exist in our bundle' do + let(:gemspec) do + instance_double(Gem::Specification, + dependencies: [Gem::Dependency.new('activerecord')], + development_dependencies: [], + name: 'my_fake_gem', + version: '123') + end + let(:gem_name) { 'my_fake_gem' } + + it 'gives a useful message' do + output = capture_both { deps.map(&:name) } + expect(output).to include('Please install the gem activerecord') + end end end @@ -54,7 +72,7 @@ end end - context 'with gem does not hat eists in our bundle' do + context 'with gem does not exist in our bundle' do let(:gem_name) { 'activerecord' } it 'gives a useful message' do From e286fa558a398f54b68e3d91c0c0a9bae87e15e3 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 29 Jul 2025 16:52:53 -0400 Subject: [PATCH 421/561] More specs --- lib/solargraph/workspace/gemspecs.rb | 4 +- .../gemspecs_resolve_require_spec.rb | 59 ++++++++++++++----- 2 files changed, 45 insertions(+), 18 deletions(-) diff --git a/lib/solargraph/workspace/gemspecs.rb b/lib/solargraph/workspace/gemspecs.rb index 5bad76d2f..e99a751b5 100644 --- a/lib/solargraph/workspace/gemspecs.rb +++ b/lib/solargraph/workspace/gemspecs.rb @@ -170,8 +170,8 @@ def to_gem_specification specish if specish.respond_to?(:spec) specish.spec else - resolve_gem_ignoring_local_bundle specish.name, - specish.version + # turn the crank again + to_gem_specification(specish) end else @@warned_on_gem_type ||= diff --git a/spec/workspace/gemspecs_resolve_require_spec.rb b/spec/workspace/gemspecs_resolve_require_spec.rb index 96cc35a77..682251f78 100644 --- a/spec/workspace/gemspecs_resolve_require_spec.rb +++ b/spec/workspace/gemspecs_resolve_require_spec.rb @@ -63,6 +63,7 @@ def install_gem gem_name, version before do lockfile = instance_double(Pathname) locked_gems = instance_double(Bundler::LockfileParser, specs: specish_objects) + definition = instance_double(Bundler::Definition, locked_gems: locked_gems, lockfile: lockfile) @@ -79,6 +80,25 @@ def install_gem gem_name, version end end + def configure_bundler_spec stub_value + platform = Gem::Platform::RUBY + bundler_stub_spec = Bundler::StubSpecification.new('solargraph', '123', platform, spec_fetcher) + specish_objects = [bundler_stub_spec] + lockfile = instance_double(Pathname) + locked_gems = instance_double(Bundler::LockfileParser, specs: specish_objects) + definition = instance_double(Bundler::Definition, + locked_gems: locked_gems, + lockfile: lockfile) + # specish_objects = Bundler.definition.locked_gems.specs + allow(Bundler).to receive(:definition).and_return(definition) + allow(lockfile).to receive(:to_s).and_return(dir_path) + allow(bundler_stub_spec).to receive(:respond_to?).with(:name).and_return(true) + allow(bundler_stub_spec).to receive(:respond_to?).with(:version).and_return(true) + allow(bundler_stub_spec).to receive(:respond_to?).with(:gem_dir).and_return(false) + allow(bundler_stub_spec).to receive(:respond_to?).with(:materialize_for_installation).and_return(false) + allow(bundler_stub_spec).to receive_messages(name: 'solargraph', stub: stub_value) + end + context 'with a Bundler::StubSpecification from Bundler / RubyGems' do # this can happen from local gems, which is hard to test # organically @@ -88,25 +108,32 @@ def install_gem gem_name, version before do platform = Gem::Platform::RUBY - gem_stub_spec = Gem::StubSpecification.new('solargraph', '123', platform, spec_fetcher) real_spec = instance_double(Gem::Specification) - bundler_stub_spec = Bundler::StubSpecification.new('solargraph', '123', platform, spec_fetcher) - specish_objects = [bundler_stub_spec] - lockfile = instance_double(Pathname) - locked_gems = instance_double(Bundler::LockfileParser, specs: specish_objects) - definition = instance_double(Bundler::Definition, - locked_gems: locked_gems, - lockfile: lockfile) - # specish_objects = Bundler.definition.locked_gems.specs - allow(Bundler).to receive(:definition).and_return(definition) - allow(lockfile).to receive(:to_s).and_return(dir_path) - allow(bundler_stub_spec).to receive(:respond_to?).with(:name).and_return(true) - allow(bundler_stub_spec).to receive(:respond_to?).with(:version).and_return(true) - allow(bundler_stub_spec).to receive(:respond_to?).with(:gem_dir).and_return(false) - allow(bundler_stub_spec).to receive(:respond_to?).with(:materialize_for_installation).and_return(false) - allow(bundler_stub_spec).to receive_messages(name: 'solargraph', stub: gem_stub_spec) + allow(real_spec).to receive(:name).and_return('solargraph') + gem_stub_spec = Gem::StubSpecification.new('solargraph', '123', platform, spec_fetcher) + configure_bundler_spec(gem_stub_spec) allow(gem_stub_spec).to receive_messages(name: 'solargraph', version: '123', spec: real_spec) + end + + it 'returns a single spec' do + expect(specs.size).to eq(1) + end + + it 'resolves to the right known gem' do + expect(specs.map(&:name)).to eq(['solargraph']) + end + end + + context 'with a Bundler::StubSpecification that resolves straight to Gem::Specification' do + # have seen different behavior with different versions of rubygems/bundler + + let(:require) { 'solargraph' } + let(:spec_fetcher) { instance_double(Gem::SpecFetcher) } + let(:real_spec) { instance_double(Gem::Specification, name: 'solargraph', version: '123') } + + before do allow(real_spec).to receive(:name).and_return('solargraph') + configure_bundler_spec(real_spec) end it 'returns a single spec' do From 4e76133ea36393e7392ff99ec65d9161cf226a3f Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 29 Jul 2025 17:12:45 -0400 Subject: [PATCH 422/561] Fix spec --- spec/workspace/gemspecs_resolve_require_spec.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/spec/workspace/gemspecs_resolve_require_spec.rb b/spec/workspace/gemspecs_resolve_require_spec.rb index 682251f78..f62ece83b 100644 --- a/spec/workspace/gemspecs_resolve_require_spec.rb +++ b/spec/workspace/gemspecs_resolve_require_spec.rb @@ -129,10 +129,9 @@ def configure_bundler_spec stub_value let(:require) { 'solargraph' } let(:spec_fetcher) { instance_double(Gem::SpecFetcher) } - let(:real_spec) { instance_double(Gem::Specification, name: 'solargraph', version: '123') } + let(:real_spec) { Gem::Specification.new('solargraph', '123') } before do - allow(real_spec).to receive(:name).and_return('solargraph') configure_bundler_spec(real_spec) end From 148521f9e2bc41df9dc693a0c4185acf7ed64a7e Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 30 Jul 2025 07:26:37 -0400 Subject: [PATCH 423/561] Fix merge issue --- lib/solargraph/workspace/gemspecs.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/solargraph/workspace/gemspecs.rb b/lib/solargraph/workspace/gemspecs.rb index 234b88091..79d66947b 100644 --- a/lib/solargraph/workspace/gemspecs.rb +++ b/lib/solargraph/workspace/gemspecs.rb @@ -46,7 +46,7 @@ def resolve_require require # Determine gem name based on the require path file = "lib/#{require}.rb" spec_with_path = Gem::Specification.find_by_path(file) -x + all_gemspecs = all_gemspecs_from_bundle gem_names_to_try = [ @@ -192,7 +192,6 @@ def query_external_bundle command 'ruby', '-e', "require 'bundler'; require 'json'; Dir.chdir('#{directory}') { puts begin; #{command}; end.to_json }" ] - # @sg-ignore o, e, s = Open3.capture3(*cmd) if s.success? Solargraph.logger.debug "External bundle: #{o}" From b6b66f397732f43f161c3e7ddc245b5318c34fbe Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 30 Jul 2025 07:59:13 -0400 Subject: [PATCH 424/561] Fix flaky spec --- spec/rbs_map/conversions_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/rbs_map/conversions_spec.rb b/spec/rbs_map/conversions_spec.rb index 75ec1c311..d9570ae0f 100644 --- a/spec/rbs_map/conversions_spec.rb +++ b/spec/rbs_map/conversions_spec.rb @@ -55,7 +55,7 @@ def bar: () -> untyped end context 'with standard loads for solargraph project' do - let(:api_map) { Solargraph::ApiMap.load('.') } + let(:api_map) { Solargraph::ApiMap.load_with_cache('.') } let(:superclass_pin) do api_map.pins.find do |pin| From 84ca5a37b37e0fab11a48cb31f98c660e1da4013 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 30 Jul 2025 08:09:46 -0400 Subject: [PATCH 425/561] Fix example in spec --- spec/shell_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/shell_spec.rb b/spec/shell_spec.rb index 8a73b291b..3cdc97276 100644 --- a/spec/shell_spec.rb +++ b/spec/shell_spec.rb @@ -141,7 +141,7 @@ def bundle_exec(*cmd) it 'has a well set up test environment' do output = bundle_exec('bundle', 'list') - expect(output).to include('language_server-protocol') + expect(output).to include('reverse_markdown') end it 'caches all without erroring out' do From 2a8f62af3858522b2a7cf7fed691eca7821dec8b Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 31 Jul 2025 08:53:47 -0400 Subject: [PATCH 426/561] Document gems command --- lib/solargraph/shell.rb | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/lib/solargraph/shell.rb b/lib/solargraph/shell.rb index 2eedca90a..29d397966 100755 --- a/lib/solargraph/shell.rb +++ b/lib/solargraph/shell.rb @@ -109,7 +109,30 @@ def cache gem, version = nil # ' end - desc 'gems [GEM[=VERSION]]', 'Cache documentation for installed gems' + desc 'gems [GEM[=VERSION]...] [STDLIB...] [core]', 'Cache documentation for + installed libraries' + long_desc %( This command will cache the + generated type documentation for the specified libraries. While + Solargraph will generate this on the fly when needed, it takes + time. This command will generate it in advance, which can be + useful for CI scenarios. + + With no arguments, it will cache all libraries in the current + workspace. If a gem or standard library name is specified, it + will cache that library's type documentation. + + An equals sign after a gem will allow a specific gem version + to be cached. + + The 'core' argument can be used to cache the type + documentation for the core Ruby libraries. + + If the library is already cached, it will be rebuilt if the + --rebuild option is set. + + Cached documentation is stored in #{PinCache.base_dir}, which + can be stored between CI runs. + ) option :rebuild, type: :boolean, desc: 'Rebuild existing documentation', default: false # @param names [Array] # @return [void] From 834e5d9f94b089dd129094601f6bfb543860ca4b Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 3 Aug 2025 19:11:01 -0400 Subject: [PATCH 427/561] Improve test speed --- lib/solargraph/api_map.rb | 23 ---- lib/solargraph/shell.rb | 10 +- lib/solargraph/workspace.rb | 13 +- spec/api_map/api_map_method_spec.rb | 45 ------- spec/api_map_spec.rb | 27 ---- spec/pin_cache_spec.rb | 52 ++++++++ spec/shell_spec.rb | 196 +++++++++------------------- spec/workspace_spec.rb | 37 ++++++ spec/yardoc_spec.rb | 4 +- 9 files changed, 171 insertions(+), 236 deletions(-) diff --git a/lib/solargraph/api_map.rb b/lib/solargraph/api_map.rb index 71169157c..d5fd46dc9 100755 --- a/lib/solargraph/api_map.rb +++ b/lib/solargraph/api_map.rb @@ -196,29 +196,6 @@ def cache_all_for_doc_map! out @doc_map.cache_doc_map_gems!(out) end - # @param out [IO, nil] - # @param rebuild [Boolean] - # @return [void] - def cache_all_for_workspace! out, rebuild: false - workspace&.cache_all_for_workspace!(out, rebuild: rebuild) - end - - # @param name [String] - # @param version [String, nil] - # - # @return [Gem::Specification, nil] - def find_gem name, version = nil - gemspecs.find_gem(name, version) - end - - # @param gemspec [Gem::Specification] - # @param rebuild [Boolean] - # @param out [IO, nil] - # @return [void] - def cache_gem gemspec, rebuild: false, out: nil - @doc_map&.cache(gemspec, rebuild: rebuild, out: out) - end - class << self include Logging end diff --git a/lib/solargraph/shell.rb b/lib/solargraph/shell.rb index 29d397966..8dd74a36f 100755 --- a/lib/solargraph/shell.rb +++ b/lib/solargraph/shell.rb @@ -137,9 +137,11 @@ def cache gem, version = nil # @param names [Array] # @return [void] def gems *names - api_map = ApiMap.load('.') + # print time with ms + workspace = Solargraph::Workspace.new('.') + if names.empty? - api_map.cache_all_for_workspace!($stdout, rebuild: options[:rebuild]) + workspace.cache_all_for_workspace!($stdout, rebuild: options[:rebuild]) else $stderr.puts("Caching these gems: #{names}") names.each do |name| @@ -148,11 +150,11 @@ def gems *names next end - gemspec = api_map.find_gem(*name.split('=')) + gemspec = workspace.find_gem(*name.split('=')) if gemspec.nil? warn "Gem '#{name}' not found" else - api_map.cache_gem(gemspec, rebuild: options[:rebuild], out: $stdout) + workspace.cache_gem(gemspec, rebuild: options[:rebuild], out: $stdout) end end $stderr.puts "Documentation cached for #{names.count} gems." diff --git a/lib/solargraph/workspace.rb b/lib/solargraph/workspace.rb index fa138bf13..6beeddc82 100644 --- a/lib/solargraph/workspace.rb +++ b/lib/solargraph/workspace.rb @@ -198,6 +198,14 @@ def rbs_collection_config_path end end + # @param name [String] + # @param version [String, nil] + # + # @return [Gem::Specification, nil] + def find_gem name, version = nil + gemspecs.find_gem(name, version) + end + # @param out [IO, nil] output stream for logging # @param rebuild [Boolean] whether to rebuild the pins even if they are cached # @return [void] @@ -212,13 +220,13 @@ def cache_all_for_workspace! out, rebuild: false specs.each do |spec| pin_cache.cache_gem(gemspec: spec, rebuild: rebuild, out: out) unless pin_cache.cached?(spec) end - out.puts "Documentation cached for all #{specs.length} gems." + out&.puts "Documentation cached for all #{specs.length} gems." # do this after so that we prefer stdlib requires from gems, # which are likely to be newer and have more pins pin_cache.cache_all_stdlibs(out: out) - out.puts "Documentation cached for core, standard library and gems." + out&.puts "Documentation cached for core, standard library and gems." end # Synchronize the workspace from the provided updater. @@ -292,7 +300,6 @@ def require_plugins def read_rbs_collection_path return unless rbs_collection_config_path - # @sg-ignore path = YAML.load_file(rbs_collection_config_path)&.fetch('path') # make fully qualified File.expand_path(path, directory) diff --git a/spec/api_map/api_map_method_spec.rb b/spec/api_map/api_map_method_spec.rb index 91899a40c..73fc6a35c 100644 --- a/spec/api_map/api_map_method_spec.rb +++ b/spec/api_map/api_map_method_spec.rb @@ -1,51 +1,6 @@ # frozen_string_literal: true describe Solargraph::ApiMap do - describe '#cache_all_for_workspace!' do - context 'with workspace' do - subject(:api_map) { described_class.load(Dir.pwd) } - - let(:out) { StringIO.new } - - it 'processes the request' do - api_map.cache_all_for_workspace!(out, rebuild: false) - - expect(out.string).to include('Documentation cached') - end - end - - context 'with no workspace' do - subject(:api_map) { described_class.new } - - it 'ignores the request' do - expect { api_map.cache_all_for_workspace!(nil, rebuild: false) }.not_to raise_error - end - end - end - - describe '#cache_gem' do - context 'with no workspace' do - subject(:api_map) { described_class.new } - - let(:out) { StringIO.new } - - it 'ignores the request' do - expect { api_map.cache_gem('backport', out: out) }.not_to raise_error - end - end - - context 'with workspace' do - subject(:api_map) { described_class.load(Dir.pwd) } - - let(:out) { StringIO.new } - - it 'processes the request' do - backport = Gem::Specification.find_by_name('backport') - expect { api_map.cache_gem(backport, out: out) }.not_to raise_error - end - end - end - describe '.load_with_cache' do it 'loads the API map with cache' do Solargraph::PinCache.uncache_core diff --git a/spec/api_map_spec.rb b/spec/api_map_spec.rb index 185b30be7..3a3664770 100755 --- a/spec/api_map_spec.rb +++ b/spec/api_map_spec.rb @@ -14,33 +14,6 @@ expect(pins.map(&:path)).to include('String#upcase') end - describe '.load_with_cache' do - context 'without core already cached' do - before do - Solargraph::PinCache.uncache_core(out: nil) - end - - it 'automatically caches core' do - api_map = described_class.load_with_cache(Dir.pwd, out: nil) - pins = api_map.get_methods('String') - expect(pins.map(&:path)).to include('String#upcase') - end - end - - context 'without gem already cached' do - before do - gemspec = @api_map.find_gem('rubocop') # rubocop:disable RSpec/InstanceVariable - @api_map.workspace.uncache_gem(gemspec) - end - - it 'automatically caches gems' do - api_map = described_class.load_with_cache(Dir.pwd, out: nil) - pins = api_map.get_methods('RuboCop::Cop::Base') - expect(pins.map(&:path)).to include('RuboCop::Cop::Base#active_support_extensions_enabled?') - end - end - end - it 'returns core classes' do pins = @api_map.get_constants('') expect(pins.map(&:path)).to include('String') diff --git a/spec/pin_cache_spec.rb b/spec/pin_cache_spec.rb index 825f0facb..0f507e286 100644 --- a/spec/pin_cache_spec.rb +++ b/spec/pin_cache_spec.rb @@ -11,6 +11,35 @@ yard_plugins: []) end + describe '#cached?' do + it 'returns true for a gem that is cached' do + gemspec = Gem::Specification.find_by_name('backport') + expect(pin_cache.cached?(gemspec)).to be true + end + + it 'returns false for a gem that is not cached' do + gemspec = Gem::Specification.new.tap do |spec| + spec.name = 'nonexistent' + spec.version = '0.0.1' + end + expect(pin_cache.cached?(gemspec)).to be false + end + end + + describe '.core?' do + it 'returns true when core pins exist' do + allow(File).to receive(:file?).with(%r{.*/core.ser$}).and_return(true) + + expect(described_class.core?).to be true + end + + it "returns true when core pins don't" do + allow(File).to receive(:file?).with(%r{.*/core.ser$}).and_return(false) + + expect(described_class.core?).to be false + end + end + describe '#possible_stdlibs' do it 'is tolerant of less usual Ruby installations' do stub_const('Gem::RUBYGEMS_DIR', nil) @@ -19,6 +48,16 @@ end end + describe '#cache_all_stdlibs' do + it 'creates stdlibmaps' do + allow(Solargraph::RbsMap::StdlibMap).to receive(:new).and_return(instance_double(Solargraph::RbsMap::StdlibMap)) + + pin_cache.cache_all_stdlibs + + expect(Solargraph::RbsMap::StdlibMap).to have_received(:new).at_least(:once) + end + end + describe '#cache_gem' do context 'with an already in-memory gem' do let(:backport_gemspec) { Gem::Specification.find_by_name('backport') } @@ -50,6 +89,19 @@ end end + context 'with the rebuild flag' do + before do + allow(Solargraph::Yardoc).to receive(:build_docs) + end + + it 'chooses not to use YARD' do + parser_gemspec = Gem::Specification.find_by_name('parser') + pin_cache.cache_gem(gemspec: parser_gemspec, rebuild: true, out: nil) + # if this fails, you may not have run `bundle exec rbs collection update` + expect(Solargraph::Yardoc).not_to have_received(:build_docs) + end + end + context 'with a stdlib gem' do let(:gem_name) { 'logger' } diff --git a/spec/shell_spec.rb b/spec/shell_spec.rb index 3cdc97276..3861b9e57 100644 --- a/spec/shell_spec.rb +++ b/spec/shell_spec.rb @@ -4,37 +4,19 @@ require 'open3' describe Solargraph::Shell do - let(:shell) { described_class.new } - - let(:temp_dir) { Dir.mktmpdir } - - before do - File.open(File.join(temp_dir, 'Gemfile'), 'w') do |file| - file.puts "source 'https://rubygems.org'" - file.puts "gem 'solargraph', path: '#{File.expand_path('..', __dir__)}'" - end - Bundler.with_unbundled_env do - output, status = Open3.capture2e('bundle install', chdir: temp_dir) - raise "Failure installing bundle: #{output}" unless status.success? - end - end + let(:shell) { described_class.new } # @type cmd [Array] # @return [String] def bundle_exec(*cmd) # run the command in the temporary directory with bundle exec Bundler.with_unbundled_env do - output, status = Open3.capture2e("bundle exec #{cmd.join(' ')}", chdir: temp_dir) + output, status = Open3.capture2e("bundle exec #{cmd.join(' ')}") expect(status.success?).to be(true), "Command failed: #{output}" output end end - after do - # remove the temporary directory after the tests - FileUtils.rm_rf(temp_dir) - end - describe '--version' do let(:output) { bundle_exec('solargraph', '--version') } @@ -66,139 +48,101 @@ def bundle_exec(*cmd) end describe 'scan' do - it 'scans without erroring out' do - output = capture_stdout do - shell.options = { directory: 'spec/fixtures/workspace' } - shell.scan - end + context 'with mocked dependencies' do + let(:api_map) { instance_double(Solargraph::ApiMap) } - expect(output).to include('Scanned ').and include(' seconds.') - end - end - - describe 'typecheck' do - it 'typechecks without erroring out' do - output = capture_stdout do - old_options = shell.options - shell.options = { level: 'normal', directory: '.', **old_options } - shell.typecheck('Gemfile') + before do + allow(Solargraph::ApiMap).to receive(:load_with_cache).and_return(api_map) end - expect(output).to include('Typecheck finished in') - end + it 'scans without erroring out' do + allow(api_map).to receive(:pins).and_return([]) + output = capture_stdout do + shell.options = { directory: 'spec/fixtures/workspace' } + shell.scan + end - it 'caches a gem if needed before typechecking' do - capture_both do - shell.uncache('backport') + expect(output).to include('Scanned ').and include(' seconds.') end - - output = capture_both do - shell.options = { level: 'normal', directory: Dir.pwd } - shell.typecheck('Gemfile') - end - - expect(output).to include('Caching ').and include('backport') end + end - it 'logs if it takes a certain amount of time to cache gems' do - capture_both do - shell.uncache('backport') - end + describe 'typecheck' do + context 'with mocked dependencies' do + let(:type_checker) { instance_double(Solargraph::TypeChecker) } + let(:api_map) { instance_double(Solargraph::ApiMap) } - allow_any_instance_of(Solargraph::PinCache) # rubocop:disable RSpec/AnyInstance - .to receive(:cache_gem) - .and_wrap_original do |original, *args, **kwargs| - sleep(0.5) # simulate a slow cache - original.call(*args, **kwargs) + before do + allow(Solargraph::ApiMap).to receive(:load_with_cache).and_return(api_map) + allow(Solargraph::TypeChecker).to receive(:new).and_return(type_checker) + allow(type_checker).to receive(:problems).and_return([]) end - output = capture_both do - shell.options = { level: 'normal', directory: Dir.pwd } - shell.typecheck('Gemfile') - end + it 'typechecks without erroring out' do + output = capture_stdout do + shell.options = { level: 'normal', directory: '.' } + shell.typecheck('Gemfile') + end - expect(output).to include('Built ').and include(' ms') + expect(output).to include('Typecheck finished in') + end end end describe 'gems' do - it 'complains when gem does not exist' do - output = capture_both do - shell.gems('nonexistentgem') - end + context 'without mocked ApiMap' do + it 'complains when gem does not exist' do + output = capture_both do + shell.gems('nonexistentgem') + end - expect(output).to include("Gem 'nonexistentgem' not found") - end - - it 'caches core without erroring out' do - capture_both do - shell.uncache('core') + expect(output).to include("Gem 'nonexistentgem' not found") end - expect { shell.cache('core') }.not_to raise_error - end - - it 'has a well set up test environment' do - output = bundle_exec('bundle', 'list') + it 'caches core without erroring out' do + capture_both do + shell.uncache('core') + end - expect(output).to include('reverse_markdown') - end + expect { shell.cache('core') }.not_to raise_error + end - it 'caches all without erroring out' do - output = bundle_exec('solargraph', 'gems') + it 'gives sensible error for gem that does not exist' do + output = capture_both do + shell.gems('solargraph123') + end - expect(output).to include('Documentation cached for all') + expect(output).to include("Gem 'solargraph123' not found") + end end - it 'caches single gem without erroring out' do - capture_both do - shell.uncache('backport') - end + context 'with mocked Workspace' do + let(:workspace) { instance_double(Solargraph::Workspace) } + let(:gemspec) { instance_double(Gem::Specification, name: 'backport') } - output = capture_both do - shell.gems('backport') + before do + allow(Solargraph::Workspace).to receive(:new).and_return(workspace) end - expect(output).to include('Caching').and include('backport') - end + it 'caches all without erroring out' do + allow(workspace).to receive(:cache_all_for_workspace!) - it 'gives sensible error for gem that does not exist' do - output = capture_both do - shell.gems('solargraph123') - end + _output = capture_both { shell.gems } - expect(output).to include("Gem 'solargraph123' not found") - end - - it 'caches all gems as needed' do - shell = described_class.new - _output = capture_stdout do - shell.uncache('backport') + expect(workspace).to have_received(:cache_all_for_workspace!) end - _output = capture_stdout do - shell.gems - end + it 'caches single gem without erroring out' do + allow(workspace).to receive(:find_gem).with('backport').and_return(gemspec) + allow(workspace).to receive(:cache_gem) - api_map = Solargraph::ApiMap.load(Dir.pwd) - methods = api_map.get_method_stack('Backport::Adapter', 'remote') - expect(methods.first.return_type.tag).to eq('Hash{Symbol => String, Integer}') - end + capture_both do + shell.options = { rebuild: false } + shell.gems('backport') + end - it 'caches a YARD-using gem and loads pins' do - shell = described_class.new - _output = capture_stdout do - shell.uncache('backport') + expect(workspace).to have_received(:cache_gem).with(gemspec, out: an_instance_of(StringIO), rebuild: false) end - - _output = capture_stdout do - shell.options = { rebuild: true } - shell.gems('backport', 'thor') - end - - api_map = Solargraph::ApiMap.load(Dir.pwd) - methods = api_map.get_method_stack('Backport::Adapter', 'remote') - expect(methods.first.return_type.tag).to eq('Hash{Symbol => String, Integer}') end end @@ -215,17 +159,5 @@ def bundle_exec(*cmd) expect { call }.to output(/not found/).to_stderr end end - - it 'caches gem without erroring out' do - _output = capture_stdout do - shell.uncache('backport') - end - - output = capture_both do - shell.cache('backport') - end - - expect(output).to include('Caching').and include('backport') - end end end diff --git a/spec/workspace_spec.rb b/spec/workspace_spec.rb index 2e9203fb7..74d232a31 100644 --- a/spec/workspace_spec.rb +++ b/spec/workspace_spec.rb @@ -128,4 +128,41 @@ Solargraph::Workspace.new('./path', config) }.not_to raise_error end + + describe '#cache_all_for_workspace!' do + let(:pin_cache) { instance_double(Solargraph::PinCache) } + let(:gemspecs) { instance_double(Solargraph::Workspace::Gemspecs) } + + before do + allow(Solargraph::Workspace::Gemspecs).to receive(:new).and_return(gemspecs) + allow(Solargraph::PinCache).to receive(:cache_core) + allow(Solargraph::PinCache).to receive(:possible_stdlibs) + allow(Solargraph::PinCache).to receive(:new).and_return(pin_cache) + allow(pin_cache).to receive(:cache_gem) + allow(pin_cache).to receive(:cache_all_stdlibs) + end + + it 'caches core pins' do + allow(Solargraph::PinCache).to receive(:core?).and_return(false) + allow(gemspecs).to receive(:all_gemspecs_from_bundle).and_return([]) + allow(pin_cache).to receive(:possible_stdlibs).and_return([]) + + workspace.cache_all_for_workspace!(nil, rebuild: false) + + expect(Solargraph::PinCache).to have_received(:cache_core).with(out: nil) + end + + it 'caches gems' do + gemspec = instance_double(Gem::Specification, name: 'test_gem', version: '1.0.0') + allow(Solargraph::PinCache).to receive(:core?).and_return(true) + allow(gemspecs).to receive(:all_gemspecs_from_bundle).and_return([gemspec]) + allow(pin_cache).to receive(:cached?).with(gemspec).and_return(false) + allow(pin_cache).to receive(:possible_stdlibs).and_return([]) + + workspace.cache_all_for_workspace!(nil, rebuild: false) + + expect(pin_cache).to have_received(:cache_gem) + .with(gemspec: gemspec, rebuild: false, out: nil) + end + end end diff --git a/spec/yardoc_spec.rb b/spec/yardoc_spec.rb index 9096ab0e3..7fadab2bf 100644 --- a/spec/yardoc_spec.rb +++ b/spec/yardoc_spec.rb @@ -38,8 +38,8 @@ end describe '#build_docs' do - let(:api_map) { Solargraph::ApiMap.load(Dir.pwd) } - let(:gemspec) { api_map.find_gem('rubocop') } + let(:workspace) { Solargraph::Workspace.new(Dir.pwd) } + let(:gemspec) { workspace.find_gem('rubocop') } let(:output) { '' } before do From 4558a10ac8b7dab9954bae0889994cfa5873e4d4 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 3 Aug 2025 19:19:21 -0400 Subject: [PATCH 428/561] Fix up .rubocop_todo.yml --- .rubocop_todo.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 3f3f654e9..762cd5dca 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,6 +1,6 @@ # This configuration was generated by # `rubocop --auto-gen-config --no-exclude-limit --no-offense-counts --no-auto-gen-timestamp` -# using RuboCop version 1.79.0. +# using RuboCop version 1.79.1. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new From 02f63178fbdcbe9697acc93b0ecac2c215884343 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 3 Aug 2025 19:30:50 -0400 Subject: [PATCH 429/561] Linting fixes --- lib/solargraph/api_map.rb | 6 - .../convention/active_support_concern.rb | 112 ++++++++++++++---- spec/convention/activesupport_concern_spec.rb | 11 +- 3 files changed, 93 insertions(+), 36 deletions(-) diff --git a/lib/solargraph/api_map.rb b/lib/solargraph/api_map.rb index 87dbda9bf..9db37a166 100755 --- a/lib/solargraph/api_map.rb +++ b/lib/solargraph/api_map.rb @@ -228,11 +228,6 @@ def self.load_with_cache directory, out load(directory) end - # @return [Array] - def yard_plugins - @doc_map.yard_plugins - end - # @return [Array] def pins store.pins.clone.freeze @@ -772,7 +767,6 @@ def inner_get_methods rooted_tag, scope, visibility, deep, skip, no_core = false # namespaces; resolving the generics in the method pins is this # class' responsibility methods = store.get_methods(fqns, scope: scope, visibility: visibility).sort{ |a, b| a.name <=> b.name } - methods = methods.map(&:as_virtual_class_method) if store.get_includes(fqns).include?('ActiveSupport::Concern') && scope == :class logger.info { "ApiMap#inner_get_methods(rooted_tag=#{rooted_tag.inspect}, scope=#{scope.inspect}, visibility=#{visibility.inspect}, deep=#{deep.inspect}, skip=#{skip.inspect}, fqns=#{fqns}) - added from store: #{methods}" } result.concat methods if deep diff --git a/lib/solargraph/convention/active_support_concern.rb b/lib/solargraph/convention/active_support_concern.rb index 178f9186e..b6e0c814f 100644 --- a/lib/solargraph/convention/active_support_concern.rb +++ b/lib/solargraph/convention/active_support_concern.rb @@ -2,49 +2,109 @@ module Solargraph module Convention + # ActiveSupport::Concern is syntactic sugar for a common + # pattern to include class methods while mixing-in a Module + # See https://api.rubyonrails.org/classes/ActiveSupport/Concern.html class ActiveSupportConcern < Base include Logging # @return [Array] attr_reader :pins - def object(api_map, rooted_tag, scope, visibility, deep, skip, no_core) - environ = Environ.new - return environ unless scope == :class + # @param api_map [ApiMap] + # @param rooted_tag [String] + # @param scope [Symbol] :class or :instance + # @param visibility [Array] :public, :protected, and/or :private + # @param deep [Boolean] whether to include methods from included modules + # @param skip [Set] + # @param _no_core [Boolean]n whether to skip core methods + def object api_map, rooted_tag, scope, visibility, deep, skip, _no_core + moo = ObjectProcessor.new(api_map, rooted_tag, scope, visibility, deep, skip) + moo.environ + end + + # yard-activesupport-concern pulls methods inside + # 'class_methods' blocks into main class visible from YARD + # + # @param _doc_map [DocMap] + def global _doc_map + Environ.new(yard_plugins: ['activesupport-concern']) + end + + # Process an object to add any class methods brought in via + # ActiveSupport::Concern + class ObjectProcessor + include Logging - rooted_type = ComplexType.parse(rooted_tag).force_rooted - fqns = rooted_type.namespace - namespace_pin = api_map.get_path_pins(fqns).select { |p| p.is_a?(Pin::Namespace) }.first + attr_reader :environ - api_map.get_includes(fqns).reverse.each do |include_tag| + # @param api_map [ApiMap] + # @param rooted_tag [String] the tag of the class or module being processed + # @param scope [Symbol] :class or :instance + # @param visibility [Array] :public, :protected, and/or :private + # @param deep [Boolean] whether to include methods from included modules + # @param skip [Set] a set of tags to skip + def initialize api_map, rooted_tag, scope, visibility, deep, skip + @api_map = api_map + @rooted_tag = rooted_tag + @scope = scope + @visibility = visibility + @deep = deep + @skip = skip + + @environ = Environ.new + return unless scope == :class + + @rooted_type = ComplexType.parse(rooted_tag).force_rooted + @fqns = rooted_type.namespace + @namespace_pin = api_map.get_path_pins(fqns).select { |p| p.is_a?(Pin::Namespace) }.first + + api_map.get_includes(fqns).reverse.each do |include_tag| + process_include include_tag + end + end + + private + + attr_reader :api_map, :rooted_tag, :rooted_type, :scope, + :visibility, :deep, :skip, :namespace_pin, + :fqns + + # @param include_tag [String] the tag of the module being included + # + # @return [void] + def process_include include_tag rooted_include_tag = api_map.qualify(include_tag, rooted_tag) - next if rooted_include_tag.nil? - logger.debug { "ActiveSupportConcern#object(#{fqns}, #{scope}, #{visibility}, #{deep}) - Handling class include include_tag=#{include_tag}" } + return if rooted_include_tag.nil? + logger.debug do + "ActiveSupportConcern#object(#{fqns}, #{scope}, #{visibility}, #{deep}) - " \ + "Handling class include include_tag=#{include_tag}" + end module_extends = api_map.get_extends(rooted_include_tag) - logger.debug { "ActiveSupportConcern#object(#{fqns}, #{scope}, #{visibility}, #{deep}) - found module extends of #{rooted_include_tag}: #{module_extends}" } - # ActiveSupport::Concern is syntactic sugar for a common - # pattern to include class methods while mixing-in a Module - # See https://api.rubyonrails.org/classes/ActiveSupport/Concern.html - next unless module_extends.include? 'ActiveSupport::Concern' - # yard-activesupport-concern pulls methods inside - # 'class_methods' blocks into main class visible from YARD + logger.debug do + "ActiveSupportConcern#object(#{fqns}, #{scope}, #{visibility}, #{deep}) - " \ + "found module extends of #{rooted_include_tag}: #{module_extends}" + end + return unless module_extends.include? 'ActiveSupport::Concern' included_class_pins = api_map.inner_get_methods_from_reference(rooted_include_tag, namespace_pin, rooted_type, :class, visibility, deep, skip, true) - logger.debug { "ActiveSupportConcern#object(#{fqns}, #{scope}, #{visibility}, #{deep}) - Found #{included_class_pins.length} inluded class methods for #{rooted_include_tag}" } - + logger.debug do + "ActiveSupportConcern#object(#{fqns}, #{scope}, #{visibility}, #{deep}) - " \ + "Found #{included_class_pins.length} inluded class methods for #{rooted_include_tag}" + end environ.pins.concat included_class_pins # another pattern is to put class methods inside a submodule classmethods_include_tag = "#{rooted_include_tag}::ClassMethods" - included_classmethods_pins = api_map.inner_get_methods_from_reference(classmethods_include_tag, namespace_pin, rooted_type, :instance, visibility, deep, skip, true) - logger.debug { "ActiveSupportConcern#object(#{fqns}, #{scope}, #{visibility}, #{deep}) - Found #{included_classmethods_pins.length} included classmethod class methods for #{classmethods_include_tag}" } + included_classmethods_pins = + api_map.inner_get_methods_from_reference(classmethods_include_tag, namespace_pin, rooted_type, + :instance, visibility, deep, skip, true) + logger.debug do + "ActiveSupportConcern#object(#{fqns}, #{scope}, #{visibility}, #{deep}) - " \ + "Found #{included_classmethods_pins.length} included classmethod " \ + "class methods for #{classmethods_include_tag}" + end environ.pins.concat included_classmethods_pins end - - environ - end - - def global(doc_map) - Environ.new(yard_plugins: ['activesupport-concern']) end end end diff --git a/spec/convention/activesupport_concern_spec.rb b/spec/convention/activesupport_concern_spec.rb index 26126a417..ffa12ee6c 100644 --- a/spec/convention/activesupport_concern_spec.rb +++ b/spec/convention/activesupport_concern_spec.rb @@ -1,6 +1,8 @@ +# frozen_string_literal: true + describe Solargraph::Convention::ActiveSupportConcern do - it 'handles block method super scenarios' do - source = Solargraph::Source.load_string(%( + let :source do + Solargraph::Source.load_string(%( # Example from here: https://api.rubyonrails.org/v7.0/classes/ActiveSupport/Concern.html require "active_support/concern" @@ -28,11 +30,12 @@ class Host # this should print 'test' ), 'test.rb') + end + it 'handles block method super scenarios' do api_map = Solargraph::ApiMap.new.map(source) pin = api_map.get_method_stack('Host', 'method_injected_by_foo', scope: :class) - expect(pin.length).to eq(1) - expect(pin.first.name).to eq('method_injected_by_foo') + expect(pin.map(&:name)).to eq(['method_injected_by_foo']) end end From ad72e8aec10d6ed10c115ad8afeeb5e55e93ad92 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 3 Aug 2025 19:32:21 -0400 Subject: [PATCH 430/561] Fix merge issues --- lib/solargraph/environ.rb | 5 ----- lib/solargraph/pin_cache.rb | 1 - 2 files changed, 6 deletions(-) diff --git a/lib/solargraph/environ.rb b/lib/solargraph/environ.rb index fb320d6da..3d24127c6 100644 --- a/lib/solargraph/environ.rb +++ b/lib/solargraph/environ.rb @@ -30,11 +30,6 @@ def initialize requires: [], domains: [], pins: [], yard_plugins: [] @yard_plugins = yard_plugins end - # @return [Array] - def yard_plugins - @yard_plugins ||= [] - end - # @return [self] def clear domains.clear diff --git a/lib/solargraph/pin_cache.rb b/lib/solargraph/pin_cache.rb index 12a871da0..2b3d73f88 100644 --- a/lib/solargraph/pin_cache.rb +++ b/lib/solargraph/pin_cache.rb @@ -1,4 +1,3 @@ -require 'yard-activesupport-concern' require 'fileutils' require 'rbs' require 'rubygems' From d506f434e71646b678eb2bfd0a90b637d13e566e Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 3 Aug 2025 20:10:19 -0400 Subject: [PATCH 431/561] Linting fixes --- lib/solargraph/api_map.rb | 2 +- lib/solargraph/complex_type.rb | 6 - lib/solargraph/complex_type/type_methods.rb | 7 +- lib/solargraph/complex_type/unique_type.rb | 21 ++- lib/solargraph/type_checker.rb | 5 +- lib/solargraph/type_checker/param_def.rb | 35 ---- spec/complex_type/conforms_to_spec.rb | 179 ++++++++++++-------- spec/source_map/clip_spec.rb | 17 ++ spec/type_checker/levels/alpha_spec.rb | 9 +- spec/type_checker/levels/strict_spec.rb | 2 +- spec/type_checker/levels/strong_spec.rb | 68 +++++--- spec/type_checker/levels/typed_spec.rb | 5 +- 12 files changed, 206 insertions(+), 150 deletions(-) delete mode 100644 lib/solargraph/type_checker/param_def.rb diff --git a/lib/solargraph/api_map.rb b/lib/solargraph/api_map.rb index 6a0edc5a4..8610e2bc1 100755 --- a/lib/solargraph/api_map.rb +++ b/lib/solargraph/api_map.rb @@ -208,7 +208,7 @@ class << self # @param directory [String] # @param out [IO] The output stream for messages # @return [ApiMap] - def self.load_with_cache directory, out + def self.load_with_cache directory, out = $stdout api_map = load(directory) if api_map.uncached_gemspecs.empty? logger.info { "All gems cached for #{directory}" } diff --git a/lib/solargraph/complex_type.rb b/lib/solargraph/complex_type.rb index a558f3ba5..8167339e0 100644 --- a/lib/solargraph/complex_type.rb +++ b/lib/solargraph/complex_type.rb @@ -103,12 +103,6 @@ def each_unique_type &block end end - # @param atype [ComplexType] type which may be assigned to this type - # @param api_map [ApiMap] The ApiMap that performs qualification - def can_assign?(api_map, atype) - atype.conforms_to?(api_map, self, :assignment) - end - # @return [Integer] def length @items.length diff --git a/lib/solargraph/complex_type/type_methods.rb b/lib/solargraph/complex_type/type_methods.rb index 791ab80b0..5496f1125 100644 --- a/lib/solargraph/complex_type/type_methods.rb +++ b/lib/solargraph/complex_type/type_methods.rb @@ -73,11 +73,12 @@ def undefined? # @return [Symbol] # @param situation [Symbol] The situation in which the variance is being considered. def erased_variance situation = :method_call - if [:method_call, :return_type, :assignment].include?(situation) - :covariant - else + # :nocov: + unless %i[method_call return_type assignment].include?(situation) raise "Unknown situation: #{situation.inspect}" end + # :nocov: + :covariant end # @param generics_to_erase [Enumerable] diff --git a/lib/solargraph/complex_type/unique_type.rb b/lib/solargraph/complex_type/unique_type.rb index 39e5e728f..a9f2a899b 100644 --- a/lib/solargraph/complex_type/unique_type.rb +++ b/lib/solargraph/complex_type/unique_type.rb @@ -164,11 +164,11 @@ def ==(other) # # "[Expected] types where neither is possible are INVARIANT" # - # @param situation [:method_call] + # @param _situation [:method_call] # @param default [Symbol] The default variance to return if the type is not one of the special cases # # @return [:invariant, :covariant, :contravariant] - def parameter_variance situation, default = :covariant + def parameter_variance _situation, default = :covariant # @todo RBS can specify variance - maybe we can use that info # and also let folks specify? # @@ -196,7 +196,7 @@ def interface? # @param other [UniqueType] def erased_version_of?(other) - return name == other.name && (all_params.empty? || all_params.all?(&:undefined?)) + name == other.name && (all_params.empty? || all_params.all?(&:undefined?)) end # @param api_map [ApiMap] @@ -257,7 +257,9 @@ def conforms_to_unique_type?(api_map, expected, situation = :method_call, api_map.super_and_sub?(inferred.name, expected.name) || inferred.name == expected.name else + # :nocov: raise "Unknown erased variance: #{erased_variance.inspect}" + # :nocov: end return true if inferred.all_params.empty? && rules.include?(:allow_empty_params) @@ -302,11 +304,8 @@ def conforms_to_unique_type?(api_map, expected, situation = :method_call, # @param situation [:method_call, :assignment, :return] # @param rules [Array<:allow_subtype_skew, :allow_empty_params, :allow_reverse_match, :allow_any_match, :allow_undefined, :allow_unresolved_generic>] # @param variance [:invariant, :covariant, :contravariant] - def conforms_to?(api_map, expected, - situation = :method_call, - rules, + def conforms_to?(api_map, expected, situation, rules, variance:) - return true if undefined? && rules.include?(:allow_undefined) # @todo teach this to validate duck types as inferred type @@ -315,7 +314,11 @@ def conforms_to?(api_map, expected, # complex types as expectations are unions - we only need to # match one of their unique types expected.any? do |expected_unique_type| - raise "Expected type must be a UniqueType, got #{expected_unique_type.class} in #{expected.inspect}" unless expected.is_a?(UniqueType) unless expected_unique_type.instance_of?(UniqueType) + # :nocov: + unless expected_unique_type.instance_of?(UniqueType) + raise "Expected type must be a UniqueType, got #{expected_unique_type.class} in #{expected.inspect}" + end + # :nocov: conforms_to_unique_type?(api_map, expected_unique_type, situation, rules, variance: variance) end @@ -360,7 +363,7 @@ def to_rbs elsif name.downcase == 'nil' 'nil' elsif name == GENERIC_TAG_NAME - all_params.first.name + all_params.first&.name elsif ['Class', 'Module'].include?(name) rbs_name elsif ['Tuple', 'Array'].include?(name) && fixed_parameters? diff --git a/lib/solargraph/type_checker.rb b/lib/solargraph/type_checker.rb index 8b86f02df..3b555bae7 100644 --- a/lib/solargraph/type_checker.rb +++ b/lib/solargraph/type_checker.rb @@ -5,7 +5,6 @@ module Solargraph # class TypeChecker autoload :Problem, 'solargraph/type_checker/problem' - autoload :ParamDef, 'solargraph/type_checker/param_def' autoload :Rules, 'solargraph/type_checker/rules' include Parser::NodeMethods @@ -100,10 +99,10 @@ def load filename, level = :normal # @param code [String] # @param filename [String, nil] # @param level [Symbol] + # @param api_map [Solargraph::ApiMap] # @return [self] - def load_string code, filename = nil, level = :normal + def load_string code, filename = nil, level = :normal, api_map: Solargraph::ApiMap.new source = Solargraph::Source.load_string(code, filename) - api_map = Solargraph::ApiMap.new api_map.map(source) new(filename, api_map: api_map, level: level) end diff --git a/lib/solargraph/type_checker/param_def.rb b/lib/solargraph/type_checker/param_def.rb deleted file mode 100644 index 2c626270a..000000000 --- a/lib/solargraph/type_checker/param_def.rb +++ /dev/null @@ -1,35 +0,0 @@ -# frozen_string_literal: true - -module Solargraph - class TypeChecker - # Data about a method parameter definition. This is the information from - # the args list in the def call, not the `@param` tags. - # - class ParamDef - # @return [String] - attr_reader :name - - # @return [Symbol] - attr_reader :type - - def initialize name, type - @name = name - @type = type - end - - class << self - # Get an array of ParamDefs from a method pin. - # - # @param pin [Solargraph::Pin::Method] - # @return [Array] - def from pin - result = [] - pin.parameters.each do |par| - result.push ParamDef.new(par.name, par.decl) - end - result - end - end - end - end -end diff --git a/spec/complex_type/conforms_to_spec.rb b/spec/complex_type/conforms_to_spec.rb index 5755721b4..581ce1d34 100644 --- a/spec/complex_type/conforms_to_spec.rb +++ b/spec/complex_type/conforms_to_spec.rb @@ -1,20 +1,54 @@ +# frozen_string_literal: true + describe Solargraph::ComplexType do it 'validates simple core types' do api_map = Solargraph::ApiMap.new - exp = Solargraph::ComplexType.parse('String') - inf = Solargraph::ComplexType.parse('String') + exp = described_class.parse('String') + inf = described_class.parse('String') match = inf.conforms_to?(api_map, exp, :method_call) expect(match).to be(true) end it 'invalidates simple core types' do api_map = Solargraph::ApiMap.new - exp = Solargraph::ComplexType.parse('String') - inf = Solargraph::ComplexType.parse('Integer') + exp = described_class.parse('String') + inf = described_class.parse('Integer') + match = inf.conforms_to?(api_map, exp, :method_call) + expect(match).to be(false) + end + + it 'allows subtype skew if told' do + api_map = Solargraph::ApiMap.new + exp = described_class.parse('Array') + inf = described_class.parse('Array') + match = inf.conforms_to?(api_map, exp, :method_call, [:allow_subtype_skew]) + expect(match).to be(true) + end + + it 'accepts valid tuple conformance' do + api_map = Solargraph::ApiMap.new + exp = described_class.parse('Array(Integer, Integer)') + inf = described_class.parse('Array(Integer, Integer)') + match = inf.conforms_to?(api_map, exp, :method_call) + expect(match).to be(true) + end + + it 'rejects invalid tuple conformance' do + api_map = Solargraph::ApiMap.new + exp = described_class.parse('Array(Integer, Integer)') + inf = described_class.parse('Array(Integer, String)') match = inf.conforms_to?(api_map, exp, :method_call) expect(match).to be(false) end + it 'allows empty params when specified' do + api_map = Solargraph::ApiMap.new + exp = described_class.parse('Array(Integer, Integer)') + inf = described_class.parse('Array') + match = inf.conforms_to?(api_map, exp, :method_call, [:allow_empty_params]) + expect(match).to be(true) + end + it 'validates expected superclasses' do source = Solargraph::Source.load_string(%( class Sup; end @@ -22,32 +56,32 @@ class Sub < Sup; end )) api_map = Solargraph::ApiMap.new api_map.map source - sup = Solargraph::ComplexType.parse('Sup') - sub = Solargraph::ComplexType.parse('Sub') + sup = described_class.parse('Sup') + sub = described_class.parse('Sub') match = sub.conforms_to?(api_map, sup, :method_call) expect(match).to be(true) end - it 'invalidates inferred superclasses (expected must be super)' do - # @todo This test might be invalid. There are use cases where inheritance - # between inferred and expected classes should be acceptable in either - # direction. - # source = Solargraph::Source.load_string(%( - # class Sup; end - # class Sub < Sup; end - # )) - # api_map = Solargraph::ApiMap.new - # api_map.map source - # sup = Solargraph::ComplexType.parse('Sup') - # sub = Solargraph::ComplexType.parse('Sub') - # match = Solargraph::TypeChecker::Checks.types_match?(api_map, sub, sup) - # expect(match).to be(false) - end + # it 'invalidates inferred superclasses (expected must be super)' do + # # @todo This test might be invalid. There are use cases where inheritance + # # between inferred and expected classes should be acceptable in either + # # direction. + # # source = Solargraph::Source.load_string(%( + # # class Sup; end + # # class Sub < Sup; end + # # )) + # # api_map = Solargraph::ApiMap.new + # # api_map.map source + # # sup = described_class.parse('Sup') + # # sub = described_class.parse('Sub') + # # match = Solargraph::TypeChecker::Checks.types_match?(api_map, sub, sup) + # # expect(match).to be(false) + # end it 'fuzzy matches arrays with parameters' do api_map = Solargraph::ApiMap.new - exp = Solargraph::ComplexType.parse('Array') - inf = Solargraph::ComplexType.parse('Array') + exp = described_class.parse('Array') + inf = described_class.parse('Array') match = inf.conforms_to?(api_map, exp, :method_call) expect(match).to be(true) end @@ -57,98 +91,107 @@ class Sub < Sup; end source_map = Solargraph::SourceMap.map(source) api_map = Solargraph::ApiMap.new api_map.catalog Solargraph::Bench.new(source_maps: [source_map], external_requires: ['set']) - exp = Solargraph::ComplexType.parse('Set') - inf = Solargraph::ComplexType.parse('Set') + exp = described_class.parse('Set') + inf = described_class.parse('Set') match = inf.conforms_to?(api_map, exp, :method_call) expect(match).to be(true) end it 'fuzzy matches hashes with parameters' do api_map = Solargraph::ApiMap.new - exp = Solargraph::ComplexType.parse('Hash{ Symbol => String}') - inf = Solargraph::ComplexType.parse('Hash') + exp = described_class.parse('Hash{ Symbol => String}') + inf = described_class.parse('Hash') match = inf.conforms_to?(api_map, exp, :method_call, [:allow_empty_params]) expect(match).to be(true) end it 'matches multiple types' do api_map = Solargraph::ApiMap.new - exp = Solargraph::ComplexType.parse('String, Integer') - inf = Solargraph::ComplexType.parse('String, Integer') + exp = described_class.parse('String, Integer') + inf = described_class.parse('String, Integer') match = inf.conforms_to?(api_map, exp, :method_call) expect(match).to be(true) end it 'matches multiple types out of order' do api_map = Solargraph::ApiMap.new - exp = Solargraph::ComplexType.parse('String, Integer') - inf = Solargraph::ComplexType.parse('Integer, String') + exp = described_class.parse('String, Integer') + inf = described_class.parse('Integer, String') match = inf.conforms_to?(api_map, exp, :method_call) expect(match).to be(true) end it 'invalidates inferred types missing from expected' do api_map = Solargraph::ApiMap.new - exp = Solargraph::ComplexType.parse('String') - inf = Solargraph::ComplexType.parse('String, Integer') + exp = described_class.parse('String') + inf = described_class.parse('String, Integer') match = inf.conforms_to?(api_map, exp, :method_call) expect(match).to be(false) end it 'matches nil' do api_map = Solargraph::ApiMap.new - exp = Solargraph::ComplexType.parse('nil') - inf = Solargraph::ComplexType.parse('nil') + exp = described_class.parse('nil') + inf = described_class.parse('nil') match = inf.conforms_to?(api_map, exp, :method_call) expect(match).to be(true) end it 'validates classes with expected superclasses' do api_map = Solargraph::ApiMap.new - exp = Solargraph::ComplexType.parse('Class') - inf = Solargraph::ComplexType.parse('Class') + exp = described_class.parse('Class') + inf = described_class.parse('Class') match = inf.conforms_to?(api_map, exp, :method_call) expect(match).to be(true) end it 'validates generic classes with expected Class' do api_map = Solargraph::ApiMap.new - exp = Solargraph::ComplexType.parse('Class') - inf = Solargraph::ComplexType.parse('Class') - match = inf.conforms_to?(api_map, exp, :method_call, [:allow_empty_params]) - expect(match).to be(true) - end - - it 'validates generic classes with expected Class' do - api_map = Solargraph::ApiMap.new - inf = Solargraph::ComplexType.parse('Class') - exp = Solargraph::ComplexType.parse('Class') + inf = described_class.parse('Class') + exp = described_class.parse('Class') match = inf.conforms_to?(api_map, exp, :method_call) expect(match).to be(true) end - it 'validates inheritance in both directions' do - source = Solargraph::Source.load_string(%( - class Sup; end - class Sub < Sup; end - )) - api_map = Solargraph::ApiMap.new - api_map.map source - sup = Solargraph::ComplexType.parse('Sup') - sub = Solargraph::ComplexType.parse('Sub') - match = sub.conforms_to?(api_map, sup, :method_call, [:allow_reverse_match]) - expect(match).to be(true) - match = sup.conforms_to?(api_map, sub, :method_call, [:allow_reverse_match]) - expect(match).to be(true) + context 'with an inheritence relationship' do + let(:source) do + Solargraph::Source.load_string(%( + class Sup; end + class Sub < Sup; end + )) + end + let(:sup) { described_class.parse('Sup') } + let(:sub) { described_class.parse('Sub') } + let(:api_map) { Solargraph::ApiMap.new } + + before do + api_map.map source + end + + it 'validates inheritance in one way' do + match = sub.conforms_to?(api_map, sup, :method_call, [:allow_reverse_match]) + expect(match).to be(true) + end + + it 'validates inheritance the other way' do + match = sup.conforms_to?(api_map, sub, :method_call, [:allow_reverse_match]) + expect(match).to be(true) + end end - it 'invalidates inheritance in both directions' do - api_map = Solargraph::ApiMap.new - sup = Solargraph::ComplexType.parse('String') - sub = Solargraph::ComplexType.parse('Array') - match = sub.conforms_to?(api_map, sup, :method_call, [:allow_reverse_match]) - expect(match).to be(false) - match = sup.conforms_to?(api_map, sub, :method_call, [:allow_reverse_match]) - expect(match).to be(false) + context 'with inheritance relationship in allow_reverse_match mode' do + let(:api_map) { Solargraph::ApiMap.new } + let(:sup) { described_class.parse('String') } + let(:sub) { described_class.parse('Array') } + + it 'conforms one way' do + match = sub.conforms_to?(api_map, sup, :method_call, [:allow_reverse_match]) + expect(match).to be(false) + end + + it 'conforms the other way' do + match = sup.conforms_to?(api_map, sub, :method_call, [:allow_reverse_match]) + expect(match).to be(false) + end end end diff --git a/spec/source_map/clip_spec.rb b/spec/source_map/clip_spec.rb index 0f83331ec..801edefa7 100644 --- a/spec/source_map/clip_spec.rb +++ b/spec/source_map/clip_spec.rb @@ -302,6 +302,23 @@ def foo expect(type.tag).to eq('String') end + it 'infers method types from return nodes' do + source = Solargraph::Source.load_string(%( + class Foo + # @return [self] + def foo + bar + end + end + Foo.new.foo + ), 'test.rb') + map = Solargraph::ApiMap.new + map.map source + clip = map.clip_at('test.rb', Solargraph::Position.new(7, 10)) + type = clip.infer + expect(type.tag).to eq('Foo') + end + it 'infers multiple method types from return nodes' do source = Solargraph::Source.load_string(%( def foo diff --git a/spec/type_checker/levels/alpha_spec.rb b/spec/type_checker/levels/alpha_spec.rb index d700ea3b7..3ff5aa6c3 100644 --- a/spec/type_checker/levels/alpha_spec.rb +++ b/spec/type_checker/levels/alpha_spec.rb @@ -1,6 +1,8 @@ +# frozen_string_literal: true + describe Solargraph::TypeChecker do - context 'alpha level' do - def type_checker(code) + context 'when at alpha level' do + def type_checker code Solargraph::TypeChecker.load_string(code, 'test.rb', :alpha) end @@ -16,7 +18,8 @@ def bar(b) foo(b) end )) - expect(checker.problems.map(&:message)).to eq(["Wrong argument type for #foo: a expected String, received String, nil"]) + expect(checker.problems.map(&:message)) + .to eq(['Wrong argument type for #foo: a expected String, received String, nil']) end end end diff --git a/spec/type_checker/levels/strict_spec.rb b/spec/type_checker/levels/strict_spec.rb index 7861c8817..9992b43e9 100644 --- a/spec/type_checker/levels/strict_spec.rb +++ b/spec/type_checker/levels/strict_spec.rb @@ -59,7 +59,7 @@ def bar(a); end require 'kramdown-parser-gfm' Kramdown::Parser::GFM.undefined_call ), 'test.rb') - api_map = Solargraph::ApiMap.load_with_cache('.', $stdout) + api_map = Solargraph::ApiMap.load '.' api_map.catalog Solargraph::Bench.new(source_maps: [source_map], external_requires: ['kramdown-parser-gfm']) checker = Solargraph::TypeChecker.new('test.rb', api_map: api_map, level: :strict) expect(checker.problems).to be_empty diff --git a/spec/type_checker/levels/strong_spec.rb b/spec/type_checker/levels/strong_spec.rb index 4c45056ea..68a62d395 100644 --- a/spec/type_checker/levels/strong_spec.rb +++ b/spec/type_checker/levels/strong_spec.rb @@ -14,7 +14,6 @@ def bar; end expect(checker.problems.first.message).to include('Missing @return tag') end - it 'ignores nilable type issues' do checker = type_checker(%( # @param a [String] @@ -30,6 +29,54 @@ def bar(b) expect(checker.problems.map(&:message)).to eq([]) end + it 'calls out keyword issues even when required arg count matches' do + checker = type_checker(%( + # @param a [String] + # @param b [String] + # @return [void] + def foo(a = 'foo', b:); end + + # @return [void] + def bar + foo('baz') + end + )) + expect(checker.problems.map(&:message)).to include('Call to #foo is missing keyword argument b') + end + + it 'calls out type issues even when keyword issues are there' do + pending('fixes to arg vs param checking algorithm') + + checker = type_checker(%( + # @param a [String] + # @param b [String] + # @return [void] + def foo(a = 'foo', b:); end + + # @return [void] + def bar + foo(123) + end + )) + expect(checker.problems.map(&:message)) + .to include('Wrong argument type for #foo: a expected String, received 123') + end + + it 'calls out keyword issues even when arg type issues are there' do + checker = type_checker(%( + # @param a [String] + # @param b [String] + # @return [void] + def foo(a = 'foo', b:); end + + # @return [void] + def bar + foo(123) + end + )) + expect(checker.problems.map(&:message)).to include('Call to #foo is missing keyword argument b') + end + it 'reports missing param tags' do checker = type_checker(%( class Foo @@ -179,23 +226,6 @@ def baz(bing) expect(checker.problems.map(&:message)).to be_empty end - it 'treats a parameter type of undefined as not provided' do - checker = type_checker(%( - class Foo - # @param foo [Class] - # @return [void] - def bar foo:; end - - # @param bing [Class] - # @return [void] - def baz(bing) - bar(foo: bing) - end - end - )) - expect(checker.problems.map(&:message)).to be_empty - end - it 'ignores generic resolution failures' do checker = type_checker(%( class Foo @@ -252,7 +282,7 @@ def block_pins expect(checker.problems.map(&:message)).to be_empty end - it 'ignores generic resolution failures' do + it 'ignores generic resolution failures with only one arg' do checker = type_checker(%( # @generic T # @param path [String] diff --git a/spec/type_checker/levels/typed_spec.rb b/spec/type_checker/levels/typed_spec.rb index 6e71ee9ff..681d813d5 100644 --- a/spec/type_checker/levels/typed_spec.rb +++ b/spec/type_checker/levels/typed_spec.rb @@ -38,7 +38,7 @@ def bar expect(checker.problems.first.message).to include('does not match') end - it 'reports mismatched key and subtypes ' do + it 'reports mismatched key and subtypes' do checker = type_checker(%( # @return [Hash{String => String}] def foo @@ -207,7 +207,8 @@ def foo # @param bar [String] def foo(bar = 123); end )) - expect(checker.problems.map(&:message)).to eq(['Declared type String does not match inferred type 123 for variable bar']) + expect(checker.problems.map(&:message)) + .to eq(['Declared type String does not match inferred type 123 for variable bar']) end it 'validates string default values of parameters' do From d33f156397ee1165070b845575b4c0b8f5d5161d Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 3 Aug 2025 20:35:30 -0400 Subject: [PATCH 432/561] Linting fix --- lib/solargraph/logging.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/solargraph/logging.rb b/lib/solargraph/logging.rb index b07ae2faf..a8bc3b3ee 100644 --- a/lib/solargraph/logging.rb +++ b/lib/solargraph/logging.rb @@ -15,7 +15,10 @@ module Logging level = if LOG_LEVELS.keys.include?(configured_level) LOG_LEVELS.fetch(configured_level) else - warn "Invalid value for SOLARGRAPH_LOG: #{configured_level.inspect} - valid values are #{LOG_LEVELS.keys}" if configured_level + if configured_level + warn "Invalid value for SOLARGRAPH_LOG: #{configured_level.inspect} - " \ + "valid values are #{LOG_LEVELS.keys}" + end DEFAULT_LOG_LEVEL end # @sg-ignore Fix cvar issue From 947bdfe013a3b17a87d853163f0ae865371d4d84 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 3 Aug 2025 20:42:56 -0400 Subject: [PATCH 433/561] More fixes --- lib/solargraph/convention/data_definition.rb | 2 +- lib/solargraph/pin/constant.rb | 2 ++ lib/solargraph/pin/method_alias.rb | 3 +++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/solargraph/convention/data_definition.rb b/lib/solargraph/convention/data_definition.rb index b65431393..8efe27932 100644 --- a/lib/solargraph/convention/data_definition.rb +++ b/lib/solargraph/convention/data_definition.rb @@ -35,7 +35,7 @@ def process comments: comments_for(node) ) - # TODO: Support both arg and kwarg initializers for Data.define + # @todo Support both arg and kwarg initializers for Data.define # Solargraph::SourceMap::Clip#complete_keyword_parameters does not seem to currently take into account [Pin::Method#signatures] hence we only one for :kwarg pins.push initialize_method_pin diff --git a/lib/solargraph/pin/constant.rb b/lib/solargraph/pin/constant.rb index 345bbd50a..94a968e7e 100644 --- a/lib/solargraph/pin/constant.rb +++ b/lib/solargraph/pin/constant.rb @@ -5,6 +5,8 @@ module Pin class Constant < BaseVariable attr_reader :visibility + # @param visibility [::Symbol] The visibility of the constant (:public, :protected, or :private) + # @param splat [Hash] Additional options supported by superclasses def initialize visibility: :public, **splat super(**splat) @visibility = visibility diff --git a/lib/solargraph/pin/method_alias.rb b/lib/solargraph/pin/method_alias.rb index 8636169a8..28be6d8b1 100644 --- a/lib/solargraph/pin/method_alias.rb +++ b/lib/solargraph/pin/method_alias.rb @@ -13,6 +13,9 @@ class MethodAlias < Method # @return [String] attr_reader :original + # @param scope [::Symbol] + # @param original [String, nil] The name of the original method + # @param splat [Hash] Additional options supported by superclasses def initialize scope: :instance, original: nil, **splat super(**splat) @scope = scope From e2fcd121f152183980dcb728bb18284b50017995 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 3 Aug 2025 20:53:12 -0400 Subject: [PATCH 434/561] Add nocov markings for undercover --- lib/solargraph/parser/parser_gem/node_processors/send_node.rb | 4 ++++ lib/solargraph/pin/base.rb | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/lib/solargraph/parser/parser_gem/node_processors/send_node.rb b/lib/solargraph/parser/parser_gem/node_processors/send_node.rb index 57c74a22c..8490fd9b0 100644 --- a/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +++ b/lib/solargraph/parser/parser_gem/node_processors/send_node.rb @@ -11,10 +11,12 @@ def process # @sg-ignore # @type [Symbol] method_name = node.children[1] + # :nocov: unless method_name.instance_of?(Symbol) Solargraph.assert_or_log(:parser_method_name, "Expected method name to be a Symbol, got #{method_name.class} for node #{node.inspect}") return process_children end + # :nocov: if node.children[0].nil? if [:private, :public, :protected].include?(method_name) process_visibility @@ -55,10 +57,12 @@ def process_visibility # @sg-ignore # @type [Symbol] visibility = node.children[1] + # :nocov: unless visibility.instance_of?(Symbol) Solargraph.assert_or_log(:parser_visibility, "Expected visibility name to be a Symbol, got #{visibility.class} for node #{node.inspect}") return process_children end + # :nocov: if child.is_a?(AST::Node) && (child.type == :sym || child.type == :str) name = child.children[0].to_s matches = pins.select{ |pin| pin.is_a?(Pin::Method) && pin.name == name && pin.namespace == region.closure.full_context.namespace && pin.context.scope == (region.scope || :instance)} diff --git a/lib/solargraph/pin/base.rb b/lib/solargraph/pin/base.rb index c002c65eb..c3efd301b 100644 --- a/lib/solargraph/pin/base.rb +++ b/lib/solargraph/pin/base.rb @@ -279,10 +279,12 @@ def assert_same_count(other, attr) # @sg-ignore # @return [undefined] def assert_same(other, attr) + # :nocov: if other.nil? Solargraph.assert_or_log("combine_with_#{attr}".to_sym, "Sent nil for comparison") return send(attr) end + # :nocov: val1 = send(attr) val2 = other.send(attr) return val1 if val1 == val2 @@ -320,9 +322,11 @@ def choose_pin_attr(other, attr) # @type [Pin::Base, nil] val2 = other.send(attr) if val1.class != val2.class + # :nocov: Solargraph.assert_or_log("combine_with_#{attr}_class".to_sym, "Inconsistent #{attr.inspect} class values between \nself =#{inspect} and \nother=#{other.inspect}:\n\n self.#{attr} = #{val1.inspect}\nother.#{attr} = #{val2.inspect}") return val1 + # :nocov: end # arbitrary way of choosing a pin # @sg-ignore Need _1 support From 8dbe7df207858b9f69b80e95b187adcfd9aaac21 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 3 Aug 2025 21:00:47 -0400 Subject: [PATCH 435/561] Improve a pin combination case around selfy types Also improve type coverage around pin combination --- lib/solargraph/pin/base.rb | 4 +-- spec/pin/combine_with_spec.rb | 67 +++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 spec/pin/combine_with_spec.rb diff --git a/lib/solargraph/pin/base.rb b/lib/solargraph/pin/base.rb index cdd6a5ace..59969f60a 100644 --- a/lib/solargraph/pin/base.rb +++ b/lib/solargraph/pin/base.rb @@ -158,9 +158,9 @@ def combine_return_type(other) return_type else all_items = return_type.items + other.return_type.items - if all_items.any? { |item| item.selfy? } && all_items.any? { |item| item.rooted_tag == context.rooted_tag } + if all_items.any? { |item| item.selfy? } && all_items.any? { |item| item.rooted_namespace == context.rooted_namespace } # assume this was a declaration that should have said 'self' - all_items.delete_if { |item| item.rooted_tag == context.rooted_tag } + all_items.delete_if { |item| item.rooted_namespace == context.rooted_namespace } end ComplexType.new(all_items) end diff --git a/spec/pin/combine_with_spec.rb b/spec/pin/combine_with_spec.rb new file mode 100644 index 000000000..cc80d76d5 --- /dev/null +++ b/spec/pin/combine_with_spec.rb @@ -0,0 +1,67 @@ +# frozen_string_literal: true + +describe Solargraph::Pin::Base, '#combine_with' do + it 'combines return types with another method pin with same arity' do + pin1 = Solargraph::Pin::Method.new(name: 'foo', parameters: [], comments: '@return [String]') + pin2 = Solargraph::Pin::Method.new(name: 'foo', parameters: [], comments: '@return [Integer]') + combined = pin1.combine_with(pin2) + expect(combined.return_type.to_s).to eq('String, Integer') + end + + it 'combines return types with another method without type parameters' do + pin1 = Solargraph::Pin::Method.new(name: 'foo', parameters: [], comments: '@return [Array]') + pin2 = Solargraph::Pin::Method.new(name: 'foo', parameters: [], comments: '@return [Array]') + combined = pin1.combine_with(pin2) + expect(combined.return_type.to_s).to eq('Array') + end + + context 'with dodgy return types' do + let(:dodgy_location_pin) do + range = Solargraph::Range.new(Solargraph::Position.new(1, 0), Solargraph::Position.new(1, 10)) + location = Solargraph::Location.new('/home/user/.rbenv/versions/3.1.7/lib/ruby/gems/3.1.0/gems' \ + '/activesupport-7.0.8.7/lib/active_support/core_ext/object' \ + '/conversions.rb', + range) + Solargraph::Pin::Method.new(name: 'foo', parameters: [], comments: '@return [Object]', + location: location) + end + + let(:normal_pin) { Solargraph::Pin::Method.new(name: 'foo', parameters: [], comments: '@return [self]') } + + it 'combines a dodgy return type with a valid one' do + combined = dodgy_location_pin.combine_with(normal_pin) + expect(combined.return_type.to_s).to eq('self') + end + + it 'combines a valid return type with a dodgy one' do + combined = normal_pin.combine_with(dodgy_location_pin) + expect(combined.return_type.to_s).to eq('self') + end + end + + context 'with return types that should probably be self' do + let(:closure) do + Solargraph::Pin::Namespace.new( + name: 'Foo', + closure: Solargraph::Pin::ROOT_PIN, + type: :class + ) + end + + let(:likely_selfy_pin) do + Solargraph::Pin::Method.new(name: 'foo', closure: closure, parameters: [], comments: '@return [::Foo]') + end + + let(:selfy_pin) { Solargraph::Pin::Method.new(name: 'foo', closure: closure, parameters: [], comments: '@return [self]') } + + it 'combines a selfy return type with a likely-selfy one' do + combined = likely_selfy_pin.combine_with(selfy_pin) + expect(combined.return_type.to_s).to eq('self') + end + + it 'combines a likely-selfy return type with a selfy one' do + combined = selfy_pin.combine_with(likely_selfy_pin) + expect(combined.return_type.to_s).to eq('self') + end + end +end From d726cc7d77c1de4dbb98db2ff104f95e8cddc3c0 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 3 Aug 2025 21:05:21 -0400 Subject: [PATCH 436/561] Linting/coverage fixes --- lib/solargraph/type_checker.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/solargraph/type_checker.rb b/lib/solargraph/type_checker.rb index bf9f5864d..1b78c62a3 100644 --- a/lib/solargraph/type_checker.rb +++ b/lib/solargraph/type_checker.rb @@ -715,7 +715,7 @@ def unneeded_sgignore_problems unprocessed_sg_ignore_lines.map do |line| Problem.new( Location.new(filename, Range.from_to(line, 0, line, 0)), - "Unneeded @sg-ignore comment" + 'Unneeded @sg-ignore comment' ) end end @@ -726,9 +726,11 @@ def without_ignored problems problems.reject do |problem| node = source.node_at(problem.location.range.start.line, problem.location.range.start.column) ignored = node && source.comments_for(node)&.include?('@sg-ignore') + # :nocov: unless !ignored || all_sg_ignore_lines.include?(problem.location.range.start.line) Solargraph.assert_or_log(:sg_ignore) { "@sg-ignore accounting issue - node is #{node}" } end + # :nocov: sg_ignore_lines_processed.add problem.location.range.start.line if ignored ignored end From fdd3810f34eeaa81e4bbac956d9e8372da1c5c6c Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 3 Aug 2025 21:07:30 -0400 Subject: [PATCH 437/561] Linting fix --- lib/solargraph/shell.rb | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/solargraph/shell.rb b/lib/solargraph/shell.rb index 153e77f0e..92f8fed38 100755 --- a/lib/solargraph/shell.rb +++ b/lib/solargraph/shell.rb @@ -266,12 +266,8 @@ def method_pin path pins.each do |pin| if options[:typify] || options[:probe] type = ComplexType::UNDEFINED - if options[:typify] - type = pin.typify(api_map) - end - if options[:probe] && type.undefined? - type = pin.probe(api_map) - end + type = pin.typify(api_map) if options[:typify] + type = pin.probe(api_map) if options[:probe] && type.undefined? print_type(type) next end From 0a2d7607d7e03b6dcd798ee73c12ad7dae0651a2 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 3 Aug 2025 21:08:05 -0400 Subject: [PATCH 438/561] Fix merge issue --- lib/solargraph/shell.rb | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/lib/solargraph/shell.rb b/lib/solargraph/shell.rb index 8dca3bd48..f9176f282 100755 --- a/lib/solargraph/shell.rb +++ b/lib/solargraph/shell.rb @@ -364,5 +364,25 @@ def pin_description pin desc += " (#{pin.location.filename} #{pin.location.range.start.line})" if pin.location desc end + + # @param type [ComplexType] + # @return [void] + def print_type(type) + if options[:rbs] + puts type.to_rbs + else + puts type.rooted_tag + end + end + + # @param pin [Solargraph::Pin::Base] + # @return [void] + def print_pin(pin) + if options[:rbs] + puts pin.to_rbs + else + puts pin.inspect + end + end end end From c4cd5bcc336bd42cec86ac29fe084acb9c68d067 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 3 Aug 2025 21:14:48 -0400 Subject: [PATCH 439/561] Linting fixes --- spec/convention/gemfile_spec.rb | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/spec/convention/gemfile_spec.rb b/spec/convention/gemfile_spec.rb index 62a346ca0..827da7993 100644 --- a/spec/convention/gemfile_spec.rb +++ b/spec/convention/gemfile_spec.rb @@ -1,6 +1,8 @@ +# frozen_string_literal: true + describe Solargraph::Convention::Gemfile do describe 'parsing Gemfiles' do - def type_checker(code) + def type_checker code Solargraph::TypeChecker.load_string(code, 'Gemfile', :strong) end @@ -32,8 +34,8 @@ def type_checker(code) )) expect(checker.problems.map(&:message).sort) - .to eq(["Unrecognized keyword argument bad_name to Bundler::Dsl#gemspec", - "Wrong argument type for Bundler::Dsl#source: source expected String, received Class"].sort) + .to eq(['Unrecognized keyword argument bad_name to Bundler::Dsl#gemspec', + 'Wrong argument type for Bundler::Dsl#source: source expected String, received Class'].sort) end it 'finds bad arguments to DSL ruby method' do @@ -44,7 +46,7 @@ def type_checker(code) )) expect(checker.problems.map(&:message)) - .to eq(["Wrong argument type for Bundler::Dsl#ruby: ruby_version expected String, received Integer"]) + .to eq(['Wrong argument type for Bundler::Dsl#ruby: ruby_version expected String, received Integer']) end end end From 8392b41e268955dd97d2748b74e798af092c1b10 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 3 Aug 2025 21:24:02 -0400 Subject: [PATCH 440/561] Move open3 to rbs/fills, as it is not always added as gem --- rbs/fills/open3/0/open3.rbs | 172 ++++++++++++++++++++++++ sig/shims/open3/0/open3.rbs | 28 ---- spec/type_checker/levels/strong_spec.rb | 14 ++ 3 files changed, 186 insertions(+), 28 deletions(-) create mode 100644 rbs/fills/open3/0/open3.rbs delete mode 100644 sig/shims/open3/0/open3.rbs diff --git a/rbs/fills/open3/0/open3.rbs b/rbs/fills/open3/0/open3.rbs new file mode 100644 index 000000000..11d909572 --- /dev/null +++ b/rbs/fills/open3/0/open3.rbs @@ -0,0 +1,172 @@ +# +# Module Open3 supports creating child processes with access to their $stdin, +# $stdout, and $stderr streams. +# +# ## What's Here +# +# Each of these methods executes a given command in a new process or subshell, +# or multiple commands in new processes and/or subshells: +# +# * Each of these methods executes a single command in a process or subshell, +# accepts a string for input to $stdin, and returns string output from +# $stdout, $stderr, or both: +# +# * Open3.capture2: Executes the command; returns the string from $stdout. +# * Open3.capture2e: Executes the command; returns the string from merged +# $stdout and $stderr. +# * Open3.capture3: Executes the command; returns strings from $stdout and +# $stderr. +# +# * Each of these methods executes a single command in a process or subshell, +# and returns pipes for $stdin, $stdout, and/or $stderr: +# +# * Open3.popen2: Executes the command; returns pipes for $stdin and +# $stdout. +# * Open3.popen2e: Executes the command; returns pipes for $stdin and +# merged $stdout and $stderr. +# * Open3.popen3: Executes the command; returns pipes for $stdin, $stdout, +# and $stderr. +# +# * Each of these methods executes one or more commands in processes and/or +# subshells, returns pipes for the first $stdin, the last $stdout, or both: +# +# * Open3.pipeline_r: Returns a pipe for the last $stdout. +# * Open3.pipeline_rw: Returns pipes for the first $stdin and the last +# $stdout. +# * Open3.pipeline_w: Returns a pipe for the first $stdin. +# * Open3.pipeline_start: Does not wait for processes to complete. +# * Open3.pipeline: Waits for processes to complete. +# +# Each of the methods above accepts: +# +# * An optional hash of environment variable names and values; see [Execution +# Environment](rdoc-ref:Process@Execution+Environment). +# * A required string argument that is a `command_line` or `exe_path`; see +# [Argument command_line or +# exe_path](rdoc-ref:Process@Argument+command_line+or+exe_path). +# * An optional hash of execution options; see [Execution +# Options](rdoc-ref:Process@Execution+Options). +# +module Open3 + # + # Basically a wrapper for Open3.popen3 that: + # + # * Creates a child process, by calling Open3.popen3 with the given arguments + # (except for certain entries in hash `options`; see below). + # * Returns as string `stdout_and_stderr_s` the merged standard output and + # standard error of the child process. + # * Returns as `status` a `Process::Status` object that represents the exit + # status of the child process. + # + # Returns the array `[stdout_and_stderr_s, status]`: + # + # stdout_and_stderr_s, status = Open3.capture2e('echo "Foo"') + # # => ["Foo\n", #] + # + # Like Process.spawn, this method has potential security vulnerabilities if + # called with untrusted input; see [Command + # Injection](rdoc-ref:command_injection.rdoc@Command+Injection). + # + # Unlike Process.spawn, this method waits for the child process to exit before + # returning, so the caller need not do so. + # + # If the first argument is a hash, it becomes leading argument `env` in the call + # to Open3.popen3; see [Execution + # Environment](rdoc-ref:Process@Execution+Environment). + # + # If the last argument is a hash, it becomes trailing argument `options` in the + # call to Open3.popen3; see [Execution + # Options](rdoc-ref:Process@Execution+Options). + # + # The hash `options` is given; two options have local effect in method + # Open3.capture2e: + # + # * If entry `options[:stdin_data]` exists, the entry is removed and its + # string value is sent to the command's standard input: + # + # Open3.capture2e('tee', stdin_data: 'Foo') + # # => ["Foo", #] + # + # * If entry `options[:binmode]` exists, the entry is removed and the internal + # streams are set to binary mode. + # + # The single required argument is one of the following: + # + # * `command_line` if it is a string, and if it begins with a shell reserved + # word or special built-in, or if it contains one or more metacharacters. + # * `exe_path` otherwise. + # + # **Argument `command_line`** + # + # String argument `command_line` is a command line to be passed to a shell; it + # must begin with a shell reserved word, begin with a special built-in, or + # contain meta characters: + # + # Open3.capture2e('if true; then echo "Foo"; fi') # Shell reserved word. + # # => ["Foo\n", #] + # Open3.capture2e('echo') # Built-in. + # # => ["\n", #] + # Open3.capture2e('date > date.tmp') # Contains meta character. + # # => ["", #] + # + # The command line may also contain arguments and options for the command: + # + # Open3.capture2e('echo "Foo"') + # # => ["Foo\n", #] + # + # **Argument `exe_path`** + # + # Argument `exe_path` is one of the following: + # + # * The string path to an executable to be called. + # * A 2-element array containing the path to an executable and the string to + # be used as the name of the executing process. + # + # Example: + # + # Open3.capture2e('/usr/bin/date') + # # => ["Sat Sep 30 09:01:46 AM CDT 2023\n", #] + # + # Ruby invokes the executable directly, with no shell and no shell expansion: + # + # Open3.capture2e('doesnt_exist') # Raises Errno::ENOENT + # + # If one or more `args` is given, each is an argument or option to be passed to + # the executable: + # + # Open3.capture2e('echo', 'C #') + # # => ["C #\n", #] + # Open3.capture2e('echo', 'hello', 'world') + # # => ["hello world\n", #] + # + def self.capture2e: (*String, ?stdin_data: String, ?chdir: String, ?binmode: boolish) -> [String, Process::Status] + | (Hash[String, String] env, *String cmds, ?chdir: String, ?stdin_data: String, ?binmode: boolish) -> [String, Process::Status] + + def self.capture2: (?Hash[String, String] env, *String cmds, ?chdir: String) -> [String, Process::Status] + + def self.capture3: (?Hash[String, String] env, *String cmds, ?chdir: String) -> [String, String, Process::Status] + + def self.pipeline: (?Hash[String, String] env, *String cmds, ?chdir: String) -> Array[Process::Status] + + def self.pipeline_r: (?Hash[String, String] env, *String cmds, ?chdir: String) -> [IO, Process::Waiter] + + def self.pipeline_rw: (?Hash[String, String] env, *String cmds, ?chdir: String) -> [IO, IO, Process::Waiter] + + def self.pipeline_start: (?Hash[String, String] env, *String cmds, ?chdir: String) -> Array[Process::Waiter] + + def self.pipeline_w: (?Hash[String, String] env, *String cmds, ?chdir: String) -> [IO, Process::Waiter] + + def self.popen2: (?Hash[String, String] env, *String exe_path_or_cmd_with_args, ?chdir: String) -> [IO, IO, Process::Waiter] + | [U] (?Hash[String, String] env, *String exe_path_or_cmd_with_args, ?chdir: String) { (IO stdin, IO stdout, Process::Waiter wait_thread) -> U } -> U + + def self.popen2e: (?Hash[String, String] env, *String exe_path_or_cmd_with_args, ?chdir: String) -> [IO, IO, Process::Waiter] + | [U] (?Hash[String, String] env, *String exe_path_or_cmd_with_args, ?chdir: String) { (IO stdin, IO stdout_and_stderr, Process::Waiter wait_thread) -> U } -> U + + def self.popen3: (?Hash[String, String] env, *String exe_path_or_cmd_with_args, ?chdir: String) -> [IO, IO, IO, Process::Waiter] + | [U] (?Hash[String, String] env, *String exe_path_or_cmd_with_args, ?chdir: String) { (IO stdin, IO stdout, IO stderr, Process::Waiter wait_thread) -> U } -> U + +end diff --git a/sig/shims/open3/0/open3.rbs b/sig/shims/open3/0/open3.rbs deleted file mode 100644 index d1397e549..000000000 --- a/sig/shims/open3/0/open3.rbs +++ /dev/null @@ -1,28 +0,0 @@ -module Open3 - def self.capture2: (?Hash[String, String] env, *String cmds) -> [String, Process::Status] - - def self.capture2e: (?Hash[String, String] env, *String cmds) -> [String, Process::Status] - - def self.capture3: (?Hash[String, String] env, *String cmds) -> [String, String, Process::Status] - - def self.pipeline: (?Hash[String, String] env, *String cmds) -> Array[Process::Status] - - def self.pipeline_r: (?Hash[String, String] env, *String cmds) -> [IO, Process::Waiter] - - def self.pipeline_rw: (?Hash[String, String] env, *String cmds) -> [IO, IO, Process::Waiter] - - def self.pipeline_start: (?Hash[String, String] env, *String cmds) -> Array[Process::Waiter] - - def self.pipeline_w: (?Hash[String, String] env, *String cmds) -> [IO, Process::Waiter] - - def self.popen2: (?Hash[String, String] env, *String exe_path_or_cmd_with_args) -> [IO, IO, Process::Waiter] - | [U] (?Hash[String, String] env, *String exe_path_or_cmd_with_args) { (IO stdin, IO stdout, Process::Waiter wait_thread) -> U } -> U - - def self.popen2e: (?Hash[String, String] env, *String exe_path_or_cmd_with_args) -> [IO, IO, Process::Waiter] - | [U] (?Hash[String, String] env, *String exe_path_or_cmd_with_args) { (IO stdin, IO stdout_and_stderr, Process::Waiter wait_thread) -> U } -> U - - def self.popen3: (?Hash[String, String] env, *String exe_path_or_cmd_with_args) -> [IO, IO, IO, Process::Waiter] - | [U] (?Hash[String, String] env, *String exe_path_or_cmd_with_args) { (IO stdin, IO stdout, IO stderr, Process::Waiter wait_thread) -> U } -> U - - VERSION: ::String -end diff --git a/spec/type_checker/levels/strong_spec.rb b/spec/type_checker/levels/strong_spec.rb index 12db1e442..5af87a23c 100644 --- a/spec/type_checker/levels/strong_spec.rb +++ b/spec/type_checker/levels/strong_spec.rb @@ -141,5 +141,19 @@ def meth arg )) expect(checker.problems).to be_empty end + + it 'understands Open3 methods' do + checker = type_checker(%( + require 'open3' + + # @return [void] + def run_command + # @type [Hash{String => String}] + foo = {'foo' => 'bar'} + Open3.capture2e(foo, 'ls', chdir: '/tmp') + end + )) + expect(checker.problems.map(&:message)).to be_empty + end end end From 30cdfc8b3091ff327c15d06eddb1c96315b2464c Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 3 Aug 2025 21:38:42 -0400 Subject: [PATCH 441/561] Add spec --- spec/shell_spec.rb | 114 ++++++++++++++++++++++++++++++++++++++++++++ spec/spec_helper.rb | 27 +++++++++++ 2 files changed, 141 insertions(+) create mode 100644 spec/shell_spec.rb diff --git a/spec/shell_spec.rb b/spec/shell_spec.rb new file mode 100644 index 000000000..1da2a98a9 --- /dev/null +++ b/spec/shell_spec.rb @@ -0,0 +1,114 @@ +# frozen_string_literal: true + +require 'tmpdir' +require 'open3' + +describe Solargraph::Shell do + let(:shell) { described_class.new } + + # @type cmd [Array] + # @return [String] + def bundle_exec(*cmd) + # run the command in the temporary directory with bundle exec + Bundler.with_unbundled_env do + output, status = Open3.capture2e("bundle exec #{cmd.join(' ')}") + expect(status.success?).to be(true), "Command failed: #{output}" + output + end + end + + describe 'method_pin' do + let(:api_map) { instance_double(Solargraph::ApiMap) } + let(:to_s_pin) { instance_double(Solargraph::Pin::Method, return_type: Solargraph::ComplexType.parse('String')) } + + before do + allow(Solargraph::ApiMap).to receive(:load_with_cache).and_return(api_map) + allow(api_map).to receive(:get_path_pins).with('String#to_s').and_return([to_s_pin]) + end + + context 'with no options' do + it 'prints a pin' do + allow(to_s_pin).to receive(:inspect).and_return('pin inspect result') + + out = capture_both { shell.method_pin('String#to_s') } + + expect(out).to eq("pin inspect result\n") + end + end + + context 'with --rbs option' do + it 'prints a pin with RBS type' do + allow(to_s_pin).to receive(:to_rbs).and_return('pin RBS result') + + out = capture_both do + shell.options = { rbs: true } + shell.method_pin('String#to_s') + end + expect(out).to eq("pin RBS result\n") + end + end + + context 'with --stack option' do + it 'prints a pin using stack results' do + allow(to_s_pin).to receive(:to_rbs).and_return('pin RBS result') + + allow(api_map).to receive(:get_method_stack).and_return([to_s_pin]) + capture_both do + shell.options = { stack: true } + shell.method_pin('String#to_s') + end + expect(api_map).to have_received(:get_method_stack).with('String', 'to_s', scope: :instance) + end + + it 'prints a static pin using stack results' do + # allow(to_s_pin).to receive(:to_rbs).and_return('pin RBS result') + string_new_pin = instance_double(Solargraph::Pin::Method, return_type: Solargraph::ComplexType.parse('String')) + + allow(api_map).to receive(:get_method_stack).with('String', 'new', scope: :class).and_return([string_new_pin]) + capture_both do + shell.options = { stack: true } + shell.method_pin('String.new') + end + expect(api_map).to have_received(:get_method_stack).with('String', 'new', scope: :class) + end + end + + context 'with --typify option' do + it 'prints a pin with typify type' do + allow(to_s_pin).to receive(:typify).and_return(Solargraph::ComplexType.parse('::String')) + + out = capture_both do + shell.options = { typify: true } + shell.method_pin('String#to_s') + end + expect(out).to eq("::String\n") + end + end + + context 'with --typify --rbs options' do + it 'prints a pin with typify type' do + allow(to_s_pin).to receive(:typify).and_return(Solargraph::ComplexType.parse('::String')) + + out = capture_both do + shell.options = { typify: true, rbs: true } + shell.method_pin('String#to_s') + end + expect(out).to eq("::String\n") + end + end + + context 'with no pin' do + it 'prints error' do + allow(api_map).to receive(:get_path_pins).with('Not#found').and_return([]) + + out = capture_both do + shell.options = {} + shell.method_pin('Not#found') + rescue SystemExit + # Ignore the SystemExit raised by the shell when no pin is found + end + expect(out).to include("Pin not found for path 'Not#found'") + end + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index faba8172e..b69e64097 100755 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -19,3 +19,30 @@ def with_env_var(name, value) ENV[name] = old_value # Restore the old value end end + + +def capture_stdout &block + original_stdout = $stdout + $stdout = StringIO.new + begin + block.call + $stdout.string + ensure + $stdout = original_stdout + end +end + +def capture_both &block + original_stdout = $stdout + original_stderr = $stderr + stringio = StringIO.new + $stdout = stringio + $stderr = stringio + begin + block.call + ensure + $stdout = original_stdout + $stderr = original_stderr + end + stringio.string +end From f4abeb4cf28a050269d457d2fbeab1765af9abfa Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 3 Aug 2025 21:43:38 -0400 Subject: [PATCH 442/561] Remove unneeded file --- sig/shims/parser/3.2.0.1/builders.rb | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 sig/shims/parser/3.2.0.1/builders.rb diff --git a/sig/shims/parser/3.2.0.1/builders.rb b/sig/shims/parser/3.2.0.1/builders.rb deleted file mode 100644 index b932108a9..000000000 --- a/sig/shims/parser/3.2.0.1/builders.rb +++ /dev/null @@ -1,2 +0,0 @@ -module Builders -end From 1ca70c122592370815a850fdaa9b2f109b6b46ae Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 3 Aug 2025 21:49:30 -0400 Subject: [PATCH 443/561] Linting fixes --- spec/library_spec.rb | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/spec/library_spec.rb b/spec/library_spec.rb index 5f2faeed9..cdaf67ea9 100644 --- a/spec/library_spec.rb +++ b/spec/library_spec.rb @@ -113,9 +113,7 @@ def bar baz, key: '' it 'diagnoses using all reporters' do directory = '' config = instance_double(Solargraph::Workspace::Config) - allow(config).to receive(:plugins).and_return([]) - allow(config).to receive(:required).and_return([]) - allow(config).to receive(:reporters).and_return(['all!']) + allow(config).to receive_messages(plugins: [], required: [], reporters: ['all!']) workspace = Solargraph::Workspace.new directory, config library = Solargraph::Library.new workspace src = Solargraph::Source.load_string(%( @@ -126,7 +124,6 @@ def bar baz, key: '' expect(result.to_s).to include('rubocop') end - it "documents symbols" do library = Solargraph::Library.new src = Solargraph::Source.load_string(%( From a30d7904818b16a1a88eafbe9977b9e8598f534d Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 3 Aug 2025 21:53:24 -0400 Subject: [PATCH 444/561] Fix spec --- lib/solargraph/shell.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/solargraph/shell.rb b/lib/solargraph/shell.rb index 92f8fed38..76f711552 100755 --- a/lib/solargraph/shell.rb +++ b/lib/solargraph/shell.rb @@ -247,7 +247,7 @@ def list # @param path [String] The path to the method pin, e.g. 'Class#method' or 'Class.method' # @return [void] def method_pin path - api_map = Solargraph::ApiMap.load_with_cache('.', STDERR) + api_map = Solargraph::ApiMap.load_with_cache('.', $stderr) pins = if options[:stack] scope, ns, meth = if path.include? '#' @@ -260,7 +260,7 @@ def method_pin path api_map.get_path_pins path end if pins.empty? - STDERR.puts "Pin not found for path '#{path}'" + $stderr.puts "Pin not found for path '#{path}'" exit 1 end pins.each do |pin| From de2cf54df9d4eb4c54fd4425b2bf0544a6737543 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 3 Aug 2025 21:56:07 -0400 Subject: [PATCH 445/561] Fix linting issue --- spec/type_checker/levels/strict_spec.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/type_checker/levels/strict_spec.rb b/spec/type_checker/levels/strict_spec.rb index 586dc0a97..d6a218b1f 100644 --- a/spec/type_checker/levels/strict_spec.rb +++ b/spec/type_checker/levels/strict_spec.rb @@ -958,6 +958,5 @@ def bar )) expect(checker.problems.map(&:message)).to eq([]) end - end end From 592e6bcd89bd0988e1245c6eac8cd14d2f6b621d Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 4 Aug 2025 06:55:28 -0400 Subject: [PATCH 446/561] Speed up spec --- spec/rbs_map/conversions_spec.rb | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/spec/rbs_map/conversions_spec.rb b/spec/rbs_map/conversions_spec.rb index d9570ae0f..008432d68 100644 --- a/spec/rbs_map/conversions_spec.rb +++ b/spec/rbs_map/conversions_spec.rb @@ -55,21 +55,23 @@ def bar: () -> untyped end context 'with standard loads for solargraph project' do - let(:api_map) { Solargraph::ApiMap.load_with_cache('.') } - - let(:superclass_pin) do - api_map.pins.find do |pin| - pin.is_a?(Solargraph::Pin::Reference::Superclass) && pin.context.namespace == 'Parser::AST::Node' - end + before :all do # rubocop:disable RSpec/BeforeAfterAll + @api_map = Solargraph::ApiMap.load_with_cache('.') end - it 'finds a superclass pin for Parser::AST::Node' do - expect(superclass_pin).not_to be_nil - end + let(:api_map) { @api_map } # rubocop:disable RSpec/InstanceVariable - it 'generates a rooted pin for superclass of Parser::AST::Node' do - # rooted! - expect(superclass_pin.name) .to eq('::AST::Node') + context 'with superclass pin for Parser::AST::Node' do + let(:superclass_pin) do + api_map.pins.find do |pin| + pin.is_a?(Solargraph::Pin::Reference::Superclass) && pin.context.namespace == 'Parser::AST::Node' + end + end + + it 'generates a rooted pin' do + # rooted! + expect(superclass_pin&.name).to eq('::AST::Node') + end end end end From 5c24a74c5c60f2297862c01ce464af94aa587b50 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 4 Aug 2025 06:59:53 -0400 Subject: [PATCH 447/561] Add Open3.capture2e chdir spec --- spec/rbs_map/conversions_spec.rb | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/spec/rbs_map/conversions_spec.rb b/spec/rbs_map/conversions_spec.rb index 09c203687..220f69190 100644 --- a/spec/rbs_map/conversions_spec.rb +++ b/spec/rbs_map/conversions_spec.rb @@ -51,4 +51,26 @@ def bar: () -> untyped expect(method_pin.return_type.tag).to eq('undefined') end end + + if Gem::Version.new(RBS::VERSION) >= Gem::Version.new('3.9.1') + context 'with method pin for Open3.capture2e' do + let(:api_map) { Solargraph::ApiMap.load_with_cache('.') } + + let(:method_pin) do + api_map.pins.find do |pin| + pin.is_a?(Solargraph::Pin::Method) && pin.path == 'Open3.capture2e' + end + end + + let(:chdir_param) do + method_pin&.signatures&.flat_map(&:parameters)&.find do |param| + param.name == 'chdir' + end + end + + it 'accepts chdir kwarg' do + expect(chdir_param).not_to be_nil, -> { "Found pin #{method_pin.to_rbs} from #{method_pin.type_location}" } + end + end + end end From 1af1613eb1abc51b452c3892e88bfd0d1d2b632a Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 4 Aug 2025 07:11:57 -0400 Subject: [PATCH 448/561] Linting fix --- spec/spec_helper.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index d6d9ac3b1..94825366f 100755 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -7,7 +7,9 @@ end require 'solargraph' # Suppress logger output in specs (if possible) -Solargraph::Logging.logger.reopen(File::NULL) if Solargraph::Logging.logger.respond_to?(:reopen) && !ENV.key?('SOLARGRAPH_LOG') +if Solargraph::Logging.logger.respond_to?(:reopen) && !ENV.key?('SOLARGRAPH_LOG') + Solargraph::Logging.logger.reopen(File::NULL) +end def with_env_var(name, value) old_value = ENV[name] # Store the old value From 97623334048a4f9442abdbbc8ae02dea01c58503 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 4 Aug 2025 07:59:13 -0400 Subject: [PATCH 449/561] Add @sg-ignore --- lib/solargraph/yardoc.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/solargraph/yardoc.rb b/lib/solargraph/yardoc.rb index ac991d6da..4ab13b11b 100644 --- a/lib/solargraph/yardoc.rb +++ b/lib/solargraph/yardoc.rb @@ -24,6 +24,7 @@ def build_docs gem_yardoc_path, yard_plugins, gemspec Solargraph.logger.debug { "Running: #{cmd}" } # @todo set these up to run in parallel # + # @sg-ignore stdout_and_stderr_str, status = Open3.capture2e(current_bundle_env_tweaks, cmd, chdir: gemspec.gem_dir) return if status.success? Solargraph.logger.warn { "YARD failed running #{cmd.inspect} in #{gemspec.gem_dir}" } From 14dacc39eba0f1906d2f7f83019d2de3b7217ffa Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 4 Aug 2025 08:04:09 -0400 Subject: [PATCH 450/561] Linting fix --- lib/solargraph/complex_type.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/solargraph/complex_type.rb b/lib/solargraph/complex_type.rb index 8167339e0..6203fb5a9 100644 --- a/lib/solargraph/complex_type.rb +++ b/lib/solargraph/complex_type.rb @@ -224,7 +224,7 @@ def duck_types_match? api_map, expected, inferred raise ArgumentError, 'Expected type must be duck type' unless expected.duck_type? expected.each do |exp| next unless exp.duck_type? - quack = exp.to_s[1..-1] + quack = exp.to_s[1..] return false if api_map.get_method_stack(inferred.namespace, quack, scope: inferred.scope).empty? end true From 1bf5a448b0c3f279c64ed61e3ab30c0d25d00b3e Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 4 Aug 2025 08:49:51 -0400 Subject: [PATCH 451/561] Add mocking to spec for reliability --- spec/pin_cache_spec.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/spec/pin_cache_spec.rb b/spec/pin_cache_spec.rb index 0f507e286..96023fa1f 100644 --- a/spec/pin_cache_spec.rb +++ b/spec/pin_cache_spec.rb @@ -13,6 +13,9 @@ describe '#cached?' do it 'returns true for a gem that is cached' do + allow(File).to receive(:file?).with(%r{.*stdlib/backport.ser$}).and_return(false) + allow(File).to receive(:file?).with(%r{.*combined/backport-.*.ser$}).and_return(true) + gemspec = Gem::Specification.find_by_name('backport') expect(pin_cache.cached?(gemspec)).to be true end From 883574e3077c57fff1e58969ef06aa5ff5f21795 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 4 Aug 2025 08:53:48 -0400 Subject: [PATCH 452/561] Add spec --- spec/complex_type/unique_type_spec.rb | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 spec/complex_type/unique_type_spec.rb diff --git a/spec/complex_type/unique_type_spec.rb b/spec/complex_type/unique_type_spec.rb new file mode 100644 index 000000000..2d9812600 --- /dev/null +++ b/spec/complex_type/unique_type_spec.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +describe Solargraph::ComplexType::UniqueType do + describe '#any?' do + let(:type) { described_class.parse('String') } + + it 'yields one and only one type, itself' do + types_encountered = [] + type.any? { |t| types_encountered << t } + expect(types_encountered).to eq([type]) + end + end +end From 05c6b86d6e2dca232cd8ce6a479da76f51308034 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 4 Aug 2025 09:10:27 -0400 Subject: [PATCH 453/561] Ignore YARD/TagTypeSyntax pending YARD PR --- .rubocop_todo.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 762cd5dca..cb9d4a592 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -2534,6 +2534,8 @@ YARD/MismatchName: YARD/TagTypeSyntax: Exclude: + - 'lib/solargraph/complex_type.rb' + - 'lib/solargraph/complex_type/unique_type.rb' - 'lib/solargraph/language_server/host.rb' - 'lib/solargraph/parser/comment_ripper.rb' - 'lib/solargraph/type_checker.rb' From 637edd0b37caabe007a9539ef8abd4198c73da68 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 4 Aug 2025 09:31:03 -0400 Subject: [PATCH 454/561] Linting fixes --- spec/type_checker/levels/strong_spec.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/spec/type_checker/levels/strong_spec.rb b/spec/type_checker/levels/strong_spec.rb index 68a62d395..1d343c6d9 100644 --- a/spec/type_checker/levels/strong_spec.rb +++ b/spec/type_checker/levels/strong_spec.rb @@ -226,7 +226,7 @@ def baz(bing) expect(checker.problems.map(&:message)).to be_empty end - it 'ignores generic resolution failures' do + it 'ignores generic resolution failure with no generic tag' do checker = type_checker(%( class Foo # @param foo [Class] @@ -262,8 +262,7 @@ def block_pins expect(checker.problems.map(&:message)).to be_empty end - - it 'ignores generic resolution failures' do + it 'ignores generic resolution failures from current Solargraph limitation' do checker = type_checker(%( class Foo # @generic T From 4fc60fb37256fff46151c4b1daea22ac4e4ca32f Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 4 Aug 2025 09:31:26 -0400 Subject: [PATCH 455/561] Linting fixes --- spec/type_checker/levels/strong_spec.rb | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/spec/type_checker/levels/strong_spec.rb b/spec/type_checker/levels/strong_spec.rb index 5af87a23c..a0bab2152 100644 --- a/spec/type_checker/levels/strong_spec.rb +++ b/spec/type_checker/levels/strong_spec.rb @@ -144,15 +144,15 @@ def meth arg it 'understands Open3 methods' do checker = type_checker(%( - require 'open3' + require 'open3' - # @return [void] - def run_command - # @type [Hash{String => String}] - foo = {'foo' => 'bar'} - Open3.capture2e(foo, 'ls', chdir: '/tmp') - end - )) + # @return [void] + def run_command + # @type [Hash{String => String}] + foo = {'foo' => 'bar'} + Open3.capture2e(foo, 'ls', chdir: '/tmp') + end + )) expect(checker.problems.map(&:message)).to be_empty end end From 0b6e3094715140e54a21ecee73ef62b85f84baa0 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 4 Aug 2025 09:33:12 -0400 Subject: [PATCH 456/561] Linting fixes --- spec/rbs_map/conversions_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/rbs_map/conversions_spec.rb b/spec/rbs_map/conversions_spec.rb index 220f69190..bb57dfd0b 100644 --- a/spec/rbs_map/conversions_spec.rb +++ b/spec/rbs_map/conversions_spec.rb @@ -63,7 +63,7 @@ def bar: () -> untyped end let(:chdir_param) do - method_pin&.signatures&.flat_map(&:parameters)&.find do |param| + method_pin&.signatures&.flat_map(&:parameters)&.find do |param| # rubocop:disable Style/SafeNavigationChainLength param.name == 'chdir' end end From cfb1479c2facee91e6f95e42cce36b3146bb6011 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 4 Aug 2025 09:36:05 -0400 Subject: [PATCH 457/561] Fix merge issue --- lib/solargraph/parser/parser_gem/class_methods.rb | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/solargraph/parser/parser_gem/class_methods.rb b/lib/solargraph/parser/parser_gem/class_methods.rb index bd8648dd7..de1110ac9 100644 --- a/lib/solargraph/parser/parser_gem/class_methods.rb +++ b/lib/solargraph/parser/parser_gem/class_methods.rb @@ -81,7 +81,6 @@ def inner_node_references name, top result = [] if top.is_a?(AST::Node) && top.to_s.include?(":#{name}") result.push top if top.children.any? { |c| c.to_s == name } - # @sg-ignore top.children.each { |c| result.concat inner_node_references(name, c) } end result @@ -130,7 +129,6 @@ def node_range node # @param node [Parser::AST::Node] # @return [Array] def string_ranges node - # @sg-ignore return [] unless is_ast_node?(node) result = [] result.push Range.from_node(node) if node.type == :str @@ -139,7 +137,6 @@ def string_ranges node end if node.type == :dstr && node.children.last.nil? last = node.children[-2] - # @sg-ignore unless last.nil? rng = Range.from_node(last) pos = Position.new(rng.ending.line, rng.ending.column - 1) From 6dba22220db7c0735a9c54b1ab6387716d49f18e Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 4 Aug 2025 09:36:21 -0400 Subject: [PATCH 458/561] Fix merge issue --- lib/solargraph/workspace/require_paths.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/solargraph/workspace/require_paths.rb b/lib/solargraph/workspace/require_paths.rb index 57ebf5044..c8eea161b 100644 --- a/lib/solargraph/workspace/require_paths.rb +++ b/lib/solargraph/workspace/require_paths.rb @@ -76,7 +76,6 @@ def require_path_from_gemspec_file gemspec_file_path "spec = eval(File.read('#{gemspec_file_path}'), TOPLEVEL_BINDING, '#{gemspec_file_path}'); " \ 'return unless Gem::Specification === spec; ' \ 'puts({name: spec.name, paths: spec.require_paths}.to_json)'] - # @sg-ignore o, e, s = Open3.capture3(*cmd) if s.success? begin From c175dbeda54532e71b2acd7052b9130e35cadc4d Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 4 Aug 2025 09:37:26 -0400 Subject: [PATCH 459/561] Ratchet .rubocop_todo.yml --- .rubocop_todo.yml | 62 ----------------------------------------------- 1 file changed, 62 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index cb9d4a592..11cbef5d3 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -57,7 +57,6 @@ Gemspec/RequiredRubyVersion: # SupportedStyles: with_first_argument, with_fixed_indentation Layout/ArgumentAlignment: Exclude: - - 'lib/solargraph/convention/struct_definition.rb' - 'lib/solargraph/pin/callable.rb' - 'spec/source/source_chainer_spec.rb' @@ -72,7 +71,6 @@ Layout/BlockAlignment: Layout/ClosingHeredocIndentation: Exclude: - 'spec/diagnostics/rubocop_spec.rb' - - 'spec/rbs_map/conversions_spec.rb' # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowForAlignment. @@ -100,7 +98,6 @@ Layout/ElseAlignment: - 'lib/solargraph/source/chain/call.rb' - 'lib/solargraph/source_map/clip.rb' - 'lib/solargraph/source_map/mapper.rb' - - 'lib/solargraph/type_checker.rb' - 'lib/solargraph/type_checker/rules.rb' - 'lib/solargraph/yard_map/mapper.rb' @@ -122,18 +119,10 @@ Layout/EmptyLines: - 'lib/solargraph/pin/delegated_method.rb' - 'lib/solargraph/rbs_map/conversions.rb' - 'spec/complex_type_spec.rb' - - 'spec/convention/struct_definition_spec.rb' - 'spec/pin/local_variable_spec.rb' - 'spec/pin/symbol_spec.rb' - 'spec/type_checker/levels/strict_spec.rb' -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle. -# SupportedStyles: empty_lines, empty_lines_except_namespace, empty_lines_special, no_empty_lines, beginning_only, ending_only -Layout/EmptyLinesAroundClassBody: - Exclude: - - 'lib/solargraph/rbs_map/core_map.rb' - # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: empty_lines, empty_lines_except_namespace, empty_lines_special, no_empty_lines @@ -179,7 +168,6 @@ Layout/ExtraSpacing: Exclude: - 'lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb' - 'lib/solargraph/pin/closure.rb' - - 'lib/solargraph/pin/local_variable.rb' - 'lib/solargraph/rbs_map/conversions.rb' - 'lib/solargraph/type_checker.rb' - 'spec/spec_helper.rb' @@ -231,14 +219,12 @@ Layout/FirstHashElementIndentation: # SupportedLastArgumentHashStyles: always_inspect, always_ignore, ignore_implicit, ignore_explicit Layout/HashAlignment: Exclude: - - 'lib/solargraph/convention/struct_definition.rb' - 'lib/solargraph/workspace/config.rb' # This cop supports safe autocorrection (--autocorrect). Layout/HeredocIndentation: Exclude: - 'spec/diagnostics/rubocop_spec.rb' - - 'spec/rbs_map/conversions_spec.rb' - 'spec/yard_map/mapper/to_method_spec.rb' # This cop supports safe autocorrection (--autocorrect). @@ -339,7 +325,6 @@ Layout/SpaceAroundOperators: Exclude: - 'lib/solargraph/library.rb' - 'lib/solargraph/parser/parser_gem/node_methods.rb' - - 'lib/solargraph/pin/local_variable.rb' - 'lib/solargraph/source.rb' - 'lib/solargraph/source/change.rb' - 'lib/solargraph/source/cursor.rb' @@ -439,20 +424,12 @@ Layout/SpaceInsideParens: - 'lib/solargraph/pin/namespace.rb' - 'lib/solargraph/source_map.rb' -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle. -# SupportedStyles: final_newline, final_blank_line -Layout/TrailingEmptyLines: - Exclude: - - 'spec/convention/struct_definition_spec.rb' - # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowInHeredoc. Layout/TrailingWhitespace: Exclude: - 'lib/solargraph/language_server/message/client/register_capability.rb' - 'spec/api_map/config_spec.rb' - - 'spec/convention/struct_definition_spec.rb' - 'spec/convention_spec.rb' # This cop supports safe autocorrection (--autocorrect). @@ -497,10 +474,6 @@ Lint/AssignmentInCondition: Exclude: - 'lib/solargraph/library.rb' -Lint/BinaryOperatorWithIdenticalOperands: - Exclude: - - 'lib/solargraph/api_map/source_to_yard.rb' - # This cop supports unsafe autocorrection (--autocorrect-all). Lint/BooleanSymbol: Exclude: @@ -527,7 +500,6 @@ Lint/DuplicateMethods: - 'lib/solargraph/complex_type.rb' - 'lib/solargraph/location.rb' - 'lib/solargraph/pin/base.rb' - - 'lib/solargraph/pin/common.rb' - 'lib/solargraph/pin/signature.rb' - 'lib/solargraph/source/chain/link.rb' @@ -576,7 +548,6 @@ Lint/NonAtomicFileOperation: # This cop supports safe autocorrection (--autocorrect). Lint/ParenthesesAsGroupedExpression: Exclude: - - 'lib/solargraph.rb' - 'lib/solargraph/parser/parser_gem/node_chainer.rb' - 'spec/language_server/host_spec.rb' - 'spec/source_map/clip_spec.rb' @@ -649,7 +620,6 @@ Lint/UnusedBlockArgument: # NotImplementedExceptions: NotImplementedError Lint/UnusedMethodArgument: Exclude: - - 'lib/solargraph.rb' - 'lib/solargraph/complex_type/type_methods.rb' - 'lib/solargraph/convention/base.rb' - 'lib/solargraph/diagnostics/base.rb' @@ -778,7 +748,6 @@ Metrics/MethodLength: - 'lib/solargraph/parser/parser_gem/node_chainer.rb' - 'lib/solargraph/source/chain/call.rb' - 'lib/solargraph/source_map/mapper.rb' - - 'lib/solargraph/type_checker.rb' # Configuration parameters: CountComments, Max, CountAsOne. Metrics/ModuleLength: @@ -892,11 +861,6 @@ RSpec/BeEq: - 'spec/complex_type_spec.rb' - 'spec/pin/method_spec.rb' -# This cop supports unsafe autocorrection (--autocorrect-all). -RSpec/BeEql: - Exclude: - - 'spec/convention/struct_definition_spec.rb' - # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: be, be_nil @@ -997,7 +961,6 @@ RSpec/DescribedClass: - 'spec/source_map/mapper_spec.rb' - 'spec/source_map_spec.rb' - 'spec/source_spec.rb' - - 'spec/type_checker/checks_spec.rb' - 'spec/type_checker/levels/normal_spec.rb' - 'spec/type_checker/levels/strict_spec.rb' - 'spec/type_checker/rules_spec.rb' @@ -1053,7 +1016,6 @@ RSpec/ExampleLength: # DisallowedExamples: works RSpec/ExampleWording: Exclude: - - 'spec/convention/struct_definition_spec.rb' - 'spec/pin/base_spec.rb' - 'spec/pin/method_spec.rb' @@ -1171,7 +1133,6 @@ RSpec/MultipleExpectations: - 'spec/source_map/node_processor_spec.rb' - 'spec/source_map_spec.rb' - 'spec/source_spec.rb' - - 'spec/type_checker/checks_spec.rb' - 'spec/type_checker/levels/normal_spec.rb' - 'spec/type_checker/levels/strict_spec.rb' - 'spec/type_checker/levels/strong_spec.rb' @@ -1196,7 +1157,6 @@ RSpec/NoExpectationExample: - 'spec/pin/block_spec.rb' - 'spec/pin/method_spec.rb' - 'spec/source/chain/call_spec.rb' - - 'spec/type_checker/checks_spec.rb' - 'spec/type_checker/levels/typed_spec.rb' # This cop supports safe autocorrection (--autocorrect). @@ -1317,7 +1277,6 @@ Style/AccessorGrouping: # SupportedStyles: always, conditionals Style/AndOr: Exclude: - - 'lib/solargraph/api_map/source_to_yard.rb' - 'lib/solargraph/complex_type/unique_type.rb' - 'lib/solargraph/language_server/message/base.rb' - 'lib/solargraph/page.rb' @@ -1752,7 +1711,6 @@ Style/FrozenStringLiteralComment: - 'spec/source_map_spec.rb' - 'spec/source_spec.rb' - 'spec/spec_helper.rb' - - 'spec/type_checker/checks_spec.rb' - 'spec/type_checker/levels/normal_spec.rb' - 'spec/type_checker/levels/strict_spec.rb' - 'spec/type_checker/levels/strong_spec.rb' @@ -1839,7 +1797,6 @@ Style/IfUnlessModifier: - 'lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb' - 'lib/solargraph/pin/base.rb' - 'lib/solargraph/pin/callable.rb' - - 'lib/solargraph/pin/common.rb' - 'lib/solargraph/pin/constant.rb' - 'lib/solargraph/pin/method.rb' - 'lib/solargraph/pin/parameter.rb' @@ -1870,7 +1827,6 @@ Style/MapIntoArray: Exclude: - 'lib/solargraph/diagnostics/update_errors.rb' - 'lib/solargraph/parser/parser_gem/node_chainer.rb' - - 'lib/solargraph/type_checker/param_def.rb' # This cop supports unsafe autocorrection (--autocorrect-all). Style/MapToHash: @@ -1935,7 +1891,6 @@ Style/MethodDefParentheses: - 'lib/solargraph/source_map.rb' - 'lib/solargraph/source_map/mapper.rb' - 'lib/solargraph/type_checker.rb' - - 'lib/solargraph/type_checker/checks.rb' - 'lib/solargraph/yard_map/helpers.rb' - 'spec/fixtures/rdoc-lib/lib/example.rb' - 'spec/source_map_spec.rb' @@ -2001,13 +1956,6 @@ Style/NegatedIfElseCondition: - 'lib/solargraph/shell.rb' - 'lib/solargraph/type_checker.rb' -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AllowedMethods. -# AllowedMethods: be, be_a, be_an, be_between, be_falsey, be_kind_of, be_instance_of, be_truthy, be_within, eq, eql, end_with, include, match, raise_error, respond_to, start_with -Style/NestedParenthesizedCalls: - Exclude: - - 'lib/solargraph/type_checker.rb' - # This cop supports safe autocorrection (--autocorrect). Style/NestedTernaryOperator: Exclude: @@ -2022,7 +1970,6 @@ Style/Next: - 'lib/solargraph/parser/parser_gem/node_processors/send_node.rb' - 'lib/solargraph/pin/signature.rb' - 'lib/solargraph/source_map/clip.rb' - - 'lib/solargraph/type_checker/checks.rb' # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: MinDigits, Strict, AllowedNumbers, AllowedPatterns. @@ -2103,7 +2050,6 @@ Style/RedundantBegin: - 'lib/solargraph/shell.rb' - 'lib/solargraph/source/cursor.rb' - 'lib/solargraph/source/encoding_fixes.rb' - - 'lib/solargraph/type_checker.rb' - 'lib/solargraph/workspace.rb' # This cop supports safe autocorrection (--autocorrect). @@ -2225,7 +2171,6 @@ Style/SlicingWithRange: - 'lib/solargraph/source/cursor.rb' - 'lib/solargraph/source/source_chainer.rb' - 'lib/solargraph/source_map/mapper.rb' - - 'lib/solargraph/type_checker/checks.rb' # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowModifier. @@ -2250,7 +2195,6 @@ Style/StringConcatenation: Exclude: - 'lib/solargraph/api_map.rb' - 'lib/solargraph/api_map/index.rb' - - 'lib/solargraph/convention/struct_definition.rb' - 'lib/solargraph/pin/base.rb' - 'lib/solargraph/pin/callable.rb' - 'lib/solargraph/pin/closure.rb' @@ -2503,13 +2447,9 @@ YARD/MismatchName: Exclude: - 'lib/solargraph/complex_type.rb' - 'lib/solargraph/complex_type/unique_type.rb' - - 'lib/solargraph/convention.rb' - - 'lib/solargraph/convention/data_definition.rb' - 'lib/solargraph/language_server/host.rb' - 'lib/solargraph/language_server/host/dispatch.rb' - - 'lib/solargraph/language_server/message/text_document/formatting.rb' - 'lib/solargraph/language_server/request.rb' - - 'lib/solargraph/parser/flow_sensitive_typing.rb' - 'lib/solargraph/parser/parser_gem/node_methods.rb' - 'lib/solargraph/parser/region.rb' - 'lib/solargraph/pin/base.rb' @@ -2527,7 +2467,6 @@ YARD/MismatchName: - 'lib/solargraph/pin/until.rb' - 'lib/solargraph/pin/while.rb' - 'lib/solargraph/pin_cache.rb' - - 'lib/solargraph/source/chain.rb' - 'lib/solargraph/source/chain/call.rb' - 'lib/solargraph/source/chain/z_super.rb' - 'lib/solargraph/type_checker.rb' @@ -2575,7 +2514,6 @@ Layout/LineLength: - 'lib/solargraph/parser/parser_gem/node_processors/send_node.rb' - 'lib/solargraph/pin/base.rb' - 'lib/solargraph/pin/callable.rb' - - 'lib/solargraph/pin/common.rb' - 'lib/solargraph/pin/documenting.rb' - 'lib/solargraph/pin/method.rb' - 'lib/solargraph/pin/parameter.rb' From 990cf2f8180331f59b07b35aa2a7182bfb0fb674 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 4 Aug 2025 09:47:41 -0400 Subject: [PATCH 460/561] Revert kwarg change on method used by (specs in) solargraph-rails --- lib/solargraph/api_map.rb | 2 +- lib/solargraph/shell.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/solargraph/api_map.rb b/lib/solargraph/api_map.rb index d5fd46dc9..23146e3b8 100755 --- a/lib/solargraph/api_map.rb +++ b/lib/solargraph/api_map.rb @@ -210,7 +210,7 @@ class << self # @param directory [String] workspace directory # @param out [IO] The output stream for messages # @return [ApiMap] - def self.load_with_cache directory, out: $stderr + def self.load_with_cache directory, out = $stderr api_map = load(directory) if api_map.uncached_gemspecs.empty? logger.info { "All gems cached for #{directory}" } diff --git a/lib/solargraph/shell.rb b/lib/solargraph/shell.rb index 8dd74a36f..7c5ea0fd9 100755 --- a/lib/solargraph/shell.rb +++ b/lib/solargraph/shell.rb @@ -206,7 +206,7 @@ def reporters # @return [void] def typecheck *files directory = File.realpath(options[:directory]) - api_map = Solargraph::ApiMap.load_with_cache(directory, out: $stdout) + api_map = Solargraph::ApiMap.load_with_cache(directory, $stdout) probcount = 0 if files.empty? files = api_map.source_maps.map(&:filename) From 5678e6b2f215374841eeb50751f75fe07a07eee7 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 4 Aug 2025 09:59:59 -0400 Subject: [PATCH 461/561] Exclude a vendored issue --- .overcommit.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.overcommit.yml b/.overcommit.yml index fdbc8152d..8990590ea 100644 --- a/.overcommit.yml +++ b/.overcommit.yml @@ -30,6 +30,8 @@ PreCommit: exclude: # don't freak out over line below - '.overcommit.yml' + # from upstream + - 'rbs/fills/rubygems/0/basic_specification.rbs' keywords: ['FIXME', 'TODO', 'XXX'] # creates false positives in CI From daec55a0178381bcad1f2d0b70a13306a9767e0b Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 4 Aug 2025 10:22:07 -0400 Subject: [PATCH 462/561] Revert kwarg change on method used by (specs in) solargraph-rails --- spec/type_checker/levels/strict_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/type_checker/levels/strict_spec.rb b/spec/type_checker/levels/strict_spec.rb index c92a76874..13c20e85f 100644 --- a/spec/type_checker/levels/strict_spec.rb +++ b/spec/type_checker/levels/strict_spec.rb @@ -59,7 +59,7 @@ def bar(a); end require 'kramdown-parser-gfm' Kramdown::Parser::GFM.undefined_call ), 'test.rb') - api_map = Solargraph::ApiMap.load_with_cache('.', out: nil) + api_map = Solargraph::ApiMap.load_with_cache('.', nil) api_map.catalog Solargraph::Bench.new(source_maps: [source_map], external_requires: ['kramdown-parser-gfm']) checker = Solargraph::TypeChecker.new('test.rb', api_map: api_map, level: :strict) expect(checker.problems).to be_empty From 6ef82c1b78e6cd50c9a87d7e4a42387c06e2efce Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 4 Aug 2025 10:46:55 -0400 Subject: [PATCH 463/561] Rubocop fix, add spec --- lib/solargraph/api_map/source_to_yard.rb | 8 ++++++-- spec/api_map/source_to_yard_spec.rb | 21 +++++++++++++++++++-- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/lib/solargraph/api_map/source_to_yard.rb b/lib/solargraph/api_map/source_to_yard.rb index 5e7784f01..5f64d9bad 100644 --- a/lib/solargraph/api_map/source_to_yard.rb +++ b/lib/solargraph/api_map/source_to_yard.rb @@ -49,8 +49,12 @@ def rake_yard store end store.get_extends(pin.path).each do |ref| extend_object = code_object_at(pin.path, YARD::CodeObjects::ClassObject) - extend_object.instance_mixins.push code_object_map[ref] unless extend_object.nil? or extend_object.nil? - extend_object.class_mixins.push code_object_map[ref] unless extend_object.nil? or extend_object.nil? + next unless extend_object + code_object = code_object_map[ref] + next unless code_object + extend_object.class_mixins.push code_object + # @todo add spec showing why this next line is necessary + extend_object.instance_mixins.push code_object end end store.method_pins.each do |pin| diff --git a/spec/api_map/source_to_yard_spec.rb b/spec/api_map/source_to_yard_spec.rb index 477c60c0e..78758b710 100644 --- a/spec/api_map/source_to_yard_spec.rb +++ b/spec/api_map/source_to_yard_spec.rb @@ -40,7 +40,7 @@ def self.baz expect(class_method_object.tag(:return).types).to eq(['Foo']) end - it "generates mixins" do + it "generates instance mixins" do source = Solargraph::SourceMap.load_string(%( module Foo def bar @@ -55,7 +55,24 @@ class Baz object.rake_yard Solargraph::ApiMap::Store.new(source.pins) module_object = object.code_object_at('Foo') class_object = object.code_object_at('Baz') - expect(class_object.mixins).to include(module_object) + expect(class_object.instance_mixins).to include(module_object) + end + + it "generates class mixins" do + source = Solargraph::SourceMap.load_string(%( + module Foo + def bar; end + end + class Baz + extend Foo + end + )) + object = Object.new + object.extend Solargraph::ApiMap::SourceToYard + object.rake_yard Solargraph::ApiMap::Store.new(source.pins) + module_object = object.code_object_at('Foo') + class_object = object.code_object_at('Baz') + expect(class_object.class_mixins).to include(module_object) end it "generates methods for attributes" do From 27a78c5b41bece5a2ed5fb4a6f17644450a68f28 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 4 Aug 2025 10:52:38 -0400 Subject: [PATCH 464/561] Fix issue from undercover --- lib/solargraph/source_map/clip.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/solargraph/source_map/clip.rb b/lib/solargraph/source_map/clip.rb index ed82fd649..4a7bfcbcd 100644 --- a/lib/solargraph/source_map/clip.rb +++ b/lib/solargraph/source_map/clip.rb @@ -56,8 +56,9 @@ def infer dfn = cursor.chain.define(api_map, closure, locals).first return ComplexType.try_parse('::BasicObject') if dfn && dfn.path == 'Class#new' end - return result unless result.tag == 'self' - cursor.chain.base.infer(api_map, closure, locals) + # should receive result with selfs resolved from infer() + Solargraph.assert_or_log(:clip_infer_self, 'Received selfy inference') if result.selfy? + result end # Get an array of all the locals that are visible from the cursors's From d41d78f5c4417c8878c21bdd402b4f38c1e9867f Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 4 Aug 2025 11:26:38 -0400 Subject: [PATCH 465/561] Bump version in rubocop todo file --- .rubocop_todo.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index c5865e228..fd4150fc0 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,6 +1,6 @@ # This configuration was generated by # `rubocop --auto-gen-config --no-exclude-limit --no-offense-counts --no-auto-gen-timestamp` -# using RuboCop version 1.79.0. +# using RuboCop version 1.79.1. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new From e8c2cca9cf8728b9b7db04fca1238f2ecc234363 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 4 Aug 2025 11:41:10 -0400 Subject: [PATCH 466/561] Improve test coverage and fix bug --- lib/solargraph/complex_type/unique_type.rb | 4 +- spec/complex_type/conforms_to_spec.rb | 68 ++++++++++++++++------ 2 files changed, 52 insertions(+), 20 deletions(-) diff --git a/lib/solargraph/complex_type/unique_type.rb b/lib/solargraph/complex_type/unique_type.rb index a9f2a899b..13fb7129f 100644 --- a/lib/solargraph/complex_type/unique_type.rb +++ b/lib/solargraph/complex_type/unique_type.rb @@ -241,7 +241,7 @@ def conforms_to_unique_type?(api_map, expected, situation = :method_call, if variance == :invariant return false unless inferred.name == expected.name - elsif erased_variance == :covariant + elsif variance == :covariant # covariant: we can pass in a more specific type # we contain the expected mix-in, or we have a more specific type @@ -249,7 +249,7 @@ def conforms_to_unique_type?(api_map, expected, situation = :method_call, api_map.super_and_sub?(expected.name, inferred.name) || inferred.name == expected.name - elsif erased_variance == :contravariant + elsif variance == :contravariant # contravariant: we can pass in a more general type # we contain the expected mix-in, or we have a more general type diff --git a/spec/complex_type/conforms_to_spec.rb b/spec/complex_type/conforms_to_spec.rb index 581ce1d34..bacf19b5e 100644 --- a/spec/complex_type/conforms_to_spec.rb +++ b/spec/complex_type/conforms_to_spec.rb @@ -1,8 +1,11 @@ # frozen_string_literal: true describe Solargraph::ComplexType do + let(:api_map) do + Solargraph::ApiMap.new + end + it 'validates simple core types' do - api_map = Solargraph::ApiMap.new exp = described_class.parse('String') inf = described_class.parse('String') match = inf.conforms_to?(api_map, exp, :method_call) @@ -10,7 +13,6 @@ end it 'invalidates simple core types' do - api_map = Solargraph::ApiMap.new exp = described_class.parse('String') inf = described_class.parse('Integer') match = inf.conforms_to?(api_map, exp, :method_call) @@ -18,7 +20,6 @@ end it 'allows subtype skew if told' do - api_map = Solargraph::ApiMap.new exp = described_class.parse('Array') inf = described_class.parse('Array') match = inf.conforms_to?(api_map, exp, :method_call, [:allow_subtype_skew]) @@ -26,7 +27,6 @@ end it 'accepts valid tuple conformance' do - api_map = Solargraph::ApiMap.new exp = described_class.parse('Array(Integer, Integer)') inf = described_class.parse('Array(Integer, Integer)') match = inf.conforms_to?(api_map, exp, :method_call) @@ -34,7 +34,6 @@ end it 'rejects invalid tuple conformance' do - api_map = Solargraph::ApiMap.new exp = described_class.parse('Array(Integer, Integer)') inf = described_class.parse('Array(Integer, String)') match = inf.conforms_to?(api_map, exp, :method_call) @@ -42,7 +41,6 @@ end it 'allows empty params when specified' do - api_map = Solargraph::ApiMap.new exp = described_class.parse('Array(Integer, Integer)') inf = described_class.parse('Array') match = inf.conforms_to?(api_map, exp, :method_call, [:allow_empty_params]) @@ -54,7 +52,6 @@ class Sup; end class Sub < Sup; end )) - api_map = Solargraph::ApiMap.new api_map.map source sup = described_class.parse('Sup') sub = described_class.parse('Sub') @@ -70,7 +67,6 @@ class Sub < Sup; end # # class Sup; end # # class Sub < Sup; end # # )) - # # api_map = Solargraph::ApiMap.new # # api_map.map source # # sup = described_class.parse('Sup') # # sub = described_class.parse('Sub') @@ -79,7 +75,6 @@ class Sub < Sup; end # end it 'fuzzy matches arrays with parameters' do - api_map = Solargraph::ApiMap.new exp = described_class.parse('Array') inf = described_class.parse('Array') match = inf.conforms_to?(api_map, exp, :method_call) @@ -89,7 +84,6 @@ class Sub < Sup; end it 'fuzzy matches sets with parameters' do source = Solargraph::Source.load_string("require 'set'") source_map = Solargraph::SourceMap.map(source) - api_map = Solargraph::ApiMap.new api_map.catalog Solargraph::Bench.new(source_maps: [source_map], external_requires: ['set']) exp = described_class.parse('Set') inf = described_class.parse('Set') @@ -98,7 +92,6 @@ class Sub < Sup; end end it 'fuzzy matches hashes with parameters' do - api_map = Solargraph::ApiMap.new exp = described_class.parse('Hash{ Symbol => String}') inf = described_class.parse('Hash') match = inf.conforms_to?(api_map, exp, :method_call, [:allow_empty_params]) @@ -106,7 +99,6 @@ class Sub < Sup; end end it 'matches multiple types' do - api_map = Solargraph::ApiMap.new exp = described_class.parse('String, Integer') inf = described_class.parse('String, Integer') match = inf.conforms_to?(api_map, exp, :method_call) @@ -114,7 +106,6 @@ class Sub < Sup; end end it 'matches multiple types out of order' do - api_map = Solargraph::ApiMap.new exp = described_class.parse('String, Integer') inf = described_class.parse('Integer, String') match = inf.conforms_to?(api_map, exp, :method_call) @@ -122,7 +113,6 @@ class Sub < Sup; end end it 'invalidates inferred types missing from expected' do - api_map = Solargraph::ApiMap.new exp = described_class.parse('String') inf = described_class.parse('String, Integer') match = inf.conforms_to?(api_map, exp, :method_call) @@ -130,7 +120,6 @@ class Sub < Sup; end end it 'matches nil' do - api_map = Solargraph::ApiMap.new exp = described_class.parse('nil') inf = described_class.parse('nil') match = inf.conforms_to?(api_map, exp, :method_call) @@ -138,7 +127,6 @@ class Sub < Sup; end end it 'validates classes with expected superclasses' do - api_map = Solargraph::ApiMap.new exp = described_class.parse('Class') inf = described_class.parse('Class') match = inf.conforms_to?(api_map, exp, :method_call) @@ -146,13 +134,58 @@ class Sub < Sup; end end it 'validates generic classes with expected Class' do - api_map = Solargraph::ApiMap.new inf = described_class.parse('Class') exp = described_class.parse('Class') match = inf.conforms_to?(api_map, exp, :method_call) expect(match).to be(true) end + context 'with invariant matching' do + it 'rejects String matching an Object' do + inf = described_class.parse('String') + exp = described_class.parse('Object') + match = inf.conforms_to?(api_map, exp, :method_call, variance: :invariant) + expect(match).to be(false) + end + + it 'rejects Object matching an String' do + inf = described_class.parse('Object') + exp = described_class.parse('String') + match = inf.conforms_to?(api_map, exp, :method_call, variance: :invariant) + expect(match).to be(false) + end + + it 'accepts String matching a String' do + inf = described_class.parse('String') + exp = described_class.parse('String') + match = inf.conforms_to?(api_map, exp, :method_call, variance: :invariant) + expect(match).to be(true) + end + end + + context 'with contravariant matching' do + it 'rejects String matching an Objet' do + inf = described_class.parse('String') + exp = described_class.parse('Object') + match = inf.conforms_to?(api_map, exp, :method_call, variance: :contravariant) + expect(match).to be(false) + end + + it 'accepts Object matching an String' do + inf = described_class.parse('Object') + exp = described_class.parse('String') + match = inf.conforms_to?(api_map, exp, :method_call, variance: :contravariant) + expect(match).to be(true) + end + + it 'accepts String matching a String' do + inf = described_class.parse('String') + exp = described_class.parse('String') + match = inf.conforms_to?(api_map, exp, :method_call, variance: :contravariant) + expect(match).to be(true) + end + end + context 'with an inheritence relationship' do let(:source) do Solargraph::Source.load_string(%( @@ -162,7 +195,6 @@ class Sub < Sup; end end let(:sup) { described_class.parse('Sup') } let(:sub) { described_class.parse('Sub') } - let(:api_map) { Solargraph::ApiMap.new } before do api_map.map source From 13d87eee2fcda17c0fb92e13d14bf82f3c27236c Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 4 Aug 2025 12:16:45 -0400 Subject: [PATCH 467/561] Linting fix --- lib/solargraph/complex_type/unique_type.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/solargraph/complex_type/unique_type.rb b/lib/solargraph/complex_type/unique_type.rb index 13fb7129f..15b7801b1 100644 --- a/lib/solargraph/complex_type/unique_type.rb +++ b/lib/solargraph/complex_type/unique_type.rb @@ -239,9 +239,10 @@ def conforms_to_unique_type?(api_map, expected, situation = :method_call, return true if inferred == expected - if variance == :invariant + case variance + when :invariant return false unless inferred.name == expected.name - elsif variance == :covariant + when :covariant # covariant: we can pass in a more specific type # we contain the expected mix-in, or we have a more specific type @@ -249,7 +250,7 @@ def conforms_to_unique_type?(api_map, expected, situation = :method_call, api_map.super_and_sub?(expected.name, inferred.name) || inferred.name == expected.name - elsif variance == :contravariant + when :contravariant # contravariant: we can pass in a more general type # we contain the expected mix-in, or we have a more general type From 1ef04188ed0a8dddf2529ba42dcdeef56562a121 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 4 Aug 2025 12:21:57 -0400 Subject: [PATCH 468/561] Spec for rbs-sord branch --- spec/shell_spec.rb | 60 +++++++++++++++++++++++++++++++++++++++++++++ spec/spec_helper.rb | 26 ++++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 spec/shell_spec.rb diff --git a/spec/shell_spec.rb b/spec/shell_spec.rb new file mode 100644 index 000000000..8d44fdce9 --- /dev/null +++ b/spec/shell_spec.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +require 'tmpdir' +require 'open3' + +describe Solargraph::Shell do + let(:shell) { described_class.new } + + describe 'rbs' do + let(:api_map) { instance_double(Solargraph::ApiMap) } + + before do + allow(shell).to receive(:`) + allow(Solargraph::ApiMap).to receive(:load).and_return(api_map) + allow(api_map).to receive(:source_maps).and_return(source_maps) + end + + context 'without inference' do + let(:source_maps) { [] } + + it 'invokes sord' do + capture_both do + shell.options = { filename: 'foo.rbs' } + shell.rbs + end + expect(shell) + .to have_received(:`) + .with("sord #{Dir.pwd}/sig/foo.rbs --rbs --no-regenerate") + end + end + + context 'with inference' do + let(:source_maps) { [source_map] } + let(:source_map) { instance_double(Solargraph::SourceMap) } + let(:pin) do + instance_double(Solargraph::Pin::Method, + namespace: 'My::Namespace', path: 'My::Namespace#foo', + visibility: :public, + parameters: [], + scope: :instance, + location: nil, + name: 'foo', + class: Solargraph::Pin::Method, + return_type: Solargraph::ComplexType::UNDEFINED) + end + + it 'infers unknown types on pins' do + allow(source_map).to receive(:pins).and_return([pin]) + allow(pin).to receive_messages(typify: Solargraph::ComplexType.parse('String'), + docstring: YARD::Docstring.new('')) + allow(pin).to receive(:code_object).and_return(nil) + capture_both do + shell.options = { filename: 'foo.rbs', inference: true } + shell.rbs + end + expect(pin).to have_received(:typify) + end + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index faba8172e..5112060c7 100755 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -19,3 +19,29 @@ def with_env_var(name, value) ENV[name] = old_value # Restore the old value end end + +def capture_stdout &block + original_stdout = $stdout + $stdout = StringIO.new + begin + block.call + $stdout.string + ensure + $stdout = original_stdout + end +end + +def capture_both &block + original_stdout = $stdout + original_stderr = $stderr + stringio = StringIO.new + $stdout = stringio + $stderr = stringio + begin + block.call + ensure + $stdout = original_stdout + $stderr = original_stderr + end + stringio.string +end From 24cd253c359d3f15e479a052633c1f2d1b73675d Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 4 Aug 2025 12:42:57 -0400 Subject: [PATCH 469/561] More specs --- spec/library_spec.rb | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/spec/library_spec.rb b/spec/library_spec.rb index 76128fedb..92129db3e 100644 --- a/spec/library_spec.rb +++ b/spec/library_spec.rb @@ -155,6 +155,42 @@ def bar end describe '#references_from' do + it "collects references to a new method on a constant from assignment of Class.new" do + workspace = Solargraph::Workspace.new('*') + library = Solargraph::Library.new(workspace) + src1 = Solargraph::Source.load_string(%( + Foo.new + ), 'file1.rb', 0) + library.merge src1 + src2 = Solargraph::Source.load_string(%( + Foo = Class.new + ), 'file2.rb', 0) + library.merge src2 + library.catalog + locs = library.references_from('file1.rb', 1, 12) + expect(locs.map { |l| [l.filename, l.range.start.line] }) + .to eq([["file1.rb", 1]]) + end + + it "collects references to a new method to a constant from assignment" do + workspace = Solargraph::Workspace.new('*') + library = Solargraph::Library.new(workspace) + src1 = Solargraph::Source.load_string(%( + Foo.new + ), 'file1.rb', 0) + library.merge src1 + src2 = Solargraph::Source.load_string(%( + class Foo + end + blah = Foo.new + ), 'file2.rb', 0) + library.merge src2 + library.catalog + locs = library.references_from('file2.rb', 3, 21) + expect(locs.map { |l| [l.filename, l.range.start.line] }) + .to eq([["file1.rb", 1], ["file2.rb", 3]]) + end + it "collects references to an instance method symbol" do workspace = Solargraph::Workspace.new('*') library = Solargraph::Library.new(workspace) From 6f0a151e9f3948679e64abce802806c7061d52bf Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 4 Aug 2025 13:05:00 -0400 Subject: [PATCH 470/561] Extract method --- lib/solargraph/complex_type.rb | 1 + lib/solargraph/complex_type/conformance.rb | 133 +++++++++++++++++++++ lib/solargraph/complex_type/unique_type.rb | 106 +--------------- 3 files changed, 137 insertions(+), 103 deletions(-) create mode 100644 lib/solargraph/complex_type/conformance.rb diff --git a/lib/solargraph/complex_type.rb b/lib/solargraph/complex_type.rb index 6203fb5a9..d0f46a28d 100644 --- a/lib/solargraph/complex_type.rb +++ b/lib/solargraph/complex_type.rb @@ -9,6 +9,7 @@ class ComplexType # include TypeMethods include Equality + autoload :Conformance, 'solargraph/complex_type/conformance' autoload :TypeMethods, 'solargraph/complex_type/type_methods' autoload :UniqueType, 'solargraph/complex_type/unique_type' diff --git a/lib/solargraph/complex_type/conformance.rb b/lib/solargraph/complex_type/conformance.rb new file mode 100644 index 000000000..f4f37bf4f --- /dev/null +++ b/lib/solargraph/complex_type/conformance.rb @@ -0,0 +1,133 @@ +# frozen_string_literal: true + +module Solargraph + class ComplexType + # Checks whether a type can be used in a given situation + class Conformance + # @param api_map [ApiMap] + # @param inferred [ComplexType::UniqueType] + # @param expected [ComplexType::UniqueType] + # @param situation [:method_call, :return_type] + # @param rules [Array<:allow_subtype_skew, :allow_empty_params, :allow_reverse_match, + # :allow_any_match, :allow_undefined, :allow_unresolved_generic, :allow_unmatched_interface>] + # @param variance [:invariant, :covariant, :contravariant] + def initialize api_map, inferred, expected, + situation = :method_call, rules = [], + variance: inferred.erased_variance(situation) + @api_map = api_map + @inferred = inferred + @expected = expected + @situation = situation + @rules = rules + @variance = variance + unless expected.is_a?(UniqueType) + raise "Expected type must be a UniqueType, got #{expected.class} in #{expected.inspect}" + end + return if inferred.is_a?(UniqueType) + raise "Inferred type must be a UniqueType, got #{inferred.class} in #{inferred.inspect}" + end + + def conforms_to_unique_type? + unless expected.is_a?(UniqueType) + raise "Expected type must be a UniqueType, got #{expected.class} in #{expected.inspect}" + end + if inferred.literal? && !expected.literal? + conformance = + self.class.new(api_map, inferred.simplify_literals, expected, situation, rules, + variance: variance) + return conformance.conforms_to_unique_type? + end + return true if expected.any?(&:interface?) && rules.include?(:allow_unmatched_interface) + return true if inferred.interface? && rules.include?(:allow_unmatched_interface) + + if rules.include? :allow_reverse_match + reversed_match = expected.conforms_to?(api_map, inferred, situation, + rules - [:allow_reverse_match], + variance: variance) + return true if reversed_match + end + expected = self.expected.downcast_to_literal_if_possible + inferred = self.inferred.downcast_to_literal_if_possible + + if rules.include? :allow_subtype_skew + # parameters are not considered in this case + expected = expected.erase_parameters + end + + inferred = inferred.erase_parameters if !expected.parameters? && inferred.parameters? + + if expected.parameters? && !inferred.parameters? && rules.include?(:allow_empty_params) + expected = expected.erase_parameters + end + + return true if inferred == expected + + case variance + when :invariant + return false unless inferred.name == expected.name + when :covariant + # covariant: we can pass in a more specific type + + # we contain the expected mix-in, or we have a more specific type + return false unless api_map.type_include?(inferred.name, expected.name) || + api_map.super_and_sub?(expected.name, inferred.name) || + inferred.name == expected.name + + when :contravariant + # contravariant: we can pass in a more general type + + # we contain the expected mix-in, or we have a more general type + return false unless api_map.type_include?(inferred.name, expected.name) || + api_map.super_and_sub?(inferred.name, expected.name) || + inferred.name == expected.name + else + # :nocov: + raise "Unknown variance: #{variance.inspect}" + # :nocov: + end + + return true if inferred.all_params.empty? && rules.include?(:allow_empty_params) + + # at this point we know the erased type is fine - time to look at parameters + + # there's an implicit 'any' on the expectation parameters + # if there are none specified + return true if expected.all_params.empty? + + unless expected.key_types.empty? + return false if inferred.key_types.empty? + + unless ComplexType.new(inferred.key_types).conforms_to?(api_map, + ComplexType.new(expected.key_types), + situation, + rules, + variance: inferred.parameter_variance(situation)) + return false + end + end + + return true if expected.subtypes.empty? + + return true if expected.subtypes.any?(&:undefined?) && rules.include?(:allow_undefined) + + return true if inferred.subtypes.any?(&:undefined?) && rules.include?(:allow_undefined) + + return true if inferred.subtypes.all?(&:generic?) && rules.include?(:allow_unresolved_generic) + + return true if expected.subtypes.all?(&:generic?) && rules.include?(:allow_unresolved_generic) + + return false if inferred.subtypes.empty? + + ComplexType.new(inferred.subtypes).conforms_to?(api_map, + ComplexType.new(expected.subtypes), + situation, + rules, + variance: inferred.parameter_variance(situation)) + end + + private + + attr_reader :api_map, :inferred, :expected, :situation, :rules, :variance + end + end +end diff --git a/lib/solargraph/complex_type/unique_type.rb b/lib/solargraph/complex_type/unique_type.rb index 15b7801b1..9327d3fc8 100644 --- a/lib/solargraph/complex_type/unique_type.rb +++ b/lib/solargraph/complex_type/unique_type.rb @@ -199,107 +199,6 @@ def erased_version_of?(other) name == other.name && (all_params.empty? || all_params.all?(&:undefined?)) end - # @param api_map [ApiMap] - # @param expected [ComplexType::UniqueType] - # @param situation [:method_call, :return_type] - # @param rules [Array<:allow_subtype_skew, :allow_empty_params, :allow_reverse_match, :allow_any_match, :allow_undefined, :allow_unresolved_generic, :allow_unmatched_interface>] - # @param variance [:invariant, :covariant, :contravariant] - def conforms_to_unique_type?(api_map, expected, situation = :method_call, - rules = [], - variance: erased_variance(situation)) - raise "Expected type must be a UniqueType, got #{expected.class} in #{expected.inspect}" unless expected.is_a?(UniqueType) - if literal? && !expected.literal? - return simplify_literals.conforms_to_unique_type?(api_map, expected, situation, - rules, variance: variance) - end - return true if expected.any?(&:interface?) && rules.include?(:allow_unmatched_interface) - return true if interface? && rules.include?(:allow_unmatched_interface) - - if rules.include? :allow_reverse_match - reversed_match = expected.conforms_to?(api_map, self, situation, - rules - [:allow_reverse_match], - variance: variance) - return true if reversed_match - end - expected = expected.downcast_to_literal_if_possible - inferred = downcast_to_literal_if_possible - - if rules.include? :allow_subtype_skew - # parameters are not considered in this case - expected = expected.erase_parameters - end - - if !expected.parameters? && inferred.parameters? - inferred = inferred.erase_parameters - end - - if expected.parameters? && !inferred.parameters? && rules.include?(:allow_empty_params) - expected = expected.erase_parameters - end - - return true if inferred == expected - - case variance - when :invariant - return false unless inferred.name == expected.name - when :covariant - # covariant: we can pass in a more specific type - - # we contain the expected mix-in, or we have a more specific type - return false unless api_map.type_include?(inferred.name, expected.name) || - api_map.super_and_sub?(expected.name, inferred.name) || - inferred.name == expected.name - - when :contravariant - # contravariant: we can pass in a more general type - - # we contain the expected mix-in, or we have a more general type - return false unless api_map.type_include?(inferred.name, expected.name) || - api_map.super_and_sub?(inferred.name, expected.name) || - inferred.name == expected.name - else - # :nocov: - raise "Unknown erased variance: #{erased_variance.inspect}" - # :nocov: - end - - return true if inferred.all_params.empty? && rules.include?(:allow_empty_params) - - # at this point we know the erased type is fine - time to look at parameters - - # there's an implicit 'any' on the expectation parameters - # if there are none specified - return true if expected.all_params.empty? - - unless expected.key_types.empty? - return false if inferred.key_types.empty? - - return false unless ComplexType.new(inferred.key_types).conforms_to?(api_map, - ComplexType.new(expected.key_types), - situation, - rules, - variance: parameter_variance(situation)) - end - - return true if expected.subtypes.empty? - - return true if expected.subtypes.any?(&:undefined?) && rules.include?(:allow_undefined) - - return true if inferred.subtypes.any?(&:undefined?) && rules.include?(:allow_undefined) - - return true if inferred.subtypes.all?(&:generic?) && rules.include?(:allow_unresolved_generic) - - return true if expected.subtypes.all?(&:generic?) && rules.include?(:allow_unresolved_generic) - - return false if inferred.subtypes.empty? - - ComplexType.new(inferred.subtypes).conforms_to?(api_map, - ComplexType.new(expected.subtypes), - situation, - rules, - variance: parameter_variance(situation)) - end - # @param api_map [ApiMap] # @param expected [ComplexType::UniqueType, ComplexType] # @param situation [:method_call, :assignment, :return] @@ -320,8 +219,9 @@ def conforms_to?(api_map, expected, situation, rules, raise "Expected type must be a UniqueType, got #{expected_unique_type.class} in #{expected.inspect}" end # :nocov: - conforms_to_unique_type?(api_map, expected_unique_type, situation, - rules, variance: variance) + conformance = Conformance.new(api_map, self, expected_unique_type, situation, + rules, variance: variance) + conformance.conforms_to_unique_type? end end From f9a493206a71882eb4603fcdd1055c957cbd91eb Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 4 Aug 2025 13:30:57 -0400 Subject: [PATCH 471/561] Refactors --- lib/solargraph/complex_type/conformance.rb | 31 ++++++++++++++-------- lib/solargraph/complex_type/unique_type.rb | 4 +++ 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/lib/solargraph/complex_type/conformance.rb b/lib/solargraph/complex_type/conformance.rb index f4f37bf4f..a055c297c 100644 --- a/lib/solargraph/complex_type/conformance.rb +++ b/lib/solargraph/complex_type/conformance.rb @@ -31,11 +31,8 @@ def conforms_to_unique_type? unless expected.is_a?(UniqueType) raise "Expected type must be a UniqueType, got #{expected.class} in #{expected.inspect}" end - if inferred.literal? && !expected.literal? - conformance = - self.class.new(api_map, inferred.simplify_literals, expected, situation, rules, - variance: variance) - return conformance.conforms_to_unique_type? + if inferred.simplifyable_literal? && !expected.literal? + return with_new_types(inferred.simplify_literals, expected).conforms_to_unique_type? end return true if expected.any?(&:interface?) && rules.include?(:allow_unmatched_interface) return true if inferred.interface? && rules.include?(:allow_unmatched_interface) @@ -46,18 +43,23 @@ def conforms_to_unique_type? variance: variance) return true if reversed_match end - expected = self.expected.downcast_to_literal_if_possible - inferred = self.inferred.downcast_to_literal_if_possible + if expected != expected.downcast_to_literal_if_possible || + inferred != inferred.downcast_to_literal_if_possible + return with_new_types(inferred.downcast_to_literal_if_possible, + expected.downcast_to_literal_if_possible).conforms_to_unique_type? + end - if rules.include? :allow_subtype_skew + if rules.include?(:allow_subtype_skew) && !expected.parameters.empty? # parameters are not considered in this case - expected = expected.erase_parameters + return with_new_types(inferred, expected.erase_parameters).conforms_to_unique_type? end - inferred = inferred.erase_parameters if !expected.parameters? && inferred.parameters? + if !expected.parameters? && inferred.parameters? + return with_new_types(inferred.erase_parameters, expected).conforms_to_unique_type? + end if expected.parameters? && !inferred.parameters? && rules.include?(:allow_empty_params) - expected = expected.erase_parameters + return with_new_types(inferred, expected.erase_parameters).conforms_to_unique_type? end return true if inferred == expected @@ -127,6 +129,13 @@ def conforms_to_unique_type? private + # @return [self] + # @param inferred [ComplexType::UniqueType] + # @param expected [ComplexType::UniqueType] + def with_new_types inferred, expected + self.class.new(api_map, inferred, expected, situation, rules, variance: variance) + end + attr_reader :api_map, :inferred, :expected, :situation, :rules, :variance end end diff --git a/lib/solargraph/complex_type/unique_type.rb b/lib/solargraph/complex_type/unique_type.rb index 9327d3fc8..b12ca6332 100644 --- a/lib/solargraph/complex_type/unique_type.rb +++ b/lib/solargraph/complex_type/unique_type.rb @@ -113,6 +113,10 @@ def simplify_literals end end + def simplifyable_literal? + literal? && name != 'nil' + end + def literal? non_literal_name != name end From bc71924f40e706990a2ad90973e13eba78c35342 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 4 Aug 2025 13:32:26 -0400 Subject: [PATCH 472/561] Coverage fixes --- lib/solargraph/complex_type/conformance.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/solargraph/complex_type/conformance.rb b/lib/solargraph/complex_type/conformance.rb index a055c297c..8d4b9ec3b 100644 --- a/lib/solargraph/complex_type/conformance.rb +++ b/lib/solargraph/complex_type/conformance.rb @@ -20,11 +20,14 @@ def initialize api_map, inferred, expected, @situation = situation @rules = rules @variance = variance + # :nocov: unless expected.is_a?(UniqueType) raise "Expected type must be a UniqueType, got #{expected.class} in #{expected.inspect}" end return if inferred.is_a?(UniqueType) + # :nocov: raise "Inferred type must be a UniqueType, got #{inferred.class} in #{inferred.inspect}" + # :nocov: end def conforms_to_unique_type? From 18bbe8a81045138d619d5abda8c4f7ec1477cb9c Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 4 Aug 2025 13:33:27 -0400 Subject: [PATCH 473/561] Coverage fixes --- lib/solargraph/complex_type/conformance.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/solargraph/complex_type/conformance.rb b/lib/solargraph/complex_type/conformance.rb index 8d4b9ec3b..888d53ec2 100644 --- a/lib/solargraph/complex_type/conformance.rb +++ b/lib/solargraph/complex_type/conformance.rb @@ -31,9 +31,11 @@ def initialize api_map, inferred, expected, end def conforms_to_unique_type? + # :nocov: unless expected.is_a?(UniqueType) raise "Expected type must be a UniqueType, got #{expected.class} in #{expected.inspect}" end + # :nocov: if inferred.simplifyable_literal? && !expected.literal? return with_new_types(inferred.simplify_literals, expected).conforms_to_unique_type? end From 669b2c3ef96be2e3e962ae1dcbde31ba321a5824 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 4 Aug 2025 13:35:57 -0400 Subject: [PATCH 474/561] Coverage fix --- lib/solargraph/type_checker.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/solargraph/type_checker.rb b/lib/solargraph/type_checker.rb index 1b78c62a3..e99f99195 100644 --- a/lib/solargraph/type_checker.rb +++ b/lib/solargraph/type_checker.rb @@ -726,11 +726,11 @@ def without_ignored problems problems.reject do |problem| node = source.node_at(problem.location.range.start.line, problem.location.range.start.column) ignored = node && source.comments_for(node)&.include?('@sg-ignore') - # :nocov: unless !ignored || all_sg_ignore_lines.include?(problem.location.range.start.line) + # :nocov: Solargraph.assert_or_log(:sg_ignore) { "@sg-ignore accounting issue - node is #{node}" } + # :nocov: end - # :nocov: sg_ignore_lines_processed.add problem.location.range.start.line if ignored ignored end From dc50122896e1923242487964b4e3b07ec08903a5 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 4 Aug 2025 15:04:28 -0400 Subject: [PATCH 475/561] Refactor --- lib/solargraph/complex_type/conformance.rb | 122 +++++++++++++-------- 1 file changed, 78 insertions(+), 44 deletions(-) diff --git a/lib/solargraph/complex_type/conformance.rb b/lib/solargraph/complex_type/conformance.rb index 888d53ec2..6a3d128a7 100644 --- a/lib/solargraph/complex_type/conformance.rb +++ b/lib/solargraph/complex_type/conformance.rb @@ -9,7 +9,8 @@ class Conformance # @param expected [ComplexType::UniqueType] # @param situation [:method_call, :return_type] # @param rules [Array<:allow_subtype_skew, :allow_empty_params, :allow_reverse_match, - # :allow_any_match, :allow_undefined, :allow_unresolved_generic, :allow_unmatched_interface>] + # :allow_any_match, :allow_undefined, :allow_unresolved_generic, + # :allow_unmatched_interface>] # @param variance [:invariant, :covariant, :contravariant] def initialize api_map, inferred, expected, situation = :method_call, rules = [], @@ -33,56 +34,92 @@ def initialize api_map, inferred, expected, def conforms_to_unique_type? # :nocov: unless expected.is_a?(UniqueType) + # :nocov: raise "Expected type must be a UniqueType, got #{expected.class} in #{expected.inspect}" + # :nocov: end - # :nocov: - if inferred.simplifyable_literal? && !expected.literal? + + if use_simplified_inferred_type? return with_new_types(inferred.simplify_literals, expected).conforms_to_unique_type? end - return true if expected.any?(&:interface?) && rules.include?(:allow_unmatched_interface) - return true if inferred.interface? && rules.include?(:allow_unmatched_interface) - - if rules.include? :allow_reverse_match - reversed_match = expected.conforms_to?(api_map, inferred, situation, - rules - [:allow_reverse_match], - variance: variance) - return true if reversed_match - end - if expected != expected.downcast_to_literal_if_possible || - inferred != inferred.downcast_to_literal_if_possible - return with_new_types(inferred.downcast_to_literal_if_possible, - expected.downcast_to_literal_if_possible).conforms_to_unique_type? + return true if ignore_interface? + return true if conforms_via_reverse_match? + + downcast_inferred = inferred.downcast_to_literal_if_possible + downcast_expected = expected.downcast_to_literal_if_possible + if (downcast_inferred.name != inferred.name) || (downcast_expected.name != expected.name) + return with_new_types(downcast_inferred, downcast_expected).conforms_to_unique_type? end - if rules.include?(:allow_subtype_skew) && !expected.parameters.empty? + if rules.include?(:allow_subtype_skew) && !expected.all_params.empty? # parameters are not considered in this case return with_new_types(inferred, expected.erase_parameters).conforms_to_unique_type? end - if !expected.parameters? && inferred.parameters? - return with_new_types(inferred.erase_parameters, expected).conforms_to_unique_type? - end + return with_new_types(inferred.erase_parameters, expected).conforms_to_unique_type? if only_inferred_parameters? - if expected.parameters? && !inferred.parameters? && rules.include?(:allow_empty_params) - return with_new_types(inferred, expected.erase_parameters).conforms_to_unique_type? - end + return conforms_via_stripped_expected_parameters? if can_strip_expected_parameters? return true if inferred == expected + return false unless erased_type_conforms? + + return true if inferred.all_params.empty? && rules.include?(:allow_empty_params) + + # at this point we know the erased type is fine - time to look at parameters + + # there's an implicit 'any' on the expectation parameters + # if there are none specified + return true if expected.all_params.empty? + + return false unless key_types_conform? + + subtypes_conform? + end + + private + + def use_simplified_inferred_type? + inferred.simplifyable_literal? && !expected.literal? + end + + def only_inferred_parameters? + !expected.parameters? && inferred.parameters? + end + + def conforms_via_stripped_expected_parameters? + with_new_types(inferred, expected.erase_parameters).conforms_to_unique_type? + end + + def ignore_interface? + (expected.any?(&:interface?) && rules.include?(:allow_unmatched_interface)) || + (inferred.interface? && rules.include?(:allow_unmatched_interface)) + end + + def can_strip_expected_parameters? + expected.parameters? && !inferred.parameters? && rules.include?(:allow_empty_params) + end + + def conforms_via_reverse_match? + return false unless rules.include? :allow_reverse_match + + expected.conforms_to?(api_map, inferred, situation, + rules - [:allow_reverse_match], + variance: variance) + end + + def erased_type_conforms? case variance when :invariant return false unless inferred.name == expected.name when :covariant # covariant: we can pass in a more specific type - # we contain the expected mix-in, or we have a more specific type return false unless api_map.type_include?(inferred.name, expected.name) || api_map.super_and_sub?(expected.name, inferred.name) || inferred.name == expected.name - when :contravariant # contravariant: we can pass in a more general type - # we contain the expected mix-in, or we have a more general type return false unless api_map.type_include?(inferred.name, expected.name) || api_map.super_and_sub?(inferred.name, expected.name) || @@ -92,27 +129,26 @@ def conforms_to_unique_type? raise "Unknown variance: #{variance.inspect}" # :nocov: end + true + end - return true if inferred.all_params.empty? && rules.include?(:allow_empty_params) - - # at this point we know the erased type is fine - time to look at parameters - - # there's an implicit 'any' on the expectation parameters - # if there are none specified - return true if expected.all_params.empty? + def key_types_conform? + return true if expected.key_types.empty? - unless expected.key_types.empty? - return false if inferred.key_types.empty? + return false if inferred.key_types.empty? - unless ComplexType.new(inferred.key_types).conforms_to?(api_map, - ComplexType.new(expected.key_types), - situation, - rules, - variance: inferred.parameter_variance(situation)) - return false - end + unless ComplexType.new(inferred.key_types).conforms_to?(api_map, + ComplexType.new(expected.key_types), + situation, + rules, + variance: inferred.parameter_variance(situation)) + return false end + true + end + + def subtypes_conform? return true if expected.subtypes.empty? return true if expected.subtypes.any?(&:undefined?) && rules.include?(:allow_undefined) @@ -132,8 +168,6 @@ def conforms_to_unique_type? variance: inferred.parameter_variance(situation)) end - private - # @return [self] # @param inferred [ComplexType::UniqueType] # @param expected [ComplexType::UniqueType] From ba3059006f2a7840ee4ae3561126d17d6d514de2 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 4 Aug 2025 15:32:55 -0400 Subject: [PATCH 476/561] Add nocov markers around unreachable area --- lib/solargraph/type_checker.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/solargraph/type_checker.rb b/lib/solargraph/type_checker.rb index 0826c1e66..b23db08aa 100644 --- a/lib/solargraph/type_checker.rb +++ b/lib/solargraph/type_checker.rb @@ -367,7 +367,15 @@ def signature_argument_problems_for location, locals, closure_pin, params, argum argchain = final_arg return errors else + # :nocov: + # + # if this fires, add a test case - otherwise I believe + # it's handled by arity_problems_for before reaching + # here + Solargraph.assert_or_log(:type_checker_not_enough_arguments, + 'Encountered unexpected path') errors.push Problem.new(location, "Not enough arguments to #{pin.path}") + # :nocov: end else final_arg = arguments.last From 7fcd43a9d705cf0e30b4c590575ec29a6f2c4ff5 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 4 Aug 2025 15:36:15 -0400 Subject: [PATCH 477/561] Fix nocov markers --- lib/solargraph/complex_type/conformance.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/solargraph/complex_type/conformance.rb b/lib/solargraph/complex_type/conformance.rb index 6a3d128a7..424413038 100644 --- a/lib/solargraph/complex_type/conformance.rb +++ b/lib/solargraph/complex_type/conformance.rb @@ -25,6 +25,7 @@ def initialize api_map, inferred, expected, unless expected.is_a?(UniqueType) raise "Expected type must be a UniqueType, got #{expected.class} in #{expected.inspect}" end + # :nocov: return if inferred.is_a?(UniqueType) # :nocov: raise "Inferred type must be a UniqueType, got #{inferred.class} in #{inferred.inspect}" @@ -32,7 +33,6 @@ def initialize api_map, inferred, expected, end def conforms_to_unique_type? - # :nocov: unless expected.is_a?(UniqueType) # :nocov: raise "Expected type must be a UniqueType, got #{expected.class} in #{expected.inspect}" From 600f45094c2eed0b99c1a32d609e2b81f3ecfd39 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 4 Aug 2025 15:47:27 -0400 Subject: [PATCH 478/561] Allow rubocop-yard to understand literal symbols in type annotations --- Gemfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Gemfile b/Gemfile index 3a5ae2b84..2da498052 100755 --- a/Gemfile +++ b/Gemfile @@ -2,6 +2,9 @@ source 'https://rubygems.org' gemspec name: 'solargraph' +# allow rubocop-yard to understand literal symbols in type annotations +gem 'yard', github: 'apiology/yard', branch: 'literal_symbols', require: false + # Local gemfile for development tools, etc. local_gemfile = File.expand_path(".Gemfile", __dir__) instance_eval File.read local_gemfile if File.exist? local_gemfile From 01a078fe44a6128e983caeec4951c05cc0acff8e Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 4 Aug 2025 16:04:46 -0400 Subject: [PATCH 479/561] Fix a coverage issue --- lib/solargraph/type_checker.rb | 8 -------- spec/type_checker/levels/strong_spec.rb | 13 +++++++++++++ 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/lib/solargraph/type_checker.rb b/lib/solargraph/type_checker.rb index b23db08aa..0826c1e66 100644 --- a/lib/solargraph/type_checker.rb +++ b/lib/solargraph/type_checker.rb @@ -367,15 +367,7 @@ def signature_argument_problems_for location, locals, closure_pin, params, argum argchain = final_arg return errors else - # :nocov: - # - # if this fires, add a test case - otherwise I believe - # it's handled by arity_problems_for before reaching - # here - Solargraph.assert_or_log(:type_checker_not_enough_arguments, - 'Encountered unexpected path') errors.push Problem.new(location, "Not enough arguments to #{pin.path}") - # :nocov: end else final_arg = arguments.last diff --git a/spec/type_checker/levels/strong_spec.rb b/spec/type_checker/levels/strong_spec.rb index 12db1e442..24594e32b 100644 --- a/spec/type_checker/levels/strong_spec.rb +++ b/spec/type_checker/levels/strong_spec.rb @@ -4,6 +4,19 @@ def type_checker(code) Solargraph::TypeChecker.load_string(code, 'test.rb', :strong) end + it 'does not complain on array dereference' do + checker = type_checker(%( + # @param idx [Integer, nil] an index + # @param arr [Array] an array of integers + # + # @return [void] + def foo(idx, arr) + arr[idx] + end + )) + expect(checker.problems.map(&:message)).to be_empty + end + it 'reports missing return tags' do checker = type_checker(%( class Foo From 861d4cb6bafe6bf14290bb8c9e26b71cb4add6b4 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 4 Aug 2025 16:09:52 -0400 Subject: [PATCH 480/561] Ratchet rubocop todo --- .rubocop_todo.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 11cbef5d3..e3ebf9415 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -767,7 +767,6 @@ Metrics/ParameterLists: # Configuration parameters: AllowedMethods, AllowedPatterns, Max. Metrics/PerceivedComplexity: Exclude: - - 'lib/solargraph/api_map/source_to_yard.rb' - 'lib/solargraph/complex_type.rb' - 'lib/solargraph/parser/parser_gem/node_chainer.rb' - 'lib/solargraph/source/chain/call.rb' @@ -2473,8 +2472,6 @@ YARD/MismatchName: YARD/TagTypeSyntax: Exclude: - - 'lib/solargraph/complex_type.rb' - - 'lib/solargraph/complex_type/unique_type.rb' - 'lib/solargraph/language_server/host.rb' - 'lib/solargraph/parser/comment_ripper.rb' - 'lib/solargraph/type_checker.rb' From 12bdeba009bd7a85e39e4dd1c5e61ade2f6b92dc Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 4 Aug 2025 16:20:40 -0400 Subject: [PATCH 481/561] Add incremental workflow for code coverage reporting in Rakefile --- Rakefile | 114 +++++++++++++++++++++++++++++++++++++++----- spec/spec_helper.rb | 14 ++++-- 2 files changed, 111 insertions(+), 17 deletions(-) diff --git a/Rakefile b/Rakefile index dfdce8fc7..1df2af47e 100755 --- a/Rakefile +++ b/Rakefile @@ -1,12 +1,7 @@ require 'rake' -require 'rspec/core/rake_task' require 'bundler/gem_tasks' - -begin - require 'rspec/core/rake_task' - RSpec::Core::RakeTask.new(:spec) -rescue LoadError -end +require 'fileutils' +require 'open3' desc "Open a Pry session preloaded with this library" task :console do @@ -14,18 +9,111 @@ task :console do end desc "Run the type checker" -task :typecheck do +task typecheck: [:typecheck_typed] + +desc "Run the type checker at typed level - return code issues provable without annotations being correct" +task :typecheck_typed do sh "bundle exec solargraph typecheck --level typed" end +desc "Run the type checker at strict level - report issues using type annotations" +task :typecheck_strict do + sh "bundle exec solargraph typecheck --level strict" +end + +desc "Run the type checker at strong level - enforce that type annotations exist" +task :typecheck_strong do + sh "bundle exec solargraph typecheck --level strong" +end + +desc "Run the type checker at alpha level - run high-false-alarm checks" +task :typecheck_alpha do + sh "bundle exec solargraph typecheck --level alpha" +end + +desc "Run RSpec tests" +task :spec do + warn 'starting spec' + sh 'TEST_COVERAGE_COMMAND_NAME=full-new bundle exec rspec' # --profile' + warn 'ending spec' + # move coverage/full-new to coverage/full on success so that we + # always have the last successful run's 'coverage info + FileUtils.rm_rf('coverage/full') + FileUtils.mv('coverage/full-new', 'coverage/full') +end + +# @return [void] +def undercover + simplecov_collate + cmd = 'bundle exec undercover ' \ + '--simplecov coverage/combined/coverage.json ' \ + '--exclude-files "Rakefile,spec/*,spec/**/*,lib/solargraph/version.rb" ' \ + '--compare origin/master' + output, _status = Bundler.with_unbundled_env do + Open3.capture2e(cmd) + end +end + desc "Check PR coverage" task :undercover do - sh 'bundle exec undercover --exclude-files "Rakefile,spec/*,spec/**/*" --compare origin/master' + undercover end -desc "Run all tests" -task :test do - Rake::Task["typecheck"].invoke - Rake::Task["spec"].invoke +desc "Branch-focused fast-feedback quality/spec/coverage checks" +task test: %i[overcommit spec_failed undercover_no_fail] do + # run these last tasks manually so that rake doesn't optimize + # undercover away + Rake::Task['spec'].invoke + undercover + Rake::Task['overcommit'].invoke + Rake::Task['typecheck'].invoke + Rake::Task['typecheck_strict'].invoke + Rake::Task['typecheck_strong'].invoke + Rake::Task['typecheck_alpha'].invoke +end + +desc "Re-run failed specs" +task :spec_failed do + sh 'TEST_COVERAGE_COMMAND_NAME=next-failure bundle exec rspec --next-failure' +end + +desc "Run undercover and show output without failing the task if it fails" +task :undercover_no_fail do Rake::Task["undercover"].invoke +rescue StandardError + puts "Undercover failed, but continuing with other tasks." +end + +# @return [void] +def simplecov_collate + require 'simplecov' + require 'simplecov-lcov' + require 'undercover/simplecov_formatter' + + SimpleCov.collate(Dir["coverage/{next-failure,full,ad-hoc}/.resultset.json"]) do + cname = 'combined' + command_name cname + new_dir = File.join('coverage', cname) + coverage_dir new_dir + + formatter \ + SimpleCov::Formatter::MultiFormatter + .new([ + SimpleCov::Formatter::HTMLFormatter, + SimpleCov::Formatter::Undercover, + SimpleCov::Formatter::LcovFormatter + ]) + SimpleCov::Formatter::LcovFormatter.config.report_with_single_file = true + end +end + +desc 'Add incremental coverage for rapid iteration with undercover' +task :simplecov_collate do + simplecov_collate +end + +desc "Show quality checks on this development branch so far, including any staged files" +task :overcommit do + # OVERCOMMIT_DEBUG=1 will show more detail + sh 'SOLARGRAPH_ASSERTS=on bundle exec overcommit --run --diff origin/master' end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index d2fa1f334..071c43157 100755 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -4,14 +4,20 @@ unless ENV['SIMPLECOV_DISABLED'] # set up lcov reporting for undercover require 'simplecov' - require 'simplecov-lcov' - SimpleCov::Formatter::LcovFormatter.config.report_with_single_file = true - SimpleCov.formatter = SimpleCov::Formatter::LcovFormatter + require 'undercover/simplecov_formatter' + SimpleCov.start do + cname = ENV.fetch('TEST_COVERAGE_COMMAND_NAME', 'ad-hoc') + command_name cname + new_dir = File.join('coverage', cname) + coverage_dir new_dir + add_filter(%r{^/spec/}) add_filter('/Rakefile') + # included via gemspec before we start, so can never be covered + add_filter('lib/solargraph/version.rb') # off by default - feel free to set if you'd like undercover to - # hold you to a thorough set of specs + # hold you to making a more thorough set of specs enable_coverage(:branch) if ENV['SOLARGRAPH_BRANCH_COVERAGE'] end end From d4a0e36db69da59db14e18ec0b59aaebb403a245 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 4 Aug 2025 16:26:18 -0400 Subject: [PATCH 482/561] Fix some Rakefile output issues --- Rakefile | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Rakefile b/Rakefile index 1df2af47e..60f560aa2 100755 --- a/Rakefile +++ b/Rakefile @@ -52,6 +52,7 @@ def undercover output, _status = Bundler.with_unbundled_env do Open3.capture2e(cmd) end + puts output end desc "Check PR coverage" @@ -105,6 +106,11 @@ def simplecov_collate ]) SimpleCov::Formatter::LcovFormatter.config.report_with_single_file = true end + puts "Simplecov collated results into coverage/combined/.resultset.json" +rescue StandardError => e + puts "Simplecov collate failed: #{e.message}" +ensure + $stdout.flush end desc 'Add incremental coverage for rapid iteration with undercover' From 904cc1d54fb4461f31b5ed5d3c7d0b3f616b71b4 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 4 Aug 2025 16:38:27 -0400 Subject: [PATCH 483/561] Add incremental workflow for code coverage reporting in Rakefile --- .github/workflows/rspec.yml | 4 ++-- Rakefile | 19 +++++++++++++++---- solargraph.gemspec | 2 +- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/.github/workflows/rspec.yml b/.github/workflows/rspec.yml index 8d1043503..ae368f1df 100644 --- a/.github/workflows/rspec.yml +++ b/.github/workflows/rspec.yml @@ -35,7 +35,7 @@ jobs: bundle install bundle update rbs # use latest available for this Ruby version - name: Run tests - run: bundle exec rspec + run: bundle exec rake spec undercover: runs-on: ubuntu-latest steps: @@ -52,6 +52,6 @@ jobs: - name: Install gems run: bundle install - name: Run tests - run: bundle exec rspec + run: bundle exec rake spec - name: Check PR coverage run: bundle exec rake undercover diff --git a/Rakefile b/Rakefile index 60f560aa2..c0550613d 100755 --- a/Rakefile +++ b/Rakefile @@ -42,22 +42,33 @@ task :spec do FileUtils.mv('coverage/full-new', 'coverage/full') end -# @return [void] +# @return [Boolean, nil] returns `true` if the command exits with +# status zero. `false` if the exit status is a non-zero integer. +# `nil` if the command could not execute. def undercover simplecov_collate cmd = 'bundle exec undercover ' \ '--simplecov coverage/combined/coverage.json ' \ '--exclude-files "Rakefile,spec/*,spec/**/*,lib/solargraph/version.rb" ' \ '--compare origin/master' - output, _status = Bundler.with_unbundled_env do - Open3.capture2e(cmd) + output, status = Bundler.with_unbundled_env do + Process.spawn(cmd) end puts output + $stdout.flush + status +rescue StandardError => e + warn "hit error: #{e.message}" + warn "Backtrace:\n#{e.backtrace.join("\n")}" + warn "output: #{output}" + puts "Flushing" + $stdout.flush + raise end desc "Check PR coverage" task :undercover do - undercover + raise "Undercover failed" unless undercover end desc "Branch-focused fast-feedback quality/spec/coverage checks" diff --git a/solargraph.gemspec b/solargraph.gemspec index 026109bfa..48503b7b9 100755 --- a/solargraph.gemspec +++ b/solargraph.gemspec @@ -52,7 +52,7 @@ Gem::Specification.new do |s| s.add_development_dependency 'rubocop-yard', '~> 1.0' s.add_development_dependency 'simplecov', '~> 0.21' s.add_development_dependency 'simplecov-lcov', '~> 0.8' - s.add_development_dependency 'undercover', '~> 0.6' + s.add_development_dependency 'undercover', '~> 0.7' s.add_development_dependency 'overcommit', '~> 0.68.0' s.add_development_dependency 'webmock', '~> 3.6' # work around missing yard dependency needed as of Ruby 3.5 From bb77106490afd84e82e5660ec8067f12473f65a8 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 4 Aug 2025 16:52:23 -0400 Subject: [PATCH 484/561] Add another spec, fix behavior --- lib/solargraph/complex_type/unique_type.rb | 4 ++-- spec/complex_type/conforms_to_spec.rb | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/solargraph/complex_type/unique_type.rb b/lib/solargraph/complex_type/unique_type.rb index b12ca6332..c009a08d9 100644 --- a/lib/solargraph/complex_type/unique_type.rb +++ b/lib/solargraph/complex_type/unique_type.rb @@ -208,8 +208,8 @@ def erased_version_of?(other) # @param situation [:method_call, :assignment, :return] # @param rules [Array<:allow_subtype_skew, :allow_empty_params, :allow_reverse_match, :allow_any_match, :allow_undefined, :allow_unresolved_generic>] # @param variance [:invariant, :covariant, :contravariant] - def conforms_to?(api_map, expected, situation, rules, - variance:) + def conforms_to?(api_map, expected, situation, rules = [], + variance: erased_variance(situation)) return true if undefined? && rules.include?(:allow_undefined) # @todo teach this to validate duck types as inferred type diff --git a/spec/complex_type/conforms_to_spec.rb b/spec/complex_type/conforms_to_spec.rb index bacf19b5e..c1bba05dc 100644 --- a/spec/complex_type/conforms_to_spec.rb +++ b/spec/complex_type/conforms_to_spec.rb @@ -56,6 +56,12 @@ class Sub < Sup; end sup = described_class.parse('Sup') sub = described_class.parse('Sub') match = sub.conforms_to?(api_map, sup, :method_call) + end + + it 'handles singleton types compared against their literals' do + exp = Solargraph::ComplexType::UniqueType.new('nil', rooted: true) + inf = Solargraph::ComplexType::UniqueType.new('NilClass', rooted: true) + match = inf.conforms_to?(api_map, exp, :method_call) expect(match).to be(true) end From ed69326afb742f37e78ce475ed38b40f93ece405 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 4 Aug 2025 16:59:12 -0400 Subject: [PATCH 485/561] Fix merge issue --- spec/complex_type/conforms_to_spec.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/complex_type/conforms_to_spec.rb b/spec/complex_type/conforms_to_spec.rb index c1bba05dc..4ac00e70a 100644 --- a/spec/complex_type/conforms_to_spec.rb +++ b/spec/complex_type/conforms_to_spec.rb @@ -56,6 +56,7 @@ class Sub < Sup; end sup = described_class.parse('Sup') sub = described_class.parse('Sub') match = sub.conforms_to?(api_map, sup, :method_call) + expect(match).to be(true) end it 'handles singleton types compared against their literals' do From bf430f091e591b9c8a851f40f04665faf08778cf Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 4 Aug 2025 17:07:39 -0400 Subject: [PATCH 486/561] Catch potential bad-path issue --- lib/solargraph/yardoc.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/solargraph/yardoc.rb b/lib/solargraph/yardoc.rb index 96e39816e..c117ec856 100644 --- a/lib/solargraph/yardoc.rb +++ b/lib/solargraph/yardoc.rb @@ -21,7 +21,12 @@ def build_docs gem_yardoc_path, yard_plugins, gemspec yard_plugins.each { |plugin| cmd << " --plugin #{plugin}" } Solargraph.logger.debug { "Running: #{cmd}" } # @todo set these up to run in parallel - # + unless File.exist?(gemspec.gem_dir) + Solargraph.logger.info { "Bad info from gemspec - #{gemspec.gem_dir} does not exist" } + Solargraph.logger.info stdout_and_stderr_str + return + end + # @sg-ignore stdout_and_stderr_str, status = Open3.capture2e(current_bundle_env_tweaks, cmd, chdir: gemspec.gem_dir) return if status.success? From 67d3f94518540befcf1732ca149f442bdfc744a3 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 4 Aug 2025 17:09:37 -0400 Subject: [PATCH 487/561] Drop bad line --- lib/solargraph/yardoc.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/solargraph/yardoc.rb b/lib/solargraph/yardoc.rb index c117ec856..a474c8d17 100644 --- a/lib/solargraph/yardoc.rb +++ b/lib/solargraph/yardoc.rb @@ -23,7 +23,6 @@ def build_docs gem_yardoc_path, yard_plugins, gemspec # @todo set these up to run in parallel unless File.exist?(gemspec.gem_dir) Solargraph.logger.info { "Bad info from gemspec - #{gemspec.gem_dir} does not exist" } - Solargraph.logger.info stdout_and_stderr_str return end From 1e7c972f314003184ac0a43828986f27fcc4aa32 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 4 Aug 2025 17:13:30 -0400 Subject: [PATCH 488/561] More specs --- spec/complex_type/conforms_to_spec.rb | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/spec/complex_type/conforms_to_spec.rb b/spec/complex_type/conforms_to_spec.rb index 4ac00e70a..f8a623bf0 100644 --- a/spec/complex_type/conforms_to_spec.rb +++ b/spec/complex_type/conforms_to_spec.rb @@ -26,6 +26,27 @@ expect(match).to be(true) end + it 'allows covariant behavior in parameters of Array' do + exp = described_class.parse('Array') + inf = described_class.parse('Array') + match = inf.conforms_to?(api_map, exp, :method_call) + expect(match).to be(true) + end + + it 'does not allow contravariant behavior in parameters of Array' do + exp = described_class.parse('Array') + inf = described_class.parse('Array') + match = inf.conforms_to?(api_map, exp, :method_call) + expect(match).to be(false) + end + + it 'allows covariant behavior in key types of Hash' do + exp = described_class.parse('Hash{Object => String}') + inf = described_class.parse('Hash{Integer => String}') + match = inf.conforms_to?(api_map, exp, :method_call) + expect(match).to be(true) + end + it 'accepts valid tuple conformance' do exp = described_class.parse('Array(Integer, Integer)') inf = described_class.parse('Array(Integer, Integer)') From 17883a2549ec72f49341c5555b7876a7fd1bef6c Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 4 Aug 2025 17:18:26 -0400 Subject: [PATCH 489/561] Fix call to ApiMap --- spec/api_map_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/api_map_spec.rb b/spec/api_map_spec.rb index 3a3664770..573a10e86 100755 --- a/spec/api_map_spec.rb +++ b/spec/api_map_spec.rb @@ -6,7 +6,7 @@ # avoid performance hit of doing this many times # rubocop:disable RSpec/InstanceVariable before :all do # rubocop:disable RSpec/BeforeAfterAll - @api_map = described_class.load_with_cache(Dir.pwd, out: nil) + @api_map = described_class.load_with_cache(Dir.pwd, nil) end it 'returns core methods' do From 0cf4006dd3208b40ce88ced3f7f509f7f0f6581b Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 4 Aug 2025 17:34:35 -0400 Subject: [PATCH 490/561] Test error andler --- spec/yardoc_spec.rb | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/spec/yardoc_spec.rb b/spec/yardoc_spec.rb index 7fadab2bf..03ae76475 100644 --- a/spec/yardoc_spec.rb +++ b/spec/yardoc_spec.rb @@ -52,6 +52,15 @@ expect(File.exist?(File.join(gem_yardoc_path, 'complete'))).to be true end + it 'bails quietly if directory given does not exist' do + allow(File).to receive(:exist?).and_return(false) + + expect do + described_class.build_docs(gem_yardoc_path, [], gemspec) + end.not_to raise_error + end + + it 'is idempotent' do described_class.build_docs(gem_yardoc_path, [], gemspec) described_class.build_docs(gem_yardoc_path, [], gemspec) # second time From 5ecbf993863ab5b39dfd9f1cbbc6dcc793bfb31b Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 4 Aug 2025 17:35:04 -0400 Subject: [PATCH 491/561] Ratchet rubocop todo --- .rubocop_todo.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index e3ebf9415..3891e37e6 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -580,11 +580,6 @@ Lint/RedundantWithIndex: Exclude: - 'lib/solargraph/language_server/message/completion_item/resolve.rb' -# Configuration parameters: AllowComments, AllowNil. -Lint/SuppressedException: - Exclude: - - 'Rakefile' - # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: strict, consistent From 8a045e29ad42aef8a93da7573f20b90c3148e267 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 4 Aug 2025 17:37:21 -0400 Subject: [PATCH 492/561] Drop blank line --- spec/yardoc_spec.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/yardoc_spec.rb b/spec/yardoc_spec.rb index 03ae76475..e2fa5b89b 100644 --- a/spec/yardoc_spec.rb +++ b/spec/yardoc_spec.rb @@ -60,7 +60,6 @@ end.not_to raise_error end - it 'is idempotent' do described_class.build_docs(gem_yardoc_path, [], gemspec) described_class.build_docs(gem_yardoc_path, [], gemspec) # second time From a3d6c03b2afbbec5335b6cb4086903fe321f6915 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Mon, 4 Aug 2025 20:16:33 -0400 Subject: [PATCH 493/561] Ratchet RuboCop todo file --- .rubocop_todo.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index fd4150fc0..738e11676 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -616,11 +616,6 @@ Lint/RedundantWithIndex: Exclude: - 'lib/solargraph/language_server/message/completion_item/resolve.rb' -# Configuration parameters: AllowComments, AllowNil. -Lint/SuppressedException: - Exclude: - - 'Rakefile' - # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: strict, consistent From 7441fd4e091e4cd2945f3e7f91aa23992eb7ddc7 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 5 Aug 2025 06:33:49 -0400 Subject: [PATCH 494/561] Move back to capture2e in undercover Rake task --- Rakefile | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Rakefile b/Rakefile index c0550613d..152358bb1 100755 --- a/Rakefile +++ b/Rakefile @@ -42,9 +42,7 @@ task :spec do FileUtils.mv('coverage/full-new', 'coverage/full') end -# @return [Boolean, nil] returns `true` if the command exits with -# status zero. `false` if the exit status is a non-zero integer. -# `nil` if the command could not execute. +# @return [Process::Status] def undercover simplecov_collate cmd = 'bundle exec undercover ' \ @@ -52,7 +50,7 @@ def undercover '--exclude-files "Rakefile,spec/*,spec/**/*,lib/solargraph/version.rb" ' \ '--compare origin/master' output, status = Bundler.with_unbundled_env do - Process.spawn(cmd) + Open3.capture2e(cmd) end puts output $stdout.flush @@ -68,7 +66,7 @@ end desc "Check PR coverage" task :undercover do - raise "Undercover failed" unless undercover + raise "Undercover failed" unless undercover.success? end desc "Branch-focused fast-feedback quality/spec/coverage checks" @@ -91,7 +89,7 @@ end desc "Run undercover and show output without failing the task if it fails" task :undercover_no_fail do - Rake::Task["undercover"].invoke + undercover rescue StandardError puts "Undercover failed, but continuing with other tasks." end From e875226638a87aaf9fb824ce64a0435d5c78319d Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 5 Aug 2025 07:33:45 -0400 Subject: [PATCH 495/561] Cover more paths in type checking --- spec/type_checker/levels/strong_spec.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/spec/type_checker/levels/strong_spec.rb b/spec/type_checker/levels/strong_spec.rb index 24594e32b..f838c1050 100644 --- a/spec/type_checker/levels/strong_spec.rb +++ b/spec/type_checker/levels/strong_spec.rb @@ -17,6 +17,13 @@ def foo(idx, arr) expect(checker.problems.map(&:message)).to be_empty end + it 'does not complain on Class.new' do + checker = type_checker(%( + Class.new + )) + expect(checker.problems.map(&:message)).to be_empty + end + it 'reports missing return tags' do checker = type_checker(%( class Foo From 4a7c6dec97997cc83883f0b9cc2b8333029eb074 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 5 Aug 2025 07:54:05 -0400 Subject: [PATCH 496/561] Define closure for Pin::Symbol, for completeness This isn't used anywhere to my knowledge, but it makes sense to think of symbols as being in the global namespace, helps guarantee that closure is always available on a pin, and of course keeps the assert happy ;) --- lib/solargraph/pin/symbol.rb | 4 ++++ spec/pin/symbol_spec.rb | 7 ++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/solargraph/pin/symbol.rb b/lib/solargraph/pin/symbol.rb index 9e11c3d7d..9c59155a1 100644 --- a/lib/solargraph/pin/symbol.rb +++ b/lib/solargraph/pin/symbol.rb @@ -20,6 +20,10 @@ def path '' end + def closure + @closure ||= Pin::ROOT_PIN + end + def completion_item_kind Solargraph::LanguageServer::CompletionItemKinds::KEYWORD end diff --git a/spec/pin/symbol_spec.rb b/spec/pin/symbol_spec.rb index 98d88137e..16961cadc 100644 --- a/spec/pin/symbol_spec.rb +++ b/spec/pin/symbol_spec.rb @@ -1,10 +1,15 @@ describe Solargraph::Pin::Symbol do context "as an unquoted literal" do - it "is a kind of keyword" do + it "is a kind of keyword to the LSP" do pin = Solargraph::Pin::Symbol.new(nil, ':symbol') expect(pin.completion_item_kind).to eq(Solargraph::LanguageServer::CompletionItemKinds::KEYWORD) end + it "has global closure" do + pin = Solargraph::Pin::Symbol.new(nil, ':symbol') + expect(pin.closure).to eq(Solargraph::Pin::ROOT_PIN) + end + it "has a Symbol return type" do pin = Solargraph::Pin::Symbol.new(nil, ':symbol') expect(pin.return_type.tag).to eq('Symbol') From dc4f82f786485d4ea5664e6b34fa06460acd5343 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 5 Aug 2025 08:13:29 -0400 Subject: [PATCH 497/561] Tweak timeout in spec --- spec/library_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/library_spec.rb b/spec/library_spec.rb index 92129db3e..5e5ad577e 100644 --- a/spec/library_spec.rb +++ b/spec/library_spec.rb @@ -44,7 +44,7 @@ def foo(adapter) end ), 'file.rb', 0) completion = nil - Timeout.timeout(10) do + Timeout.timeout(15) do # give Solargraph time to cache the gem while (completion = library.completions_at('file.rb', 5, 19)).pins.empty? sleep 0.25 From 5f2c8b859e3a558427960a602f5657df1de5509c Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 5 Aug 2025 08:14:48 -0400 Subject: [PATCH 498/561] Add @sg-ignore --- Rakefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Rakefile b/Rakefile index 152358bb1..8d771ee6e 100755 --- a/Rakefile +++ b/Rakefile @@ -42,6 +42,7 @@ task :spec do FileUtils.mv('coverage/full-new', 'coverage/full') end +# @sg-ignore #undercover return type could not be inferred # @return [Process::Status] def undercover simplecov_collate From 0cea83506dbd9357077a9813edf52da6515e00a2 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 5 Aug 2025 08:38:28 -0400 Subject: [PATCH 499/561] Fix a code coverage issue --- lib/solargraph/type_checker.rb | 6 ++---- spec/type_checker/levels/strong_spec.rb | 26 +++++++++++++++++++++++-- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/lib/solargraph/type_checker.rb b/lib/solargraph/type_checker.rb index 0826c1e66..925695e61 100644 --- a/lib/solargraph/type_checker.rb +++ b/lib/solargraph/type_checker.rb @@ -301,10 +301,8 @@ def argument_problems_for chain, api_map, closure_pin, locals, location pins = base.define(api_map, closure_pin, locals) first_pin = pins.first - if first_pin.is_a?(Pin::DelegatedMethod) && !first_pin.resolvable?(api_map) - # Do nothing, as we can't find the actual method implementation - return [] - elsif first_pin.is_a?(Pin::Method) + unresolvable = first_pin.is_a?(Pin::DelegatedMethod) && !first_pin.resolvable?(api_map) + if !unresolvable && first_pin.is_a?(Pin::Method) # @type [Pin::Method] pin = first_pin ap = if base.links.last.is_a?(Solargraph::Source::Chain::ZSuper) diff --git a/spec/type_checker/levels/strong_spec.rb b/spec/type_checker/levels/strong_spec.rb index f838c1050..4e988a970 100644 --- a/spec/type_checker/levels/strong_spec.rb +++ b/spec/type_checker/levels/strong_spec.rb @@ -17,9 +17,31 @@ def foo(idx, arr) expect(checker.problems.map(&:message)).to be_empty end - it 'does not complain on Class.new' do + it 'complains on bad @type assignment' do checker = type_checker(%( - Class.new + # @type [Integer] + c = Class.new + )) + expect(checker.problems.map(&:message)) + .to eq ['Declared type Integer does not match inferred type Class for variable c'] + end + + it 'does not complain on another variant of Class.new' do + checker = type_checker(%( + class Class + # @return [self] + def self.blah + new + end + end + )) + expect(checker.problems.map(&:message)).to be_empty + end + + it 'does not complain on indirect Class.new', skip: 'hangs in a loop currently' do + checker = type_checker(%( + class Foo < Class; end + Foo.new )) expect(checker.problems.map(&:message)).to be_empty end From cf8a5ed31d29aabf890ebffe50973ba467b5efd7 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 5 Aug 2025 08:39:42 -0400 Subject: [PATCH 500/561] Fix merge issue --- lib/solargraph/type_checker.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/solargraph/type_checker.rb b/lib/solargraph/type_checker.rb index f4d55d0ca..7c3a74829 100644 --- a/lib/solargraph/type_checker.rb +++ b/lib/solargraph/type_checker.rb @@ -108,7 +108,7 @@ def load_string code, filename = nil, level = :normal, api_map: Solargraph::ApiM end end - private + private # @return [Array] def method_tag_problems @@ -810,5 +810,5 @@ def without_ignored problems ignored end end -end + end end From 4fe4eb438c387ae64efeae162f3a1eae5ed769c5 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 5 Aug 2025 08:40:21 -0400 Subject: [PATCH 501/561] Show profiling info by default --- .rspec | 1 + 1 file changed, 1 insertion(+) diff --git a/.rspec b/.rspec index 83e16f804..7198dff51 100644 --- a/.rspec +++ b/.rspec @@ -1,2 +1,3 @@ --color --require spec_helper +--profile From 861d8f63362e02d823708f3764e3c5b89ec68df0 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 5 Aug 2025 08:44:01 -0400 Subject: [PATCH 502/561] Ignore other local linting/testing-related files while we're at it --- .gitignore | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index e10c43432..2819165b1 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,8 @@ doc coverage /tmp/ -/rspec-examples.txt \ No newline at end of file +/rspec-examples.txt +/.ruby-version +/Makefile +/.pryrc +/.rspec-local From 443cdae67b31ceca67b652937dfcfdaf4fdfcde4 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 5 Aug 2025 11:10:47 -0400 Subject: [PATCH 503/561] Protect another Rubocop::Runner use with mutex --- lib/solargraph.rb | 2 ++ lib/solargraph/diagnostics/rubocop.rb | 7 ++++++- .../language_server/message/text_document/formatting.rb | 3 +-- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/solargraph.rb b/lib/solargraph.rb index b3dc5990b..038e7bccf 100755 --- a/lib/solargraph.rb +++ b/lib/solargraph.rb @@ -53,6 +53,8 @@ class InvalidRubocopVersionError < RuntimeError; end dir = File.dirname(__FILE__) VIEWS_PATH = File.join(dir, 'solargraph', 'views') + CHDIR_MUTEX = Mutex.new + # @param type [Symbol] Type of assert. def self.asserts_on?(type) if ENV['SOLARGRAPH_ASSERTS'].nil? || ENV['SOLARGRAPH_ASSERTS'].empty? diff --git a/lib/solargraph/diagnostics/rubocop.rb b/lib/solargraph/diagnostics/rubocop.rb index 0ea58c955..0a55d0367 100644 --- a/lib/solargraph/diagnostics/rubocop.rb +++ b/lib/solargraph/diagnostics/rubocop.rb @@ -28,7 +28,12 @@ def diagnose source, _api_map options, paths = generate_options(source.filename, source.code) store = RuboCop::ConfigStore.new runner = RuboCop::Runner.new(options, store) - result = redirect_stdout{ runner.run(paths) } + # Ensure only one instance of RuboCop::Runner is running at + # a time - it uses 'chdir' to read config files with ERB, + # which can conflict with other chdirs. + result = Solargraph::CHDIR_MUTEX.synchronize do + redirect_stdout{ runner.run(paths) } + end return [] if result.empty? diff --git a/lib/solargraph/language_server/message/text_document/formatting.rb b/lib/solargraph/language_server/message/text_document/formatting.rb index aa8fba6db..fd19cc533 100644 --- a/lib/solargraph/language_server/message/text_document/formatting.rb +++ b/lib/solargraph/language_server/message/text_document/formatting.rb @@ -23,8 +23,7 @@ def process # Ensure only one instance of RuboCop::Runner is running at # a time - it uses 'chdir' to read config files with ERB, # which can conflict with other chdirs. - @@rubocop_mutex ||= Mutex.new - corrections = @@rubocop_mutex.synchronize do + corrections = Solargraph::CHDIR_MUTEX.synchronize do redirect_stdout do ::RuboCop::Runner.new(options, ::RuboCop::ConfigStore.new).run(paths) end From 2bf7f31cfec3d65d679486b00d7e0d2072251f3d Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 5 Aug 2025 12:35:37 -0400 Subject: [PATCH 504/561] Move away from Set due to Ruby 3.0 failure --- lib/solargraph/workspace/gemspecs.rb | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/lib/solargraph/workspace/gemspecs.rb b/lib/solargraph/workspace/gemspecs.rb index e99a751b5..421b4f063 100644 --- a/lib/solargraph/workspace/gemspecs.rb +++ b/lib/solargraph/workspace/gemspecs.rb @@ -109,22 +109,30 @@ def find_gem name, version = nil, out: $stderr def fetch_dependencies gemspec, out: $stderr gemspecs = all_gemspecs_from_bundle + # @type [Hash{String => Gem::Specification}] + deps_so_far = {} + # @param runtime_dep [Gem::Dependency] - # @param deps [Set] - gem_dep_gemspecs = only_runtime_dependencies(gemspec).each_with_object(Set.new) do |runtime_dep, deps| + # @param deps [Hash{String => Gem::Specification}] + gem_dep_gemspecs = only_runtime_dependencies(gemspec).each_with_object(deps_so_far) do |runtime_dep, deps| + next if deps[runtime_dep.name] + Solargraph.logger.info "Adding #{runtime_dep.name} dependency for #{gemspec.name}" dep = gemspecs.find { |dep| dep.name == runtime_dep.name } dep ||= Gem::Specification.find_by_name(runtime_dep.name, runtime_dep.requirement) rescue Gem::MissingSpecError dep = resolve_gem_ignoring_local_bundle runtime_dep.name, out: out ensure - deps.merge fetch_dependencies(dep, out: out) if dep && deps.add?(dep) - end.to_a.compact + next unless dep + + fetch_dependencies(dep, out: out).each { |sub_dep| deps[sub_dep.name] ||= sub_dep } + deps[dep.name] ||= dep + end # RBS tracks implicit dependencies, like how the YAML standard # library implies pulling in the psych library. stdlib_deps = RbsMap::StdlibMap.stdlib_dependencies(gemspec.name, gemspec.version) || [] stdlib_dep_gemspecs = stdlib_deps.map { |dep| find_gem(dep['name'], dep['version']) }.compact - (gem_dep_gemspecs + stdlib_dep_gemspecs).uniq(&:name) + (gem_dep_gemspecs.values.compact + stdlib_dep_gemspecs).uniq(&:name) end # Returns all gemspecs directly depended on by this workspace's From 5c83d0f432c65f3fc07daf527b4542e30b1ca0af Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 5 Aug 2025 12:41:10 -0400 Subject: [PATCH 505/561] Add new RuboCop failure from new RuboCop --- .rubocop_todo.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 762cd5dca..0d8953664 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,6 +1,6 @@ # This configuration was generated by # `rubocop --auto-gen-config --no-exclude-limit --no-offense-counts --no-auto-gen-timestamp` -# using RuboCop version 1.79.1. +# using RuboCop version 1.79.2. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new @@ -2127,6 +2127,7 @@ Style/RedundantInterpolation: # This cop supports safe autocorrection (--autocorrect). Style/RedundantParentheses: Exclude: + - 'lib/solargraph/diagnostics/type_check.rb' - 'lib/solargraph/language_server/message/initialize.rb' - 'lib/solargraph/parser/parser_gem/node_chainer.rb' - 'lib/solargraph/pin/method.rb' From bab49a6d0263eeec252f5c93b55327e7afb648d9 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 5 Aug 2025 13:15:26 -0400 Subject: [PATCH 506/561] Ratchet Rubocop todo --- .rubocop_todo.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 15d3a480b..589164a5f 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -149,7 +149,6 @@ Layout/EndAlignment: - 'lib/solargraph/source/chain/call.rb' - 'lib/solargraph/source_map/clip.rb' - 'lib/solargraph/source_map/mapper.rb' - - 'lib/solargraph/type_checker.rb' - 'lib/solargraph/type_checker/rules.rb' - 'lib/solargraph/yard_map/mapper.rb' From 18a5f068896c6c030dc33f3cb062baa6526d3e8c Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 5 Aug 2025 13:20:45 -0400 Subject: [PATCH 507/561] Update to latest rbs --- .github/workflows/plugins.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index ecb6a3b2d..5abe73567 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -96,9 +96,11 @@ jobs: set -x BUNDLE_PATH="${GITHUB_WORKSPACE:?}/vendor/bundle" export BUNDLE_PATH + bundle update rbs cd ../solargraph-rails echo "gem 'solargraph', path: '${GITHUB_WORKSPACE:?}'" >> Gemfile bundle install + bundle update rbs RAILS_DIR="$(pwd)/spec/rails7" export RAILS_DIR cd ${RAILS_DIR} From 782f4d698674cae2fa9ca1da35431a74974baeb3 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 5 Aug 2025 13:22:14 -0400 Subject: [PATCH 508/561] Update to latest rbs --- .github/workflows/plugins.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index 5abe73567..eedf78687 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -94,9 +94,9 @@ jobs: - name: Install gems run: | set -x + bundle update rbs BUNDLE_PATH="${GITHUB_WORKSPACE:?}/vendor/bundle" export BUNDLE_PATH - bundle update rbs cd ../solargraph-rails echo "gem 'solargraph', path: '${GITHUB_WORKSPACE:?}'" >> Gemfile bundle install From c6f650cc049b73e6459597a3982cbabc4af36c20 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 5 Aug 2025 13:23:28 -0400 Subject: [PATCH 509/561] Update to latest rbs --- .github/workflows/plugins.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index eedf78687..539a7afc8 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -94,7 +94,6 @@ jobs: - name: Install gems run: | set -x - bundle update rbs BUNDLE_PATH="${GITHUB_WORKSPACE:?}/vendor/bundle" export BUNDLE_PATH cd ../solargraph-rails From 852dd51b02e7c9307e04ad2e6f95b3fc2626f0b5 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 6 Aug 2025 19:23:08 -0400 Subject: [PATCH 510/561] Update todo file from new RuboCop --- .rubocop_todo.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 738e11676..20237481b 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,6 +1,6 @@ # This configuration was generated by # `rubocop --auto-gen-config --no-exclude-limit --no-offense-counts --no-auto-gen-timestamp` -# using RuboCop version 1.79.1. +# using RuboCop version 1.79.2. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new @@ -2280,6 +2280,7 @@ Style/RedundantInterpolation: # This cop supports safe autocorrection (--autocorrect). Style/RedundantParentheses: Exclude: + - 'lib/solargraph/diagnostics/type_check.rb' - 'lib/solargraph/language_server/message/initialize.rb' - 'lib/solargraph/parser/parser_gem/node_chainer.rb' - 'lib/solargraph/pin/method.rb' From 4e6dd757c173456ac5aee7f64503f23eb9c3f3c2 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 9 Aug 2025 15:56:54 -0400 Subject: [PATCH 511/561] Rakefile tweaks after more extended use --- Rakefile | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/Rakefile b/Rakefile index 8d771ee6e..659a08ed5 100755 --- a/Rakefile +++ b/Rakefile @@ -13,26 +13,31 @@ task typecheck: [:typecheck_typed] desc "Run the type checker at typed level - return code issues provable without annotations being correct" task :typecheck_typed do - sh "bundle exec solargraph typecheck --level typed" + sh "SOLARGRAPH_ASSERTS=on bundle exec solargraph typecheck --level typed" end desc "Run the type checker at strict level - report issues using type annotations" task :typecheck_strict do - sh "bundle exec solargraph typecheck --level strict" + sh "SOLARGRAPH_ASSERTS=on bundle exec solargraph typecheck --level strict" end desc "Run the type checker at strong level - enforce that type annotations exist" task :typecheck_strong do - sh "bundle exec solargraph typecheck --level strong" + sh "SOLARGRAPH_ASSERTS=on bundle exec solargraph typecheck --level strong" end desc "Run the type checker at alpha level - run high-false-alarm checks" task :typecheck_alpha do - sh "bundle exec solargraph typecheck --level alpha" + sh "SOLARGRAPH_ASSERTS=on bundle exec solargraph typecheck --level alpha" end -desc "Run RSpec tests" -task :spec do +desc "Run RSpec tests, starting with the ones that failed last time" +task spec: %i[spec_failed undercover_no_fail full_spec] do + undercover +end + +desc "Run all RSpec tests" +task :full_spec do warn 'starting spec' sh 'TEST_COVERAGE_COMMAND_NAME=full-new bundle exec rspec' # --profile' warn 'ending spec' @@ -71,21 +76,16 @@ task :undercover do end desc "Branch-focused fast-feedback quality/spec/coverage checks" -task test: %i[overcommit spec_failed undercover_no_fail] do - # run these last tasks manually so that rake doesn't optimize - # undercover away - Rake::Task['spec'].invoke - undercover - Rake::Task['overcommit'].invoke - Rake::Task['typecheck'].invoke +task test: %i[overcommit spec typecheck] do + # do these in order Rake::Task['typecheck_strict'].invoke Rake::Task['typecheck_strong'].invoke Rake::Task['typecheck_alpha'].invoke end -desc "Re-run failed specs" +desc "Re-run failed specs. Add --fail-fast in your .rspec-local file if desired." task :spec_failed do - sh 'TEST_COVERAGE_COMMAND_NAME=next-failure bundle exec rspec --next-failure' + sh 'TEST_COVERAGE_COMMAND_NAME=next-failure bundle exec rspec --only-failures' end desc "Run undercover and show output without failing the task if it fails" From 0c26f156fe7f4329b8eae8ba51f919adf5794286 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 10 Aug 2025 17:16:33 -0400 Subject: [PATCH 512/561] Resolve class aliases via Constant pins This also eliminates the need for Parser::NodeMethods as a searately defined class. --- lib/solargraph.rb | 18 +- lib/solargraph/api_map.rb | 186 +++++++++++++----- lib/solargraph/api_map/store.rb | 9 +- lib/solargraph/complex_type.rb | 3 + lib/solargraph/complex_type/type_methods.rb | 12 +- lib/solargraph/complex_type/unique_type.rb | 5 +- lib/solargraph/parser/node_methods.rb | 97 --------- .../parser/parser_gem/node_methods.rb | 2 +- lib/solargraph/pin/base.rb | 2 +- lib/solargraph/rbs_map/conversions.rb | 2 +- spec/api_map/api_map_method_spec.rb | 154 +++++++++++++++ spec/api_map_spec.rb | 58 ++++++ 12 files changed, 385 insertions(+), 163 deletions(-) delete mode 100644 lib/solargraph/parser/node_methods.rb create mode 100644 spec/api_map/api_map_method_spec.rb diff --git a/lib/solargraph.rb b/lib/solargraph.rb index b3dc5990b..2b2a39d00 100755 --- a/lib/solargraph.rb +++ b/lib/solargraph.rb @@ -70,7 +70,23 @@ def self.asserts_on?(type) # @param block [Proc] A block that returns a message to log # @return [void] def self.assert_or_log(type, msg = nil, &block) - raise (msg || block.call) if asserts_on?(type) && ![:combine_with_visibility].include?(type) + if asserts_on? type + # @type [String, nil] + msg ||= block.call + + raise "No message given for #{type.inspect}" if msg.nil? + + # @todo :combine_with_visibility is not ready for prime time - + # lots of disagreements found in practice that heuristics need + # to be created for and/or debugging needs to resolve in pin + # generation. + # @todo :api_map_namespace_pin_stack triggers in a badly handled + # self type case - 'keeps track of self type in method + # parameters in subclass' in call_spec.rb + return if %i[api_map_namespace_pin_stack combine_with_visibility].include?(type) + + raise msg + end logger.info msg, &block end diff --git a/lib/solargraph/api_map.rb b/lib/solargraph/api_map.rb index 6a0edc5a4..d3532c282 100755 --- a/lib/solargraph/api_map.rb +++ b/lib/solargraph/api_map.rb @@ -302,12 +302,11 @@ def qualify tag, context_tag = '' return unless type return tag if type.literal? - context_type = ComplexType.try_parse(context_tag) - return unless context_type - fqns = qualify_namespace(type.rooted_namespace, context_type.rooted_namespace) return unless fqns + return fqns if %w[Class Module].include? type + fqns + type.substring end @@ -388,16 +387,18 @@ def get_block_pins # @param deep [Boolean] True to include superclasses, mixins, etc. # @return [Array] def get_methods rooted_tag, scope: :instance, visibility: [:public], deep: true + rooted_tag = qualify(rooted_tag, '') + return [] unless rooted_tag if rooted_tag.start_with? 'Array(' # Array() are really tuples - use our fill, as the RBS repo # does not give us definitions for it rooted_tag = "Solargraph::Fills::Tuple(#{rooted_tag[6..-2]})" end - rooted_type = ComplexType.try_parse(rooted_tag) - fqns = rooted_type.namespace - namespace_pin = store.get_path_pins(fqns).select { |p| p.is_a?(Pin::Namespace) }.first cached = cache.get_methods(rooted_tag, scope, visibility, deep) return cached.clone unless cached.nil? + rooted_type = ComplexType.try_parse(rooted_tag) + fqns = rooted_type.namespace + namespace_pin = get_namespace_pin(fqns) # @type [Array] result = [] skip = Set.new @@ -515,10 +516,20 @@ def get_complex_type_methods complex_type, context = '', internal = false # @param name [String] Method name to look up # @param scope [Symbol] :instance or :class # @return [Array] - def get_method_stack rooted_tag, name, scope: :instance, visibility: [:private, :protected, :public], preserve_generics: false - rooted_type = ComplexType.parse(rooted_tag) + def get_method_stack rooted_tag, name, scope: :instance, visibility: [:private, :protected, :public], + preserve_generics: false + rooted_tag = qualify(rooted_tag, '') + return [] unless rooted_tag + rooted_type = ComplexType.try_parse(rooted_tag) + return [] if rooted_type.nil? fqns = rooted_type.namespace - namespace_pin = store.get_path_pins(fqns).select { |p| p.is_a?(Pin::Namespace) }.first + namespace_pin = get_namespace_pin(fqns) + if namespace_pin.nil? + # :nocov: + Solargraph.assert_or_log(:api_map_namespace_pin_stack, "Could not find namespace pin for #{fqns} while looking for method #{name}") + return [] + # :nocov: + end methods = get_methods(rooted_tag, scope: scope, visibility: visibility).select { |p| p.name == name } methods = erase_generics(namespace_pin, rooted_type, methods) unless preserve_generics methods @@ -664,6 +675,46 @@ def resolve_method_aliases pins, visibility = [:public, :private, :protected] GemPins.combine_method_pins_by_path(with_resolved_aliases) end + # @param fq_reference_tag [String] A fully qualified whose method should be pulled in + # @param namespace_pin [Pin::Base] Namespace pin for the rooted_type + # parameter - used to pull generics information + # @param type [ComplexType] The type which is having its + # methods supplemented from fq_reference_tag + # @param scope [Symbol] :class or :instance + # @param visibility [Array] :public, :protected, and/or :private + # @param deep [Boolean] + # @param skip [Set] + # @param no_core [Boolean] Skip core classes if true + # @return [Array] + def inner_get_methods_from_reference fq_reference_tag, namespace_pin, type, scope, visibility, deep, skip, no_core + logger.debug { "ApiMap#add_methods_from_reference(type=#{type}) starting" } + + # Ensure the types returned by the methods in the referenced + # type are relative to the generic values passed in the + # reference. e.g., Foo might include Enumerable + # + # @todo perform the same translation in the other areas + # here after adding a spec and handling things correctly + # in ApiMap::Store and RbsMap::Conversions for each + resolved_reference_type = ComplexType.parse(fq_reference_tag).force_rooted.resolve_generics(namespace_pin, type) + # @todo Can inner_get_methods be cached? Lots of lookups of base types going on. + methods = inner_get_methods(resolved_reference_type.tag, scope, visibility, deep, skip, no_core) + if namespace_pin && !resolved_reference_type.all_params.empty? + reference_pin = get_namespace_pin(resolved_reference_type.namespace) + # logger.debug { "ApiMap#add_methods_from_reference(type=#{type}) - resolving generics with #{reference_pin.generics}, #{resolved_reference_type.rooted_tags}" } + methods = methods.map do |method_pin| + method_pin.resolve_generics(reference_pin, resolved_reference_type) + end + end + # logger.debug { "ApiMap#add_methods_from_reference(type=#{type}) - resolved_reference_type: #{resolved_reference_type} for type=#{type}: #{methods.map(&:name)}" } + methods + end + + # @return [Workspace, nil] + def workspace + @doc_map&.workspace + end + private # A hash of source maps with filename keys. @@ -679,6 +730,13 @@ def store # @return [Solargraph::ApiMap::Cache] attr_reader :cache + # @param fqns [String] + # @return [Pin::Namespace, nil] + def get_namespace_pin fqns + # fqns = ComplexType.parse(fqns).namespace + store.get_path_pins(fqns).select { |p| p.is_a?(Pin::Namespace) }.first + end + # @param rooted_tag [String] A fully qualified namespace, with # generic parameter values if applicable # @param scope [Symbol] :class or :instance @@ -688,11 +746,20 @@ def store # @param no_core [Boolean] Skip core classes if true # @return [Array] def inner_get_methods rooted_tag, scope, visibility, deep, skip, no_core = false + rooted_tag = qualify(rooted_tag, '') + return [] if rooted_tag.nil? + return [] unless rooted_tag rooted_type = ComplexType.parse(rooted_tag).force_rooted fqns = rooted_type.namespace - fqns_generic_params = rooted_type.all_params - namespace_pin = store.get_path_pins(fqns).select { |p| p.is_a?(Pin::Namespace) }.first + namespace_pin = get_namespace_pin(fqns) + if namespace_pin.nil? + # :nocov: + Solargraph.assert_or_log(:api_map_namespace_pin_inner, "Could not find namespace pin for #{fqns}") + return [] + # :nocov: + end return [] if no_core && fqns =~ /^(Object|BasicObject|Class|Module)$/ + # @todo should this by by rooted_tag_? reqstr = "#{fqns}|#{scope}|#{visibility.sort}|#{deep}" return [] if skip.include?(reqstr) skip.add reqstr @@ -712,7 +779,10 @@ def inner_get_methods rooted_tag, scope, visibility, deep, skip, no_core = false if scope == :instance store.get_includes(fqns).reverse.each do |include_tag| rooted_include_tag = qualify(include_tag, rooted_tag) - result.concat inner_get_methods_from_reference(rooted_include_tag, namespace_pin, rooted_type, scope, visibility, deep, skip, true) + if rooted_include_tag + result.concat inner_get_methods_from_reference(rooted_include_tag, namespace_pin, rooted_type, scope, + visibility, deep, skip, true) + end end rooted_sc_tag = qualify_superclass(rooted_tag) unless rooted_sc_tag.nil? @@ -741,41 +811,6 @@ def inner_get_methods rooted_tag, scope, visibility, deep, skip, no_core = false result end - # @param fq_reference_tag [String] A fully qualified whose method should be pulled in - # @param namespace_pin [Pin::Base] Namespace pin for the rooted_type - # parameter - used to pull generics information - # @param type [ComplexType] The type which is having its - # methods supplemented from fq_reference_tag - # @param scope [Symbol] :class or :instance - # @param visibility [Array] :public, :protected, and/or :private - # @param deep [Boolean] - # @param skip [Set] - # @param no_core [Boolean] Skip core classes if true - # @return [Array] - def inner_get_methods_from_reference(fq_reference_tag, namespace_pin, type, scope, visibility, deep, skip, no_core) - # logger.debug { "ApiMap#add_methods_from_reference(type=#{type}) starting" } - - # Ensure the types returned by the methods in the referenced - # type are relative to the generic values passed in the - # reference. e.g., Foo might include Enumerable - # - # @todo perform the same translation in the other areas - # here after adding a spec and handling things correctly - # in ApiMap::Store and RbsMap::Conversions for each - resolved_reference_type = ComplexType.parse(fq_reference_tag).force_rooted.resolve_generics(namespace_pin, type) - # @todo Can inner_get_methods be cached? Lots of lookups of base types going on. - methods = inner_get_methods(resolved_reference_type.tag, scope, visibility, deep, skip, no_core) - if namespace_pin && !resolved_reference_type.all_params.empty? - reference_pin = store.get_path_pins(resolved_reference_type.name).select { |p| p.is_a?(Pin::Namespace) }.first - # logger.debug { "ApiMap#add_methods_from_reference(type=#{type}) - resolving generics with #{reference_pin.generics}, #{resolved_reference_type.rooted_tags}" } - methods = methods.map do |method_pin| - method_pin.resolve_generics(reference_pin, resolved_reference_type) - end - end - # logger.debug { "ApiMap#add_methods_from_reference(type=#{type}) - resolved_reference_type: #{resolved_reference_type} for type=#{type}: #{methods.map(&:name)}" } - methods - end - # @param fqns [String] # @param visibility [Array] # @param skip [Set] @@ -840,16 +875,21 @@ def inner_qualify name, root, skip if root == '' return '' else + root = root[2..-1] if root&.start_with?('::') return inner_qualify(root, '', skip) end else - return name if root == '' && store.namespace_exists?(name) roots = root.to_s.split('::') while roots.length > 0 - fqns = roots.join('::') + '::' + name - return fqns if store.namespace_exists?(fqns) - incs = store.get_includes(roots.join('::')) + potential_root = roots.join('::') + potential_root = potential_root[2..-1] if potential_root.start_with?('::') + potential_fqns = potential_root + '::' + name + potential_fqns = potential_fqns[2..-1] if potential_fqns.start_with?('::') + fqns = resolve_fqns(potential_fqns) + return fqns if fqns + incs = store.get_includes(potential_root) incs.each do |inc| + next if potential_root == root && inc == name foundinc = inner_qualify(name, inc, skip) possibles.push foundinc unless foundinc.nil? end @@ -862,11 +902,51 @@ def inner_qualify name, root, skip possibles.push foundinc unless foundinc.nil? end end - return name if store.namespace_exists?(name) + resolved_fqns = resolve_fqns(name) + return resolved_fqns if resolved_fqns + return possibles.last end end + # @param fqns [String] + # @return [String, nil] + def resolve_fqns fqns + return fqns if store.namespace_exists?(fqns) + + constant_namespace = nil + constant = store.constant_pins.find do |c| + constant_fqns = if c.namespace.empty? + c.name + else + c.namespace + '::' + c.name + end + constant_namespace = c.namespace + constant_fqns == fqns + end + return nil unless constant + + return constant.return_type.namespace if constant.return_type.defined? + + target_ns = resolve_trivial_constant(constant.assignment) + return nil unless target_ns + qualify_namespace target_ns, constant_namespace + end + + # @param node [AST::Node] + # @return [String, nil] + def resolve_trivial_constant node + return nil unless node.is_a?(::Parser::AST::Node) + return nil unless node.type == :const + return nil if node.children.empty? + prefix_node = node.children[0] + prefix = '' + prefix = resolve_trivial_constant(prefix_node) + '::' unless prefix_node.nil? || prefix_node.children.empty? + const_name = node.children[1].to_s + return nil if const_name.empty? + return prefix + const_name + end + # Get the namespace's type (Class or Module). # # @param fqns [String] A fully qualified namespace @@ -874,7 +954,7 @@ def inner_qualify name, root, skip def get_namespace_type fqns return nil if fqns.nil? # @type [Pin::Namespace, nil] - pin = store.get_path_pins(fqns).select{|p| p.is_a?(Pin::Namespace)}.first + pin = get_namespace_pin(fqns) return nil if pin.nil? pin.type end diff --git a/lib/solargraph/api_map/store.rb b/lib/solargraph/api_map/store.rb index 47f92194c..fe48a256b 100644 --- a/lib/solargraph/api_map/store.rb +++ b/lib/solargraph/api_map/store.rb @@ -72,13 +72,13 @@ def get_methods fqns, scope: :instance, visibility: [:public] def get_superclass fq_tag raise "Do not prefix fully qualified tags with '::' - #{fq_tag.inspect}" if fq_tag.start_with?('::') sub = ComplexType.parse(fq_tag) + return sub.simplify_literals.name if sub.literal? + return 'Boolean' if %w[TrueClass FalseClass].include?(fq_tag) fqns = sub.namespace return superclass_references[fq_tag].first if superclass_references.key?(fq_tag) return superclass_references[fqns].first if superclass_references.key?(fqns) return 'Object' if fqns != 'BasicObject' && namespace_exists?(fqns) return 'Object' if fqns == 'Boolean' - simplified_literal_name = ComplexType.parse("#{fqns}").simplify_literals.name - return simplified_literal_name if simplified_literal_name != fqns nil end @@ -142,6 +142,11 @@ def namespace_pins pins_by_class(Solargraph::Pin::Namespace) end + # @return [Enumerable] + def constant_pins + pins_by_class(Solargraph::Pin::Constant) + end + # @return [Enumerable] def method_pins pins_by_class(Solargraph::Pin::Method) diff --git a/lib/solargraph/complex_type.rb b/lib/solargraph/complex_type.rb index 9e23eb502..bbf481de3 100644 --- a/lib/solargraph/complex_type.rb +++ b/lib/solargraph/complex_type.rb @@ -17,10 +17,13 @@ def initialize types = [UniqueType::UNDEFINED] # @todo @items here should not need an annotation # @type [Array] items = types.flat_map(&:items).uniq(&:to_s) + + # Canonicalize 'true, false' to the non-runtime-type 'Boolean' if items.any? { |i| i.name == 'false' } && items.any? { |i| i.name == 'true' } items.delete_if { |i| i.name == 'false' || i.name == 'true' } items.unshift(ComplexType::BOOLEAN) end + items = [UniqueType::UNDEFINED] if items.any?(&:undefined?) @items = items end diff --git a/lib/solargraph/complex_type/type_methods.rb b/lib/solargraph/complex_type/type_methods.rb index e6d596244..6bf383a1a 100644 --- a/lib/solargraph/complex_type/type_methods.rb +++ b/lib/solargraph/complex_type/type_methods.rb @@ -10,11 +10,7 @@ class ComplexType # @name: String # @subtypes: Array # @rooted: boolish - # methods: - # transform() - # all_params() - # rooted?() - # can_root_name?() + # methods: (see @!method declarations below) module TypeMethods # @!method transform(new_name = nil, &transform_type) # @param new_name [String, nil] @@ -24,6 +20,9 @@ module TypeMethods # @!method all_params # @return [Array] # @!method rooted? + # @!method literal? + # @!method simplify_literals + # @return [ComplexType::UniqueType, ComplexType] # @!method can_root_name?(name_to_check = nil) # @param name_to_check [String, nil] @@ -124,7 +123,8 @@ def key_types def namespace # if priority higher than ||=, old implements cause unnecessary check @namespace ||= lambda do - return 'Object' if duck_type? + return simplify_literals.namespace if literal? + return 'Object' if duck_type? || name == 'Boolean' return 'NilClass' if nil_type? return (name == 'Class' || name == 'Module') && !subtypes.empty? ? subtypes.first.name : name end.call diff --git a/lib/solargraph/complex_type/unique_type.rb b/lib/solargraph/complex_type/unique_type.rb index 0f4ec430d..c76bd27c8 100644 --- a/lib/solargraph/complex_type/unique_type.rb +++ b/lib/solargraph/complex_type/unique_type.rb @@ -78,6 +78,8 @@ def initialize(name, key_types = [], subtypes = [], rooted:, parameters_type: ni if parameters_type.nil? raise "You must supply parameters_type if you provide parameters" unless key_types.empty? && subtypes.empty? end + + raise "name must be a String" unless name.is_a?(String) raise "Please remove leading :: and set rooted instead - #{name.inspect}" if name.start_with?('::') @name = name @parameters_type = parameters_type @@ -130,7 +132,8 @@ def determine_non_literal_name # | `false` return name if name.empty? return 'NilClass' if name == 'nil' - return 'Boolean' if ['true', 'false'].include?(name) + return 'TrueClass' if name == 'true' + return 'FalseClass' if name == 'false' return 'Symbol' if name[0] == ':' return 'String' if ['"', "'"].include?(name[0]) return 'Integer' if name.match?(/^-?\d+$/) diff --git a/lib/solargraph/parser/node_methods.rb b/lib/solargraph/parser/node_methods.rb deleted file mode 100644 index 12e974c16..000000000 --- a/lib/solargraph/parser/node_methods.rb +++ /dev/null @@ -1,97 +0,0 @@ -module Solargraph - module Parser - class NodeMethods - module_function - - # @abstract - # @param node [Parser::AST::Node] - # @return [String] - def unpack_name node - raise NotImplementedError - end - - # @abstract - # @todo Temporarily here for testing. Move to Solargraph::Parser. - # @param node [Parser::AST::Node] - # @return [Array] - def call_nodes_from node - raise NotImplementedError - end - - # Find all the nodes within the provided node that potentially return a - # value. - # - # The node parameter typically represents a method's logic, e.g., the - # second child (after the :args node) of a :def node. A simple one-line - # method would typically return itself, while a node with conditions - # would return the resulting node from each conditional branch. Nodes - # that follow a :return node are assumed to be unreachable. Nil values - # are converted to nil node types. - # - # @abstract - # @param node [Parser::AST::Node] - # @return [Array] - def returns_from_method_body node - raise NotImplementedError - end - - # @abstract - # @param node [Parser::AST::Node] - # - # @return [Array] - def const_nodes_from node - raise NotImplementedError - end - - # @abstract - # @param cursor [Solargraph::Source::Cursor] - # @return [Parser::AST::Node, nil] - def find_recipient_node cursor - raise NotImplementedError - end - - # @abstract - # @param node [Parser::AST::Node] - # @return [Array] low-level value nodes in - # value position. Does not include explicit return - # statements - def value_position_nodes_only(node) - raise NotImplementedError - end - - # @abstract - # @param nodes [Enumerable] - def any_splatted_call?(nodes) - raise NotImplementedError - end - - # @abstract - # @param node [Parser::AST::Node] - # @return [void] - def process node - raise NotImplementedError - end - - # @abstract - # @param node [Parser::AST::Node] - # @return [Hash{Parser::AST::Node => Source::Chain}] - def convert_hash node - raise NotImplementedError - end - - # @abstract - # @param node [Parser::AST::Node] - # @return [Position] - def get_node_start_position(node) - raise NotImplementedError - end - - # @abstract - # @param node [Parser::AST::Node] - # @return [Position] - def get_node_end_position(node) - raise NotImplementedError - end - end - end -end diff --git a/lib/solargraph/parser/parser_gem/node_methods.rb b/lib/solargraph/parser/parser_gem/node_methods.rb index b716b352d..d18eb72ab 100644 --- a/lib/solargraph/parser/parser_gem/node_methods.rb +++ b/lib/solargraph/parser/parser_gem/node_methods.rb @@ -120,7 +120,7 @@ def drill_signature node, signature end # @param node [Parser::AST::Node] - # @return [Hash{Parser::AST::Node => Chain}] + # @return [Hash{Parser::AST::Node, Symbol => Source::Chain}] def convert_hash node return {} unless Parser.is_ast_node?(node) return convert_hash(node.children[0]) if node.type == :kwsplat diff --git a/lib/solargraph/pin/base.rb b/lib/solargraph/pin/base.rb index cdd6a5ace..115ce6f0e 100644 --- a/lib/solargraph/pin/base.rb +++ b/lib/solargraph/pin/base.rb @@ -271,8 +271,8 @@ def assert_same_count(other, attr) # # @return [Object, nil] def assert_same(other, attr) - return false if other.nil? val1 = send(attr) + return val1 if other.nil? val2 = other.send(attr) return val1 if val1 == val2 Solargraph.assert_or_log("combine_with_#{attr}".to_sym, diff --git a/lib/solargraph/rbs_map/conversions.rb b/lib/solargraph/rbs_map/conversions.rb index 6e50c022a..f37d99baa 100644 --- a/lib/solargraph/rbs_map/conversions.rb +++ b/lib/solargraph/rbs_map/conversions.rb @@ -240,6 +240,7 @@ def module_decl_to_pin decl # # @return [Solargraph::Pin::Constant] def create_constant(name, tag, comments, decl, base = nil) + tag = "#{base}<#{tag}>" if base parts = name.split('::') if parts.length > 1 name = parts.last @@ -255,7 +256,6 @@ def create_constant(name, tag, comments, decl, base = nil) comments: comments, source: :rbs ) - tag = "#{base}<#{tag}>" if base rooted_tag = ComplexType.parse(tag).force_rooted.rooted_tags constant_pin.docstring.add_tag(YARD::Tags::Tag.new(:return, '', rooted_tag)) constant_pin diff --git a/spec/api_map/api_map_method_spec.rb b/spec/api_map/api_map_method_spec.rb new file mode 100644 index 000000000..a3adc9b94 --- /dev/null +++ b/spec/api_map/api_map_method_spec.rb @@ -0,0 +1,154 @@ +# frozen_string_literal: true + +describe Solargraph::ApiMap do + let(:api_map) { described_class.new } + let(:bench) do + Solargraph::Bench.new(external_requires: external_requires, workspace: Solargraph::Workspace.new('.')) + end + let(:external_requires) { [] } + + before do + api_map.catalog bench + end + + describe '#qualify' do + let(:external_requires) { ['yaml'] } + + it 'resolves YAML to Psych' do + expect(api_map.qualify('YAML', '')).to eq('Psych') + end + + it 'resolves constants used to alias namespaces' do + map = Solargraph::SourceMap.load_string(%( + class Foo + def bing; end + end + + module Bar + Baz = ::Foo + end + )) + api_map.index map.pins + fqns = api_map.qualify('Bar::Baz') + expect(fqns).to eq('Foo') + end + + it 'understands alias namespaces resolving types' do + source = Solargraph::Source.load_string(%( + class Foo + # @return [Symbol] + def bing; end + end + + module Bar + Baz = ::Foo + end + + a = Bar::Baz.new.bing + a + Bar::Baz + ), 'test.rb') + + api_map = described_class.new.map(source) + + clip = api_map.clip_at('test.rb', [11, 8]) + expect(clip.infer.to_s).to eq('Symbol') + end + + it 'understands nested alias namespaces to nested classes resolving types' do + source = Solargraph::Source.load_string(%( + module A + class Foo + # @return [Symbol] + def bing; end + end + end + + module Bar + Baz = A::Foo + end + + a = Bar::Baz.new.bing + a + ), 'test.rb') + + api_map = described_class.new.map(source) + + clip = api_map.clip_at('test.rb', [13, 8]) + expect(clip.infer.to_s).to eq('Symbol') + end + + it 'understands nested alias namespaces resolving types' do + source = Solargraph::Source.load_string(%( + module Bar + module A + class Foo + # @return [Symbol] + def bing; :bingo; end + end + end + end + + module Bar + Foo = A::Foo + end + + a = Bar::Foo.new.bing + a + ), 'test.rb') + + api_map = described_class.new.map(source) + + clip = api_map.clip_at('test.rb', [15, 8]) + expect(clip.infer.to_s).to eq('Symbol') + end + + it 'understands includes using nested alias namespaces resolving types' do + source = Solargraph::Source.load_string(%( + module Foo + # @return [Symbol] + def bing; :yay; end + end + + module Bar + Baz = Foo + end + + class B + include Foo + end + + a = B.new.bing + a + ), 'test.rb') + + api_map = described_class.new.map(source) + + clip = api_map.clip_at('test.rb', [15, 8]) + expect(clip.infer.to_s).to eq('Symbol') + end + end + + describe '#get_method_stack' do + let(:out) { StringIO.new } + let(:api_map) { described_class.load_with_cache(Dir.pwd, out) } + + context 'with stdlib that has vital dependencies' do + let(:external_requires) { ['yaml'] } + let(:method_stack) { api_map.get_method_stack('YAML', 'safe_load', scope: :class) } + + it 'handles the YAML gem aliased to Psych' do + expect(method_stack).not_to be_empty + end + end + + context 'with thor' do + let(:external_requires) { ['thor'] } + let(:method_stack) { api_map.get_method_stack('Thor', 'desc', scope: :class) } + + it 'handles finding Thor.desc' do + expect(method_stack).not_to be_empty + end + end + end +end diff --git a/spec/api_map_spec.rb b/spec/api_map_spec.rb index 8dc6db842..a2abed682 100755 --- a/spec/api_map_spec.rb +++ b/spec/api_map_spec.rb @@ -817,4 +817,62 @@ def baz clip = api_map.clip_at('test.rb', [11, 10]) expect(clip.infer.to_s).to eq('Symbol') end + + it 'resolves aliases in identically named deeply nested classes' do + source = Solargraph::Source.load_string(%( + module A + module Bar + # @return [Integer] + def quux; 123; end + end + + Baz = Bar + + class Foo + include Baz + end + end + + def c + b = A::Foo.new.quux + b + end + ), 'test.rb') + + api_map = described_class.new.map(source) + + clip = api_map.clip_at('test.rb', [16, 4]) + expect(clip.infer.to_s).to eq('Integer') + end + + it 'resolves aliases in nested classes' do + source = Solargraph::Source.load_string(%( + module A + module Bar + class Baz + # @return [Integer] + def quux; 123; end + end + end + + Baz = Bar::Baz + + class Foo + include Baz + end + end + + def c + b = A::Foo.new.quux + b + end + ), 'test.rb') + + api_map = described_class.new.map(source) + + clip = api_map.clip_at('test.rb', [18, 4]) + expect(clip.infer.to_s).to eq('Integer') + end + + # rubocop:enable RSpec/InstanceVariable end From 92424261e3a17d242b91f5a51ad0dd7fd81ca98c Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 10 Aug 2025 17:54:42 -0400 Subject: [PATCH 513/561] spec_failed tweak --- Rakefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Rakefile b/Rakefile index 659a08ed5..d731fc786 100755 --- a/Rakefile +++ b/Rakefile @@ -85,7 +85,9 @@ end desc "Re-run failed specs. Add --fail-fast in your .rspec-local file if desired." task :spec_failed do - sh 'TEST_COVERAGE_COMMAND_NAME=next-failure bundle exec rspec --only-failures' + # allow user to check out any persistent failures while looking for + # more in the whole test suite + sh 'TEST_COVERAGE_COMMAND_NAME=next-failure bundle exec rspec --only-failures || true' end desc "Run undercover and show output without failing the task if it fails" From 95cf765ddc5dbbb965370b81618877e2d710273e Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 12 Aug 2025 14:43:08 -0400 Subject: [PATCH 514/561] Reduce load of overcommit --- Rakefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rakefile b/Rakefile index 5e6e799d2..2e3f2bed0 100755 --- a/Rakefile +++ b/Rakefile @@ -134,5 +134,5 @@ end desc "Show quality checks on this development branch so far, including any staged files" task :overcommit do # OVERCOMMIT_DEBUG=1 will show more detail - sh 'SOLARGRAPH_ASSERTS=on bundle exec overcommit --run --diff origin/master' + sh 'SOLARGRAPH_ASSERTS=on bundle exec overcommit --run --diff origin/2025-07-02' end From cf532b6158fde64097ad7776d3875bd0473c69eb Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 19 Aug 2025 11:51:07 -0400 Subject: [PATCH 515/561] Ratchet rubocop todo file --- .rubocop_todo.yml | 172 +--------------------------------------------- 1 file changed, 1 insertion(+), 171 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index f9686413c..9c61d760c 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -107,7 +107,6 @@ Layout/ElseAlignment: # Configuration parameters: EmptyLineBetweenMethodDefs, EmptyLineBetweenClassDefs, EmptyLineBetweenModuleDefs, DefLikeMacros, AllowAdjacentOneLineDefs, NumberOfEmptyLines. Layout/EmptyLineBetweenDefs: Exclude: - - 'lib/solargraph/doc_map.rb' - 'lib/solargraph/language_server/message/initialize.rb' - 'lib/solargraph/pin/delegated_method.rb' @@ -116,7 +115,6 @@ Layout/EmptyLines: Exclude: - 'lib/solargraph/bench.rb' - 'lib/solargraph/complex_type/unique_type.rb' - - 'lib/solargraph/doc_map.rb' - 'lib/solargraph/language_server/message/extended/check_gem_version.rb' - 'lib/solargraph/language_server/message/initialize.rb' - 'lib/solargraph/pin/delegated_method.rb' @@ -252,7 +250,6 @@ Layout/IndentationWidth: - 'lib/solargraph/parser/parser_gem/node_processors/block_node.rb' - 'lib/solargraph/pin/method.rb' - 'lib/solargraph/pin/namespace.rb' - - 'lib/solargraph/rbs_map.rb' - 'lib/solargraph/shell.rb' - 'lib/solargraph/source/chain/call.rb' - 'lib/solargraph/source_map/clip.rb' @@ -306,7 +303,6 @@ Layout/MultilineMethodCallIndentation: # SupportedStyles: aligned, indented Layout/MultilineOperationIndentation: Exclude: - - 'lib/solargraph/api_map.rb' - 'lib/solargraph/language_server/host/dispatch.rb' - 'lib/solargraph/source.rb' @@ -348,7 +344,6 @@ Layout/SpaceAroundOperators: - 'lib/solargraph/source_map/clip.rb' - 'lib/solargraph/workspace/config.rb' - 'spec/library_spec.rb' - - 'spec/yard_map/mapper_spec.rb' # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces. @@ -523,7 +518,6 @@ Lint/DuplicateMethods: - 'lib/solargraph/pin/base.rb' - 'lib/solargraph/pin/signature.rb' - 'lib/solargraph/rbs_map.rb' - - 'lib/solargraph/rbs_map/core_map.rb' - 'lib/solargraph/source/chain/link.rb' # Configuration parameters: AllowComments, AllowEmptyLambdas. @@ -544,6 +538,7 @@ Lint/EmptyClass: # Configuration parameters: AllowComments. Lint/EmptyFile: Exclude: + - '.pryrc' - 'spec/fixtures/vendored/vendor/do_not_use.gemspec' # This cop supports unsafe autocorrection (--autocorrect-all). @@ -644,7 +639,6 @@ Lint/UnusedMethodArgument: - 'lib/solargraph/convention/base.rb' - 'lib/solargraph/diagnostics/base.rb' - 'lib/solargraph/diagnostics/update_errors.rb' - - 'lib/solargraph/doc_map.rb' - 'lib/solargraph/pin/namespace.rb' - 'lib/solargraph/rbs_map/conversions.rb' - 'lib/solargraph/source.rb' @@ -664,12 +658,6 @@ Lint/UnusedMethodArgument: - 'lib/solargraph/source/chain/z_super.rb' - 'spec/doc_map_spec.rb' -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AutoCorrect, ContextCreatingMethods, MethodCreatingMethods. -Lint/UselessAccessModifier: - Exclude: - - 'lib/solargraph/api_map.rb' - # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AutoCorrect. Lint/UselessAssignment: @@ -714,7 +702,6 @@ Metrics/AbcSize: - 'lib/solargraph/api_map.rb' - 'lib/solargraph/api_map/source_to_yard.rb' - 'lib/solargraph/complex_type.rb' - - 'lib/solargraph/doc_map.rb' - 'lib/solargraph/language_server/host.rb' - 'lib/solargraph/language_server/message/initialize.rb' - 'lib/solargraph/library.rb' @@ -785,7 +772,6 @@ Metrics/ModuleLength: Exclude: - 'lib/solargraph/complex_type/type_methods.rb' - 'lib/solargraph/parser/parser_gem/node_methods.rb' - - 'lib/solargraph/pin_cache.rb' # Configuration parameters: Max, CountKeywordArgs, MaxOptionalParameters. Metrics/ParameterLists: @@ -832,7 +818,6 @@ Naming/MemoizedInstanceVariableName: - 'lib/solargraph/convention/gemfile.rb' - 'lib/solargraph/convention/gemspec.rb' - 'lib/solargraph/convention/rakefile.rb' - - 'lib/solargraph/doc_map.rb' - 'lib/solargraph/rbs_map.rb' - 'lib/solargraph/workspace.rb' @@ -886,7 +871,6 @@ Naming/VariableName: RSpec/Be: Exclude: - 'spec/rbs_map/stdlib_map_spec.rb' - - 'spec/rbs_map_spec.rb' - 'spec/source/source_chainer_spec.rb' # This cop supports unsafe autocorrection (--autocorrect-all). @@ -909,7 +893,6 @@ RSpec/BeforeAfterAll: - '**/spec/rails_helper.rb' - '**/spec/support/**/*.rb' - 'spec/api_map_spec.rb' - - 'spec/doc_map_spec.rb' - 'spec/language_server/host/dispatch_spec.rb' - 'spec/language_server/protocol_spec.rb' @@ -922,10 +905,6 @@ RSpec/ContextWording: - 'spec/pin/method_spec.rb' - 'spec/pin/parameter_spec.rb' - 'spec/pin/symbol_spec.rb' - - 'spec/type_checker/levels/normal_spec.rb' - - 'spec/type_checker/levels/strict_spec.rb' - - 'spec/type_checker/levels/strong_spec.rb' - - 'spec/type_checker/levels/typed_spec.rb' # Configuration parameters: IgnoredMetadata. RSpec/DescribeClass: @@ -946,7 +925,6 @@ RSpec/DescribedClass: - 'spec/api_map/cache_spec.rb' - 'spec/api_map/source_to_yard_spec.rb' - 'spec/api_map/store_spec.rb' - - 'spec/api_map_spec.rb' - 'spec/diagnostics/base_spec.rb' - 'spec/diagnostics/require_not_found_spec.rb' - 'spec/diagnostics/rubocop_helpers_spec.rb' @@ -955,7 +933,6 @@ RSpec/DescribedClass: - 'spec/diagnostics/update_errors_spec.rb' - 'spec/diagnostics_spec.rb' - 'spec/doc_map_spec.rb' - - 'spec/gem_pins_spec.rb' - 'spec/language_server/host/diagnoser_spec.rb' - 'spec/language_server/host/dispatch_spec.rb' - 'spec/language_server/host/message_worker_spec.rb' @@ -1101,7 +1078,6 @@ RSpec/ImplicitExpect: RSpec/InstanceVariable: Exclude: - 'spec/api_map/config_spec.rb' - - 'spec/api_map_spec.rb' - 'spec/diagnostics/require_not_found_spec.rb' - 'spec/language_server/host/dispatch_spec.rb' - 'spec/language_server/host_spec.rb' @@ -1140,8 +1116,6 @@ RSpec/MultipleExpectations: - 'spec/diagnostics/type_check_spec.rb' - 'spec/diagnostics/update_errors_spec.rb' - 'spec/diagnostics_spec.rb' - - 'spec/doc_map_spec.rb' - - 'spec/gem_pins_spec.rb' - 'spec/language_server/host/message_worker_spec.rb' - 'spec/language_server/host_spec.rb' - 'spec/language_server/message/completion_item/resolve_spec.rb' @@ -1168,7 +1142,6 @@ RSpec/MultipleExpectations: - 'spec/rbs_map/core_map_spec.rb' - 'spec/rbs_map/stdlib_map_spec.rb' - 'spec/rbs_map_spec.rb' - - 'spec/shell_spec.rb' - 'spec/source/chain/call_spec.rb' - 'spec/source/chain/class_variable_spec.rb' - 'spec/source/chain/global_variable_spec.rb' @@ -1215,7 +1188,6 @@ RSpec/NoExpectationExample: # SupportedStyles: not_to, to_not RSpec/NotToNot: Exclude: - - 'spec/api_map_spec.rb' - 'spec/rbs_map/core_map_spec.rb' RSpec/PendingWithoutReason: @@ -1251,7 +1223,6 @@ RSpec/RemoveConst: RSpec/RepeatedDescription: Exclude: - - 'spec/api_map_spec.rb' - 'spec/language_server/protocol_spec.rb' - 'spec/parser/node_methods_spec.rb' - 'spec/source/chain/call_spec.rb' @@ -1275,107 +1246,6 @@ RSpec/ScatteredLet: Exclude: - 'spec/complex_type_spec.rb' -# Configuration parameters: Include, CustomTransform, IgnoreMethods, IgnoreMetadata. -# Include: **/*_spec.rb -RSpec/SpecFilePathFormat: - Exclude: - - '**/spec/routing/**/*' - - 'spec/api_map/cache_spec.rb' - - 'spec/api_map/config_spec.rb' - - 'spec/api_map/source_to_yard_spec.rb' - - 'spec/api_map/store_spec.rb' - - 'spec/api_map_spec.rb' - - 'spec/convention/activesupport_concern_spec.rb' - - 'spec/convention/struct_definition_spec.rb' - - 'spec/convention_spec.rb' - - 'spec/diagnostics/base_spec.rb' - - 'spec/diagnostics/require_not_found_spec.rb' - - 'spec/diagnostics/rubocop_helpers_spec.rb' - - 'spec/diagnostics/rubocop_spec.rb' - - 'spec/diagnostics/type_check_spec.rb' - - 'spec/diagnostics/update_errors_spec.rb' - - 'spec/diagnostics_spec.rb' - - 'spec/doc_map_spec.rb' - - 'spec/gem_pins_spec.rb' - - 'spec/language_server/host/diagnoser_spec.rb' - - 'spec/language_server/host/dispatch_spec.rb' - - 'spec/language_server/host/message_worker_spec.rb' - - 'spec/language_server/host_spec.rb' - - 'spec/language_server/message/completion_item/resolve_spec.rb' - - 'spec/language_server/message/extended/check_gem_version_spec.rb' - - 'spec/language_server/message/initialize_spec.rb' - - 'spec/language_server/message/text_document/definition_spec.rb' - - 'spec/language_server/message/text_document/formatting_spec.rb' - - 'spec/language_server/message/text_document/hover_spec.rb' - - 'spec/language_server/message/text_document/rename_spec.rb' - - 'spec/language_server/message/text_document/type_definition_spec.rb' - - 'spec/language_server/message/workspace/did_change_configuration_spec.rb' - - 'spec/language_server/message/workspace/did_change_watched_files_spec.rb' - - 'spec/language_server/message_spec.rb' - - 'spec/language_server/transport/adapter_spec.rb' - - 'spec/language_server/transport/data_reader_spec.rb' - - 'spec/language_server/uri_helpers_spec.rb' - - 'spec/library_spec.rb' - - 'spec/logging_spec.rb' - - 'spec/parser/flow_sensitive_typing_spec.rb' - - 'spec/parser/node_methods_spec.rb' - - 'spec/parser/node_processor_spec.rb' - - 'spec/parser_spec.rb' - - 'spec/pin/base_spec.rb' - - 'spec/pin/base_variable_spec.rb' - - 'spec/pin/block_spec.rb' - - 'spec/pin/constant_spec.rb' - - 'spec/pin/delegated_method_spec.rb' - - 'spec/pin/documenting_spec.rb' - - 'spec/pin/instance_variable_spec.rb' - - 'spec/pin/keyword_spec.rb' - - 'spec/pin/local_variable_spec.rb' - - 'spec/pin/method_spec.rb' - - 'spec/pin/namespace_spec.rb' - - 'spec/pin/parameter_spec.rb' - - 'spec/pin/search_spec.rb' - - 'spec/pin/symbol_spec.rb' - - 'spec/position_spec.rb' - - 'spec/rbs_map/conversions_spec.rb' - - 'spec/rbs_map/core_map_spec.rb' - - 'spec/rbs_map/stdlib_map_spec.rb' - - 'spec/rbs_map_spec.rb' - - 'spec/shell_spec.rb' - - 'spec/source/chain/array_spec.rb' - - 'spec/source/chain/call_spec.rb' - - 'spec/source/chain/class_variable_spec.rb' - - 'spec/source/chain/constant_spec.rb' - - 'spec/source/chain/global_variable_spec.rb' - - 'spec/source/chain/head_spec.rb' - - 'spec/source/chain/instance_variable_spec.rb' - - 'spec/source/chain/link_spec.rb' - - 'spec/source/chain/literal_spec.rb' - - 'spec/source/chain/z_super_spec.rb' - - 'spec/source/chain_spec.rb' - - 'spec/source/change_spec.rb' - - 'spec/source/cursor_spec.rb' - - 'spec/source/source_chainer_spec.rb' - - 'spec/source/updater_spec.rb' - - 'spec/source_map/clip_spec.rb' - - 'spec/source_map/mapper_spec.rb' - - 'spec/source_map_spec.rb' - - 'spec/source_spec.rb' - - 'spec/type_checker/checks_spec.rb' - - 'spec/type_checker/levels/normal_spec.rb' - - 'spec/type_checker/levels/strict_spec.rb' - - 'spec/type_checker/levels/strong_spec.rb' - - 'spec/type_checker/levels/typed_spec.rb' - - 'spec/type_checker/rules_spec.rb' - - 'spec/type_checker_spec.rb' - - 'spec/workspace/config_spec.rb' - - 'spec/workspace_spec.rb' - - 'spec/yard_map/mapper/to_method_spec.rb' - - 'spec/yard_map/mapper_spec.rb' - -RSpec/StubbedMock: - Exclude: - - 'spec/language_server/host/message_worker_spec.rb' - # Configuration parameters: IgnoreNameless, IgnoreSymbolicNames. RSpec/VerifiedDoubles: Exclude: @@ -1774,7 +1644,6 @@ Style/FrozenStringLiteralComment: - 'spec/api_map/cache_spec.rb' - 'spec/api_map/config_spec.rb' - 'spec/api_map/source_to_yard_spec.rb' - - 'spec/api_map_spec.rb' - 'spec/complex_type_spec.rb' - 'spec/convention/struct_definition_spec.rb' - 'spec/convention_spec.rb' @@ -1846,7 +1715,6 @@ Style/FrozenStringLiteralComment: - 'spec/rbs_map/core_map_spec.rb' - 'spec/rbs_map/stdlib_map_spec.rb' - 'spec/rbs_map_spec.rb' - - 'spec/shell_spec.rb' - 'spec/source/chain/array_spec.rb' - 'spec/source/chain/call_spec.rb' - 'spec/source/chain/class_variable_spec.rb' @@ -1895,7 +1763,6 @@ Style/GuardClause: - 'lib/solargraph/api_map.rb' - 'lib/solargraph/library.rb' - 'lib/solargraph/parser/parser_gem/node_processors/send_node.rb' - - 'lib/solargraph/pin_cache.rb' - 'lib/solargraph/range.rb' - 'lib/solargraph/rbs_map/conversions.rb' - 'lib/solargraph/source.rb' @@ -1945,17 +1812,12 @@ Style/IfInsideElse: # This cop supports safe autocorrection (--autocorrect). Style/IfUnlessModifier: Exclude: - - 'lib/solargraph/api_map.rb' - - 'lib/solargraph/api_map/index.rb' - 'lib/solargraph/complex_type.rb' - 'lib/solargraph/complex_type/unique_type.rb' - - 'lib/solargraph/doc_map.rb' - 'lib/solargraph/language_server/message/completion_item/resolve.rb' - 'lib/solargraph/language_server/message/initialize.rb' - 'lib/solargraph/language_server/message/text_document/completion.rb' - 'lib/solargraph/language_server/message/text_document/hover.rb' - - 'lib/solargraph/library.rb' - - 'lib/solargraph/parser/parser_gem/class_methods.rb' - 'lib/solargraph/parser/parser_gem/node_chainer.rb' - 'lib/solargraph/parser/parser_gem/node_methods.rb' - 'lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb' @@ -1976,7 +1838,6 @@ Style/IfUnlessModifier: - 'lib/solargraph/source_map/clip.rb' - 'lib/solargraph/source_map/mapper.rb' - 'lib/solargraph/type_checker.rb' - - 'lib/solargraph/workspace.rb' - 'lib/solargraph/workspace/config.rb' - 'lib/solargraph/yard_map/helpers.rb' - 'lib/solargraph/yard_map/mapper/to_method.rb' @@ -2026,7 +1887,6 @@ Style/MethodDefParentheses: - 'lib/solargraph/convention/struct_definition/struct_assignment_node.rb' - 'lib/solargraph/convention/struct_definition/struct_definition_node.rb' - 'lib/solargraph/diagnostics/rubocop_helpers.rb' - - 'lib/solargraph/doc_map.rb' - 'lib/solargraph/equality.rb' - 'lib/solargraph/gem_pins.rb' - 'lib/solargraph/language_server/host/message_worker.rb' @@ -2063,7 +1923,6 @@ Style/MethodDefParentheses: - 'lib/solargraph/type_checker.rb' - 'lib/solargraph/type_checker/checks.rb' - 'lib/solargraph/yard_map/helpers.rb' - - 'lib/solargraph/yardoc.rb' - 'spec/doc_map_spec.rb' - 'spec/fixtures/rdoc-lib/lib/example.rb' - 'spec/source_map_spec.rb' @@ -2071,7 +1930,6 @@ Style/MethodDefParentheses: - 'spec/type_checker/levels/normal_spec.rb' - 'spec/type_checker/levels/strict_spec.rb' - 'spec/type_checker/levels/strong_spec.rb' - - 'spec/type_checker/levels/typed_spec.rb' Style/MultilineBlockChain: Exclude: @@ -2130,13 +1988,6 @@ Style/NegatedIfElseCondition: - 'lib/solargraph/shell.rb' - 'lib/solargraph/type_checker.rb' -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AllowedMethods. -# AllowedMethods: be, be_a, be_an, be_between, be_falsey, be_kind_of, be_instance_of, be_truthy, be_within, eq, eql, end_with, include, match, raise_error, respond_to, start_with -Style/NestedParenthesizedCalls: - Exclude: - - 'lib/solargraph/type_checker.rb' - # This cop supports safe autocorrection (--autocorrect). Style/NestedTernaryOperator: Exclude: @@ -2232,7 +2083,6 @@ Style/RedundantBegin: - 'lib/solargraph/shell.rb' - 'lib/solargraph/source/cursor.rb' - 'lib/solargraph/source/encoding_fixes.rb' - - 'lib/solargraph/type_checker.rb' - 'lib/solargraph/workspace.rb' # This cop supports safe autocorrection (--autocorrect). @@ -2246,12 +2096,6 @@ Style/RedundantFreeze: - 'lib/solargraph/complex_type.rb' - 'lib/solargraph/source_map/mapper.rb' -# This cop supports unsafe autocorrection (--autocorrect-all). -# Configuration parameters: AutoCorrect, AllowComments. -Style/RedundantInitialize: - Exclude: - - 'lib/solargraph/rbs_map/core_map.rb' - # This cop supports unsafe autocorrection (--autocorrect-all). Style/RedundantInterpolation: Exclude: @@ -2295,7 +2139,6 @@ Style/RedundantReturn: Exclude: - 'lib/solargraph/api_map.rb' - 'lib/solargraph/complex_type/type_methods.rb' - - 'lib/solargraph/doc_map.rb' - 'lib/solargraph/parser/parser_gem/node_methods.rb' - 'lib/solargraph/source/chain/z_super.rb' @@ -2332,7 +2175,6 @@ Style/RescueStandardError: Style/SafeNavigation: Exclude: - 'lib/solargraph/api_map/index.rb' - - 'lib/solargraph/doc_map.rb' - 'lib/solargraph/language_server/message/completion_item/resolve.rb' - 'lib/solargraph/language_server/request.rb' - 'lib/solargraph/language_server/transport/data_reader.rb' @@ -2340,15 +2182,9 @@ Style/SafeNavigation: - 'lib/solargraph/pin/base.rb' - 'lib/solargraph/pin/conversions.rb' - 'lib/solargraph/pin/method.rb' - - 'lib/solargraph/pin_cache.rb' - 'lib/solargraph/range.rb' - 'lib/solargraph/type_checker.rb' -# Configuration parameters: Max. -Style/SafeNavigationChainLength: - Exclude: - - 'lib/solargraph/doc_map.rb' - # This cop supports unsafe autocorrection (--autocorrect-all). Style/SlicingWithRange: Exclude: @@ -2413,7 +2249,6 @@ Style/StringLiterals: - 'lib/solargraph/complex_type.rb' - 'lib/solargraph/complex_type/unique_type.rb' - 'lib/solargraph/convention/struct_definition.rb' - - 'lib/solargraph/doc_map.rb' - 'lib/solargraph/language_server/host.rb' - 'lib/solargraph/language_server/message/extended/document_gems.rb' - 'lib/solargraph/language_server/message/extended/download_core.rb' @@ -2648,7 +2483,6 @@ YARD/MismatchName: Exclude: - 'lib/solargraph/complex_type.rb' - 'lib/solargraph/complex_type/unique_type.rb' - - 'lib/solargraph/gem_pins.rb' - 'lib/solargraph/language_server/host.rb' - 'lib/solargraph/language_server/host/dispatch.rb' - 'lib/solargraph/language_server/request.rb' @@ -2691,7 +2525,6 @@ Layout/LineLength: - 'lib/solargraph/complex_type.rb' - 'lib/solargraph/complex_type/unique_type.rb' - 'lib/solargraph/convention/data_definition.rb' - - 'lib/solargraph/doc_map.rb' - 'lib/solargraph/gem_pins.rb' - 'lib/solargraph/language_server/host.rb' - 'lib/solargraph/language_server/message/extended/check_gem_version.rb' @@ -2736,10 +2569,8 @@ Layout/LineLength: - 'lib/solargraph/source_map/clip.rb' - 'lib/solargraph/source_map/mapper.rb' - 'lib/solargraph/type_checker.rb' - - 'lib/solargraph/workspace.rb' - 'lib/solargraph/workspace/config.rb' - 'lib/solargraph/yard_map/mapper/to_method.rb' - - 'spec/api_map_spec.rb' - 'spec/complex_type_spec.rb' - 'spec/language_server/message/completion_item/resolve_spec.rb' - 'spec/language_server/message/extended/check_gem_version_spec.rb' @@ -2749,4 +2580,3 @@ Layout/LineLength: - 'spec/source/chain_spec.rb' - 'spec/source_map/clip_spec.rb' - 'spec/source_map_spec.rb' - - 'spec/workspace_spec.rb' From 07e8fde1778744630d5df052ff8525f885f98978 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 19 Aug 2025 11:56:50 -0400 Subject: [PATCH 516/561] Bump pin caching --- spec/library_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/library_spec.rb b/spec/library_spec.rb index 7d2a2fef7..0cf6084e9 100644 --- a/spec/library_spec.rb +++ b/spec/library_spec.rb @@ -44,7 +44,7 @@ def foo(adapter) end ), 'file.rb', 0) completion = nil - Timeout.timeout(20) do + Timeout.timeout(25) do # give Solargraph time to cache the gem while (completion = library.completions_at('file.rb', 5, 19)).pins.empty? sleep 0.25 From 30c2831cc0efa80c38d6d5c7da4ed19ef8618fa0 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 19 Aug 2025 12:16:21 -0400 Subject: [PATCH 517/561] Drop .pryrc from file --- .rubocop_todo.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 9c61d760c..bfdba89a7 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -538,7 +538,6 @@ Lint/EmptyClass: # Configuration parameters: AllowComments. Lint/EmptyFile: Exclude: - - '.pryrc' - 'spec/fixtures/vendored/vendor/do_not_use.gemspec' # This cop supports unsafe autocorrection (--autocorrect-all). From c208f5c99d7e12d5c778d3290a5ee32c9f905b1b Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 19 Aug 2025 12:31:37 -0400 Subject: [PATCH 518/561] Drop dead code --- lib/solargraph/pin_cache.rb | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/lib/solargraph/pin_cache.rb b/lib/solargraph/pin_cache.rb index 2c4649cda..0f49290d3 100644 --- a/lib/solargraph/pin_cache.rb +++ b/lib/solargraph/pin_cache.rb @@ -639,18 +639,6 @@ def core? def cache_core out: $stderr RbsMap::CoreMap.new.cache_core(out: out) end - - # @return [Array, nil] - def load_core - load(core_path) - end - - private - - # @param path [String] - def exist? *path - File.file? File.join(*path) - end end end end From eb25912754e69f6dc9258a1f80e5cdae3bd02411 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 19 Aug 2025 13:22:15 -0400 Subject: [PATCH 519/561] Debug --- spec/pin_cache_spec.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/pin_cache_spec.rb b/spec/pin_cache_spec.rb index 96023fa1f..41434e1f4 100644 --- a/spec/pin_cache_spec.rb +++ b/spec/pin_cache_spec.rb @@ -88,7 +88,7 @@ parser_gemspec = Gem::Specification.find_by_name('parser') pin_cache.cache_gem(gemspec: parser_gemspec, out: nil) # if this fails, you may not have run `bundle exec rbs collection update` - expect(Solargraph::Yardoc).not_to have_received(:build_docs) + expect(Solargraph::Yardoc).not_to have_received(:build_docs).with(any_args) end end @@ -101,7 +101,7 @@ parser_gemspec = Gem::Specification.find_by_name('parser') pin_cache.cache_gem(gemspec: parser_gemspec, rebuild: true, out: nil) # if this fails, you may not have run `bundle exec rbs collection update` - expect(Solargraph::Yardoc).not_to have_received(:build_docs) + expect(Solargraph::Yardoc).not_to have_received(:build_docs).with(any_args) end end @@ -137,7 +137,7 @@ pin_cache.cache_gem(gemspec: yaml_gemspec, out: nil) # match arguments with regexp using rspec-matchers syntax - expect(File).to have_received(:write).with(%r{combined/base64-.*-export.ser$}, any_args).once + expect(File).to have_received(:write).with(%r{combined/base64-.*-export.ser$}, any_args, mode: 'wb').once end end end From 81f6b1c7cb880add7fbfda17d08d448c9b01dce9 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 19 Aug 2025 14:00:16 -0400 Subject: [PATCH 520/561] Ensure rbs collection update is run in rspec tests --- .github/workflows/rspec.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/rspec.yml b/.github/workflows/rspec.yml index 37068f0d7..44a008f8c 100644 --- a/.github/workflows/rspec.yml +++ b/.github/workflows/rspec.yml @@ -47,7 +47,8 @@ jobs: - name: Install gems run: | bundle install - bundle update rbs # use latest available for this Ruby version + - name: Install types + run: bundle exec rbs collection update - name: Run tests run: bundle exec rake spec undercover: From dbacbe51bd65efef83f6c9e52af5454c54aa0e18 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 19 Aug 2025 14:06:01 -0400 Subject: [PATCH 521/561] Exclude a test combination --- .github/workflows/rspec.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/rspec.yml b/.github/workflows/rspec.yml index 44a008f8c..e97224f7a 100644 --- a/.github/workflows/rspec.yml +++ b/.github/workflows/rspec.yml @@ -23,12 +23,15 @@ jobs: matrix: ruby-version: ['3.0', '3.1', '3.2', '3.3', '3.4', 'head'] rbs-version: ['3.6.1', '3.9.4', '4.0.0.dev.4'] - # Ruby 3.0 doesn't work with RBS 3.9.4 or 4.0.0.dev.4 exclude: + # Ruby 3.0 doesn't work with RBS 3.9.4 or 4.0.0.dev.4 - ruby-version: '3.0' rbs-version: '3.9.4' - ruby-version: '3.0' rbs-version: '4.0.0.dev.4' + # Missing require in 'rbs collection update' + - ruby-version: 'head' + rbs-version: '4.0.0.dev.4' steps: - uses: actions/checkout@v3 - name: Set up Ruby From 09062ceca6a9398d12316346c7d4a4d03858d8b6 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 19 Aug 2025 14:26:40 -0400 Subject: [PATCH 522/561] Update excluded scenarios --- .github/workflows/rspec.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/rspec.yml b/.github/workflows/rspec.yml index e97224f7a..a025d5eee 100644 --- a/.github/workflows/rspec.yml +++ b/.github/workflows/rspec.yml @@ -29,9 +29,14 @@ jobs: rbs-version: '3.9.4' - ruby-version: '3.0' rbs-version: '4.0.0.dev.4' - # Missing require in 'rbs collection update' + # Missing require in 'rbs collection update' - hopefully + # fixed in next RBS release - ruby-version: 'head' rbs-version: '4.0.0.dev.4' + - ruby-version: 'head' + rbs-version: '3.9.4' + - ruby-version: 'head' + rbs-version: '3.6.1' steps: - uses: actions/checkout@v3 - name: Set up Ruby From 35ff86b2486ff64c9c794d37ece9cc33afb0e3b0 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 19 Aug 2025 15:53:35 -0400 Subject: [PATCH 523/561] Fix missing coverage --- lib/solargraph/pin_cache.rb | 10 ---------- spec/pin_cache_spec.rb | 15 ++++++++++++++- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/lib/solargraph/pin_cache.rb b/lib/solargraph/pin_cache.rb index 0f49290d3..b9f384438 100644 --- a/lib/solargraph/pin_cache.rb +++ b/lib/solargraph/pin_cache.rb @@ -1,4 +1,3 @@ -require 'yard-activesupport-concern' require 'fileutils' require 'rbs' require 'rubygems' @@ -466,15 +465,6 @@ def work_dir File.join(base_dir, "ruby-#{RUBY_VERSION}", "rbs-#{RBS::VERSION}", "solargraph-#{Solargraph::VERSION}") end - # @param gemspec [Gem::Specification] - # @return [String] - def yardoc_path gemspec - File.join(base_dir, - "yard-#{YARD::VERSION}", - "yard-activesupport-concern-#{YARD::ActiveSupport::Concern::VERSION}", - "#{gemspec.name}-#{gemspec.version}.yardoc") - end - # @return [String] def stdlib_path File.join(work_dir, 'stdlib') diff --git a/spec/pin_cache_spec.rb b/spec/pin_cache_spec.rb index 41434e1f4..3370f1fd1 100644 --- a/spec/pin_cache_spec.rb +++ b/spec/pin_cache_spec.rb @@ -8,7 +8,7 @@ described_class.new(rbs_collection_path: '.gem_rbs_collection', rbs_collection_config_path: 'rbs_collection.yaml', directory: Dir.pwd, - yard_plugins: []) + yard_plugins: ['activesupport-concern']) end describe '#cached?' do @@ -92,6 +92,19 @@ end end + context 'with an installed gem' do + before do + Solargraph::Shell.new.gems('kramdown') + end + + it 'uncaches when asked' do + gemspec = Gem::Specification.find_by_name('kramdown') + expect do + pin_cache.uncache_gem(gemspec, out: nil) + end.not_to raise_error + end + end + context 'with the rebuild flag' do before do allow(Solargraph::Yardoc).to receive(:build_docs) From 542bcb78969f60d27d4f3cce899fd4041d9c407d Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 19 Aug 2025 16:13:12 -0400 Subject: [PATCH 524/561] Fix spec --- spec/pin_cache_spec.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/pin_cache_spec.rb b/spec/pin_cache_spec.rb index 3370f1fd1..0a11686f5 100644 --- a/spec/pin_cache_spec.rb +++ b/spec/pin_cache_spec.rb @@ -14,7 +14,7 @@ describe '#cached?' do it 'returns true for a gem that is cached' do allow(File).to receive(:file?).with(%r{.*stdlib/backport.ser$}).and_return(false) - allow(File).to receive(:file?).with(%r{.*combined/backport-.*.ser$}).and_return(true) + allow(File).to receive(:file?).with(%r{.*combined/.*/backport-.*.ser$}).and_return(true) gemspec = Gem::Specification.find_by_name('backport') expect(pin_cache.cached?(gemspec)).to be true @@ -132,7 +132,7 @@ pin_cache.cache_gem(gemspec: yaml_gemspec, out: nil) # match arguments with regexp using rspec-matchers syntax - expect(File).to have_received(:write).with(%r{combined/logger-.*-stdlib.ser$}, any_args).once + expect(File).to have_received(:write).with(%r{combined/.*/logger-.*-stdlib.ser$}, any_args).once end end @@ -150,7 +150,7 @@ pin_cache.cache_gem(gemspec: yaml_gemspec, out: nil) # match arguments with regexp using rspec-matchers syntax - expect(File).to have_received(:write).with(%r{combined/base64-.*-export.ser$}, any_args, mode: 'wb').once + expect(File).to have_received(:write).with(%r{combined/.*/base64-.*-export.ser$}, any_args, mode: 'wb').once end end end From b0f05177b0b13fcf61f81dac2d2a6004b714c7bc Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 19 Aug 2025 17:24:29 -0400 Subject: [PATCH 525/561] Debug --- .github/workflows/plugins.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index b0f22ec3e..29baec75f 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -29,14 +29,15 @@ jobs: echo 'gem "solargraph-rails"' > .Gemfile echo 'gem "solargraph-rspec"' >> .Gemfile bundle install + bundle update rbs - name: Configure to use plugins run: | bundle exec solargraph config yq -yi '.plugins += ["solargraph-rails"]' .solargraph.yml yq -yi '.plugins += ["solargraph-rspec"]' .solargraph.yml - name: Install gem types - run: bundle exec rbs collection install + run: bundle exec rbs collection update - name: Ensure typechecking still works run: bundle exec solargraph typecheck --level typed - name: Ensure specs still run - run: bundle exec rake spec + run: bundle exec rspec From 68b294a1f2c26ac013ea7585ba6b27825fae03e4 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 19 Aug 2025 17:24:54 -0400 Subject: [PATCH 526/561] Debug --- .github/workflows/plugins.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index 29baec75f..3afb0bb44 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -40,4 +40,4 @@ jobs: - name: Ensure typechecking still works run: bundle exec solargraph typecheck --level typed - name: Ensure specs still run - run: bundle exec rspec + run: bundle exec rspec --fail-fast From c2689739e511b25cdfa807369eb72e736b8aa8a9 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 20 Aug 2025 09:10:25 -0400 Subject: [PATCH 527/561] Fix rspec integration --- spec/doc_map_spec.rb | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/spec/doc_map_spec.rb b/spec/doc_map_spec.rb index fe06277eb..2ea86e7f9 100644 --- a/spec/doc_map_spec.rb +++ b/spec/doc_map_spec.rb @@ -39,7 +39,18 @@ end it 'tracks unresolved requires' do - expect(doc_map.unresolved_requires).to eq(['not_a_gem']) + # These are auto-required by solargraph-rspec in case the bundle + # includes these gems. In our case, it doesn't! + unprovided_solargraph_rspec_requires = [ + 'rspec-rails', + 'actionmailer', + 'activerecord', + 'shoulda-matchers', + 'rspec-sidekiq', + 'airborne' + ] + expect(doc_map.unresolved_requires - unprovided_solargraph_rspec_requires) + .to eq(['not_a_gem']) end end From 6abaefc3ea1e03eb51604baaf0e790c434be23ae Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 20 Aug 2025 10:03:44 -0400 Subject: [PATCH 528/561] Another solargraph-rspec fix --- spec/doc_map_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/doc_map_spec.rb b/spec/doc_map_spec.rb index 2ea86e7f9..4c9bd3de7 100644 --- a/spec/doc_map_spec.rb +++ b/spec/doc_map_spec.rb @@ -81,9 +81,9 @@ it 'tracks uncached_gemspecs' do pincache = instance_double(Solargraph::PinCache) uncached_gemspec = Gem::Specification.new('uncached_gem', '1.0.0') + allow(workspace).to receive_messages(resolve_require: [], fresh_pincache: pincache) allow(workspace).to receive(:resolve_require).with('uncached_gem').and_return([uncached_gemspec]) allow(workspace).to receive(:fetch_dependencies).with(uncached_gemspec, out: out).and_return([]) - allow(workspace).to receive(:fresh_pincache).and_return(pincache) allow(pincache).to receive(:deserialize_combined_pin_cache).with(uncached_gemspec).and_return(nil) expect(doc_map.uncached_gemspecs).to eq([uncached_gemspec]) end From 98ec2eb7266fe81e42804366f612531251e89598 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 20 Aug 2025 11:44:35 -0400 Subject: [PATCH 529/561] Handle BundleNotFoundError --- lib/solargraph/workspace/gemspecs.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/solargraph/workspace/gemspecs.rb b/lib/solargraph/workspace/gemspecs.rb index ffe6776d9..9777355b1 100644 --- a/lib/solargraph/workspace/gemspecs.rb +++ b/lib/solargraph/workspace/gemspecs.rb @@ -323,6 +323,10 @@ def all_gemspecs_from_external_bundle query_external_bundle(command).map do |name, version| resolve_gem_ignoring_local_bundle(name, version) end.compact + rescue Solargraph::BundleNotFoundError => e + Solargraph.logger.info e.message + Solargraph.logger.debug e.backtrace.join("\n") + [] end end From 2e3b867756c727dd0dde74d4a65a4c165338ea07 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 20 Aug 2025 12:10:57 -0400 Subject: [PATCH 530/561] Use rspec-time-guard to debug slow specs --- solargraph.gemspec | 1 + spec/library_spec.rb | 11 ++++------- spec/spec_helper.rb | 7 +++++++ 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/solargraph.gemspec b/solargraph.gemspec index e6bb9394a..56dd78e48 100755 --- a/solargraph.gemspec +++ b/solargraph.gemspec @@ -48,6 +48,7 @@ Gem::Specification.new do |s| s.add_development_dependency 'public_suffix', '~> 3.1' s.add_development_dependency 'rake', '~> 13.2' s.add_development_dependency 'rspec', '~> 3.5' + s.add_development_dependency 'rspec-time-guard', '~> 0.2.0' s.add_development_dependency 'rubocop-rake', '~> 0.7' s.add_development_dependency 'rubocop-rspec', '~> 3.6' s.add_development_dependency 'rubocop-yard', '~> 1.0' diff --git a/spec/library_spec.rb b/spec/library_spec.rb index 0cf6084e9..63b36d82a 100644 --- a/spec/library_spec.rb +++ b/spec/library_spec.rb @@ -1,6 +1,5 @@ require 'tmpdir' require 'yard' -require 'timeout' describe Solargraph::Library do it "does not open created files in the workspace" do @@ -32,7 +31,7 @@ Solargraph::Shell.new.uncache('backport') end - it "returns a Completion" do + it "returns a Completion", time_limit_seconds: 50 do library = Solargraph::Library.new(Solargraph::Workspace.new(Dir.pwd, Solargraph::Workspace::Config.new)) library.attach Solargraph::Source.load_string(%( @@ -44,11 +43,9 @@ def foo(adapter) end ), 'file.rb', 0) completion = nil - Timeout.timeout(25) do - # give Solargraph time to cache the gem - while (completion = library.completions_at('file.rb', 5, 19)).pins.empty? - sleep 0.25 - end + # give Solargraph time to cache the gem + while (completion = library.completions_at('file.rb', 5, 19)).pins.empty? + sleep 0.25 end expect(completion).to be_a(Solargraph::SourceMap::Completion) expect(completion.pins.map(&:name)).to include('remote') diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index bdc8be36a..097aca550 100755 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,5 +1,6 @@ require 'bundler/setup' require 'webmock/rspec' +require 'rspec_time_guard' WebMock.disable_net_connect!(allow_localhost: true) unless ENV['SIMPLECOV_DISABLED'] # set up lcov reporting for undercover @@ -27,6 +28,12 @@ # Allow use of --only-failures with rspec, handy for local development c.example_status_persistence_file_path = 'rspec-examples.txt' end +RspecTimeGuard.setup +RspecTimeGuard.configure do |config| + config.global_time_limit_seconds = 60 + config.continue_on_timeout = false +end + require 'solargraph' # execute any logging blocks to make sure they don't blow up Solargraph::Logging.logger.sev_threshold = Logger::DEBUG From d20e1d96272b5aa8813cb3ae228db32ad0de07f8 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 20 Aug 2025 12:33:10 -0400 Subject: [PATCH 531/561] Fix spec glitches --- spec/api_map/api_map_method_spec.rb | 12 ++++++++---- .../message/text_document/definition_spec.rb | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/spec/api_map/api_map_method_spec.rb b/spec/api_map/api_map_method_spec.rb index 73fc6a35c..704a67431 100644 --- a/spec/api_map/api_map_method_spec.rb +++ b/spec/api_map/api_map_method_spec.rb @@ -1,12 +1,16 @@ # frozen_string_literal: true +require 'tmpdir' + describe Solargraph::ApiMap do describe '.load_with_cache' do - it 'loads the API map with cache' do + it 'loads the API map with cache', time_limit_seconds: 120 do Solargraph::PinCache.uncache_core - output = capture_both do - described_class.load_with_cache(Dir.pwd) + output = Dir.mktmpdir do |dir| + capture_both do + described_class.load_with_cache(dir) + end end expect(output).to include('aching RBS pins for Ruby core') @@ -15,7 +19,7 @@ describe '#get_method_stack' do let(:out) { StringIO.new } - let(:api_map) { described_class.load_with_cache(Dir.pwd, out: out) } + let(:api_map) { described_class.load_with_cache(Dir.pwd, out) } context 'with stdlib that has vital dependencies' do let(:method_stack) { api_map.get_method_stack('YAML', 'safe_load', scope: :class) } diff --git a/spec/language_server/message/text_document/definition_spec.rb b/spec/language_server/message/text_document/definition_spec.rb index 72ff77f1e..beebb48de 100644 --- a/spec/language_server/message/text_document/definition_spec.rb +++ b/spec/language_server/message/text_document/definition_spec.rb @@ -21,7 +21,7 @@ expect(message.result.first[:uri]).to eq(other_uri) end - it 'finds definitions of require paths' do + it 'finds definitions of require paths', time_limit_seconds: 120 do path = File.absolute_path('spec/fixtures/workspace') host = Solargraph::LanguageServer::Host.new host.prepare(path) From 629ee5bdc1329764ea6f3d97db919ad1feb13bad Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 20 Aug 2025 12:44:08 -0400 Subject: [PATCH 532/561] Bump timeout on spec --- spec/api_map/api_map_method_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/api_map/api_map_method_spec.rb b/spec/api_map/api_map_method_spec.rb index 704a67431..8c38b808b 100644 --- a/spec/api_map/api_map_method_spec.rb +++ b/spec/api_map/api_map_method_spec.rb @@ -17,7 +17,7 @@ end end - describe '#get_method_stack' do + describe '#get_method_stack', time_limit_seconds: 120 do let(:out) { StringIO.new } let(:api_map) { described_class.load_with_cache(Dir.pwd, out) } From 2671de56aedad453feeda6745864ba42666be478 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 20 Aug 2025 13:40:22 -0400 Subject: [PATCH 533/561] Avoid using operator- on Gem::Specification https://github.com/castwide/solargraph/actions/runs/17104765774/job/48510666106?pr=1006 --- lib/solargraph/doc_map.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/solargraph/doc_map.rb b/lib/solargraph/doc_map.rb index 81e92b559..fecf94d5d 100644 --- a/lib/solargraph/doc_map.rb +++ b/lib/solargraph/doc_map.rb @@ -89,7 +89,12 @@ def unresolved_requires # @return [Set] # @param out [IO] def dependencies out: $stderr - @dependencies ||= (gemspecs.flat_map { |spec| workspace.fetch_dependencies(spec, out: out) } - gemspecs).to_set + @dependencies ||= + begin + all_deps = gemspecs.flat_map { |spec| workspace.fetch_dependencies(spec, out: out) } + existing_gems = gemspecs.map(&:name) + all_deps.reject { |gemspec| existing_gems.include? gemspec.name } + end end # Cache gem documentation if needed for this doc_map From d118208dadb2eef3295566dfc430b84412b9635b Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 20 Aug 2025 13:45:59 -0400 Subject: [PATCH 534/561] Fix typechecking issue --- lib/solargraph/doc_map.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/solargraph/doc_map.rb b/lib/solargraph/doc_map.rb index fecf94d5d..d9d64b5b0 100644 --- a/lib/solargraph/doc_map.rb +++ b/lib/solargraph/doc_map.rb @@ -93,7 +93,7 @@ def dependencies out: $stderr begin all_deps = gemspecs.flat_map { |spec| workspace.fetch_dependencies(spec, out: out) } existing_gems = gemspecs.map(&:name) - all_deps.reject { |gemspec| existing_gems.include? gemspec.name } + all_deps.reject { |gemspec| existing_gems.include? gemspec.name }.to_set end end From d16773100be1f56a24133e935e14d96f744da1cf Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 20 Aug 2025 14:27:10 -0400 Subject: [PATCH 535/561] Debug specs --- spec/language_server/host_spec.rb | 2 +- spec/language_server/protocol_spec.rb | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/spec/language_server/host_spec.rb b/spec/language_server/host_spec.rb index 40c8b7292..bccf00a87 100644 --- a/spec/language_server/host_spec.rb +++ b/spec/language_server/host_spec.rb @@ -247,7 +247,7 @@ def initialize(foo); end expect(symbols).not_to be_empty end - it "opens a file outside of prepared libraries" do + it "opens a file outside of prepared libraries", time_limit_seconds: 120 do @host.prepare(File.absolute_path(File.join('spec', 'fixtures', 'workspace'))) @host.open('file:///file.rb', 'class Foo; end', 1) symbols = @host.document_symbols('file:///file.rb') diff --git a/spec/language_server/protocol_spec.rb b/spec/language_server/protocol_spec.rb index 0b54729b5..e238865af 100644 --- a/spec/language_server/protocol_spec.rb +++ b/spec/language_server/protocol_spec.rb @@ -34,7 +34,7 @@ def stop end end -describe Protocol, order: :defined do +describe Protocol, order: :defined, fail_fast: true do before :all do @protocol = Protocol.new(Solargraph::LanguageServer::Host.new) end @@ -173,7 +173,12 @@ def bar baz item = response['result']['items'].select{|h| h['label'] == 'bar'}.first @protocol.request 'completionItem/resolve', item response = @protocol.response - expect(response['result']['documentation']['value']).to include('bar method') + expect(response).not_to be_nil + expect(response['error']).to be_nil + expect(response['result']).to be_a(Hash) + expect(response['result']['documentation']['value']).to include('bar method'), Proc.new do + "Response was not correct: #{response.inspect}" + end end it 'suppresses FileNotFoundError in textDocument/completion' do From 0dc01e437700a8cc32da189103729078ea5255a5 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 20 Aug 2025 15:20:00 -0400 Subject: [PATCH 536/561] Fix issue with not being in directory workspace --- lib/solargraph/library.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/solargraph/library.rb b/lib/solargraph/library.rb index d764a25a9..c111614a5 100644 --- a/lib/solargraph/library.rb +++ b/lib/solargraph/library.rb @@ -600,8 +600,11 @@ def cache_next_gemspec logger.info "Caching #{spec.name} #{spec.version}" Thread.new do report_cache_progress spec.name, pending + kwargs = {} + kwargs[:chdir] = workspace.directory.to_s if workspace.directory && !workspace.directory.empty? # @sg-ignore Unresolved call to capture3 - _o, e, s = Open3.capture3(workspace.command_path, 'cache', spec.name, spec.version.to_s) + _o, e, s = Open3.capture3(workspace.command_path, 'cache', spec.name, spec.version.to_s, + chdir: workspace.directory.to_s) if s.success? logger.info "Cached #{spec.name} #{spec.version}" else From c615ce9343ea851c5588d80ca69326fd146f4ad3 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 20 Aug 2025 15:21:12 -0400 Subject: [PATCH 537/561] Fix linting issue --- spec/language_server/protocol_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/language_server/protocol_spec.rb b/spec/language_server/protocol_spec.rb index e238865af..8904eb1b3 100644 --- a/spec/language_server/protocol_spec.rb +++ b/spec/language_server/protocol_spec.rb @@ -34,7 +34,7 @@ def stop end end -describe Protocol, order: :defined, fail_fast: true do +describe Protocol, order: :defined do before :all do @protocol = Protocol.new(Solargraph::LanguageServer::Host.new) end From 62e00ea4a8d613c3193c2469820e72e2834dc4c9 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 20 Aug 2025 15:32:03 -0400 Subject: [PATCH 538/561] Fix invalid spec --- spec/language_server/protocol_spec.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/spec/language_server/protocol_spec.rb b/spec/language_server/protocol_spec.rb index 8904eb1b3..4306fe7a7 100644 --- a/spec/language_server/protocol_spec.rb +++ b/spec/language_server/protocol_spec.rb @@ -176,9 +176,7 @@ def bar baz expect(response).not_to be_nil expect(response['error']).to be_nil expect(response['result']).to be_a(Hash) - expect(response['result']['documentation']['value']).to include('bar method'), Proc.new do - "Response was not correct: #{response.inspect}" - end + expect(response['result']['documentation']['value']).to include('bar method') end it 'suppresses FileNotFoundError in textDocument/completion' do From 8672a5ea301507ee7f22f41acdf2e82fef0dae20 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 20 Aug 2025 15:48:03 -0400 Subject: [PATCH 539/561] Fix time limit --- spec/api_map/api_map_method_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/api_map/api_map_method_spec.rb b/spec/api_map/api_map_method_spec.rb index 8c38b808b..17bca8446 100644 --- a/spec/api_map/api_map_method_spec.rb +++ b/spec/api_map/api_map_method_spec.rb @@ -17,7 +17,7 @@ end end - describe '#get_method_stack', time_limit_seconds: 120 do + describe '#get_method_stack', time_limit_seconds: 240 do let(:out) { StringIO.new } let(:api_map) { described_class.load_with_cache(Dir.pwd, out) } From 5d27b40ceb77a9d31b88e30a6081824f48cb3df3 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 20 Aug 2025 15:48:13 -0400 Subject: [PATCH 540/561] Fix expectations in face of solargraph-rspec requires --- spec/source_map/clip_spec.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/spec/source_map/clip_spec.rb b/spec/source_map/clip_spec.rb index 0f83331ec..a0bf1956a 100644 --- a/spec/source_map/clip_spec.rb +++ b/spec/source_map/clip_spec.rb @@ -1644,10 +1644,12 @@ def foo; end api_map = Solargraph::ApiMap.new.map(source) array_names = api_map.clip_at('test.rb', [5, 22]).complete.pins.map(&:name) - expect(array_names).to eq(["byteindex", "byterindex", "bytes", "bytesize", "byteslice", "bytesplice"]) + # other methods may come in via plugin default requires + expect(array_names).to include("byteindex", "byterindex", "bytes", "bytesize", "byteslice", "bytesplice") string_names = api_map.clip_at('test.rb', [6, 22]).complete.pins.map(&:name) - expect(string_names).to eq(['upcase', 'upcase!', 'upto']) + # other methods may come in via plugin default requires + expect(string_names).to include('upcase', 'upcase!', 'upto') end it 'completes global methods defined in top level scope inside class when referenced inside a namespace' do From 8e3c1480a0e0ddfbe1963fcdda0a9299a6b425b8 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 20 Aug 2025 15:55:26 -0400 Subject: [PATCH 541/561] Ratchet rubocop todo file --- .rubocop_todo.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index bfdba89a7..6df8abedd 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -2448,7 +2448,6 @@ Style/WordArray: - 'spec/pin/method_spec.rb' - 'spec/source/cursor_spec.rb' - 'spec/source/source_chainer_spec.rb' - - 'spec/source_map/clip_spec.rb' - 'spec/source_map/mapper_spec.rb' - 'spec/source_map_spec.rb' - 'spec/source_spec.rb' From f505318f3802ffa99d834e630c0ffd5c56022377 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 20 Aug 2025 15:59:12 -0400 Subject: [PATCH 542/561] Fix spec bug --- lib/solargraph/library.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/solargraph/library.rb b/lib/solargraph/library.rb index c111614a5..27e268299 100644 --- a/lib/solargraph/library.rb +++ b/lib/solargraph/library.rb @@ -604,7 +604,7 @@ def cache_next_gemspec kwargs[:chdir] = workspace.directory.to_s if workspace.directory && !workspace.directory.empty? # @sg-ignore Unresolved call to capture3 _o, e, s = Open3.capture3(workspace.command_path, 'cache', spec.name, spec.version.to_s, - chdir: workspace.directory.to_s) + **kwargs) if s.success? logger.info "Cached #{spec.name} #{spec.version}" else From 65843fd7c8177282d5416d5f9ea561e7ba065575 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 20 Aug 2025 16:00:28 -0400 Subject: [PATCH 543/561] Debug spec failure --- spec/language_server/message/text_document/rename_spec.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/spec/language_server/message/text_document/rename_spec.rb b/spec/language_server/message/text_document/rename_spec.rb index 32fb6d011..d43793716 100644 --- a/spec/language_server/message/text_document/rename_spec.rb +++ b/spec/language_server/message/text_document/rename_spec.rb @@ -55,6 +55,9 @@ def foo(bar) } }) rename.process + expect(rename.result).not_to be_nil + expect(rename.result[:changes]).not_to be_nil + expect(rename.result[:changes]['file:///file.rb']).not_to be_nil expect(rename.result[:changes]['file:///file.rb'].length).to eq(3) end From cb0178b13f6570daf310d8e9655060784ad02b04 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 20 Aug 2025 17:28:47 -0400 Subject: [PATCH 544/561] Reproduce bug with spec --- .../message/text_document/definition_spec.rb | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/spec/language_server/message/text_document/definition_spec.rb b/spec/language_server/message/text_document/definition_spec.rb index beebb48de..b6c98b99b 100644 --- a/spec/language_server/message/text_document/definition_spec.rb +++ b/spec/language_server/message/text_document/definition_spec.rb @@ -1,4 +1,33 @@ describe Solargraph::LanguageServer::Message::TextDocument::Definition do + it 'prepares empty directory' do + Dir.mktmpdir do |dir| + host = Solargraph::LanguageServer::Host.new + test_rb_path = File.join(dir, 'test.rb') + thing_rb_path = File.join(dir, 'thing.rb') + FileUtils.cp('spec/fixtures/workspace/lib/other.rb', test_rb_path) + FileUtils.cp('spec/fixtures/workspace/lib/thing.rb', thing_rb_path) + host.prepare(dir) + sleep 0.1 until host.libraries.all?(&:mapped?) + host.catalog + file_uri = Solargraph::LanguageServer::UriHelpers.file_to_uri(test_rb_path) + other_uri = Solargraph::LanguageServer::UriHelpers.file_to_uri(thing_rb_path) + message = Solargraph::LanguageServer::Message::TextDocument::Definition + .new(host, { + 'params' => { + 'textDocument' => { + 'uri' => file_uri + }, + 'position' => { + 'line' => 4, + 'character' => 10 + } + } + }) + message.process + expect(message.result.first[:uri]).to eq(other_uri) + end + end + it 'finds definitions of methods' do host = Solargraph::LanguageServer::Host.new host.prepare('spec/fixtures/workspace') From 5844b75253cc98c6c96e8f8a55e73b2a548d116c Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Wed, 20 Aug 2025 18:19:08 -0400 Subject: [PATCH 545/561] Restore test command --- .github/workflows/plugins.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index 3afb0bb44..633ce563d 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -40,4 +40,4 @@ jobs: - name: Ensure typechecking still works run: bundle exec solargraph typecheck --level typed - name: Ensure specs still run - run: bundle exec rspec --fail-fast + run: bundle exec rake spec From 585f914d91e7fa4bc146850b75f52116149ac25f Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 21 Aug 2025 07:00:56 -0400 Subject: [PATCH 546/561] Add explicit reset_pins! method --- lib/solargraph/doc_map.rb | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/lib/solargraph/doc_map.rb b/lib/solargraph/doc_map.rb index d9d64b5b0..f35a98af3 100644 --- a/lib/solargraph/doc_map.rb +++ b/lib/solargraph/doc_map.rb @@ -19,15 +19,6 @@ class DocMap attr_reader :global_environ - # @return [Array] - def uncached_gemspecs - if @uncached_gemspecs.nil? - @uncached_gemspecs = [] - pins # force lazy-loaded pin lookup - end - @uncached_gemspecs - end - # @return [Workspace] # @return [Workspace, nil] attr_reader :workspace @@ -43,11 +34,25 @@ def initialize requires, workspace, out: $stderr @out = out end + # @return [Array] + def uncached_gemspecs + if @uncached_gemspecs.nil? + @uncached_gemspecs = [] + pins # force lazy-loaded pin lookup + end + @uncached_gemspecs + end + # @return [Array] def pins @pins ||= load_serialized_gem_pins + global_environ.pins end + def reset_pins! + @uncached_gemspecs = nil + @pins = nil + end + # @return [Solargraph::PinCache] def pin_cache @pin_cache ||= workspace.fresh_pincache @@ -76,8 +81,7 @@ def cache_doc_map_gems! out if (milliseconds > 500) && uncached_gemspecs.any? && out && uncached_gemspecs.any? out.puts "Built #{uncached_gemspecs.length} gems in #{milliseconds} ms" end - @uncached_gemspecs = nil - @pins = nil + reset_pins! end # @return [Array] From ddca860641e4033f566414178d26c4bf84d9b471 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 21 Aug 2025 07:01:28 -0400 Subject: [PATCH 547/561] Revert formatting --- lib/solargraph/library.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/solargraph/library.rb b/lib/solargraph/library.rb index 27e268299..8fecef2a2 100644 --- a/lib/solargraph/library.rb +++ b/lib/solargraph/library.rb @@ -249,7 +249,7 @@ def references_from filename, line, column, strip: false, only: false return [] unless pin result = [] files = if only - [api_map.source_map(filename)] + [api_map.source_map(filename)] else (workspace.sources + (@current ? [@current] : [])) end From 0082b431bf941c698399be99175bd295683260d2 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 21 Aug 2025 07:01:36 -0400 Subject: [PATCH 548/561] Revert run tests command --- .github/workflows/rspec.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rspec.yml b/.github/workflows/rspec.yml index a025d5eee..34d915bec 100644 --- a/.github/workflows/rspec.yml +++ b/.github/workflows/rspec.yml @@ -79,6 +79,6 @@ jobs: - name: Install types run: bundle exec rbs collection update - name: Run tests - run: bundle exec rspec + run: bundle exec rake spec - name: Check PR coverage run: bundle exec rake undercover From 40bdb5a5fc3364760815bc9f7aa84a8ad92443ee Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 21 Aug 2025 07:11:09 -0400 Subject: [PATCH 549/561] Fix annotation --- lib/solargraph/doc_map.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/solargraph/doc_map.rb b/lib/solargraph/doc_map.rb index f35a98af3..eef2d4dbf 100644 --- a/lib/solargraph/doc_map.rb +++ b/lib/solargraph/doc_map.rb @@ -48,6 +48,7 @@ def pins @pins ||= load_serialized_gem_pins + global_environ.pins end + # @return [void] def reset_pins! @uncached_gemspecs = nil @pins = nil From 390aed271f9246c5f0d2387e0778b75391b0d147 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 21 Aug 2025 07:50:38 -0400 Subject: [PATCH 550/561] Cache global environ pins --- lib/solargraph/doc_map.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/solargraph/doc_map.rb b/lib/solargraph/doc_map.rb index eef2d4dbf..5306fecf6 100644 --- a/lib/solargraph/doc_map.rb +++ b/lib/solargraph/doc_map.rb @@ -43,9 +43,14 @@ def uncached_gemspecs @uncached_gemspecs end + # @return [Array] + def global_environ_pins + @global_environ_pins ||= global_environ.pins + end + # @return [Array] def pins - @pins ||= load_serialized_gem_pins + global_environ.pins + @pins ||= load_serialized_gem_pins + global_environ_pins end # @return [void] From 4ee8c007ee48d86baa7043f9800c87af6e936535 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 21 Aug 2025 08:11:34 -0400 Subject: [PATCH 551/561] Add convention-based requires more lazily --- lib/solargraph/doc_map.rb | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/lib/solargraph/doc_map.rb b/lib/solargraph/doc_map.rb index 5306fecf6..77986d44f 100644 --- a/lib/solargraph/doc_map.rb +++ b/lib/solargraph/doc_map.rb @@ -13,12 +13,6 @@ module Solargraph class DocMap include Logging - # @return [Array] - attr_reader :requires - alias required requires - - attr_reader :global_environ - # @return [Workspace] # @return [Workspace, nil] attr_reader :workspace @@ -27,13 +21,21 @@ class DocMap # @param workspace [Workspace] # @param out [IO, nil] output stream for logging def initialize requires, workspace, out: $stderr - @requires = requires.compact + @provided_requires = requires.compact @workspace = workspace - @global_environ = Convention.for_global(self) - @requires.concat @global_environ.requires if @global_environ @out = out end + def global_environ + @global_environ ||= Convention.for_global(self) + end + + # @return [Array] + def requires + @requires ||= @provided_requires + (global_environ&.requires || []) + end + alias required requires + # @return [Array] def uncached_gemspecs if @uncached_gemspecs.nil? From c2d1674f862b94865f2b74d8ed121b518ffe0f98 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 21 Aug 2025 08:18:00 -0400 Subject: [PATCH 552/561] Rely on workspace to own global environ --- lib/solargraph/doc_map.rb | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/lib/solargraph/doc_map.rb b/lib/solargraph/doc_map.rb index 77986d44f..43a4a9520 100644 --- a/lib/solargraph/doc_map.rb +++ b/lib/solargraph/doc_map.rb @@ -14,7 +14,6 @@ class DocMap include Logging # @return [Workspace] - # @return [Workspace, nil] attr_reader :workspace # @param requires [Array] @@ -26,13 +25,9 @@ def initialize requires, workspace, out: $stderr @out = out end - def global_environ - @global_environ ||= Convention.for_global(self) - end - # @return [Array] def requires - @requires ||= @provided_requires + (global_environ&.requires || []) + @requires ||= @provided_requires + (workspace.global_environ&.requires || []) end alias required requires From 25624dc7b9ebe6137ec2bbb58fae1a156c418af4 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 21 Aug 2025 08:30:06 -0400 Subject: [PATCH 553/561] Rely on workspace to own global environ --- lib/solargraph/doc_map.rb | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/lib/solargraph/doc_map.rb b/lib/solargraph/doc_map.rb index 43a4a9520..750eee375 100644 --- a/lib/solargraph/doc_map.rb +++ b/lib/solargraph/doc_map.rb @@ -40,14 +40,9 @@ def uncached_gemspecs @uncached_gemspecs end - # @return [Array] - def global_environ_pins - @global_environ_pins ||= global_environ.pins - end - # @return [Array] def pins - @pins ||= load_serialized_gem_pins + global_environ_pins + @pins ||= load_serialized_gem_pins + (workspace.global_environ&.pins || []) end # @return [void] From 14bdb8cf6898a579562b982a3ddf2b73f76e1999 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 21 Aug 2025 08:43:01 -0400 Subject: [PATCH 554/561] Ratchet .rubocop_todo.yml --- .rubocop_todo.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 6df8abedd..139f5de09 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -2501,7 +2501,6 @@ YARD/MismatchName: - 'lib/solargraph/pin/until.rb' - 'lib/solargraph/pin/while.rb' - 'lib/solargraph/pin_cache.rb' - - 'lib/solargraph/source/chain.rb' - 'lib/solargraph/source/chain/call.rb' - 'lib/solargraph/source/chain/z_super.rb' - 'lib/solargraph/type_checker.rb' From a16fcc1c4e4951ccac3f66de330b220b99d7052a Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 21 Aug 2025 08:58:18 -0400 Subject: [PATCH 555/561] Fix specs --- spec/doc_map_spec.rb | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/spec/doc_map_spec.rb b/spec/doc_map_spec.rb index 4c9bd3de7..3eeb932ea 100644 --- a/spec/doc_map_spec.rb +++ b/spec/doc_map_spec.rb @@ -82,6 +82,7 @@ pincache = instance_double(Solargraph::PinCache) uncached_gemspec = Gem::Specification.new('uncached_gem', '1.0.0') allow(workspace).to receive_messages(resolve_require: [], fresh_pincache: pincache) + allow(workspace).to receive(:global_environ).and_return(Solargraph::Environ.new) allow(workspace).to receive(:resolve_require).with('uncached_gem').and_return([uncached_gemspec]) allow(workspace).to receive(:fetch_dependencies).with(uncached_gemspec, out: out).and_return([]) allow(pincache).to receive(:deserialize_combined_pin_cache).with(uncached_gemspec).and_return(nil) @@ -133,22 +134,26 @@ end end - it 'includes convention requires from environ' do - dummy_convention = Class.new(Solargraph::Convention::Base) do - def global(doc_map) - Solargraph::Environ.new( - requires: ['convention_gem1', 'convention_gem2'] - ) - end - end + context 'with convention' do + let(:pre_cache) { false } - Solargraph::Convention.register dummy_convention + it 'includes convention requires from environ' do + dummy_convention = Class.new(Solargraph::Convention::Base) do + def global(doc_map) + Solargraph::Environ.new( + requires: ['convention_gem1', 'convention_gem2'] + ) + end + end - doc_map = Solargraph::DocMap.new(['original_gem'], workspace) + Solargraph::Convention.register dummy_convention - expect(doc_map.requires).to include('original_gem', 'convention_gem1', 'convention_gem2') + doc_map = Solargraph::DocMap.new(['original_gem'], workspace) - # Clean up the registered convention - Solargraph::Convention.deregister dummy_convention + expect(doc_map.requires).to include('original_gem', 'convention_gem1', 'convention_gem2') + ensure + # Clean up the registered convention + Solargraph::Convention.deregister dummy_convention + end end end From 247c1395d1d245b6ff80e78af9fcd978cd2c815d Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 21 Aug 2025 09:39:49 -0400 Subject: [PATCH 556/561] Time rspec and rails plugins separately --- .github/workflows/plugins.yml | 62 +++++++++++++++++++++++++++++++++-- 1 file changed, 60 insertions(+), 2 deletions(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index 633ce563d..9bd6d979c 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -1,4 +1,4 @@ -name: Plugin Backwards Compatibility Tests +name: Plugin on: push: @@ -10,7 +10,7 @@ permissions: contents: read jobs: - test: + regression: runs-on: ubuntu-latest steps: @@ -41,3 +41,61 @@ jobs: run: bundle exec solargraph typecheck --level typed - name: Ensure specs still run run: bundle exec rake spec + rails: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.0' + bundler-cache: false + - uses: awalsh128/cache-apt-pkgs-action@latest + with: + packages: yq + version: 1.0 + - name: Install gems + run: | + echo 'gem "solargraph-rails"' > .Gemfile + bundle install + bundle update rbs + - name: Configure to use plugins + run: | + bundle exec solargraph config + yq -yi '.plugins += ["solargraph-rails]' .solargraph.yml + - name: Install gem types + run: bundle exec rbs collection update + - name: Ensure typechecking still works + run: bundle exec solargraph typecheck --level typed + - name: Ensure specs still run + run: bundle exec rake spec + rspec: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.0' + bundler-cache: false + - uses: awalsh128/cache-apt-pkgs-action@latest + with: + packages: yq + version: 1.0 + - name: Install gems + run: | + echo 'gem "solargraph-rspec"' >> .Gemfile + bundle install + bundle update rbs + - name: Configure to use plugins + run: | + bundle exec solargraph config + yq -yi '.plugins += ["solargraph-rspec"]' .solargraph.yml + - name: Install gem types + run: bundle exec rbs collection update + - name: Ensure typechecking still works + run: bundle exec solargraph typecheck --level typed + - name: Ensure specs still run + run: bundle exec rake spec From bd8e64d907873e1b06f1cc6313a62227fc4debce Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 21 Aug 2025 09:44:31 -0400 Subject: [PATCH 557/561] Fix syntax --- .github/workflows/plugins.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index 9bd6d979c..3f306a166 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -63,7 +63,7 @@ jobs: - name: Configure to use plugins run: | bundle exec solargraph config - yq -yi '.plugins += ["solargraph-rails]' .solargraph.yml + yq -yi '.plugins += ["solargraph-rails"]' .solargraph.yml - name: Install gem types run: bundle exec rbs collection update - name: Ensure typechecking still works From fdf9c7d17812b11c7a6ed436c359f11dffafff1e Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 21 Aug 2025 10:03:56 -0400 Subject: [PATCH 558/561] Add to unprovided solargraph-rspec requires --- spec/doc_map_spec.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spec/doc_map_spec.rb b/spec/doc_map_spec.rb index 3eeb932ea..cdf12d75a 100644 --- a/spec/doc_map_spec.rb +++ b/spec/doc_map_spec.rb @@ -47,7 +47,8 @@ 'activerecord', 'shoulda-matchers', 'rspec-sidekiq', - 'airborne' + 'airborne', + 'activesupport' ] expect(doc_map.unresolved_requires - unprovided_solargraph_rspec_requires) .to eq(['not_a_gem']) From 12ae6f0e5a6ce147588b0f7248d28b38ae321e6e Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 23 Aug 2025 18:26:43 -0400 Subject: [PATCH 559/561] Refine order of object convention method pins The object convention pins currently take priority before all other sources of pins. This is probably appropriate for the directly defined methods of the class itself, but others methods included by references should come later in the list. This allows a user to call get_method_stack, take the first pin in the list, and use typify() to walk the superclasses and define the correct types. This behavior also makes resolution of ActiveRecord pins from its RBS collection better today in practice - there are lots of cases where the method pulled in from a reference is auto-generated (say, 'ActiveRecord::Transactions::ClassMethods#after_commit'), but someone has manually overridden the type by defining an RBS method in ActiveRecord::Base. --- lib/solargraph/api_map.rb | 9 ++- spec/convention/activesupport_concern_spec.rb | 70 +++++++++++++++++-- 2 files changed, 72 insertions(+), 7 deletions(-) diff --git a/lib/solargraph/api_map.rb b/lib/solargraph/api_map.rb index eed02b4ef..c333c28e2 100755 --- a/lib/solargraph/api_map.rb +++ b/lib/solargraph/api_map.rb @@ -753,7 +753,12 @@ def inner_get_methods rooted_tag, scope, visibility, deep, skip, no_core = false skip.add reqstr result = [] environ = Convention.for_object(self, rooted_tag, scope, visibility, deep, skip, no_core) - result.concat environ.pins + # ensure we start out with any immediate methods in this + # namespace so we roughly match the same ordering of get_methods + # and obey the 'deep' instruction + direct_convention_methods, convention_methods_by_reference = environ.pins.partition { |p| p.namespace == rooted_tag } + result.concat direct_convention_methods + if deep && scope == :instance store.get_prepends(fqns).reverse.each do |im| fqim = qualify(im, fqns) @@ -767,6 +772,8 @@ def inner_get_methods rooted_tag, scope, visibility, deep, skip, no_core = false logger.info { "ApiMap#inner_get_methods(rooted_tag=#{rooted_tag.inspect}, scope=#{scope.inspect}, visibility=#{visibility.inspect}, deep=#{deep.inspect}, skip=#{skip.inspect}, fqns=#{fqns}) - added from store: #{methods}" } result.concat methods if deep + result.concat convention_methods_by_reference + if scope == :instance store.get_includes(fqns).reverse.each do |include_tag| rooted_include_tag = qualify(include_tag, rooted_tag) diff --git a/spec/convention/activesupport_concern_spec.rb b/spec/convention/activesupport_concern_spec.rb index ffa12ee6c..30fdd2f36 100644 --- a/spec/convention/activesupport_concern_spec.rb +++ b/spec/convention/activesupport_concern_spec.rb @@ -1,8 +1,11 @@ # frozen_string_literal: true describe Solargraph::Convention::ActiveSupportConcern do - let :source do - Solargraph::Source.load_string(%( + let(:api_map) { Solargraph::ApiMap.new.map(source) } + + context 'with a simple activesupport concern' do + let :source do + Solargraph::Source.load_string(%( # Example from here: https://api.rubyonrails.org/v7.0/classes/ActiveSupport/Concern.html require "active_support/concern" @@ -30,12 +33,67 @@ class Host # this should print 'test' ), 'test.rb') + end + + it 'handles block method super scenarios' do + api_map = Solargraph::ApiMap.new.map(source) + + pin = api_map.get_method_stack('Host', 'method_injected_by_foo', scope: :class) + expect(pin.map(&:name)).to eq(['method_injected_by_foo']) + end end - it 'handles block method super scenarios' do - api_map = Solargraph::ApiMap.new.map(source) + context 'with static method defined in both included module and class' do + let :source do + Solargraph::Source.load_string(%( + # Example from here: https://api.rubyonrails.org/v7.0/classes/ActiveSupport/Concern.html + require "active_support/concern" + + module Foo + extend ActiveSupport::Concern + included do + def self.my_method + puts 'test' + end + end + end + + module Bar + extend ActiveSupport::Concern + include Foo + + included do + self.my_method + end + end + + class B + # @return [Numeric] + def self.my_method; end + end + + class A < B + include Bar # It works, now Bar takes care of its dependencies + + def self.my_method; end + end + + # this should print 'test' + ), 'test.rb') + end + + let(:pins) { api_map.get_method_stack('A', 'my_method', scope: :class) } + + it 'sees all three methods' do + expect(pins.map(&:name)).to eq(['my_method', 'my_method', 'my_method']) + end + + it 'prefers directly defined method' do + expect(pins.map(&:path).first).to eq("A.my_method") + end - pin = api_map.get_method_stack('Host', 'method_injected_by_foo', scope: :class) - expect(pin.map(&:name)).to eq(['method_injected_by_foo']) + it 'is able to typify from superclass' do + expect(pins.first.typify(api_map).map(&:tag)).to include('Numeric') + end end end From 5b457fef61c5a7eb713e977e3d654b5d9b409322 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 23 Aug 2025 18:40:03 -0400 Subject: [PATCH 560/561] Linting fix --- spec/convention/activesupport_concern_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/convention/activesupport_concern_spec.rb b/spec/convention/activesupport_concern_spec.rb index 30fdd2f36..e75e8749c 100644 --- a/spec/convention/activesupport_concern_spec.rb +++ b/spec/convention/activesupport_concern_spec.rb @@ -85,11 +85,11 @@ def self.my_method; end let(:pins) { api_map.get_method_stack('A', 'my_method', scope: :class) } it 'sees all three methods' do - expect(pins.map(&:name)).to eq(['my_method', 'my_method', 'my_method']) + expect(pins.map(&:name)).to eq(%w[my_method my_method my_method]) end it 'prefers directly defined method' do - expect(pins.map(&:path).first).to eq("A.my_method") + expect(pins.map(&:path).first).to eq('A.my_method') end it 'is able to typify from superclass' do From e61a7d01c457b9ec6cf24ff06440abdc26f51b3c Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 23 Aug 2025 18:42:52 -0400 Subject: [PATCH 561/561] Catch up with new RuboCop version --- .rubocop_todo.yml | 58 ++++++++++++++++++++--------------------------- 1 file changed, 24 insertions(+), 34 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index d8283c5c6..67fcdf8ed 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,51 +1,44 @@ # This configuration was generated by # `rubocop --auto-gen-config --no-exclude-limit --no-offense-counts --no-auto-gen-timestamp` -# using RuboCop version 1.79.2. +# using RuboCop version 1.80.0. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. # This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: Include. -# Include: **/*.gemspec Gemspec/AddRuntimeDependency: Exclude: - 'solargraph.gemspec' # This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: Severity, Include. -# Include: **/*.gemspec +# Configuration parameters: Severity. Gemspec/DeprecatedAttributeAssignment: Exclude: - 'solargraph.gemspec' - 'spec/fixtures/rdoc-lib/rdoc-lib.gemspec' -# Configuration parameters: EnforcedStyle, AllowedGems, Include. +# Configuration parameters: EnforcedStyle, AllowedGems. # SupportedStyles: Gemfile, gems.rb, gemspec -# Include: **/*.gemspec, **/Gemfile, **/gems.rb Gemspec/DevelopmentDependencies: Exclude: - 'solargraph.gemspec' # This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: TreatCommentsAsGroupSeparators, ConsiderPunctuation, Include. -# Include: **/*.gemspec +# Configuration parameters: TreatCommentsAsGroupSeparators, ConsiderPunctuation. Gemspec/OrderedDependencies: Exclude: - 'solargraph.gemspec' # This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: Severity, Include. -# Include: **/*.gemspec +# Configuration parameters: Severity. Gemspec/RequireMFA: Exclude: - 'solargraph.gemspec' - 'spec/fixtures/rdoc-lib/rdoc-lib.gemspec' - 'spec/fixtures/rubocop-custom-version/specifications/rubocop-0.0.0.gemspec' -# Configuration parameters: Severity, Include. -# Include: **/*.gemspec +# Configuration parameters: Severity. Gemspec/RequiredRubyVersion: Exclude: - 'spec/fixtures/rdoc-lib/rdoc-lib.gemspec' @@ -627,7 +620,7 @@ Lint/UnmodifiedReduceAccumulator: - 'lib/solargraph/pin/method.rb' # This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AutoCorrect, IgnoreEmptyBlocks, AllowUnusedKeywordArguments. +# Configuration parameters: IgnoreEmptyBlocks, AllowUnusedKeywordArguments. Lint/UnusedBlockArgument: Exclude: - 'lib/solargraph/language_server/message/workspace/did_change_workspace_folders.rb' @@ -635,7 +628,7 @@ Lint/UnusedBlockArgument: - 'spec/language_server/transport/data_reader_spec.rb' # This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AutoCorrect, AllowUnusedKeywordArguments, IgnoreEmptyMethods, IgnoreNotImplementedMethods, NotImplementedExceptions. +# Configuration parameters: AllowUnusedKeywordArguments, IgnoreEmptyMethods, IgnoreNotImplementedMethods, NotImplementedExceptions. # NotImplementedExceptions: NotImplementedError Lint/UnusedMethodArgument: Exclude: @@ -665,13 +658,12 @@ Lint/UnusedMethodArgument: - 'spec/doc_map_spec.rb' # This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AutoCorrect, ContextCreatingMethods, MethodCreatingMethods. +# Configuration parameters: ContextCreatingMethods, MethodCreatingMethods. Lint/UselessAccessModifier: Exclude: - 'lib/solargraph/api_map.rb' # This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AutoCorrect. Lint/UselessAssignment: Exclude: - 'lib/solargraph/api_map.rb' @@ -703,7 +695,6 @@ Lint/UselessConstantScoping: - 'lib/solargraph/rbs_map/conversions.rb' # This cop supports unsafe autocorrection (--autocorrect-all). -# Configuration parameters: AutoCorrect. Lint/UselessMethodDefinition: Exclude: - 'lib/solargraph/pin/signature.rb' @@ -1015,7 +1006,6 @@ RSpec/DescribedClass: - 'spec/yard_map/mapper_spec.rb' # This cop supports unsafe autocorrection (--autocorrect-all). -# Configuration parameters: AutoCorrect. RSpec/EmptyExampleGroup: Exclude: - 'spec/convention_spec.rb' @@ -1117,7 +1107,6 @@ RSpec/LeakyConstantDeclaration: - 'spec/complex_type_spec.rb' # This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AutoCorrect. RSpec/LetBeforeExamples: Exclude: - 'spec/complex_type_spec.rb' @@ -1275,13 +1264,11 @@ RSpec/RepeatedExample: - 'spec/type_checker/levels/strict_spec.rb' # This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AutoCorrect. RSpec/ScatteredLet: Exclude: - 'spec/complex_type_spec.rb' -# Configuration parameters: Include, CustomTransform, IgnoreMethods, IgnoreMetadata. -# Include: **/*_spec.rb +# Configuration parameters: CustomTransform, IgnoreMethods, IgnoreMetadata. RSpec/SpecFilePathFormat: Exclude: - '**/spec/routing/**/*' @@ -1711,7 +1698,7 @@ Style/EmptyLambdaParameter: - 'spec/rbs_map/core_map_spec.rb' # This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AutoCorrect, EnforcedStyle. +# Configuration parameters: EnforcedStyle. # SupportedStyles: compact, expanded Style/EmptyMethod: Exclude: @@ -2135,13 +2122,6 @@ Style/NegatedIfElseCondition: - 'lib/solargraph/shell.rb' - 'lib/solargraph/type_checker.rb' -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AllowedMethods. -# AllowedMethods: be, be_a, be_an, be_between, be_falsey, be_kind_of, be_instance_of, be_truthy, be_within, eq, eql, end_with, include, match, raise_error, respond_to, start_with -Style/NestedParenthesizedCalls: - Exclude: - - 'lib/solargraph/type_checker.rb' - # This cop supports safe autocorrection (--autocorrect). Style/NestedTernaryOperator: Exclude: @@ -2237,7 +2217,7 @@ Style/RedundantBegin: - 'lib/solargraph/shell.rb' - 'lib/solargraph/source/cursor.rb' - 'lib/solargraph/source/encoding_fixes.rb' - - 'lib/solargraph/type_checker.rb' + - 'lib/solargraph/source_map/mapper.rb' - 'lib/solargraph/workspace.rb' # This cop supports safe autocorrection (--autocorrect). @@ -2252,7 +2232,7 @@ Style/RedundantFreeze: - 'lib/solargraph/source_map/mapper.rb' # This cop supports unsafe autocorrection (--autocorrect-all). -# Configuration parameters: AutoCorrect, AllowComments. +# Configuration parameters: AllowComments. Style/RedundantInitialize: Exclude: - 'lib/solargraph/rbs_map/core_map.rb' @@ -2284,13 +2264,24 @@ Style/RedundantRegexpArgument: - 'spec/diagnostics/rubocop_spec.rb' - 'spec/language_server/host_spec.rb' +# This cop supports safe autocorrection (--autocorrect). +Style/RedundantRegexpCharacterClass: + Exclude: + - 'lib/solargraph/source/cursor.rb' + - 'lib/solargraph/source/source_chainer.rb' + # This cop supports safe autocorrection (--autocorrect). Style/RedundantRegexpEscape: Exclude: - 'lib/solargraph/complex_type.rb' - 'lib/solargraph/diagnostics/rubocop.rb' - 'lib/solargraph/language_server/uri_helpers.rb' + - 'lib/solargraph/parser/parser_gem/node_methods.rb' + - 'lib/solargraph/pin/method.rb' + - 'lib/solargraph/pin/parameter.rb' - 'lib/solargraph/shell.rb' + - 'lib/solargraph/source/change.rb' + - 'lib/solargraph/source/cursor.rb' - 'lib/solargraph/source_map/clip.rb' - 'lib/solargraph/source_map/mapper.rb' @@ -2674,7 +2665,6 @@ YARD/MismatchName: - 'lib/solargraph/pin/until.rb' - 'lib/solargraph/pin/while.rb' - 'lib/solargraph/pin_cache.rb' - - 'lib/solargraph/source/chain.rb' - 'lib/solargraph/source/chain/call.rb' - 'lib/solargraph/source/chain/z_super.rb' - 'lib/solargraph/type_checker.rb'