rubocop-sorbet 0.8.7 → 0.9.0

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 (33) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +3 -1
  3. data/.rubocop.yml +15 -0
  4. data/.ruby-version +1 -1
  5. data/Gemfile.lock +27 -20
  6. data/README.md +8 -5
  7. data/config/default.yml +24 -1
  8. data/config/rbi.yml +4 -1
  9. data/lib/rubocop/cop/sorbet/callback_conditionals_binding.rb +1 -0
  10. data/lib/rubocop/cop/sorbet/constants_from_strings.rb +1 -0
  11. data/lib/rubocop/cop/sorbet/forbid_include_const_literal.rb +3 -3
  12. data/lib/rubocop/cop/sorbet/forbid_t_enum.rb +40 -0
  13. data/lib/rubocop/cop/sorbet/forbid_t_struct.rb +2 -2
  14. data/lib/rubocop/cop/sorbet/forbid_t_unsafe.rb +1 -0
  15. data/lib/rubocop/cop/sorbet/mixin/signature_help.rb +20 -7
  16. data/lib/rubocop/cop/sorbet/sigils/has_sigil.rb +1 -0
  17. data/lib/rubocop/cop/sorbet/sigils/strict_sigil.rb +15 -0
  18. data/lib/rubocop/cop/sorbet/sigils/valid_sigil.rb +1 -0
  19. data/lib/rubocop/cop/sorbet/signatures/enforce_signatures.rb +19 -1
  20. data/lib/rubocop/cop/sorbet/signatures/forbid_sig.rb +34 -0
  21. data/lib/rubocop/cop/sorbet/signatures/forbid_sig_with_runtime.rb +34 -0
  22. data/lib/rubocop/cop/sorbet/signatures/forbid_sig_without_runtime.rb +40 -0
  23. data/lib/rubocop/cop/sorbet/signatures/keyword_argument_ordering.rb +1 -1
  24. data/lib/rubocop/cop/sorbet/signatures/void_checked_tests.rb +1 -1
  25. data/lib/rubocop/cop/sorbet_cops.rb +7 -3
  26. data/lib/rubocop/sorbet/plugin.rb +36 -0
  27. data/lib/rubocop/sorbet/version.rb +1 -1
  28. data/lib/rubocop/sorbet.rb +10 -5
  29. data/lib/rubocop-sorbet.rb +5 -2
  30. data/manual/cops.md +4 -0
  31. data/manual/cops_sorbet.md +109 -1
  32. data/rubocop-sorbet.gemspec +2 -0
  33. metadata +23 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8a44258e5997d8d76d11a35e921aaa0312daaadb5d159a2cf8425213baabdc1e
4
- data.tar.gz: de2a3ed4f84838094981247acce55eb873c620fbcea454ed145c174008734cd7
3
+ metadata.gz: 743baa95aeb8e124bebb3d354867e42a397e874d43ab75e0f8fcc2b7d40e7757
4
+ data.tar.gz: 705349f2986c114712c99303de229cf95128a66c79dc97de54d274623ab9a785
5
5
  SHA512:
6
- metadata.gz: af324556ad44f2eda2a26ba9cd006a92af766c8cc46872d761f1db769f9aa0b528d4d828b7d6a5e335a557cbce277f271da5a295f9c7d4a74f7babda35be6106
7
- data.tar.gz: a927134808ef57e9ddaa7fb0798d61e2e00011b68c86f429e8c53d12f693072ef69368657963e587165693d84f0d814abdc2b1ff29531aa58fa36fb9ad849ae1
6
+ metadata.gz: 9fac22901d690391f93b24ceef20727e3b6e79a9de887792d7cdb50cd3237caa38ac5655162aec3c748d01e747dd838ed6ca9efa4c5b727ff5d218fe495867f6
7
+ data.tar.gz: c988d90e224c3cfd3c93d13190e8d100165d35d3e9968c15930319ca94f5c6d787484bbb9bff3a92cb7b84deb4643b86720bc841751f823560865cf429121c4c
@@ -11,7 +11,7 @@ jobs:
11
11
  strategy:
12
12
  fail-fast: false
13
13
  matrix:
14
- ruby: ["3.1", "3.2", "3.3"]
14
+ ruby: ["3.1", "3.2", "3.3", "3.4"]
15
15
  name: Test Ruby ${{ matrix.ruby }}
16
16
  steps:
17
17
  - uses: actions/checkout@v4
@@ -42,5 +42,7 @@ jobs:
42
42
  bin/rubocop --config config/default.yml config -r rubocop-sorbet
43
43
  - name: Lint Ruby files
44
44
  run: bin/rubocop
45
+ - name: Verify cop examples
46
+ run: bundle exec rake documentation_syntax_check
45
47
  - name: Verify documentation is up to date
46
48
  run: bundle exec rake generate_cops_documentation
data/.rubocop.yml CHANGED
@@ -24,3 +24,18 @@ Layout/LineLength:
24
24
 
25
25
  InternalAffairs/UndefinedConfig:
26
26
  Enabled: false # Bug in implementation fails to find our configs
