rubocop-sorbet 0.7.0 → 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +1 -1
  3. data/.rubocop.yml +12 -1
  4. data/.yardopts +1 -0
  5. data/Gemfile +1 -0
  6. data/Gemfile.lock +23 -17
  7. data/README.md +1 -1
  8. data/Rakefile +1 -1
  9. data/bin/rspec +4 -2
  10. data/bin/rubocop +4 -2
  11. data/config/default.yml +23 -2
  12. data/lib/rubocop/cop/sorbet/binding_constant_without_type_alias.rb +105 -0
  13. data/lib/rubocop/cop/sorbet/callback_conditionals_binding.rb +39 -11
  14. data/lib/rubocop/cop/sorbet/constants_from_strings.rb +5 -3
  15. data/lib/rubocop/cop/sorbet/forbid_include_const_literal.rb +27 -23
  16. data/lib/rubocop/cop/sorbet/forbid_superclass_const_literal.rb +26 -27
  17. data/lib/rubocop/cop/sorbet/forbid_t_unsafe.rb +7 -3
  18. data/lib/rubocop/cop/sorbet/forbid_t_untyped.rb +7 -3
  19. data/lib/rubocop/cop/sorbet/forbid_untyped_struct_props.rb +13 -11
  20. data/lib/rubocop/cop/sorbet/implicit_conversion_method.rb +56 -0
  21. data/lib/rubocop/cop/sorbet/mixin/target_sorbet_version.rb +49 -0
  22. data/lib/rubocop/cop/sorbet/mutable_constant_sorbet_aware_behaviour.rb +8 -5
  23. data/lib/rubocop/cop/sorbet/obsolete_strict_memoization.rb +92 -0
  24. data/lib/rubocop/cop/sorbet/one_ancestor_per_line.rb +8 -3
  25. data/lib/rubocop/cop/sorbet/rbi/forbid_extend_t_sig_helpers_in_shims.rb +12 -18
  26. data/lib/rubocop/cop/sorbet/rbi/forbid_rbi_outside_of_allowed_paths.rb +8 -7
  27. data/lib/rubocop/cop/sorbet/rbi/single_line_rbi_class_module_definitions.rb +12 -18
  28. data/lib/rubocop/cop/sorbet/redundant_extend_t_sig.rb +5 -6
  29. data/lib/rubocop/cop/sorbet/sigils/enforce_sigil_order.rb +3 -2
  30. data/lib/rubocop/cop/sorbet/sigils/enforce_single_sigil.rb +6 -5
  31. data/lib/rubocop/cop/sorbet/sigils/false_sigil.rb +1 -1
  32. data/lib/rubocop/cop/sorbet/sigils/has_sigil.rb +1 -1
  33. data/lib/rubocop/cop/sorbet/sigils/ignore_sigil.rb +1 -1
  34. data/lib/rubocop/cop/sorbet/sigils/strict_sigil.rb +1 -1
  35. data/lib/rubocop/cop/sorbet/sigils/strong_sigil.rb +1 -1
  36. data/lib/rubocop/cop/sorbet/sigils/true_sigil.rb +1 -1
  37. data/lib/rubocop/cop/sorbet/sigils/valid_sigil.rb +50 -25
  38. data/lib/rubocop/cop/sorbet/signatures/allow_incompatible_override.rb +19 -30
  39. data/lib/rubocop/cop/sorbet/signatures/checked_true_in_signature.rb +3 -2
  40. data/lib/rubocop/cop/sorbet/signatures/empty_line_after_sig.rb +3 -3
  41. data/lib/rubocop/cop/sorbet/signatures/enforce_signatures.rb +8 -4
  42. data/lib/rubocop/cop/sorbet/signatures/keyword_argument_ordering.rb +5 -4
  43. data/lib/rubocop/cop/sorbet/signatures/signature_build_order.rb +8 -3
  44. data/lib/rubocop/cop/sorbet/signatures/signature_cop.rb +6 -1
  45. data/lib/rubocop/cop/sorbet/type_alias_name.rb +10 -17
  46. data/lib/rubocop/cop/sorbet_cops.rb +6 -1
  47. data/lib/rubocop/sorbet/inject.rb +9 -7
  48. data/lib/rubocop/sorbet/version.rb +2 -1
  49. data/lib/rubocop/sorbet.rb +1 -0
  50. data/manual/cops.md +2 -0
  51. data/manual/cops_sorbet.md +137 -31
  52. data/rubocop-sorbet.gemspec +1 -0
  53. data/tasks/cops_documentation.rake +16 -5
  54. metadata +8 -4
  55. data/lib/rubocop/cop/sorbet/binding_constants_without_type_alias.rb +0 -127
