solargraph-rspec 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
 - data/.rspec +3 -0
 - data/.rubocop.yml +41 -0
 - data/CHANGELOG.md +22 -0
 - data/CODE_OF_CONDUCT.md +84 -0
 - data/LICENSE.txt +21 -0
 - data/README.md +115 -0
 - data/Rakefile +12 -0
 - data/lib/solargraph/rspec/config.rb +41 -0
 - data/lib/solargraph/rspec/convention.rb +223 -0
 - data/lib/solargraph/rspec/correctors/base.rb +46 -0
 - data/lib/solargraph/rspec/correctors/context_block_methods_corrector.rb +38 -0
 - data/lib/solargraph/rspec/correctors/context_block_namespace_corrector.rb +65 -0
 - data/lib/solargraph/rspec/correctors/described_class_corrector.rb +39 -0
 - data/lib/solargraph/rspec/correctors/dsl_methods_corrector.rb +68 -0
 - data/lib/solargraph/rspec/correctors/example_and_hook_blocks_binding_corrector.rb +74 -0
 - data/lib/solargraph/rspec/correctors/implicit_subject_method_corrector.rb +48 -0
 - data/lib/solargraph/rspec/correctors/let_methods_corrector.rb +44 -0
 - data/lib/solargraph/rspec/correctors/subject_method_corrector.rb +24 -0
 - data/lib/solargraph/rspec/correctors/walker_base.rb +27 -0
 - data/lib/solargraph/rspec/spec_walker.rb +247 -0
 - data/lib/solargraph/rspec/util.rb +77 -0
 - data/lib/solargraph/rspec/version.rb +7 -0
 - data/lib/solargraph/rspec/walker.rb +120 -0
 - data/lib/solargraph-rspec.rb +19 -0
 - data/lib/solargraph_rspec.rb +3 -0
 - data/sig/solargraph/rspec.rbs +6 -0
 - data/solargraph-rspec.gemspec +37 -0
 - metadata +107 -0
 
| 
         @@ -0,0 +1,65 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require_relative 'walker_base'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            module Solargraph
         
     | 
| 
      
 6 
     | 
    
         
            +
              module Rspec
         
     | 
| 
      
 7 
     | 
    
         
            +
                module Correctors
         
     | 
| 
      
 8 
     | 
    
         
            +
                  # A corrector of RSpec parsed pins by Solargraph
         
     | 
| 
      
 9 
     | 
    
         
            +
                  class ContextBlockNamespaceCorrector < WalkerBase
         
     | 
| 
      
 10 
     | 
    
         
            +
                    # @param source_map [Solargraph::SourceMap]
         
     | 
| 
      
 11 
     | 
    
         
            +
                    def correct(source_map)
         
     | 
| 
      
 12 
     | 
    
         
            +
                      rspec_walker.on_each_context_block do |namespace_name, ast|
         
     | 
| 
      
 13 
     | 
    
         
            +
                        original_block_pin = source_map.locate_block_pin(ast.location.begin.line, ast.location.begin.column)
         
     | 
| 
      
 14 
     | 
    
         
            +
                        original_block_pin_index = source_map.pins.index(original_block_pin)
         
     | 
| 
      
 15 
     | 
    
         
            +
                        location = Util.build_location(ast, source_map.filename)
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                        # Define a dynamic module for the example group block
         
     | 
| 
      
 18 
     | 
    
         
            +
                        # Example:
         
     | 
| 
      
 19 
     | 
    
         
            +
                        #   RSpec.describe Foo::Bar do  # => module RSpec::ExampleGroups::FooBar
         
     | 
| 
      
 20 
     | 
    
         
            +
                        #     context 'some context' do # => module RSpec::ExampleGroups::FooBar::SomeContext
         
     | 
| 
      
 21 
     | 
    
         
            +
                        #     end
         
     | 
| 
      
 22 
     | 
    
         
            +
                        #   end
         
     | 
| 
      
 23 
     | 
    
         
            +
                        namespace_pin = Solargraph::Pin::Namespace.new(
         
     | 
| 
      
 24 
     | 
    
         
            +
                          name: namespace_name,
         
     | 
| 
      
 25 
     | 
    
         
            +
                          location: location
         
     | 
| 
      
 26 
     | 
    
         
            +
                        )
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                        fixed_namespace_block_pin = Solargraph::Pin::Block.new(
         
     | 
| 
      
 29 
     | 
    
         
            +
                          closure: namespace_pin,
         
     | 
| 
      
 30 
     | 
    
         
            +
                          location: original_block_pin.location,
         
     | 
| 
      
 31 
     | 
    
         
            +
                          receiver: original_block_pin.receiver,
         
     | 
| 
      
 32 
     | 
    
         
            +
                          scope: original_block_pin.scope
         
     | 
| 
      
 33 
     | 
    
         
            +
                        )
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                        source_map.pins[original_block_pin_index] = fixed_namespace_block_pin
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                        # Include DSL methods in the example group block
         
     | 
| 
      
 38 
     | 
    
         
            +
                        # TOOD: This does not work on solagraph! Class methods are not included from parent class.
         
     | 
| 
      
 39 
     | 
    
         
            +
                        namespace_extend_pin = Util.build_module_extend(
         
     | 
| 
      
 40 
     | 
    
         
            +
                          namespace_pin,
         
     | 
| 
      
 41 
     | 
    
         
            +
                          root_example_group_namespace_pin.name,
         
     | 
| 
      
 42 
     | 
    
         
            +
                          location
         
     | 
| 
      
 43 
     | 
    
         
            +
                        )
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                        # Include parent example groups to share let definitions
         
     | 
| 
      
 46 
     | 
    
         
            +
                        parent_namespace_name = namespace_name.split('::')[0..-2].join('::')
         
     | 
| 
      
 47 
     | 
    
         
            +
                        namespace_include_pin = Util.build_module_include(
         
     | 
| 
      
 48 
     | 
    
         
            +
                          namespace_pin,
         
     | 
| 
      
 49 
     | 
    
         
            +
                          parent_namespace_name,
         
     | 
| 
      
 50 
     | 
    
         
            +
                          location
         
     | 
| 
      
 51 
     | 
    
         
            +
                        )
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                        namespace_pins << namespace_pin
         
     | 
| 
      
 54 
     | 
    
         
            +
                        if block_given?
         
     | 
| 
      
 55 
     | 
    
         
            +
                          yield [
         
     | 
| 
      
 56 
     | 
    
         
            +
                            namespace_include_pin,
         
     | 
| 
      
 57 
     | 
    
         
            +
                            namespace_extend_pin
         
     | 
| 
      
 58 
     | 
    
         
            +
                          ]
         
     | 