27
+
28
+ InternalAffairs/OnSendWithoutOnCSend:
29
+ Exclude:
30
+ # Cases where we don't expect a conditional send
31
+ - lib/rubocop/cop/sorbet/callback_conditionals_binding.rb
32
+ - lib/rubocop/cop/sorbet/forbid_include_const_literal.rb
33
+ - lib/rubocop/cop/sorbet/forbid_t_struct.rb
34
+ - lib/rubocop/cop/sorbet/forbid_t_untyped.rb
35
+ - lib/rubocop/cop/sorbet/implicit_conversion_method.rb
36
+ - lib/rubocop/cop/sorbet/rbi/forbid_extend_t_sig_helpers_in_shims.rb
37
+ - lib/rubocop/cop/sorbet/redundant_extend_t_sig.rb
38
+ - lib/rubocop/cop/sorbet/refinement.rb
39
+ - lib/rubocop/cop/sorbet/signatures/allow_incompatible_override.rb
40
+ - lib/rubocop/cop/sorbet/signatures/enforce_signatures.rb
41
+ - lib/rubocop/cop/sorbet/t_enum/forbid_comparable_t_enum.rb
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 3.3.3
1
+ 3.4.1
data/Gemfile.lock CHANGED
@@ -1,36 +1,40 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rubocop-sorbet (0.8.7)
4
+ rubocop-sorbet (0.9.0)
5
+ lint_roller (~> 1.1)
5
6
  rubocop (>= 1)
6
7
 
7
8
  GEM
8
9
  remote: https://rubygems.org/
9
10
  specs:
10
11
  ast (2.4.2)
11
- debug (1.9.2)
12
+ date (3.4.1)
13
+ debug (1.10.0)
12
14
  irb (~> 1.10)
13
15
  reline (>= 0.3.8)
14
16
  diff-lcs (1.5.1)
15
- io-console (0.7.2)
16
- irb (1.12.0)
17
- rdoc
17
+ io-console (0.8.0)
18
+ irb (1.14.3)
19
+ rdoc (>= 4.0.0)
18
20
  reline (>= 0.4.2)
19
- json (2.7.2)
20
- language_server-protocol (3.17.0.3)
21
+ json (2.10.1)
22
+ language_server-protocol (3.17.0.4)
23
+ lint_roller (1.1.0)
21
24
  parallel (1.26.3)
22
- parser (3.3.5.0)
25
+ parser (3.3.7.1)
23
26
  ast (~> 2.4.1)
24
27
  racc
25
- psych (5.1.2)
28
+ psych (5.2.2)
29
+ date
26
30
  stringio
27
31
  racc (1.8.1)
28
32
  rainbow (3.1.1)
29
33
  rake (13.2.1)
30
- rdoc (6.6.3.1)
34
+ rdoc (6.10.0)
31
35
  psych (>= 4.0.0)
32
- regexp_parser (2.9.2)
33
- reline (0.5.1)
36
+ regexp_parser (2.10.0)
37
+ reline (0.6.0)
34
38
  io-console (~> 0.5)
35
39
  rspec (3.13.0)
36
40
  rspec-core (~> 3.13.0)
@@ -45,23 +49,26 @@ GEM
45
49
  diff-lcs (>= 1.2.0, < 2.0)
46
50
  rspec-support (~> 3.13.0)
47
51
  rspec-support (3.13.1)
48
- rubocop (1.67.0)
52
+ rubocop (1.73.2)
49
53
  json (~> 2.3)
50
- language_server-protocol (>= 3.17.0)
54
+ language_server-protocol (~> 3.17.0.2)
55
+ lint_roller (~> 1.1.0)
51
56
  parallel (~> 1.10)
52
57
  parser (>= 3.3.0.2)
53
58
  rainbow (>= 2.2.2, < 4.0)
54
- regexp_parser (>= 2.4, < 3.0)
55
- rubocop-ast (>= 1.32.2, < 2.0)
59
+ regexp_parser (>= 2.9.3, < 3.0)
60
+ rubocop-ast (>= 1.38.0, < 2.0)
56
61
  ruby-progressbar (~> 1.7)
57
- unicode-display_width (>= 2.4.0, < 3.0)
58
- rubocop-ast (1.32.3)
62
+ unicode-display_width (>= 2.4.0, < 4.0)
63
+ rubocop-ast (1.38.1)
59
64
  parser (>= 3.3.1.0)
60
65
  rubocop-shopify (2.15.1)
61
66
  rubocop (~> 1.51)
62
67
  ruby-progressbar (1.13.0)
63
- stringio (3.1.0)
64
- unicode-display_width (2.6.0)
68
+ stringio (3.1.2)
69
+ unicode-display_width (3.1.4)
70
+ unicode-emoji (~> 4.0, >= 4.0.4)
71
+ unicode-emoji (4.0.4)
65
72
  yard (0.9.37)
66
73
 
67
74
  PLATFORMS
data/README.md CHANGED
@@ -24,30 +24,33 @@ You need to tell RuboCop to load the Sorbet extension. There are three ways to d
24
24
  Put this into your `.rubocop.yml`:
25
25
 
26
26
  ```yaml
27
- require: rubocop-sorbet
27
+ plugins: rubocop-sorbet
28
28
  ```
29
29
 
30
30
  Alternatively, use the following array notation when specifying multiple extensions:
31
31
 
32
32
  ```yaml
33
- require:
34
- - rubocop-other-extension
33
+ plugins:
35
34
  - rubocop-sorbet
35
+ - rubocop-other-extension
36
36
  ```
37
37
 
38
38
  Now you can run `rubocop` and it will automatically load the RuboCop Sorbet cops together with the standard cops.
39
39
 
