Class: RuboCop::Cop::Sorbet::ObsoleteStrictMemoization

Inherits:
Base
  • Object
show all
Extended by:
AutoCorrector
Includes:
Alignment, LineLengthHelp, MatchRange, RangeHelp, TargetSorbetVersion
Defined in:
lib/rubocop/cop/sorbet/obsolete_strict_memoization.rb

Overview

Checks for the obsolete pattern for initializing instance variables that was required for older Sorbet versions in ‘#typed: strict` files.

It’s no longer required, as of Sorbet 0.5.10210 See sorbet.org/docs/type-assertions#put-type-assertions-behind-memoization

Examples:


# bad
sig { returns(Foo) }
def foo
  @foo = T.let(@foo, T.nilable(Foo))
  @foo ||= Foo.new
end

# bad
sig { returns(Foo) }
def foo
  # This would have been a mistake, causing the memoized value to be discarded and recomputed on every call.
  @foo = T.let(nil, T.nilable(Foo))
  @foo ||= Foo.new
end

# good
sig { returns(Foo) }
def foo
  @foo ||= T.let(Foo.new, T.nilable(Foo))
end

Constant Summary collapse

MSG =
"This two-stage workaround for memoization in `#typed: strict` files is no longer necessary. " \
"See https://sorbet.org/docs/type-assertions#put-type-assertions-behind-memoization."

Instance Method Summary collapse

Methods included from TargetSorbetVersion

#enabled_for_sorbet_static_version?, included, #read_sorbet_static_version_from_bundler_lock_file, #sorbet_enabled?, #target_sorbet_static_version_from_bundler_lock_file

Instance Method Details

#legacy_memoization_pattern?(node) ⇒ Object



51
52
53
54
55
56
57
58
59
60
# File 'lib/rubocop/cop/sorbet/obsolete_strict_memoization.rb', line 51

def_node_matcher :legacy_memoization_pattern?, <<~PATTERN
  (begin
    ...                                                       # Ignore any other lines that come first.
    $(ivasgn $_ivar                                           # First line: @_ivar = ...
      (send                                                   # T.let(_ivar, T.nilable(_ivar_type))
        $(const {nil? cbase} :T) :let
        (ivar _ivar)
        (send (const {nil? cbase} :T) :nilable $_ivar_type))) # T.nilable(_ivar_type)
    $(or-asgn (ivasgn _ivar) $_initialization_expr))          # Second line: @_ivar ||= _initialization_expr
PATTERN

#on_begin(node) ⇒ Object



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/rubocop/cop/sorbet/obsolete_strict_memoization.rb', line 62

def on_begin(node)
  legacy_memoization_pattern?(node) do |first_asgn_node, ivar, t, ivar_type, second_or_asgn_node, init_expr| # rubocop:disable Metrics/ParameterLists
    add_offense(first_asgn_node) do |corrector|
      indent = offset(node)
      correction = "#{ivar} ||= #{t.source}.let(#{init_expr.source}, #{t.source}.nilable(#{ivar_type.source}))"

      # We know good places to put line breaks, if required.
      if line_length(indent + correction) > max_line_length || correction.include?("\n")
        correction = <<~RUBY.chomp
          #{ivar} ||= #{t.source}.let(
          #{indent}  #{init_expr.source.gsub("\n", "\n#{indent}")},
          #{indent}  #{t.source}.nilable(#{ivar_type.source.gsub("\n", "\n#{indent}")}),
          #{indent})
        RUBY
      end

      corrector.replace(
        range_between(first_asgn_node.source_range.begin_pos, second_or_asgn_node.source_range.end_pos),
        correction,
      )
    end
  end
end

#relevant_file?(file) ⇒ Boolean

Returns:

  • (Boolean)


86
87
88
# File 'lib/rubocop/cop/sorbet/obsolete_strict_memoization.rb', line 86

def relevant_file?(file)
  super && enabled_for_sorbet_static_version?
end