| 
      
 59 
     | 
    
         
            +
                        end
         
     | 
| 
      
 60 
     | 
    
         
            +
                      end
         
     | 
| 
      
 61 
     | 
    
         
            +
                    end
         
     | 
| 
      
 62 
     | 
    
         
            +
                  end
         
     | 
| 
      
 63 
     | 
    
         
            +
                end
         
     | 
| 
      
 64 
     | 
    
         
            +
              end
         
     | 
| 
      
 65 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,39 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require_relative 'walker_base'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            module Solargraph
         
     | 
| 
      
 6 
     | 
    
         
            +
              module Rspec
         
     | 
| 
      
 7 
     | 
    
         
            +
                module Correctors
         
     | 
| 
      
 8 
     | 
    
         
            +
                  class DescribedClassCorrector < WalkerBase
         
     | 
| 
      
 9 
     | 
    
         
            +
                    # @param source_map [Solargraph::SourceMap]
         
     | 
| 
      
 10 
     | 
    
         
            +
                    # @return [void]
         
     | 
| 
      
 11 
     | 
    
         
            +
                    def correct(_source_map)
         
     | 
| 
      
 12 
     | 
    
         
            +
                      rspec_walker.on_described_class do |ast, described_class_name|
         
     | 
| 
      
 13 
     | 
    
         
            +
                        namespace_pin = closest_namespace_pin(namespace_pins, ast.loc.line)
         
     | 
| 
      
 14 
     | 
    
         
            +
                        next unless namespace_pin
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                        described_class_pin = rspec_described_class_method(namespace_pin, ast, described_class_name)
         
     | 
| 
      
 17 
     | 
    
         
            +
                        yield [described_class_pin].compact if block_given?
         
     | 
| 
      
 18 
     | 
    
         
            +
                      end
         
     | 
| 
      
 19 
     | 
    
         
            +
                    end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                    private
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                    # @param namespace [Pin::Namespace]
         
     | 
| 
      
 24 
     | 
    
         
            +
                    # @param ast [Parser::AST::Node]
         
     | 
| 
      
 25 
     | 
    
         
            +
                    # @param described_class_name [String]
         
     | 
| 
      
 26 
     | 
    
         
            +
                    # @return [Pin::Method, nil]
         
     | 
| 
      
 27 
     | 
    
         
            +
                    def rspec_described_class_method(namespace, ast, described_class_name)
         
     | 
| 
      
 28 
     | 
    
         
            +
                      Util.build_public_method(
         
     | 
| 
      
 29 
     | 
    
         
            +
                        namespace,
         
     | 
| 
      
 30 
     | 
    
         
            +
                        'described_class',
         
     | 
| 
      
 31 
     | 
    
         
            +
                        types: ["Class<#{described_class_name}>"],
         
     | 
| 
      
 32 
     | 
    
         
            +
                        location: Util.build_location(ast, namespace.filename),
         
     | 
| 
      
 33 
     | 
    
         
            +
                        scope: :instance
         
     | 
| 
      
 34 
     | 
    
         
            +
                      )
         
     | 
| 
      
 35 
     | 
    
         
            +
                    end
         
     | 
| 
      
 36 
     | 
    
         
            +
                  end
         
     | 
| 
      
 37 
     | 
    
         
            +
                end
         
     | 
| 
      
 38 
     | 
    
         
            +
              end
         
     | 
| 
      
 39 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,68 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require_relative 'walker_base'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            module Solargraph
         
     | 
| 
      
 6 
     | 
    
         
            +
              module Rspec
         
     | 
| 
      
 7 
     | 
    
         
            +
                module Correctors
         
     | 
| 
      
 8 
     | 
    
         
            +
                  class DslMethodsCorrector < WalkerBase
         
     | 
| 
      
 9 
     | 
    
         
            +
                    # @param namespace_pins [Array<Solargraph::Pin::Base>]
         
     | 
| 
      
 10 
     | 
    
         
            +
                    # @param rspec_walker [Solargraph::Rspec::SpecWalker]
         
     | 
| 
      
 11 
     | 
    
         
            +
                    # @param config [Solargraph::Rspec::Config]
         
     | 
| 
      
 12 
     | 
    
         
            +
                    def initialize(namespace_pins:, rspec_walker:, config:)
         
     | 
| 
      
 13 
     | 
    
         
            +
                      super(namespace_pins: namespace_pins, rspec_walker: rspec_walker)
         
     | 
| 
      
 14 
     | 
    
         
            +
                      @config = config
         
     | 
| 
      
 15 
     | 
    
         
            +
                    end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                    # @param source_map [Solargraph::SourceMap]
         
     | 
| 
      
 18 
     | 
    
         
            +
                    # @return [void]
         
     | 
| 
      
 19 
     | 
    
         
            +
                    def correct(_source_map)
         
     | 
| 
      
 20 
     | 
    
         
            +
                      rspec_walker.after_walk do
         
     | 
| 
      
 21 
     | 
    
         
            +
                        if block_given?
         
     | 
| 
      
 22 
     | 
    
         
            +
                          yield namespace_pins.flat_map { |namespace_pin| add_context_dsl_methods(namespace_pin) }
         
     | 
| 
      
 23 
     | 
    
         
            +
                          yield namespace_pins.flat_map { |namespace_pin| add_methods_with_example_binding(namespace_pin) }
         
     | 
| 
      
 24 
     | 
    
         
            +
                        end
         
     | 
| 
      
 25 
     | 
    
         
            +
                      end
         
     | 
| 
      
 26 
     | 
    
         
            +
                    end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                    private
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                    # @return [Solargraph::Rspec::Config]
         
     | 
| 
      
 31 
     | 
    
         
            +
                    attr_reader :config
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                    # RSpec executes example and hook blocks (eg. it, before, after)in the context of the example group.
         
     | 
| 
      
 34 
     | 
    
         
            +
                    # @yieldsef changes the binding of the block to correct class.
         
     | 
| 
      
 35 
     | 
    
         
            +
                    # @return [Array<Solargraph::Pin::Method>]
         
     | 
| 
      
 36 
     | 
    
         
            +
                    def add_methods_with_example_binding(namespace_pin)
         
     | 
| 
      
 37 
     | 
    
         
            +
                      rspec_context_block_methods.map do |method|
         
     | 