40
+ > [!NOTE]
41
+ > The plugin system is supported in RuboCop 1.72+. In earlier versions, use `require` instead of `plugins`.
42
+
40
43
  ### Command line
41
44
 
42
45
  ```sh
43
- rubocop --require rubocop-sorbet
46
+ rubocop --plugin rubocop-sorbet
44
47
  ```
45
48
 
46
49
  ### Rake task
47
50
 
48
51
  ```ruby
49
52
  RuboCop::RakeTask.new do |task|
50
- task.requires << 'rubocop-sorbet'
53
+ task.plugins << 'rubocop-sorbet'
51
54
  end
52
55
  ```
53
56
 
data/config/default.yml CHANGED
@@ -1,5 +1,5 @@
1
1
  Sorbet/Refinement:
2
- Description: >-
2
+ Description: >-
3
3
  Checks for the use of Ruby Refinements library. Refinements add
4
4
  complexity and incur a performance penalty that can be significant
5
5
  for large code bases. They are also not supported by Sorbet.
@@ -102,6 +102,21 @@ Sorbet/ForbidTypeAliasedShapes:
102
102
  Enabled: false
103
103
  VersionAdded: 0.7.6
104
104
 
105
+ Sorbet/ForbidSig:
106
+ Description: 'Forbid usage of sig'
107
+ Enabled: false
108
+ VersionAdded: <<next>>
109
+
110
+ Sorbet/ForbidSigWithRuntime:
111
+ Description: 'Forbid usage of T::Sig.sig'
112
+ Enabled: false
113
+ VersionAdded: <<next>>
114
+
115
+ Sorbet/ForbidSigWithoutRuntime:
116
+ Description: 'Forbid usage of T::Sig::WithoutRuntime.sig'
117
+ Enabled: false
118
+ VersionAdded: <<next>>
119
+
105
120
  Sorbet/ForbidSuperclassConstLiteral:
106
121
  Description: 'Forbid superclasses which are non-literal constants.'
107
122
  Enabled: false
@@ -110,6 +125,13 @@ Sorbet/ForbidSuperclassConstLiteral:
110
125
  Exclude:
