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

Inherits:
Base
  • Object
show all
Extended by:
AutoCorrector
Includes:
ConfigurableEnforcedStyle, FrozenStringLiteral, OperationWithThreadsafeResult
Defined in:
lib/rubocop/cop/thread_safety/mutable_class_instance_variable.rb

Overview

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/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.'
FROZEN_STRING_LITERAL_TYPES_RUBY27 =
%i[str dstr].freeze
FROZEN_STRING_LITERAL_TYPES_RUBY30 =
%i[str].freeze

Instance Method Summary collapse

Methods included from OperationWithThreadsafeResult

#operation_produces_threadsafe_object?

Instance Method Details

#autocorrect(corrector, node) ⇒ Object



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

def autocorrect(corrector, node)
  expr = node.source_range

  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

#on_ivasgn(node) ⇒ Object



86
87
88
89
90
# File 'lib/rubocop/cop/thread_safety/mutable_class_instance_variable.rb', line 86

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

  on_assignment(node.expression)
end

#on_masgn(node) ⇒ Object



99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/rubocop/cop/thread_safety/mutable_class_instance_variable.rb', line 99

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

  mlhs, values = *node # rubocop:disable InternalAffairs/NodeDestructuring
  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



92
93
94
95
96
97
# File 'lib/rubocop/cop/thread_safety/mutable_class_instance_variable.rb', line 92

def on_or_asgn(node)
  return unless node.assignment_node.ivasgn_type?
  return unless in_class?(node)

  on_assignment(node.expression)
end