| 
      
 38 
     | 
    
         
            +
                        Util.build_public_method(
         
     | 
| 
      
 39 
     | 
    
         
            +
                          namespace_pin,
         
     | 
| 
      
 40 
     | 
    
         
            +
                          method.to_s,
         
     | 
| 
      
 41 
     | 
    
         
            +
                          comments: ["@yieldself [#{namespace_pin.path}]"], # Fixes the binding of the block to the correct class
         
     | 
| 
      
 42 
     | 
    
         
            +
                          scope: :class
         
     | 
| 
      
 43 
     | 
    
         
            +
                        )
         
     | 
| 
      
 44 
     | 
    
         
            +
                      end
         
     | 
| 
      
 45 
     | 
    
         
            +
                    end
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                    # TODO: DSL methods should be defined once in the root example group and extended to all example groups.
         
     | 
| 
      
 48 
     | 
    
         
            +
                    #   Fix this once Solargraph supports extending class methods.
         
     | 
| 
      
 49 
     | 
    
         
            +
                    # @param namespace_pin [Solargraph::Pin::Base]
         
     | 
| 
      
 50 
     | 
    
         
            +
                    # @return [Array<Solargraph::Pin::Base>]
         
     | 
| 
      
 51 
     | 
    
         
            +
                    def add_context_dsl_methods(namespace_pin)
         
     | 
| 
      
 52 
     | 
    
         
            +
                      Rspec::CONTEXT_METHODS.map do |method|
         
     | 
| 
      
 53 
     | 
    
         
            +
                        Util.build_public_method(
         
     | 
| 
      
 54 
     | 
    
         
            +
                          namespace_pin,
         
     | 
| 
      
 55 
     | 
    
         
            +
                          method.to_s,
         
     | 
| 
      
 56 
     | 
    
         
            +
                          scope: :class
         
     | 
| 
      
 57 
     | 
    
         
            +
                        )
         
     | 
| 
      
 58 
     | 
    
         
            +
                      end
         
     | 
| 
      
 59 
     | 
    
         
            +
                    end
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                    # @return [Array<String>]
         
     | 
| 
      
 62 
     | 
    
         
            +
                    def rspec_context_block_methods
         
     | 
| 
      
 63 
     | 
    
         
            +
                      config.let_methods + Rspec::HOOK_METHODS + Rspec::EXAMPLE_METHODS
         
     | 
| 
      
 64 
     | 
    
         
            +
                    end
         
     | 
| 
      
 65 
     | 
    
         
            +
                  end
         
     | 
| 
      
 66 
     | 
    
         
            +
                end
         
     | 
| 
      
 67 
     | 
    
         
            +
              end
         
     | 
| 
      
 68 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,74 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require_relative 'walker_base'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            module Solargraph
         
     | 
| 
      
 6 
     | 
    
         
            +
              module Rspec
         
     | 
| 
      
 7 
     | 
    
         
            +
                module Correctors
         
     | 
| 
      
 8 
     | 
    
         
            +
                  # Sets the correct namespace binding for example group blocks (it, example, etc.) and
         
     | 
| 
      
 9 
     | 
    
         
            +
                  # hook blocks (before, after, around)
         
     | 
| 
      
 10 
     | 
    
         
            +
                  class ExampleAndHookBlocksBindingCorrector < WalkerBase
         
     | 
| 
      
 11 
     | 
    
         
            +
                    # @param source_map [Solargraph::SourceMap]
         
     | 
| 
      
 12 
     | 
    
         
            +
                    # @return [void]
         
     | 
| 
      
 13 
     | 
    
         
            +
                    def correct(source_map)
         
     | 
| 
      
 14 
     | 
    
         
            +
                      rspec_walker.on_example_block do |block_ast|
         
     | 
| 
      
 15 
     | 
    
         
            +
                        bind_closest_namespace(block_ast, source_map)
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                        yield [] if block_given?
         
     | 
| 
      
 18 
     | 
    
         
            +
                      end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                      rspec_walker.on_hook_block do |block_ast|
         
     | 
| 
      
 21 
     | 
    
         
            +
                        bind_closest_namespace(block_ast, source_map)
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                        yield [] if block_given?
         
     | 
| 
      
 24 
     | 
    
         
            +
                      end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                      rspec_walker.on_let_method do |let_method_ast|
         
     | 
| 
      
 27 
     | 
    
         
            +
                        bind_closest_namespace(let_method_ast, source_map)
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                        yield [] if block_given?
         
     | 
| 
      
 30 
     | 
    
         
            +
                      end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                      rspec_walker.on_blocks_in_examples do |block_ast|
         
     | 
| 
      
 33 
     | 
    
         
            +
                        bind_closest_namespace(block_ast, source_map)
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                        yield [] if block_given?
         
     | 
| 
      
 36 
     | 
    
         
            +
                      end
         
     | 
| 
      
 37 
     | 
    
         
            +
                    end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                    private
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                    # @param block_ast [Parser::AST::Node]
         
     | 
| 
      
 42 
     | 
    
         
            +
                    # @param source_map [Solargraph::SourceMap]
         
     | 
| 
      
 43 
     | 
    
         
            +
                    # @return [void]
         
     | 
| 
      
 44 
     | 
    
         
            +
                    def bind_closest_namespace(block_ast, source_map)
         
     | 
| 
      
 45 
     | 
    
         
            +
                      namespace_pin = closest_namespace_pin(namespace_pins, block_ast.loc.line)
         
     | 
| 
      
 46 
     | 
    
         
            +
                      return unless namespace_pin
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                      original_block_pin = source_map.locate_block_pin(block_ast.location.begin.line,
         
     | 
| 
      
 49 
     | 
    
         
            +
                                                                       block_ast.location.begin.column)
         
     | 
| 
      
 50 
     | 
    
         
            +
                      original_block_pin_index = source_map.pins.index(original_block_pin)
         
     | 
| 
      
 51 
     | 
    
         
            +
                      fixed_namespace_block_pin = Solargraph::Pin::Block.new(
         
     | 
| 
      
 52 
     | 
    
         
            +
                        closure: example_run_method(namespace_pin),
         
     | 
| 
      
 53 
     | 
    
         
            +
                        location: original_block_pin.location,
         
     | 
| 
      
 54 
     | 
    
         
            +
                        receiver: original_block_pin.receiver,
         
     | 
| 
      
 55 
     | 
    
         
            +
                        scope: original_block_pin.scope
         
     | 
| 
      
 56 
     | 
    
         
            +
                      )
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                      source_map.pins[original_block_pin_index] = fixed_namespace_block_pin
         
     | 
| 
      
 59 
     | 
    
         
            +
                    end
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                    # @param namespace_pin [Solargraph::Pin::Namespace]
         
     | 