111
126
  - db/migrate/*.rb
112
127
 
128
+ Sorbet/ForbidTEnum:
129
+ Description: 'Forbid usage of T::Enum.'
130
+ Enabled: false
131
+ VersionAdded: <<next>>
132
+ VersionChanged: <<next>>
133
+ Safe: false
134
+
113
135
  Sorbet/ForbidTStruct:
114
136
  Description: 'Forbid usage of T::Struct.'
115
137
  Enabled: false
@@ -252,6 +274,7 @@ Sorbet/SingleLineRbiClassModuleDefinitions:
252
274
  Sorbet/StrictSigil:
253
275
  Description: 'All files must be at least at strictness `strict`.'
254
276
  Enabled: false
277
+ Safe: false
255
278
  SuggestedStrictness: "strict"
256
279
  VersionAdded: 0.3.3
257
280
  Include:
data/config/rbi.yml CHANGED
@@ -103,11 +103,11 @@ Layout/InitialIndentation:
103
103
 
104
104
  Layout/LeadingCommentSpace:
105
105
  Enabled: true
106
+ AllowRBSInlineAnnotation: true
106
107
 
107
108
  Layout/LeadingEmptyLines:
108
109
  Enabled: true
109
110
 
110
- # TODO: make Tapioca break long lines?
111
111
  Layout/LineLength:
112
112
  Enabled: false
113
113
 
@@ -199,6 +199,9 @@ Lint/Syntax:
199
199
 
200
200
  ## Sorbet
201
201
 
202
+ Sorbet/ForbidSigWithoutRuntime:
203
+ Enabled: true
204
+
202
205
  Sorbet/EnforceSigilOrder:
203
206
  Enabled: true
204
207
 
@@ -6,6 +6,7 @@ module RuboCop
6
6
  # Ensures that callback conditionals are bound to the right type
7
7
  # so that they are type checked properly.
8
8
  #
9
+ # @safety
9
10
  # Auto-correction is unsafe because other libraries define similar style callbacks as Rails, but don't always need
10
11
  # binding to the attached class. Auto-correcting those usages can lead to false positives and auto-correction
11
12
  # introduces new typing errors.
@@ -41,6 +41,7 @@ module RuboCop
41
41
  :constantize,
42
42
  :constants,
43
43
  :const_get,
44
+ :safe_constantize,
44
45
  ].freeze
45
46
 
46
47
  def on_send(node)
@@ -50,14 +50,14 @@ module RuboCop
50
50
  private
51
51
 
52
52
  def neither_const_nor_self?(node)
53
- !node.const_type? && !node.self_type?
53
+ !node.type?(:const, :self)
54
54
  end
55
55
 
56
56
  # Returns true if the node is within a module declaration that is not anonymous.
57
57
  def within_onymous_module?(node)
58
58
  parent = node.parent
59
- parent = parent.parent while parent&.begin_type? || parent&.block_type?
60
- parent && (parent.module_type? || parent.class_type? || parent.sclass_type?)
59
+ parent = parent.parent while parent&.type?(:begin, :block)
60
+ parent&.type?(:module, :class, :sclass)
61
61
  end
62
62
  end
63
63
  end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rubocop"
4
+
5
+ module RuboCop
6
+ module Cop
7
+ module Sorbet
8
+ # Disallow using `T::Enum`.
9
+ #
10
+ # @example
11
+ #
12
+ # # bad
13
+ # class MyEnum < T::Enum
14
+ # enums do
15
+ # A = new
16
+ # B = new
17
+ # end
18
+ # end
19
+ #
20
+ # # good
21
+ # class MyEnum
22
+ # A = "a"
23
+ # B = "b"
24
+ # C = "c"
25
+ # end
26
+ class ForbidTEnum < RuboCop::Cop::Base
27
+ MSG = "Using `T::Enum` is deprecated in this codebase."
28
+
29
+ # @!method t_enum?(node)
30
+ def_node_matcher(:t_enum?, <<~PATTERN)
31
+ (const (const {nil? cbase} :T) :Enum)
32
+ PATTERN
33
+
34
+ def on_class(node)
35
+ add_offense(node) if t_enum?(node.parent_class)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -43,8 +43,8 @@ module RuboCop
43
43
 
44
44
  RESTRICT_ON_SEND = [:include, :prepend, :extend].freeze
45
45
 
46
- MSG_STRUCT = "Using `T::Struct` or its variants is deprecated."
47
- MSG_PROPS = "Using `T::Props` or its variants is deprecated."
46
+ MSG_STRUCT = "Using `T::Struct` or its variants is deprecated in this codebase."
47
+ MSG_PROPS = "Using `T::Props` or its variants is deprecated in this codebase."
48
48
 
49
49
  # This class walks down the class body of a T::Struct and collects all the properties that will need to be
50
50
  # translated into `attr_reader` and `attr_accessor` methods.
@@ -24,6 +24,7 @@ module RuboCop
24
24
  def on_send(node)
25
25
  add_offense(node) if t_unsafe?(node)
26
26
  end
27
+ alias_method :on_csend, :on_send
27
28
  end
28
29
  end
29
30
  end
@@ -9,21 +9,34 @@ module RuboCop
9
9
 
10
10
  # @!method signature?(node)
11
11
  def_node_matcher(:signature?, <<~PATTERN)
12
+ {#bare_sig? #sig_with_runtime? #sig_without_runtime?}
13
+ PATTERN
14
+
15
+ # @!method bare_sig?(node)
16
+ def_node_matcher(:bare_sig?, <<~PATTERN)
12
17
  (block (send
13
- {nil? #with_runtime? #without_runtime?}
18
+ nil?
14
19
  :sig
15
20
  (sym :final)?
16
21
  ) (args) ...)
17
22
  PATTERN
18
23
 
19
- # @!method with_runtime?(node)
20
- def_node_matcher(:with_runtime?, <<~PATTERN)
21
- (const (const {nil? cbase} :T) :Sig)
24
+ # @!method sig_with_runtime?(node)
25
+ def_node_matcher(:sig_with_runtime?, <<~PATTERN)
26
+ (block (send
27
+ (const (const {nil? cbase} :T) :Sig)
28
+ :sig
29
+ (sym :final)?
30
+ ) (args) ...)
22
31
  PATTERN
23
32
 
24
- # @!method without_runtime?(node)
25
- def_node_matcher(:without_runtime?, <<~PATTERN)
26
- (const (const (const {nil? cbase} :T) :Sig) :WithoutRuntime)
33
+ # @!method sig_without_runtime?(node)
34
+ def_node_matcher(:sig_without_runtime?, <<~PATTERN)
35
+ (block (send
36
+ (const (const (const {nil? cbase} :T) :Sig) :WithoutRuntime)
37
+ :sig
38
+ (sym :final)?
39
+ ) (args) ...)
27
40
  PATTERN
28
41
 
29
42
  def on_block(node)
@@ -13,6 +13,7 @@ module RuboCop
13
13
  # * `SuggestedStrictness`: Sorbet strictness level suggested in offense messages (default: 'false')
14
14
  # * `MinimumStrictness`: If set, make offense if the strictness level in the file is below this one
15
15
  #
16
+ # If a `SuggestedStrictness` level is specified, it will be used in autocorrect.
16
17
  # If a `MinimumStrictness` level is specified, it will be used in offense messages and autocorrect.
17
18
  class HasSigil < ValidSigil
18
19
  def require_sigil_on_all_files?
@@ -7,6 +7,21 @@ module RuboCop
7
7
  module Cop
8
8
  module Sorbet
9
9
  # Makes the Sorbet `strict` sigil mandatory in all files.
10
+ #
11
+ # @safety
12
+ # This cop is unsafe because Sorbet sigils may not exist yet when it is run.
13
+ #
14
+ # @example
15
+ #
16
+ # # bad
17
+ # # typed: true
18
+ #
19
+ # # bad
20
+ # # typed: false
21
+ #
22
+ # # good
23
+ # # typed: strict
24
+ #
10
25
  class StrictSigil < HasSigil
11
26
  def minimum_strictness
12
27
  "strict"
@@ -16,6 +16,7 @@ module RuboCop
16
16
  # * `ExactStrictness`: If set, make offense if the strictness level in the file is different than this one
17
17
  #
18
18
  # If an `ExactStrictness` level is specified, it will be used in offense messages and autocorrect.
19
+ # If a `SuggestedStrictness` level is specified, it will be used in autocorrect.
19
20
  # Otherwise, if a `MinimumStrictness` level is specified, it will be used in offense messages and autocorrect.
20
21
  class ValidSigil < RuboCop::Cop::Base
21
22
  extend AutoCorrector
@@ -28,6 +28,8 @@ module RuboCop
28
28
  extend AutoCorrector
29
29
  include SignatureHelp
30
30
 
31
+ RBS_COMMENT_REGEX = /^#:.*$/
32
+
31
33
  def initialize(config = nil, options = nil)
32
34
  super(config, options)
33
35
  @last_sig_for_scope = {}
@@ -65,7 +67,7 @@ module RuboCop
65
67
 
66
68
  def check_node(node)
67
69
  scope = self.scope(node)
68
- unless @last_sig_for_scope[scope]
70
+ unless @last_sig_for_scope[scope] || has_rbs_comment?(node)
69
71
  add_offense(
70
72
  node,
71
73
  message: "Each method is required to have a signature.",
@@ -76,6 +78,22 @@ module RuboCop
76
78
  @last_sig_for_scope[scope] = nil
77
79
  end
78
80
 
81
+ def has_rbs_comment?(node)
82
+ return false unless cop_config["AllowRBS"] == true
83
+
84
+ node = node.parent while RuboCop::AST::SendNode === node.parent
85
+ return false if (comments = preceeding_comments(node)).empty?
86
+
87
+ last_comment = comments.last
88
+ return false if last_comment.loc.line + 1 < node.loc.line
89
+
90
+ comments.any? { |comment| RBS_COMMENT_REGEX.match?(comment.text) }
91
+ end
92
+
93
+ def preceeding_comments(node)
94
+ processed_source.ast_with_comments[node].select { |comment| comment.loc.line < node.loc.line }
95
+ end
96
+
79
97
  def autocorrect(corrector, node)
80
98
  suggest = SigSuggestion.new(node.loc.column, param_type_placeholder, return_type_placeholder)
81
99
 
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "stringio"
4
+
5
+ module RuboCop
6
+ module Cop
7
+ module Sorbet
8
+ # Check that definitions do not use a `sig` block.
9
+ #
10
+ # Good:
11
+ #
12
+ # ```
13
+ # #: -> void
14
+ # def foo; end
15
+ # ```
16
+ #
17
+ # Bad:
18
+ #
19
+ # ```
20
+ # sig { void }
21
+ # def foo; end
22
+ # ```
23
+ class ForbidSig < ::RuboCop::Cop::Base
24
+ include SignatureHelp
25
+
26
+ MSG = "Do not use `T::Sig`."
27
+
28
+ def on_signature(node)
29
+ add_offense(node) if bare_sig?(node)
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "stringio"
4
+
5
+ module RuboCop
6
+ module Cop
7
+ module Sorbet
8
+ # Check that definitions do not use a `sig` block.
9
+ #
10
+ # Good:
11
+ #
12
+ # ```
13
+ # #: -> void
14
+ # def foo; end
15
+ # ```
16
+ #
17
+ # Bad:
18
+ #
19
+ # ```
20
+ # T::Sig.sig { void }
21
+ # def foo; end
22
+ # ```
23
+ class ForbidSigWithRuntime < ::RuboCop::Cop::Base
24
+ include SignatureHelp
25
+
26
+ MSG = "Do not use `T::Sig.sig`."
27
+
28
+ def on_signature(node)
29
+ add_offense(node) if sig_with_runtime?(node)
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "stringio"
4
+
5
+ module RuboCop
6
+ module Cop
7
+ module Sorbet
8
+ # Check that `sig` is used instead of `T::Sig::WithoutRuntime.sig`.
9
+ #
10
+ # Good:
11
+ #
12
+ # ```
13
+ # sig { void }
14
+ # def foo; end
15
+ # ```
16
+ #
17
+ # Bad:
18
+ #
19
+ # ```
20
+ # T::Sig::WithoutRuntime.sig { void }
21
+ # def foo; end
22
+ # ```
23
+ class ForbidSigWithoutRuntime < ::RuboCop::Cop::Base
24
+ include SignatureHelp
25
+ extend AutoCorrector
26
+
27
+ MSG = "Do not use `T::Sig::WithoutRuntime.sig`."
28
+
29
+ def on_signature(node)
30
+ return unless sig_without_runtime?(node)
31
+
32
+ sig = node.children[0]
33
+ add_offense(sig) do |corrector|
34
+ corrector.replace(sig, sig.source.gsub(/T\s*::\s*Sig\s*::\s*WithoutRuntime\s*\.\s*/m, ""))
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -35,7 +35,7 @@ module RuboCop
35
35
  out_of_kwoptarg = false
