Class: RuboCop::Cop::ThreadSafety::MutableClassInstanceVariable

Inherits:
RuboCop::Cop
  • Object
show all
Includes:
ConfigurableEnforcedStyle, FrozenStringLiteral
Defined in:
lib/rubocop/cop/thread_safety/mutable_class_instance_variable.rb

Overview

This cop checks whether some class instance variable isn’t a mutable literal (e.g. array or hash).

It is based on Style/MutableConstant from RuboCop. See github.com/rubocop-hq/rubocop/blob/master/lib/rubocop/cop/style/mutable_constant.rb

Class instance variables are a risk to threaded code as they are shared between threads. A mutable object such as an array or hash may be updated via an attr_reader so would not be detected by the ThreadSafety/ClassAndModuleAttributes cop.

Strict mode can be used to freeze all class instance variables, rather than just literals. Strict mode is considered an experimental feature. It has not been updated with an exhaustive list of all methods that will produce frozen objects so there is a decent chance of getting some false positives. Luckily, there is no harm in freezing an already frozen object.

Examples:

EnforcedStyle: literals (default)

# bad
class Model
  @list = [1, 2, 3]
end

# good
class Model
  @list = [1, 2, 3].freeze
end

# good
class Model
  @var = <<-TESTING.freeze
    This is a heredoc
  TESTING
end

# good
class Model
  @var = Something.new
end

EnforcedStyle: strict

# bad
class Model
  @var = Something.new
end

# bad
class Model
  @var = Struct.new do
    def foo
      puts 1
    end
  end
end

# good
class Model
  @var = Something.new.freeze
end

# good
class Model
  @var = Struct.new do
    def foo
      puts 1
    end
  end.freeze
end

Constant Summary collapse

MSG =
'Freeze mutable objects assigned to class instance variables.'

Instance Method Summary collapse

Instance Method Details

#autocorrect(node) ⇒ Object



109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/rubocop/cop/thread_safety/mutable_class_instance_variable.rb', line 109

def autocorrect(node)
  expr = node.source_range

  lambda do |corrector|
    splat_value = splat_value(node)
    if splat_value
      correct_splat_expansion(corrector, expr, splat_value)
    elsif node.array_type? && !node.bracketed?
      corrector.insert_before(expr, '[')
      corrector.insert_after(expr, ']')
    elsif requires_parentheses?(node)
      corrector.insert_before(expr, '(')
      corrector.insert_after(expr, ')')
    end

    corrector.insert_after(expr, '.freeze')
  end
end

#on_ivasgn(node) ⇒ Object



81
82
83
84
85
86
# File 'lib/rubocop/cop/thread_safety/mutable_class_instance_variable.rb', line 81

def on_ivasgn(node)
  return unless in_class?(node)

  _, value = *node
  on_assignment(value)
end

#on_masgn(node) ⇒ Object



96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/rubocop/cop/thread_safety/mutable_class_instance_variable.rb', line 96

def on_masgn(node)
  return unless in_class?(node)

  mlhs, values = *node
  return unless values.array_type?

  mlhs.to_a.zip(values.to_a).each do |lhs, value|
    next unless lhs.ivasgn_type?

    on_assignment(value)
  end
end

#on_or_asgn(node) ⇒ Object



88
89
90
91
92
93
94
# File 'lib/rubocop/cop/thread_safety/mutable_class_instance_variable.rb', line 88

def on_or_asgn(node)
  lhs, value = *node
  return unless lhs&.ivasgn_type?
  return unless in_class?(node)

  on_assignment(value)
end