| 
      
 62 
     | 
    
         
            +
                    # @return [Solargraph::Pin::Method]
         
     | 
| 
      
 63 
     | 
    
         
            +
                    def example_run_method(namespace_pin)
         
     | 
| 
      
 64 
     | 
    
         
            +
                      Util.build_public_method(
         
     | 
| 
      
 65 
     | 
    
         
            +
                        namespace_pin,
         
     | 
| 
      
 66 
     | 
    
         
            +
                        'run',
         
     | 
| 
      
 67 
     | 
    
         
            +
                        # https://github.com/rspec/rspec-core/blob/main/lib/rspec/core/example.rb#L246
         
     | 
| 
      
 68 
     | 
    
         
            +
                        location: Solargraph::Location.new('lib/rspec/core/example.rb', Solargraph::Range.from_to(246, 1, 297, 1))
         
     | 
| 
      
 69 
     | 
    
         
            +
                      )
         
     | 
| 
      
 70 
     | 
    
         
            +
                    end
         
     | 
| 
      
 71 
     | 
    
         
            +
                  end
         
     | 
| 
      
 72 
     | 
    
         
            +
                end
         
     | 
| 
      
 73 
     | 
    
         
            +
              end
         
     | 
| 
      
 74 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,48 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require_relative 'let_methods_corrector'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            module Solargraph
         
     | 
| 
      
 6 
     | 
    
         
            +
              module Rspec
         
     | 
| 
      
 7 
     | 
    
         
            +
                module Correctors
         
     | 
| 
      
 8 
     | 
    
         
            +
                  # Defines let-like methods in the example group block
         
     | 
| 
      
 9 
     | 
    
         
            +
                  class ImplicitSubjectMethodCorrector < Base
         
     | 
| 
      
 10 
     | 
    
         
            +
                    # @return [Pin::Method]
         
     | 
| 
      
 11 
     | 
    
         
            +
                    attr_reader :described_class_pin
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                    # @param namespace_pins [Array<Pin::Namespace>]
         
     | 
| 
      
 14 
     | 
    
         
            +
                    # @param described_class_pin [Pin::Method]
         
     | 
| 
      
 15 
     | 
    
         
            +
                    def initialize(namespace_pins:, described_class_pin:)
         
     | 
| 
      
 16 
     | 
    
         
            +
                      super(namespace_pins: namespace_pins)
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                      @described_class_pin = described_class_pin
         
     | 
| 
      
 19 
     | 
    
         
            +
                    end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                    # @param source_map [Solargraph::SourceMap]
         
     | 
| 
      
 22 
     | 
    
         
            +
                    # @return [void]
         
     | 
| 
      
 23 
     | 
    
         
            +
                    def correct(_source_map)
         
     | 
| 
      
 24 
     | 
    
         
            +
                      namespace_pin = closest_namespace_pin(namespace_pins, described_class_pin.location.range.start.line)
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                      yield [implicit_subject_pin(described_class_pin, namespace_pin)] if block_given? && namespace_pin
         
     | 
| 
      
 27 
     | 
    
         
            +
                    end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                    private
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                    # @param described_class_pin [Pin::Method]
         
     | 
| 
      
 32 
     | 
    
         
            +
                    # @param namespace_pin [Pin::Namespace]
         
     | 
| 
      
 33 
     | 
    
         
            +
                    # @return [Pin::Method]
         
     | 
| 
      
 34 
     | 
    
         
            +
                    def implicit_subject_pin(described_class_pin, namespace_pin)
         
     | 
| 
      
 35 
     | 
    
         
            +
                      described_class = described_class_pin.return_type.first.subtypes.first.name
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                      Util.build_public_method(
         
     | 
| 
      
 38 
     | 
    
         
            +
                        namespace_pin,
         
     | 
| 
      
 39 
     | 
    
         
            +
                        'subject',
         
     | 
| 
      
 40 
     | 
    
         
            +
                        types: [described_class],
         
     | 
| 
      
 41 
     | 
    
         
            +
                        location: described_class_pin.location,
         
     | 
| 
      
 42 
     | 
    
         
            +
                        scope: :instance
         
     | 
| 
      
 43 
     | 
    
         
            +
                      )
         
     | 
| 
      
 44 
     | 
    
         
            +
                    end
         
     | 
| 
      
 45 
     | 
    
         
            +
                  end
         
     | 
| 
      
 46 
     | 
    
         
            +
                end
         
     | 
| 
      
 47 
     | 
    
         
            +
              end
         
     | 
| 
      
 48 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,44 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require_relative 'walker_base'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            module Solargraph
         
     | 
| 
      
 6 
     | 
    
         
            +
              module Rspec
         
     | 
| 
      
 7 
     | 
    
         
            +
                module Correctors
         
     | 
| 
      
 8 
     | 
    
         
            +
                  # Defines let-like methods in the example group block
         
     | 
| 
      
 9 
     | 
    
         
            +
                  class LetMethodsCorrector < WalkerBase
         
     | 
| 
      
 10 
     | 
    
         
            +
                    # @param source_map [Solargraph::SourceMap]
         
     | 
| 
      
 11 
     | 
    
         
            +
                    # @return [void]
         
     | 
| 
      
 12 
     | 
    
         
            +
                    def correct(_source_map)
         
     | 
| 
      
 13 
     | 
    
         
            +
                      rspec_walker.on_let_method do |ast|
         
     | 
| 
      
 14 
     | 
    
         
            +
                        namespace_pin = closest_namespace_pin(namespace_pins, ast.loc.line)
         
     | 
| 
      
 15 
     | 
    
         
            +
                        next unless namespace_pin
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                        pin = rspec_let_method(namespace_pin, ast)
         
     | 
| 
      
 18 
     | 
    
         
            +
                        yield [pin] if block_given?
         
     | 
| 
      
 19 
     | 
    
         
            +
                      end
         
     | 
| 
      
 20 
     | 
    
         
            +
                    end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                    private
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                    # @param namespace [Pin::Namespace]
         
     | 
| 
      
 25 
     | 
    
         
            +
                    # @param ast [Parser::AST::Node]
         
     | 
| 
      
 26 
     | 
    
         
            +
                    # @param types [Array<String>, nil]
         
     | 
| 
      
 27 
     | 
    
         
            +
                    # @return [Pin::Method, nil]
         
     | 
| 
      
 28 
     | 
    
         
            +
                    def rspec_let_method(namespace, ast, types: nil)
         
     | 
| 
      
 29 
     | 
    
         
            +
                      return unless ast.children
         
     | 