36
36
 
37
37
  parameters.reverse.each do |param|
38
- out_of_kwoptarg = true unless param.kwoptarg_type? || param.blockarg_type? || param.kwrestarg_type?
38
+ out_of_kwoptarg = true unless param.type?(:kwoptarg, :blockarg, :kwrestarg)
39
39
 
40
40
  next unless param.kwoptarg_type? && out_of_kwoptarg
41
41
 
@@ -35,7 +35,7 @@ module RuboCop
35
35
 
36
36
  # @!method checked_tests(node)
37
37
  def_node_search(:checked_tests, <<~PATTERN)
38
- ({csend send} _ :checked (sym :tests))
38
+ (call _ :checked (sym :tests))
39
39
  PATTERN
40
40
 
41
41
  MESSAGE =
@@ -12,6 +12,7 @@ require_relative "sorbet/forbid_type_aliased_shapes"
12
12
  require_relative "sorbet/forbid_untyped_struct_props"
13
13
  require_relative "sorbet/implicit_conversion_method"
14
14
  require_relative "sorbet/callback_conditionals_binding"
15
+ require_relative "sorbet/forbid_t_enum"
15
16
  require_relative "sorbet/forbid_t_struct"
16
17
  require_relative "sorbet/forbid_t_unsafe"
17
18
  require_relative "sorbet/forbid_t_untyped"
