rubocop-sorbet 0.7.0 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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"