| 
      
 30 
     | 
    
         
            +
                      return unless ast.children[2]&.children
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                      method_name = ast.children[2].children[0]&.to_s or return
         
     | 
| 
      
 33 
     | 
    
         
            +
                      Util.build_public_method(
         
     | 
| 
      
 34 
     | 
    
         
            +
                        namespace,
         
     | 
| 
      
 35 
     | 
    
         
            +
                        method_name,
         
     | 
| 
      
 36 
     | 
    
         
            +
                        types: types,
         
     | 
| 
      
 37 
     | 
    
         
            +
                        location: Util.build_location(ast, namespace.filename),
         
     | 
| 
      
 38 
     | 
    
         
            +
                        scope: :instance
         
     | 
| 
      
 39 
     | 
    
         
            +
                      )
         
     | 
| 
      
 40 
     | 
    
         
            +
                    end
         
     | 
| 
      
 41 
     | 
    
         
            +
                  end
         
     | 
| 
      
 42 
     | 
    
         
            +
                end
         
     | 
| 
      
 43 
     | 
    
         
            +
              end
         
     | 
| 
      
 44 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,24 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require_relative 'let_methods_corrector'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            module Solargraph
         
     | 
| 
      
 6 
     | 
    
         
            +
              module Rspec
         
     | 
| 
      
 7 
     | 
    
         
            +
                module Correctors
         
     | 
| 
      
 8 
     | 
    
         
            +
                  # Defines let-like methods in the example group block
         
     | 
| 
      
 9 
     | 
    
         
            +
                  class SubjectMethodCorrector < LetMethodsCorrector
         
     | 
| 
      
 10 
     | 
    
         
            +
                    # @param source_map [Solargraph::SourceMap]
         
     | 
| 
      
 11 
     | 
    
         
            +
                    # @return [void]
         
     | 
| 
      
 12 
     | 
    
         
            +
                    def correct(_source_map)
         
     | 
| 
      
 13 
     | 
    
         
            +
                      rspec_walker.on_subject do |ast|
         
     | 
| 
      
 14 
     | 
    
         
            +
                        namespace_pin = closest_namespace_pin(namespace_pins, ast.loc.line)
         
     | 
| 
      
 15 
     | 
    
         
            +
                        next unless namespace_pin
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                        subject_pin = rspec_let_method(namespace_pin, ast)
         
     | 
| 
      
 18 
     | 
    
         
            +
                        yield [subject_pin].compact if block_given?
         
     | 
| 
      
 19 
     | 
    
         
            +
                      end
         
     | 
| 
      
 20 
     | 
    
         
            +
                    end
         
     | 
| 
      
 21 
     | 
    
         
            +
                  end
         
     | 
| 
      
 22 
     | 
    
         
            +
                end
         
     | 
| 
      
 23 
     | 
    
         
            +
              end
         
     | 
| 
      
 24 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,27 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require_relative 'base'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            # A corrector that walks through RSpec AST nodes and corrects them
         
     | 
| 
      
 6 
     | 
    
         
            +
            module Solargraph
         
     | 
| 
      
 7 
     | 
    
         
            +
              module Rspec
         
     | 
| 
      
 8 
     | 
    
         
            +
                module Correctors
         
     | 
| 
      
 9 
     | 
    
         
            +
                  # A corrector of RSpec parsed pins by Solargraph
         
     | 
| 
      
 10 
     | 
    
         
            +
                  # @abstract
         
     | 
| 
      
 11 
     | 
    
         
            +
                  class WalkerBase < Base
         
     | 
| 
      
 12 
     | 
    
         
            +
                    # @return [Array<Solargraph::Pin::Namespace>]
         
     | 
| 
      
 13 
     | 
    
         
            +
                    attr_reader :namespace_pins
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                    # @return [Solargraph::Rspec::SpecWalker]
         
     | 
| 
      
 16 
     | 
    
         
            +
                    attr_reader :rspec_walker
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                    # @param namespace_pins [Array<Solargraph::Pin::Base>]
         
     | 
| 
      
 19 
     | 
    
         
            +
                    # @param rspec_walker [Solargraph::Rspec::SpecWalker]
         
     | 
| 
      
 20 
     | 
    
         
            +
                    def initialize(namespace_pins:, rspec_walker:)
         
     | 
| 
      
 21 
     | 
    
         
            +
                      super(namespace_pins: namespace_pins)
         
     | 
| 
      
 22 
     | 
    
         
            +
                      @rspec_walker = rspec_walker
         
     | 
| 
      
 23 
     | 
    
         
            +
                    end
         
     | 
| 
      
 24 
     | 
    
         
            +
                  end
         
     | 
| 
      
 25 
     | 
    
         
            +
                end
         
     | 
| 
      
 26 
     | 
    
         
            +
              end
         
     | 
| 
      
 27 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,247 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require_relative 'walker'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            module Solargraph
         
     | 
| 
      
 6 
     | 
    
         
            +
              module Rspec
         
     | 
| 
      
 7 
     | 
    
         
            +
                class SpecWalker
         
     | 
| 
      
 8 
     | 
    
         
            +
                  # @param source_map [SourceMap]
         
     | 
| 
      
 9 
     | 
    
         
            +
                  # @param config [Config]
         
     | 
| 
      
 10 
     | 
    
         
            +
                  def initialize(source_map:, config:)
         
     | 
| 
      
 11 
     | 
    
         
            +
                    @source_map = source_map
         
     | 
| 
      
 12 
     | 
    
         
            +
                    @config = config
         
     | 
| 
      
 13 
     | 
    
         
            +
                    @walker = Rspec::Walker.from_source(source_map.source)
         
     | 
| 
      
 14 
     | 
    
         
            +
                    @handlers = {
         
     | 
| 
      
 15 
     | 
    
         
            +
                      on_described_class: [],
         
     | 
| 
      
 16 
     | 
    
         
            +
                      on_let_method: [],
         
     | 
| 
      
 17 
     | 
    
         
            +
                      on_subject: [],
         
     | 
| 
      
 18 
     | 
    
         
            +
                      on_each_context_block: [],
         
     | 
| 
      
 19 
     | 
    
         
            +
                      on_example_block: [],
         
     | 
| 
      
 20 
     | 
    
         
            +
                      on_hook_block: [],
         
     | 
| 
      
 21 
     | 
    
         
            +
                      on_blocks_in_examples: [],
         
     | 
| 
      
 22 
     | 
    
         
            +
                      after_walk: []
         
     | 
| 
      
 23 
     | 
    
         
            +
                    }
         
     | 