@@ -30,11 +31,14 @@ require_relative "sorbet/rbi_versioning/valid_gem_version_annotations"
30
31
 
31
32
  require_relative "sorbet/signatures/allow_incompatible_override"
32
33
  require_relative "sorbet/signatures/checked_true_in_signature"
33
- require_relative "sorbet/signatures/void_checked_tests"
34
+ require_relative "sorbet/signatures/empty_line_after_sig"
35
+ require_relative "sorbet/signatures/enforce_signatures"
36
+ require_relative "sorbet/signatures/forbid_sig"
37
+ require_relative "sorbet/signatures/forbid_sig_with_runtime"
38
+ require_relative "sorbet/signatures/forbid_sig_without_runtime"
34
39
  require_relative "sorbet/signatures/keyword_argument_ordering"
35
40
  require_relative "sorbet/signatures/signature_build_order"
36
- require_relative "sorbet/signatures/enforce_signatures"
37
- require_relative "sorbet/signatures/empty_line_after_sig"
41
+ require_relative "sorbet/signatures/void_checked_tests"
38
42
 
39
43
  require_relative "sorbet/sigils/valid_sigil"
40
44
  require_relative "sorbet/sigils/has_sigil"
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rubocop"
4
+ require "lint_roller"
5
+ require "pathname"
6
+
7
+ module RuboCop
8
+ module Sorbet
9
+ # A plugin that integrates RuboCop Sorbet with RuboCop's plugin system.
10
+ class Plugin < LintRoller::Plugin
11
+ RUBOCOP_MIN_VERSION = "1.72.1"
12
+ SUPPORTED = Gem::Version.create(RuboCop::Version.version) < RUBOCOP_MIN_VERSION
13
+
14
+ def about
15
+ LintRoller::About.new(
16
+ name: "rubocop-sorbet",
17
+ version: VERSION,
18
+ homepage: "https://github.com/Shopify/rubocop-sorbet",
19
+ description: "A collection of Rubocop rules for Sorbet.",
20
+ )
21
+ end
22
+
23
+ def supported?(context)
24
+ context.engine == :rubocop
25
+ end
26
+
27
+ def rules(_context)
28
+ project_root = Pathname.new(__dir__).join("../../..")
29
+
30
+ ConfigObsoletion.files << project_root.join("config", "obsoletion.yml")
31
+
32
+ LintRoller::Rules.new(type: :path, config_format: :rubocop, value: project_root.join("config", "default.yml"))
33
+ end
34
+ end
35
+ end
36
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RuboCop
4
4
  module Sorbet
5
- VERSION = "0.8.7"
5
+ VERSION = "0.9.0"
6
6
  end
7
7
  end
@@ -1,18 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "rubocop"
3
4
  require "rubocop/sorbet/version"
5
+ require "rubocop/sorbet/plugin"
6
+ require "pathname"
4
7
  require "yaml"
5
8
 
6
9
  module RuboCop
7
10
  module Sorbet
8
11
  class Error < StandardError; end
9
12
 
10
- PROJECT_ROOT = Pathname.new(__dir__).parent.parent.expand_path.freeze
11
- CONFIG_DEFAULT = PROJECT_ROOT.join("config", "default.yml").freeze
12
- CONFIG = YAML.safe_load(CONFIG_DEFAULT.read).freeze
13
+ unless Plugin::SUPPORTED
14
+ PROJECT_ROOT = Pathname.new(__dir__).parent.parent.expand_path.freeze
15
+ CONFIG_DEFAULT = PROJECT_ROOT.join("config", "default.yml").freeze
16
+ CONFIG = YAML.safe_load(CONFIG_DEFAULT.read).freeze
13
17
 
14
- private_constant(:CONFIG_DEFAULT, :PROJECT_ROOT)
18
+ private_constant(:CONFIG_DEFAULT, :PROJECT_ROOT)
15
19
 
16
- ::RuboCop::ConfigObsoletion.files << PROJECT_ROOT.join("config", "obsoletion.yml")
20
+ ::RuboCop::ConfigObsoletion.files << PROJECT_ROOT.join("config", "obsoletion.yml")
21
+ end
17
22
  end
18
23
  end
@@ -4,8 +4,11 @@ require "rubocop"
4
4
 
5
5
  require_relative "rubocop/sorbet"
6
6
  require_relative "rubocop/sorbet/version"
7
- require_relative "rubocop/sorbet/inject"
7
+ require_relative "rubocop/sorbet/plugin"
8
8
 
9
- RuboCop::Sorbet::Inject.defaults!
9
+ unless RuboCop::Sorbet::Plugin::SUPPORTED
10
+ require_relative "rubocop/sorbet/inject"
11
+ RuboCop::Sorbet::Inject.defaults!
12
+ end
10
13
 
11
14
  require_relative "rubocop/cop/sorbet_cops"