@@ -6,7 +6,7 @@ require "rubocop"
6
6
  module RuboCop
7
7
  module Cop
8
8
  module Sorbet
9
- # This cop disallows use of `T.untyped` or `T.nilable(T.untyped)`
9
+ # Disallows use of `T.untyped` or `T.nilable(T.untyped)`
10
10
  # as a prop type for `T::Struct` or `T::ImmutableStruct`.
11
11
  #
12
12
  # @example
@@ -22,38 +22,40 @@ module RuboCop
22
22
  # const :foo, Integer
23
23
  # prop :bar, T.nilable(String)
24
24
  # end
25
- class ForbidUntypedStructProps < RuboCop::Cop::Cop
25
+ class ForbidUntypedStructProps < RuboCop::Cop::Base
26
26
  MSG = "Struct props cannot be T.untyped"
27
27
 
28
+ # @!method t_struct(node)
28
29
  def_node_matcher :t_struct, <<~PATTERN
29
- (const (const nil? :T) :Struct)
30
- PATTERN
31
-
32
- def_node_matcher :t_immutable_struct, <<~PATTERN
33
- (const (const nil? :T) :ImmutableStruct)
30
+ (const (const nil? :T) {:Struct :ImmutableStruct})
34
31
  PATTERN
35
32
 
33
+ # @!method t_untyped(node)
36
34
  def_node_matcher :t_untyped, <<~PATTERN
37
35
  (send (const nil? :T) :untyped)
38
36
  PATTERN
39
37
 
38
+ # @!method t_nilable_untyped(node)
40
39
  def_node_matcher :t_nilable_untyped, <<~PATTERN