| 
      
 24 
     | 
    
         
            +
                  end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                  # @return [Walker]
         
     | 
| 
      
 27 
     | 
    
         
            +
                  attr_reader :walker
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                  # @return [Config]
         
     | 
| 
      
 30 
     | 
    
         
            +
                  attr_reader :config
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                  # @param block [Proc]
         
     | 
| 
      
 33 
     | 
    
         
            +
                  # @return [void]
         
     | 
| 
      
 34 
     | 
    
         
            +
                  def on_described_class(&block)
         
     | 
| 
      
 35 
     | 
    
         
            +
                    @handlers[:on_described_class] << block
         
     | 
| 
      
 36 
     | 
    
         
            +
                  end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                  # @param block [Proc]
         
     | 
| 
      
 39 
     | 
    
         
            +
                  # @return [void]
         
     | 
| 
      
 40 
     | 
    
         
            +
                  def on_let_method(&block)
         
     | 
| 
      
 41 
     | 
    
         
            +
                    @handlers[:on_let_method] << block
         
     | 
| 
      
 42 
     | 
    
         
            +
                  end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                  # @param block [Proc]
         
     | 
| 
      
 45 
     | 
    
         
            +
                  # @return [void]
         
     | 
| 
      
 46 
     | 
    
         
            +
                  def on_subject(&block)
         
     | 
| 
      
 47 
     | 
    
         
            +
                    @handlers[:on_subject] << block
         
     | 
| 
      
 48 
     | 
    
         
            +
                  end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                  # @param block [Proc]
         
     | 
| 
      
 51 
     | 
    
         
            +
                  # @return [void]
         
     | 
| 
      
 52 
     | 
    
         
            +
                  def on_each_context_block(&block)
         
     | 
| 
      
 53 
     | 
    
         
            +
                    @handlers[:on_each_context_block] << block
         
     | 
| 
      
 54 
     | 
    
         
            +
                  end
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                  #
         
     | 
| 
      
 57 
     | 
    
         
            +
                  # @param block [Proc]
         
     | 
| 
      
 58 
     | 
    
         
            +
                  # @return [void]
         
     | 
| 
      
 59 
     | 
    
         
            +
                  def on_example_block(&block)
         
     | 
| 
      
 60 
     | 
    
         
            +
                    @handlers[:on_example_block] << block
         
     | 
| 
      
 61 
     | 
    
         
            +
                  end
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                  # @param block [Proc]
         
     | 
| 
      
 64 
     | 
    
         
            +
                  # @return [void]
         
     | 
| 
      
 65 
     | 
    
         
            +
                  def on_hook_block(&block)
         
     | 
| 
      
 66 
     | 
    
         
            +
                    @handlers[:on_hook_block] << block
         
     | 
| 
      
 67 
     | 
    
         
            +
                  end
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                  # @param block [Proc]
         
     | 
| 
      
 70 
     | 
    
         
            +
                  # @return [void]
         
     | 
| 
      
 71 
     | 
    
         
            +
                  def on_blocks_in_examples(&block)
         
     | 
| 
      
 72 
     | 
    
         
            +
                    @handlers[:on_blocks_in_examples] << block
         
     | 
| 
      
 73 
     | 
    
         
            +
                  end
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                  # @param block [Proc]
         
     | 
| 
      
 76 
     | 
    
         
            +
                  # @return [void]
         
     | 
| 
      
 77 
     | 
    
         
            +
                  def after_walk(&block)
         
     | 
| 
      
 78 
     | 
    
         
            +
                    @handlers[:after_walk] << block
         
     | 
| 
      
 79 
     | 
    
         
            +
                  end
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
                  # @return [void]
         
     | 
| 
      
 82 
     | 
    
         
            +
                  def walk!
         
     | 
| 
      
 83 
     | 
    
         
            +
                    each_context_block(@walker.ast, Rspec::ROOT_NAMESPACE) do |namespace_name, ast|
         
     | 
| 
      
 84 
     | 
    
         
            +
                      @handlers[:on_each_context_block].each do |handler|
         
     | 
| 
      
 85 
     | 
    
         
            +
                        handler.call(namespace_name, ast)
         
     | 
| 
      
 86 
     | 
    
         
            +
                      end
         
     | 
| 
      
 87 
     | 
    
         
            +
                    end
         
     | 
| 
      
 88 
     | 
    
         
            +
             
     | 
| 
      
 89 
     | 
    
         
            +
                    rspec_const = ::Parser::AST::Node.new(:const, [nil, :RSpec])
         
     | 
| 
      
 90 
     | 
    
         
            +
                    walker.on :send, [rspec_const, :describe, :any] do |ast|
         
     | 
| 
      
 91 
     | 
    
         
            +
                      @handlers[:on_described_class].each do |handler|
         
     | 
| 
      
 92 
     | 
    
         
            +
                        class_ast = ast.children[2]
         
     | 
| 
      
 93 
     | 
    
         
            +
                        next unless class_ast
         
     | 
| 
      
 94 
     | 
    
         
            +
             
     | 
| 
      
 95 
     | 
    
         
            +
                        class_name = full_constant_name(class_ast)
         
     | 
| 
      
 96 
     | 
    
         
            +
                        handler.call(class_ast, class_name)
         
     | 
| 
      
 97 
     | 
    
         
            +
                      end
         
     | 
| 
      
 98 
     | 
    
         
            +
                    end
         
     | 
| 
      
 99 
     | 
    
         
            +
             
     | 
| 
      
 100 
     | 
    
         
            +
                    config.let_methods.each do |let_method|
         
     | 
| 
      
 101 
     | 
    
         
            +
                      walker.on :send, [nil, let_method] do |ast|
         
     | 
| 
      
 102 
     | 
    
         
            +
                        @handlers[:on_let_method].each do |handler|
         
     | 
| 
      
 103 
     | 
    
         
            +
                          handler.call(ast)
         
     | 
| 
      
 104 
     | 
    
         
            +
                        end
         
     | 
| 
      
 105 
     | 
    
         
            +
                      end
         
     | 
| 
      
 106 
     | 
    
         
            +
                    end
         
     | 
| 
      
 107 
     | 
    
         
            +
             
     | 
| 
      
 108 
     | 
    
         
            +
                    walker.on :send, [nil, :subject] do |ast|
         
     | 
| 
      
 109 
     | 
    
         
            +
                      @handlers[:on_subject].each do |handler|
         
     | 
| 
      
 110 
     | 
    
         
            +
                        handler.call(ast)
         
     | 
| 
      
 111 
     | 
    
         
            +
                      end
         
     | 