data/manual/cops.md CHANGED
@@ -20,7 +20,11 @@ In the following section you find all available cops:
20
20
  * [Sorbet/ForbidExtendTSigHelpersInShims](cops_sorbet.md#sorbetforbidextendtsighelpersinshims)
21
21
  * [Sorbet/ForbidIncludeConstLiteral](cops_sorbet.md#sorbetforbidincludeconstliteral)
22
22
  * [Sorbet/ForbidRBIOutsideOfAllowedPaths](cops_sorbet.md#sorbetforbidrbioutsideofallowedpaths)
23
+ * [Sorbet/ForbidSig](cops_sorbet.md#sorbetforbidsig)
24
+ * [Sorbet/ForbidSigWithRuntime](cops_sorbet.md#sorbetforbidsigwithruntime)
25
+ * [Sorbet/ForbidSigWithoutRuntime](cops_sorbet.md#sorbetforbidsigwithoutruntime)
23
26
  * [Sorbet/ForbidSuperclassConstLiteral](cops_sorbet.md#sorbetforbidsuperclassconstliteral)
27
+ * [Sorbet/ForbidTEnum](cops_sorbet.md#sorbetforbidtenum)
24
28
  * [Sorbet/ForbidTStruct](cops_sorbet.md#sorbetforbidtstruct)
25
29
  * [Sorbet/ForbidTUnsafe](cops_sorbet.md#sorbetforbidtunsafe)
26
30
  * [Sorbet/ForbidTUntyped](cops_sorbet.md#sorbetforbidtuntyped)
@@ -417,6 +417,72 @@ Name | Default value | Configurable values
417
417
  AllowedPaths | `rbi/**`, `sorbet/rbi/**` | Array
418
418
  Include | `**/*.rbi` | Array
419
419
 
420
+ ## Sorbet/ForbidSig
421
+
422
+ Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
423
+ --- | --- | --- | --- | ---
424
+ Disabled | Yes | No | <<next>> | -
425
+
426
+ Check that definitions do not use a `sig` block.
427
+
428
+ Good:
429
+
430
+ ```
431
+ #: -> void
432
+ def foo; end
433
+ ```
434
+
435
+ Bad:
436
+
437
+ ```
438
+ sig { void }
439
+ def foo; end
440
+ ```
441
+
442
+ ## Sorbet/ForbidSigWithRuntime
443
+
444
+ Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
445
+ --- | --- | --- | --- | ---
446
+ Disabled | Yes | No | <<next>> | -
447
+
448
+ Check that definitions do not use a `sig` block.
449
+
450
+ Good:
451
+
452
+ ```
453
+ #: -> void
454
+ def foo; end
455
+ ```
456
+
457
+ Bad:
458
+
459
+ ```
460
+ T::Sig.sig { void }
461
+ def foo; end
462
+ ```
463
+
464
+ ## Sorbet/ForbidSigWithoutRuntime
465
+
466
+ Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
467
+ --- | --- | --- | --- | ---
468
+ Disabled | Yes | Yes | <<next>> | -
469
+
470
+ Check that `sig` is used instead of `T::Sig::WithoutRuntime.sig`.
471
+
472
+ Good:
473
+
474
+ ```
475
+ sig { void }
476
+ def foo; end
477
+ ```
478
+
479
+ Bad:
480
+
481
+ ```
482
+ T::Sig::WithoutRuntime.sig { void }
483
+ def foo; end
484
+ ```
485
+
420
486
  ## Sorbet/ForbidSuperclassConstLiteral
421
487
 
422
488
  Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
@@ -448,6 +514,33 @@ Name | Default value | Configurable values
448
514
  --- | --- | ---
449
515
  Exclude | `db/migrate/*.rb` | Array
450
516
 
517
+ ## Sorbet/ForbidTEnum
518
+
519
+ Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
520
+ --- | --- | --- | --- | ---
521
+ Disabled | No | No | <<next>> | <<next>>
522
+
523
+ Disallow using `T::Enum`.
524
+
525
+ ### Examples
526
+
527
+ ```ruby
528
+ # bad
529
+ class MyEnum < T::Enum
530
+ enums do
531
+ A = new
532
+ B = new
533
+ end
534
+ end
535
+
536
+ # good
537
+ class MyEnum
538
+ A = "a"
539
+ B = "b"
540
+ C = "c"
541
+ end
542
+ ```
543
+
451
544
  ## Sorbet/ForbidTStruct
452
545
 
453
546
  Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
@@ -588,6 +681,7 @@ Options:
588
681
  * `SuggestedStrictness`: Sorbet strictness level suggested in offense messages (default: 'false')
589
682
  * `MinimumStrictness`: If set, make offense if the strictness level in the file is below this one
590
683
 
684
+ If a `SuggestedStrictness` level is specified, it will be used in autocorrect.
591
685
  If a `MinimumStrictness` level is specified, it will be used in offense messages and autocorrect.
592
686
 
593
687
  ### Configurable attributes
@@ -860,10 +954,23 @@ Include | `**/*.rbi` | Array
860
954
 
861
955
  Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
862
956
  --- | --- | --- | --- | ---
863
- Disabled | Yes | Yes | 0.3.3 | -
957
+ Disabled | No | Yes | 0.3.3 | -
864
958
 
865
959
  Makes the Sorbet `strict` sigil mandatory in all files.
866
960
 
961
+ ### Examples
962
+
963
+ ```ruby
964
+ # bad
965
+ # typed: true
966
+
967
+ # bad
968
+ # typed: false
969
+
970
+ # good
971
+ # typed: strict
972
+ ```
973
+
867
974
  ### Configurable attributes
868
975
 
869
976
  Name | Default value | Configurable values
@@ -970,6 +1077,7 @@ Options:
970
1077
  * `ExactStrictness`: If set, make offense if the strictness level in the file is different than this one
971
1078
 
972
1079
  If an `ExactStrictness` level is specified, it will be used in offense messages and autocorrect.
1080
+ If a `SuggestedStrictness` level is specified, it will be used in autocorrect.
973
1081
  Otherwise, if a `MinimumStrictness` level is specified, it will be used in offense messages and autocorrect.
974
1082
 
975
1083
  ### Configurable attributes
@@ -17,6 +17,7 @@ Gem::Specification.new do |spec|
17
17
  spec.metadata["allowed_push_host"] = "https://rubygems.org"
18
18
  spec.metadata["homepage_uri"] = spec.homepage
19
19
  spec.metadata["source_code_uri"] = "https://github.com/shopify/rubocop-sorbet"
20
+ spec.metadata["default_lint_roller_plugin"] = "RuboCop::Sorbet::Plugin"
20
21
 
21
22
  # Specify which files should be added to the gem when it is released.
22
23
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
@@ -27,5 +28,6 @@ Gem::Specification.new do |spec|
27
28
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
28
29
  spec.require_paths = ["lib"]
29
30
 
31
+ spec.add_runtime_dependency("lint_roller", "~> 1.1")
30
32
  spec.add_runtime_dependency("rubocop", ">= 1")
31
33
  end
metadata CHANGED
@@ -1,18 +1,31 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop-sorbet
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.7
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ufuk Kayserilioglu
8
8
  - Alan Wu
9
9
  - Alexandre Terrasa
10
10
  - Peter Zhu
11
- autorequire:
12
11
  bindir: exe
13
12
  cert_chain: []
14
- date: 2024-11-01 00:00:00.000000000 Z
13
+ date: 2025-03-10 00:00:00.000000000 Z
15
14
  dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: lint_roller
17
+ requirement: !ruby/object:Gem::Requirement
18
+ requirements:
19
+ - - "~>"
20
+ - !ruby/object:Gem::Version
21
+ version: '1.1'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - "~>"
27
+ - !ruby/object:Gem::Version
28
+ version: '1.1'
16
29
  - !ruby/object:Gem::Dependency
17
30
  name: rubocop
18
31
  requirement: !ruby/object:Gem::Requirement
@@ -27,7 +40,6 @@ dependencies:
27
40
  - - ">="
28
41
  - !ruby/object:Gem::Version
29
42
  version: '1'
30
- description:
31
43
  email:
32
44
  - ruby@shopify.com
33
45
  executables: []
@@ -68,6 +80,7 @@ files:
68
80
  - lib/rubocop/cop/sorbet/constants_from_strings.rb
69
81
  - lib/rubocop/cop/sorbet/forbid_include_const_literal.rb
70
82
  - lib/rubocop/cop/sorbet/forbid_superclass_const_literal.rb
83
+ - lib/rubocop/cop/sorbet/forbid_t_enum.rb
71
84
  - lib/rubocop/cop/sorbet/forbid_t_struct.rb
72
85
  - lib/rubocop/cop/sorbet/forbid_t_unsafe.rb
73
86
  - lib/rubocop/cop/sorbet/forbid_t_untyped.rb
@@ -99,6 +112,9 @@ files:
99
112
  - lib/rubocop/cop/sorbet/signatures/checked_true_in_signature.rb
100
113
  - lib/rubocop/cop/sorbet/signatures/empty_line_after_sig.rb
101
114
  - lib/rubocop/cop/sorbet/signatures/enforce_signatures.rb
115
+ - lib/rubocop/cop/sorbet/signatures/forbid_sig.rb
116
+ - lib/rubocop/cop/sorbet/signatures/forbid_sig_with_runtime.rb
117
+ - lib/rubocop/cop/sorbet/signatures/forbid_sig_without_runtime.rb
102
118
  - lib/rubocop/cop/sorbet/signatures/keyword_argument_ordering.rb
103
119
  - lib/rubocop/cop/sorbet/signatures/signature_build_order.rb
104
120
  - lib/rubocop/cop/sorbet/signatures/void_checked_tests.rb
@@ -108,6 +124,7 @@ files:
108
124
  - lib/rubocop/cop/sorbet_cops.rb
109
125
  - lib/rubocop/sorbet.rb
110
126
  - lib/rubocop/sorbet/inject.rb
127
+ - lib/rubocop/sorbet/plugin.rb
111
128
  - lib/rubocop/sorbet/version.rb
112
129
  - manual/cops.md
113
130
  - manual/cops_sorbet.md
@@ -122,7 +139,7 @@ metadata:
122
139
  allowed_push_host: https://rubygems.org
123
140
  homepage_uri: https://github.com/shopify/rubocop-sorbet
124
141
  source_code_uri: https://github.com/shopify/rubocop-sorbet
125
- post_install_message:
142
+ default_lint_roller_plugin: RuboCop::Sorbet::Plugin
126
143
  rdoc_options: []
127
144
  require_paths:
128
145
  - lib
@@ -137,8 +154,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
137
154
  - !ruby/object:Gem::Version
138
155
  version: '0'
139
156
  requirements: []
140
- rubygems_version: 3.5.22
141
- signing_key:
157
+ rubygems_version: 3.6.5
142
158
  specification_version: 4
143
159
  summary: Automatic Sorbet code style checking tool.
144
160
  test_files: []