Class: RuboCop::Cop::RSpec::UnusedLet::ScopeStack

Inherits:
Object
  • Object
show all
Defined in:
lib/rubocop/cop/rspec/unused_let/scope_stack.rb,
sig/rubocop/cop/rspec/unused_let/scope_stack.rbs

Overview

The cop's traversal state: the scopes currently being descended into, innermost last. Resolves a scope's references against its ancestry as it is entered, and reports which of the scopes still on the stack are shared groups once one is left, so the cop itself is left with only lifecycle and reporting.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(check_helper_specs:) ⇒ ScopeStack

Returns a new instance of ScopeStack.

RBS:

  • check_helper_specs: bool

Parameters:

  • check_helper_specs: (Boolean)


14
15
16
17
# File 'lib/rubocop/cop/rspec/unused_let/scope_stack.rb', line 14

def initialize(check_helper_specs:) #: void
  @check_helper_specs = check_helper_specs
  @stack = []
end

Instance Attribute Details

#check_helper_specsBoolean (readonly)

Signature:

  • bool

Returns:

  • (Boolean)


46
47
48
# File 'lib/rubocop/cop/rspec/unused_let/scope_stack.rb', line 46

def check_helper_specs
  @check_helper_specs
end

#stackArray[Scope] (readonly)

Signature:

  • Array[Scope]

Returns:



45
46
47
# File 'lib/rubocop/cop/rspec/unused_let/scope_stack.rb', line 45

def stack
  @stack
end

Instance Method Details

#helper_spec?(scope) ⇒ Boolean

The effective type: is the innermost one in the group's ancestry (each scope already carries its explicit type, or :helper inferred from a spec/helpers location).

RBS:

  • scope: Scope

Parameters:

Returns:

  • (Boolean)


97
98
99
# File 'lib/rubocop/cop/rspec/unused_let/scope_stack.rb', line 97

def helper_spec?(scope) #: bool
  [scope, *stack].filter_map(&:type).first == :helper
end

#ignore_helper_spec?(scope) ⇒ Boolean

A helper spec's auto-included module lives in another file and may reference any let in scope, so every definition is treated as referenced unless CheckHelperSpecs opts in. This judgement is independent of shared inclusions.

RBS:

  • scope: Scope

Parameters:

Returns:

  • (Boolean)


86
87
88
89
90
# File 'lib/rubocop/cop/rspec/unused_let/scope_stack.rb', line 86

def ignore_helper_spec?(scope) #: bool
  return false if check_helper_specs

  helper_spec?(scope)
end

#mark(scope) ⇒ void

This method returns an undefined value.

Resolve scope's references against its ancestors and mark every definition they reach. scope is not on the stack yet, so stack is exactly its ancestors.

RBS:

  • scope: Scope

Parameters:



53
54
55
56
57
# File 'lib/rubocop/cop/rspec/unused_let/scope_stack.rb', line 53

def mark(scope) #: void
  mark_upward(scope)
  mark_downward(scope)
  mark_referenced_all(scope) if scope.inclusion || ignore_helper_spec?(scope)
end

#mark_downward(scope) ⇒ void

This method returns an undefined value.

A helper body in an enclosing group can reference a let defined here, since it runs in the example's scope.

RBS:

  • scope: Scope

Parameters:



74
75
76
77
78
# File 'lib/rubocop/cop/rspec/unused_let/scope_stack.rb', line 74

def mark_downward(scope) #: void
  scope.defined_names.each do |name|
    scope.mark_referenced(name) if stack.any? { _1.refs.include?(name) }
  end
end

#mark_referenced_all(scope) ⇒ void

This method returns an undefined value.

Treat every let visible in scope (its own and its ancestors') as referenced. Used where references can't be fully seen from this file: a shared inclusion, or a helper spec's auto-included module.

RBS:

  • scope: Scope

Parameters:



107
108
109
110
111
# File 'lib/rubocop/cop/rspec/unused_let/scope_stack.rb', line 107

def mark_referenced_all(scope) #: void
  [scope, *stack].each do |group|
    group.defined_names.each { group.mark_referenced(_1) }
  end
end

#mark_upward(scope) ⇒ void

This method returns an undefined value.

A reference made in this group, whether in an example or a helper body, reaches a let defined here or in an enclosing group.

RBS:

  • scope: Scope

Parameters:



63
64
65
66
67
68
# File 'lib/rubocop/cop/rspec/unused_let/scope_stack.rb', line 63

def mark_upward(scope) #: void
  (scope.refs | scope.refs_in_example).each do |name|
    scope.mark_referenced(name)
    stack.each { _1.mark_referenced(name) }
  end
end

#popScope?

Pop and return the innermost scope, or nil if the stack is empty.

Returns:



30
31
32
# File 'lib/rubocop/cop/rspec/unused_let/scope_stack.rb', line 30

def pop #: Scope?
  stack.pop
end

#push(scope) ⇒ void

This method returns an undefined value.

Resolve scope's references against its ancestors, then push it as the new innermost scope.

RBS:

  • scope: Scope

Parameters:



23
24
25
26
# File 'lib/rubocop/cop/rspec/unused_let/scope_stack.rb', line 23

def push(scope) #: void
  mark(scope)
  stack.push(scope)
end

#shared_groups_for(scope) ⇒ Array[Scope]

The shared blocks among scope and the scopes still on the stack (its ancestors), in no particular order. Call once scope itself has been popped.

RBS:

  • scope: Scope

Parameters:

Returns:



39
40
41
# File 'lib/rubocop/cop/rspec/unused_let/scope_stack.rb', line 39

def shared_groups_for(scope) #: Array[Scope]
  [scope, *stack].select(&:shared?)
end