| 
      
 112 
     | 
    
         
            +
                    end
         
     | 
| 
      
 113 
     | 
    
         
            +
             
     | 
| 
      
 114 
     | 
    
         
            +
                    walker.on :block do |block_ast|
         
     | 
| 
      
 115 
     | 
    
         
            +
                      next if block_ast.children.first.type != :send
         
     | 
| 
      
 116 
     | 
    
         
            +
             
     | 
| 
      
 117 
     | 
    
         
            +
                      method_ast = block_ast.children.first
         
     | 
| 
      
 118 
     | 
    
         
            +
                      method_name = method_ast.children[1]
         
     | 
| 
      
 119 
     | 
    
         
            +
                      next unless Rspec::EXAMPLE_METHODS.include?(method_name.to_s)
         
     | 
| 
      
 120 
     | 
    
         
            +
             
     | 
| 
      
 121 
     | 
    
         
            +
                      @handlers[:on_example_block].each do |handler|
         
     | 
| 
      
 122 
     | 
    
         
            +
                        handler.call(block_ast)
         
     | 
| 
      
 123 
     | 
    
         
            +
                      end
         
     | 
| 
      
 124 
     | 
    
         
            +
             
     | 
| 
      
 125 
     | 
    
         
            +
                      # @param blocks_in_examples [Parser::AST::Node]
         
     | 
| 
      
 126 
     | 
    
         
            +
                      each_block(block_ast.children[2]) do |blocks_in_examples|
         
     | 
| 
      
 127 
     | 
    
         
            +
                        @handlers[:on_blocks_in_examples].each do |handler|
         
     | 
| 
      
 128 
     | 
    
         
            +
                          handler.call(blocks_in_examples)
         
     | 
| 
      
 129 
     | 
    
         
            +
                        end
         
     | 
| 
      
 130 
     | 
    
         
            +
                      end
         
     | 
| 
      
 131 
     | 
    
         
            +
                    end
         
     | 
| 
      
 132 
     | 
    
         
            +
             
     | 
| 
      
 133 
     | 
    
         
            +
                    walker.on :block do |block_ast|
         
     | 
| 
      
 134 
     | 
    
         
            +
                      next if block_ast.children.first.type != :send
         
     | 
| 
      
 135 
     | 
    
         
            +
             
     | 
| 
      
 136 
     | 
    
         
            +
                      method_ast = block_ast.children.first
         
     | 
| 
      
 137 
     | 
    
         
            +
                      method_name = method_ast.children[1]
         
     | 
| 
      
 138 
     | 
    
         
            +
                      next unless Rspec::HOOK_METHODS.include?(method_name.to_s)
         
     | 
| 
      
 139 
     | 
    
         
            +
             
     | 
| 
      
 140 
     | 
    
         
            +
                      @handlers[:on_hook_block].each do |handler|
         
     | 
| 
      
 141 
     | 
    
         
            +
                        handler.call(block_ast)
         
     | 
| 
      
 142 
     | 
    
         
            +
                      end
         
     | 
| 
      
 143 
     | 
    
         
            +
             
     | 
| 
      
 144 
     | 
    
         
            +
                      # @param blocks_in_examples [Parser::AST::Node]
         
     | 
| 
      
 145 
     | 
    
         
            +
                      each_block(block_ast.children[2]) do |blocks_in_examples|
         
     | 
| 
      
 146 
     | 
    
         
            +
                        @handlers[:on_blocks_in_examples].each do |handler|
         
     | 
| 
      
 147 
     | 
    
         
            +
                          handler.call(blocks_in_examples)
         
     | 
| 
      
 148 
     | 
    
         
            +
                        end
         
     | 
| 
      
 149 
     | 
    
         
            +
                      end
         
     | 
| 
      
 150 
     | 
    
         
            +
                    end
         
     | 
| 
      
 151 
     | 
    
         
            +
             
     | 
| 
      
 152 
     | 
    
         
            +
                    walker.walk
         
     | 
| 
      
 153 
     | 
    
         
            +
             
     | 
| 
      
 154 
     | 
    
         
            +
                    @handlers[:after_walk].each(&:call)
         
     | 
| 
      
 155 
     | 
    
         
            +
                  end
         
     | 
| 
      
 156 
     | 
    
         
            +
             
     | 
| 
      
 157 
     | 
    
         
            +
                  private
         
     | 
| 
      
 158 
     | 
    
         
            +
             
     | 
| 
      
 159 
     | 
    
         
            +
                  # @param ast [Parser::AST::Node]
         
     | 
| 
      
 160 
     | 
    
         
            +
                  # @param parent_result [Object]
         
     | 
| 
      
 161 
     | 
    
         
            +
                  def each_block(ast, parent_result = nil, &block)
         
     | 
| 
      
 162 
     | 
    
         
            +
                    return unless ast.is_a?(::Parser::AST::Node)
         
     | 
| 
      
 163 
     | 
    
         
            +
             
     | 
| 
      
 164 
     | 
    
         
            +
                    is_a_block = ast.type == :block && ast.children[0].type == :send
         
     | 
| 
      
 165 
     | 
    
         
            +
             
     | 
| 
      
 166 
     | 
    
         
            +
                    if is_a_block
         
     | 
| 
      
 167 
     | 
    
         
            +
                      result = block&.call(ast, parent_result)
         
     | 
| 
      
 168 
     | 
    
         
            +
                      parent_result = result if result
         
     | 
| 
      
 169 
     | 
    
         
            +
                    end
         
     | 
| 
      
 170 
     | 
    
         
            +
             
     | 
| 
      
 171 
     | 
    
         
            +
                    ast.children.each { |child| each_block(child, parent_result, &block) }
         
     | 
| 
      
 172 
     | 
    
         
            +
                  end
         
     | 
| 
      
 173 
     | 
    
         
            +
             
     | 
| 
      
 174 
     | 
    
         
            +
                  # Find all describe/context blocks in the AST.
         
     | 
| 
      
 175 
     | 
    
         
            +
                  # @param ast [Parser::AST::Node]
         
     | 
| 
      
 176 
     | 
    
         
            +
                  # @yield [String, Parser::AST::Node]
         
     | 
| 
      
 177 
     | 
    
         
            +
                  def each_context_block(ast, root_namespace = Rspec::ROOT_NAMESPACE, &block)
         
     | 
| 
      
 178 
     | 
    
         
            +
                    each_block(ast, root_namespace) do |block_ast, parent_namespace|
         
     | 
| 
      
 179 
     | 
    
         
            +
                      is_a_context = %i[describe context].include?(block_ast.children[0].children[1])
         
     | 