41
40
  (send (const nil? :T) :nilable {#t_untyped #t_nilable_untyped})
42
41
  PATTERN
43
42
 
43
+ # @!method subclass_of_t_struct?(node)
44
44
  def_node_matcher :subclass_of_t_struct?, <<~PATTERN
45
- (class (const ...) {#t_struct #t_immutable_struct} ...)
45
+ (class (const ...) #t_struct ...)
46
46
  PATTERN
47
47
 
48
+ # @!method untyped_props(node)
49
+ # Search for untyped prop/const declarations and capture their types
48
50
  def_node_search :untyped_props, <<~PATTERN
49
- (send nil? {:prop :const} _ {#t_untyped #t_nilable_untyped} ...)
51
+ (send nil? {:prop :const} _ ${#t_untyped #t_nilable_untyped} ...)
50
52
  PATTERN
51
53
 
52
54
  def on_class(node)
53
55
  return unless subclass_of_t_struct?(node)
54
56
 
55
- untyped_props(node).each do |untyped_prop|
56
- add_offense(untyped_prop.child_nodes[1])
57
+ untyped_props(node).each do |prop_type|
58
+ add_offense(prop_type)
57
59
  end
58
60
  end
59
61
  end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rubocop"
4
+
5
+ module RuboCop
6
+ module Cop
7
+ module Sorbet
8
+ # Disallows declaring implicit conversion methods.
9
+ # Since Sorbet is a nominal (not structural) type system,
10
+ # implicit conversion is currently unsupported.
11
+ #
12
+ # @example
13
+ #
14
+ # # bad
15
+ # def to_str; end
16
+ #
17
+ # # good
18
+ # def to_str(x); end
19
+ #
20
+ # # bad
21
+ # def self.to_str; end
22
+ #
23
+ # # good
24
+ # def self.to_str(x); end
25
+ #
26
+ # # bad
27
+ # alias to_str to_s
28
+ #
29
+ # @see https://docs.ruby-lang.org/en/master/implicit_conversion_rdoc.html
30
+ # @note Since the arity of aliased methods is not checked, false positives may result.
31
+ class ImplicitConversionMethod < RuboCop::Cop::Base
32
+ IMPLICIT_CONVERSION_METHODS = [:to_ary, :to_int, :to_hash, :to_str].freeze
33
+ MSG = "Avoid implicit conversion methods, as Sorbet does not support them. " \
34
+ "Explicity convert to the desired type instead."
35
+ RESTRICT_ON_SEND = [:alias_method].freeze
36
+
37
+ def on_alias(node)
38
+ new_id = node.new_identifier
39
+ add_offense(new_id) if IMPLICIT_CONVERSION_METHODS.include?(new_id.value)
40
+ end
41
+
42
+ def on_def(node)
43
+ return unless IMPLICIT_CONVERSION_METHODS.include?(node.method_name)
44
+ return unless node.arguments.empty?
45
+
46
+ add_offense(node)
47
+ end
48
+ alias_method :on_defs, :on_def
49
+
50
+ def on_send(node)
51
+ add_offense(node.first_argument) if IMPLICIT_CONVERSION_METHODS.include?(node.first_argument.value)
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Sorbet
6
+ module TargetSorbetVersion
7
+ class << self
8
+ def included(target)
9
+ target.extend(ClassMethods)
10
+ end
11
+ end
12
+
13
+ module ClassMethods
14
+ # The version of the Sorbet static type checker required by this cop
15
+ def minimum_target_sorbet_static_version(version)
16
+ @minimum_target_sorbet_static_version = Gem::Version.new(version)
17
+ end
18
+
19
+ def support_target_sorbet_static_version?(version)
20
+ @minimum_target_sorbet_static_version <= Gem::Version.new(version)
21
+ end
22
+ end
23
+
24
+ def enabled_for_sorbet_static_version?
25
+ sorbet_static_version = target_sorbet_static_version_from_bundler_lock_file
26
+ return false unless sorbet_static_version
27
+
28
+ self.class.support_target_sorbet_static_version?(sorbet_static_version)
29
+ end
30
+
31
+ def target_sorbet_static_version_from_bundler_lock_file
32
+ # Do memoization with the `defined?` pattern since sorbet-static version might be `nil`
33
+ if defined?(@target_sorbet_static_version_from_bundler_lock_file)
34
+ @target_sorbet_static_version_from_bundler_lock_file
35
+ else
36
+ @target_sorbet_static_version_from_bundler_lock_file = read_sorbet_static_version_from_bundler_lock_file
37
+ end
38
+ end
39
+
40
+ def read_sorbet_static_version_from_bundler_lock_file
41
+ require "bundler"
42
+ ::Bundler.locked_gems.specs.find { |spec| spec.name == "sorbet-static" }&.version
43
+ rescue LoadError, Bundler::GemfileNotFound
44
+ nil
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -6,10 +6,13 @@ module RuboCop
6
6
  module Cop
7
7
  module Sorbet
8
8
  module MutableConstantSorbetAwareBehaviour
9
- def self.prepended(base)
10
- base.def_node_matcher(:t_let, <<~PATTERN)
11
- (send (const nil? :T) :let $_constant _type)
12
- PATTERN
9
+ class << self
10
+ def prepended(base)
11
+ # @!method t_let(node)
12
+ base.def_node_matcher(:t_let, <<~PATTERN)
13
+ (send (const nil? :T) :let $_constant _type)
14
+ PATTERN
15
+ end
13
16
  end
14
17
 
15
18
  def on_assignment(value)
@@ -25,5 +28,5 @@ module RuboCop
25
28
  end
26
29
 
27
30
  RuboCop::Cop::Style::MutableConstant.prepend(
28
- RuboCop::Cop::Sorbet::MutableConstantSorbetAwareBehaviour
31
+ RuboCop::Cop::Sorbet::MutableConstantSorbetAwareBehaviour,
29
32
  )
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rubocop"
4
+
5
+ module RuboCop
6
+ module Cop
7
+ module Sorbet
8
+ # Checks for the obsolete pattern for initializing instance variables that was required for older Sorbet
9
+ # versions in `#typed: strict` files.
10
+ #
11
+ # It's no longer required, as of Sorbet 0.5.10210
12
+ # See https://sorbet.org/docs/type-assertions#put-type-assertions-behind-memoization
13
+ #
14
+ # @example
15
+ #
16
+ # # bad
17
+ # sig { returns(Foo) }
18
+ # def foo
19
+ # @foo = T.let(@foo, T.nilable(Foo))
20
+ # @foo ||= Foo.new
21
+ # end
22
+ #
23
+ # # bad
24
+ # sig { returns(Foo) }
25
+ # def foo
26
+ # # This would have been a mistake, causing the memoized value to be discarded and recomputed on every call.
27
+ # @foo = T.let(nil, T.nilable(Foo))
28
+ # @foo ||= Foo.new
29
+ # end
30
+ #
31
+ # # good
32
+ # sig { returns(Foo) }
33
+ # def foo
34
+ # @foo ||= T.let(Foo.new, T.nilable(Foo))
35
+ # end
36
+ #
37
+ class ObsoleteStrictMemoization < RuboCop::Cop::Base
38
+ include RuboCop::Cop::MatchRange
39
+ include RuboCop::Cop::Alignment
40
+ include RuboCop::Cop::LineLengthHelp
41
+ include RuboCop::Cop::RangeHelp
42
+ extend AutoCorrector
43
+
44
+ include TargetSorbetVersion
45
+ minimum_target_sorbet_static_version "0.5.10210"
46
+
47
+ MSG = "This two-stage workaround for memoization in `#typed: strict` files is no longer necessary. " \
48
+ "See https://sorbet.org/docs/type-assertions#put-type-assertions-behind-memoization."
49
+
50
+ # @!method legacy_memoization_pattern?(node)
51
+ def_node_matcher :legacy_memoization_pattern?, <<~PATTERN
52
+ (begin
53
+ ... # Ignore any other lines that come first.
54
+ $(ivasgn $_ivar # First line: @_ivar = ...
55
+ (send # T.let(_ivar, T.nilable(_ivar_type))
56
+ $(const {nil? cbase} :T) :let
57
+ {(ivar _ivar) nil}
58
+ (send (const {nil? cbase} :T) :nilable $_ivar_type))) # T.nilable(_ivar_type)
59
+ $(or-asgn (ivasgn _ivar) $_initialization_expr)) # Second line: @_ivar ||= _initialization_expr
60
+ PATTERN
61
+
62
+ def on_begin(node)
63
+ legacy_memoization_pattern?(node) do |first_asgn_node, ivar, t, ivar_type, second_or_asgn_node, init_expr| # rubocop:disable Metrics/ParameterLists
64
+ add_offense(first_asgn_node) do |corrector|
65
+ indent = offset(node)
66
+ correction = "#{ivar} ||= #{t.source}.let(#{init_expr.source}, #{t.source}.nilable(#{ivar_type.source}))"
67
+
68
+ # We know good places to put line breaks, if required.
69
+ if line_length(indent + correction) > max_line_length || correction.include?("\n")
70
+ correction = <<~RUBY.chomp
71
+ #{ivar} ||= #{t.source}.let(
72
+ #{indent} #{init_expr.source.gsub("\n", "\n#{indent}")},
73
+ #{indent} #{t.source}.nilable(#{ivar_type.source.gsub("\n", "\n#{indent}")}),
74
+ #{indent})
75
+ RUBY
76
+ end
77
+
78
+ corrector.replace(
79
+ range_between(first_asgn_node.source_range.begin_pos, second_or_asgn_node.source_range.end_pos),
80
+ correction,
81
+ )
82
+ end
83
+ end
84
+ end
85
+
86
+ def relevant_file?(file)
87
+ super && enabled_for_sorbet_static_version?
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
@@ -6,7 +6,7 @@ require "rubocop"
6
6
  module RuboCop
7
7
  module Cop
8
8
  module Sorbet
9
- # This cop ensures one ancestor per requires_ancestor line
9
+ # Ensures one ancestor per requires_ancestor line
10
10
  # rather than chaining them as a comma-separated list.
11
11
  #
12
12
  # @example
@@ -21,17 +21,20 @@ module RuboCop
21
21
  # requires_ancestor Kernel
22
22
  # requires_ancestor Minitest::Assertions
23
23
  # end
24
- class OneAncestorPerLine < RuboCop::Cop::Cop
24
+ class OneAncestorPerLine < RuboCop::Cop::Cop # rubocop:todo InternalAffairs/InheritDeprecatedCopClass
25
25
  MSG = "Cannot require more than one ancestor per line"
26
26
 
27
+ # @!method requires_ancestors(node)
27
28
  def_node_search :requires_ancestors, <<~PATTERN
28
29
  (send nil? :requires_ancestor ...)
29
30
  PATTERN
30
31
 
32
+ # @!method more_than_one_ancestor(node)
31
33
  def_node_matcher :more_than_one_ancestor, <<~PATTERN
32
34
  (send nil? :requires_ancestor const const+)
33
35
  PATTERN
34
36
 
37
+ # @!method abstract?(node)
35
38
  def_node_search :abstract?, <<~PATTERN
36
39
  (send nil? :abstract!)
37
40
  PATTERN
@@ -39,17 +42,19 @@ module RuboCop
39
42
  def on_module(node)
40
43
  return unless node.body
41
44
  return unless requires_ancestors(node)
45
+
42
46
  process_node(node)
43
47
  end
44
48
 
45
49
  def on_class(node)
46
50
  return unless abstract?(node)
47
51
  return unless requires_ancestors(node)
52
+
48
53
  process_node(node)
49
54
  end
50
55
 
51
56
  def autocorrect(node)
52
- -> (corrector) do
57
+ ->(corrector) do
53
58
  ra_call = node.parent
54
59
  split_ra_calls = ra_call.source.gsub(/,\s+/, new_ra_line(ra_call.loc.column))
55
60
  corrector.replace(ra_call, split_ra_calls)
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Sorbet
6
- # This cop ensures RBI shims do not include a call to extend T::Sig
6
+ # Ensures RBI shims do not include a call to extend T::Sig
7
7
  # or to extend T::Helpers
8
8
  #
9
9
  # @example
@@ -22,30 +22,24 @@ module RuboCop
22
22
  # sig { returns(String) }
23
23
  # def foo; end
24
24
  # end
25
- class ForbidExtendTSigHelpersInShims < RuboCop::Cop::Cop
25
+ class ForbidExtendTSigHelpersInShims < RuboCop::Cop::Base
26
+ extend AutoCorrector
26
27
  include RangeHelp
27
28
 
28
29
  MSG = "Extending T::Sig or T::Helpers in a shim is unnecessary"
29
- RESTRICT_ON_SEND = [:extend]
30
+ RESTRICT_ON_SEND = [:extend].freeze
30
31
 
31
- def_node_matcher :extend_t_sig?, <<~PATTERN
32
- (send nil? :extend (const (const nil? :T) :Sig))
32
+ # @!method extend_t_sig_or_helpers?(node)
33
+ def_node_matcher :extend_t_sig_or_helpers?, <<~PATTERN
34
+ (send nil? :extend (const (const nil? :T) {:Sig :Helpers}))
33
35
  PATTERN
34
36
 
35
- def_node_matcher :extend_t_helpers?, <<~PATTERN
36
- (send nil? :extend (const (const nil? :T) :Helpers))
37
- PATTERN
38
-
39
- def autocorrect(node)
40
- -> (corrector) do
41
- corrector.remove(
42
- range_by_whole_lines(node.source_range, include_final_newline: true)
43
- )
44
- end
45
- end
46
-
47
37
  def on_send(node)
48
- add_offense(node) if extend_t_helpers?(node) || extend_t_sig?(node)
38
+ extend_t_sig_or_helpers?(node) do
39
+ add_offense(node) do |corrector|
40
+ corrector.remove(range_by_whole_lines(node.source_range, include_final_newline: true))
41
+ end
42
+ end
49
43
  end
50
44
  end
51
45
  end
@@ -5,7 +5,7 @@ require "pathname"
5
5
  module RuboCop
6
6
  module Cop
7
7
  module Sorbet
8
- # This cop makes sure that RBI files are always located under the defined allowed paths.
8
+ # Makes sure that RBI files are always located under the defined allowed paths.
9
9
  #
10
10
  # Options:
11
11
  #
@@ -20,7 +20,7 @@ module RuboCop
20
20
  # # rbi/external_interface.rbi
21
21
  # # sorbet/rbi/some_file.rbi
22
22
  # # sorbet/rbi/any/path/for/file.rbi
23
- class ForbidRBIOutsideOfAllowedPaths < RuboCop::Cop::Cop
23
+ class ForbidRBIOutsideOfAllowedPaths < RuboCop::Cop::Cop # rubocop:todo InternalAffairs/InheritDeprecatedCopClass
24
24
  include RangeHelp
25
25
 
26
26
  def investigate(processed_source)
@@ -30,14 +30,14 @@ module RuboCop
30
30
  add_offense(
31
31
  nil,
32
32
  location: source_range(processed_source.buffer, 1, 0),
33
- message: "AllowedPaths expects an array"
33
+ message: "AllowedPaths expects an array",
34
34
  )
35
35
  return
36
36
  elsif paths.empty?
37
37
  add_offense(
38
38
  nil,
39
39
  location: source_range(processed_source.buffer, 1, 0),
40
- message: "AllowedPaths cannot be empty"
40
+ message: "AllowedPaths cannot be empty",
41
41
  )
42
42
  return
43
43
  end
@@ -49,15 +49,16 @@ module RuboCop
49
49
  add_offense(
50
50
  nil,
51
51
  location: source_range(processed_source.buffer, 1, 0),
52
- message: "RBI file path should match one of: #{paths.join(", ")}"
52
+ message: "RBI file path should match one of: #{paths.join(", ")}",
53
53
  ) if paths.none? { |pattern| File.fnmatch(pattern, rel_path) }
54
54
  end
55
55
 
56
56
  private
57
57
 
58
58
  def allowed_paths
59
- paths = cop_config["AllowedPaths"]
60
- return nil unless paths.is_a?(Array)
59
+ paths = cop_config["AllowedPaths"] # rubocop:todo InternalAffairs/UndefinedConfig
60
+ return unless paths.is_a?(Array)
61
+
61
62
  paths.compact
62
63
  end
63
64
  end
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Sorbet
6
- # This cop ensures empty class/module definitions in RBI files are
6
+ # Ensures empty class/module definitions in RBI files are
7
7
  # done on a single line rather than being split across multiple lines.
8
8
  #
9
9
  # @example
@@ -14,32 +14,26 @@ module RuboCop
14
14
  #
15
15
  # # good
16
16
  # module SomeModule; end
17
- class SingleLineRbiClassModuleDefinitions < RuboCop::Cop::Cop
17
+ class SingleLineRbiClassModuleDefinitions < RuboCop::Cop::Base
18
+ extend AutoCorrector
19
+
18
20
  MSG = "Empty class/module definitions in RBI files should be on a single line."
19
21
 
20
22
  def on_module(node)
21
- process_node(node)
22
- end
23
-
24
- def on_class(node)
25
- process_node(node)
26
- end
23
+ return if node.body
24
+ return if node.single_line?
27
25
 
28
- def autocorrect(node)
29
- -> (corrector) { corrector.replace(node, convert_newlines(node.source)) }
26
+ add_offense(node) do |corrector|
27
+ corrector.replace(node, convert_newlines_to_semicolons(node.source))
28
+ end
30
29
  end
30
+ alias_method :on_class, :on_module
31
31
 
32
- protected
32
+ private
33
33
 
34
- def convert_newlines(source)
34
+ def convert_newlines_to_semicolons(source)
35
35
  source.sub(/[\r\n]+\s*[\r\n]*/, "; ")
36
36
  end
37
-
38
- def process_node(node)
39
- return if node.body
40
- return if node.single_line?
41
- add_offense(node)
42
- end
43
37
  end
44
38
  end
45
39
  end
@@ -25,10 +25,13 @@ module RuboCop
25
25
  # def no_op; end
26
26
  # end
27
27
  #
28
- class RedundantExtendTSig < RuboCop::Cop::Cop
28
+ class RedundantExtendTSig < RuboCop::Cop::Base
29
+ extend AutoCorrector
30
+
29
31
  MSG = "Do not redundantly `extend T::Sig` when it is already included in all modules."
30
32
  RESTRICT_ON_SEND = [:extend].freeze
31
33
 
34
+ # @!method extend_t_sig?(node)
32
35
  def_node_matcher :extend_t_sig?, <<~PATTERN
33
36
  (send _ :extend (const (const {nil? | cbase} :T) :Sig))
34
37
  PATTERN
@@ -36,11 +39,7 @@ module RuboCop
36
39
  def on_send(node)
37
40
  return unless extend_t_sig?(node)
38
41
 
39
- add_offense(node)
40
- end
41
-
42
- def autocorrect(node)
43
- lambda do |corrector|
42
+ add_offense(node) do |corrector|
44
43
  corrector.remove(node)
45
44
  end
46
45
  end
@@ -5,7 +5,7 @@ require "rubocop"
5
5
  module RuboCop
6
6
  module Cop
7
7
  module Sorbet
8
- # This cop checks that the Sorbet sigil comes as the first magic comment in the file.
8
+ # Checks that the Sorbet sigil comes as the first magic comment in the file.
9
9
  #
10
10
  # The expected order for magic comments is: (en)?coding, typed, warn_indent then frozen_string_literal.
11
11
  #
@@ -57,6 +57,7 @@ module RuboCop
57
57
  (lines.min...lines.max).each do |line|
58
58
  next if lines.include?(line)
59
59
  next unless processed_source[line - 1].empty?
60
+
60
61
  corrector.remove(source_range(processed_source.buffer, line, 0))
61
62
  end
62
63
  end
@@ -104,7 +105,7 @@ module RuboCop
104
105
  add_offense(
105
106
  token,
106
107
  location: token.pos,
107
- message: "Magic comments should be in the following order: #{PREFERRED_ORDER.values.join(", ")}."
108
+ message: "Magic comments should be in the following order: #{PREFERRED_ORDER.values.join(", ")}.",
108
109
  )
109
110
  end
110
111
  end
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Sorbet
6
- # This cop checks that there is only one Sorbet sigil in a given file
6
+ # Checks that there is only one Sorbet sigil in a given file
7
7
  #
8
8
  # For example, the following class with two sigils
9
9
  #
@@ -28,8 +28,9 @@ module RuboCop
28
28
 
29
29
  def investigate(processed_source)
30
30
  return if processed_source.tokens.empty?
31
+
31
32
  sigils = extract_all_sigils(processed_source)
32
- return unless sigils.size > 1
33
+ return if sigils.empty?
33
34
 
34
35
  sigils[1..sigils.size].each do |token|
35
36
  add_offense(token, location: token.pos, message: "Files must only contain one sigil")
@@ -37,14 +38,14 @@ module RuboCop
37
38
  end
38
39
 
39
40
  def autocorrect(_node)
40
- -> (corrector) do
41
+ ->(corrector) do
41
42
  sigils = extract_all_sigils(processed_source)
42
- return unless sigils.size > 1
43
+ return if sigils.empty?
43
44
 
44
45
  # The first sigil encountered represents the "real" strictness so remove any below
45
46
  sigils[1..sigils.size].each do |token|
46
47
  corrector.remove(
47
- source_range(processed_source.buffer, token.line, (0..token.pos.last_column))
48
+ source_range(processed_source.buffer, token.line, (0..token.pos.last_column)),
48
49
  )
49
50
  end
50
51
  end
@@ -6,7 +6,7 @@ require_relative "has_sigil"
6
6
  module RuboCop
7
7
  module Cop
8
8
  module Sorbet
9
- # This cop makes the Sorbet `false` sigil mandatory in all files.
9
+ # Makes the Sorbet `false` sigil mandatory in all files.
10
10
  class FalseSigil < HasSigil
11
11
  def minimum_strictness
12
12
  "false"
@@ -6,7 +6,7 @@ require_relative "valid_sigil"
6
6
  module RuboCop
7
7
  module Cop
8
8
  module Sorbet
9
- # This cop makes the Sorbet typed sigil mandatory in all files.
9
+ # Makes the Sorbet typed sigil mandatory in all files.
10
10
  #
11
11
  # Options:
12
12
  #
@@ -6,7 +6,7 @@ require_relative "has_sigil"
6
6
  module RuboCop
7
7
  module Cop
8
8
  module Sorbet
9
- # This cop makes the Sorbet `ignore` sigil mandatory in all files.
9
+ # Makes the Sorbet `ignore` sigil mandatory in all files.
10
10
  class IgnoreSigil < HasSigil
11
11
  def minimum_strictness
12
12
  "ignore"
@@ -6,7 +6,7 @@ require_relative "has_sigil"
6
6
  module RuboCop
7
7
  module Cop
8
8
  module Sorbet
9
- # This cop makes the Sorbet `strict` sigil mandatory in all files.
9
+ # Makes the Sorbet `strict` sigil mandatory in all files.
10
10
  class StrictSigil < HasSigil
11
11
  def minimum_strictness
12
12
  "strict"
@@ -6,7 +6,7 @@ require_relative "has_sigil"
6
6
  module RuboCop
7
7
  module Cop
8
8
  module Sorbet
9
- # This cop makes the Sorbet `strong` sigil mandatory in all files.
9
+ # Makes the Sorbet `strong` sigil mandatory in all files.
10
10
  class StrongSigil < HasSigil
11
11
  def minimum_strictness
12
12
  "strong"
@@ -6,7 +6,7 @@ require_relative "has_sigil"
6
6
  module RuboCop
7
7
  module Cop
8
8
  module Sorbet
9
- # This cop makes the Sorbet `true` sigil mandatory in all files.
9
+ # Makes the Sorbet `true` sigil mandatory in all files.
10
10
  class TrueSigil < HasSigil
11
11
  def minimum_strictness
12
12
  "true"