| 
      
 180 
     | 
    
         
            +
             
     | 
| 
      
 181 
     | 
    
         
            +
                      next unless is_a_context
         
     | 
| 
      
 182 
     | 
    
         
            +
             
     | 
| 
      
 183 
     | 
    
         
            +
                      description_node = block_ast.children[0].children[2]
         
     | 
| 
      
 184 
     | 
    
         
            +
                      block_name = rspec_describe_class_name(description_node)
         
     | 
| 
      
 185 
     | 
    
         
            +
                      next unless block_name
         
     | 
| 
      
 186 
     | 
    
         
            +
             
     | 
| 
      
 187 
     | 
    
         
            +
                      parent_namespace = namespace_name = "#{parent_namespace}::#{block_name}"
         
     | 
| 
      
 188 
     | 
    
         
            +
                      block&.call(namespace_name, block_ast)
         
     | 
| 
      
 189 
     | 
    
         
            +
                      next parent_namespace
         
     | 
| 
      
 190 
     | 
    
         
            +
                    end
         
     | 
| 
      
 191 
     | 
    
         
            +
                  end
         
     | 
| 
      
 192 
     | 
    
         
            +
             
     | 
| 
      
 193 
     | 
    
         
            +
                  # @param ast [Parser::AST::Node]
         
     | 
| 
      
 194 
     | 
    
         
            +
                  # @return [String, nil]
         
     | 
| 
      
 195 
     | 
    
         
            +
                  def rspec_describe_class_name(ast)
         
     | 
| 
      
 196 
     | 
    
         
            +
                    if ast.type == :str
         
     | 
| 
      
 197 
     | 
    
         
            +
                      string_to_const_name(ast)
         
     | 
| 
      
 198 
     | 
    
         
            +
                    elsif ast.type == :const
         
     | 
| 
      
 199 
     | 
    
         
            +
                      full_constant_name(ast).gsub('::', '')
         
     | 
| 
      
 200 
     | 
    
         
            +
                    else
         
     | 
| 
      
 201 
     | 
    
         
            +
                      Solargraph.logger.warn "[RSpec] Unexpected AST type #{ast.type}"
         
     | 
| 
      
 202 
     | 
    
         
            +
                      nil
         
     | 
| 
      
 203 
     | 
    
         
            +
                    end
         
     | 
| 
      
 204 
     | 
    
         
            +
                  end
         
     | 
| 
      
 205 
     | 
    
         
            +
             
     | 
| 
      
 206 
     | 
    
         
            +
                  # @param ast [Parser::AST::Node]
         
     | 
| 
      
 207 
     | 
    
         
            +
                  # @return [String]
         
     | 
| 
      
 208 
     | 
    
         
            +
                  def full_constant_name(ast)
         
     | 
| 
      
 209 
     | 
    
         
            +
                    raise 'Node is not a constant' unless ast.type == :const
         
     | 
| 
      
 210 
     | 
    
         
            +
             
     | 
| 
      
 211 
     | 
    
         
            +
                    name = ast.children[1].to_s
         
     | 
| 
      
 212 
     | 
    
         
            +
                    if ast.children[0].nil?
         
     | 
| 
      
 213 
     | 
    
         
            +
                      name
         
     | 
| 
      
 214 
     | 
    
         
            +
                    else
         
     | 
| 
      
 215 
     | 
    
         
            +
                      "#{full_constant_name(ast.children[0])}::#{name}"
         
     | 
| 
      
 216 
     | 
    
         
            +
                    end
         
     | 
| 
      
 217 
     | 
    
         
            +
                  end
         
     | 
| 
      
 218 
     | 
    
         
            +
             
     | 
| 
      
 219 
     | 
    
         
            +
                  # @see https://github.com/rspec/rspec-core/blob/1eeadce5aa7137ead054783c31ff35cbfe9d07cc/lib/rspec/core/example_group.rb#L862
         
     | 
| 
      
 220 
     | 
    
         
            +
                  # @param ast [Parser::AST::Node]
         
     | 
| 
      
 221 
     | 
    
         
            +
                  # @return [String]
         
     | 
| 
      
 222 
     | 
    
         
            +
                  def string_to_const_name(string_ast)
         
     | 
| 
      
 223 
     | 
    
         
            +
                    return unless string_ast.type == :str
         
     | 
| 
      
 224 
     | 
    
         
            +
             
     | 
| 
      
 225 
     | 
    
         
            +
                    name = string_ast.children[0]
         
     | 
| 
      
 226 
     | 
    
         
            +
                    return 'Anonymous'.dup if name.empty?
         
     | 
| 
      
 227 
     | 
    
         
            +
             
     | 
| 
      
 228 
     | 
    
         
            +
                    # Convert to CamelCase.
         
     | 
| 
      
 229 
     | 
    
         
            +
                    name = +" #{name}"
         
     | 
| 
      
 230 
     | 
    
         
            +
                    name.gsub!(/[^0-9a-zA-Z]+([0-9a-zA-Z])/) do
         
     | 
| 
      
 231 
     | 
    
         
            +
                      match = ::Regexp.last_match[1]
         
     | 
| 
      
 232 
     | 
    
         
            +
                      match.upcase!
         
     | 
| 
      
 233 
     | 
    
         
            +
                      match
         
     | 
| 
      
 234 
     | 
    
         
            +
                    end
         
     | 
| 
      
 235 
     | 
    
         
            +
             
     | 
| 
      
 236 
     | 
    
         
            +
                    name.lstrip! # Remove leading whitespace
         
     | 
| 
      
 237 
     | 
    
         
            +
                    name.gsub!(/\W/, '') # JRuby, RBX and others don't like non-ascii in const names
         
     | 
| 
      
 238 
     | 
    
         
            +
             
     | 
| 
      
 239 
     | 
    
         
            +
                    # Ruby requires first const letter to be A-Z. Use `Nested`
         
     | 
| 
      
 240 
     | 
    
         
            +
                    # as necessary to enforce that.
         
     | 
| 
      
 241 
     | 
    
         
            +
                    name.gsub!(/\A([^A-Z]|\z)/, 'Nested\1')
         
     | 
| 
      
 242 
     | 
    
         
            +
             
     | 
| 
      
 243 
     | 
    
         
            +
                    name
         
     | 
| 
      
 244 
     | 
    
         
            +
                  end
         
     | 
| 
      
 245 
     | 
    
         
            +
                end
         
     | 
| 
      
 246 
     | 
    
         
            +
              end
         
     | 
| 
      
 247 
     | 
    
         
            +
            end
         
     |