rubocop-sorbet 0.8.0 → 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ddc9a9caf478aaa31d5b985064aadc77e9e2d199927b7db01eb5823ea2190c28
4
- data.tar.gz: d440f179cff018d9ff54d68c01fafa9265d4020ca738f5311fe552b44c7d2074
3
+ metadata.gz: d0993b56c619f271efe5948d6a3b090695290b91d55db8948b95cf690c610cbf
4
+ data.tar.gz: 0f69733c05f1391747711ee316152b6541800a35682b71482d50903c8981bfdc
5
5
  SHA512:
6
- metadata.gz: b046329f103125db498e57acb613d387b780c6ec00c3d433cdb88e90ed1110091f5c6a1f59f0d3907abc98e1755ef332046b545b8dd54665939d997716ecf057
7
- data.tar.gz: 68348b3e0ad4a8a6141526fb6c5406b42e7ea12661256f83258551b2a11c3a6c04ded7843b7398a4903fa9877cbd16c1acb4bf7f3ccce69c600fca5bc51b18bc
6
+ metadata.gz: aa9413ff649b22b1afab51d3e6f8c48cfb1387ff52a0d3e8547db70c42824d9d2e0da53e093b79fb57f916d07100b655368831bf23abe12c69371281185faab9
7
+ data.tar.gz: 8cffb6fdef999a34b5f0c92ec7c2712b993f7851b8d7cb7c9ae7c9e764bd132f4d25b3bb733bb31facfb38cc17e62cbe042ac77aee92e74df093a20e56f5c7d4
@@ -0,0 +1,10 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "bundler"
4
+ directory: "/"
5
+ schedule:
6
+ interval: "weekly"
7
+ - package-ecosystem: "github-actions"
8
+ directory: "/"
9
+ schedule:
10
+ interval: "weekly"
@@ -11,10 +11,10 @@ jobs:
11
11
  strategy:
12
12
  fail-fast: false
13
13
  matrix:
14
- ruby: ["2.7", "3.0", "3.1", "3.2"]
14
+ ruby: ["3.0", "3.1", "3.2", "3.3"]
15
15
  name: Test Ruby ${{ matrix.ruby }}
16
16
  steps:
17
- - uses: actions/checkout@v2
17
+ - uses: actions/checkout@v4
18
18
  - name: Set up Ruby
19
19
  uses: ruby/setup-ruby@v1
20
20
  with:
@@ -29,7 +29,7 @@ jobs:
29
29
  fail-fast: false
30
30
  name: Lint & Docs
31
31
  steps:
32
- - uses: actions/checkout@v2
32
+ - uses: actions/checkout@v4
33
33
  - name: Set up Ruby
34
34
  uses: ruby/setup-ruby@v1
35
35
  with:
@@ -12,7 +12,7 @@ jobs:
12
12
  stale:
13
13
  runs-on: ubuntu-latest
14
14
  steps:
15
- - uses: actions/stale@v5
15
+ - uses: actions/stale@v9
16
16
  with:
17
17
  stale-pr-message: >
18
18
  This PR has been automatically marked as stale because it has not had
data/.rubocop.yml CHANGED
@@ -7,7 +7,6 @@ require:
7
7
  - rubocop/cop/internal_affairs
8
8
 
9
9
  AllCops:
10
- TargetRubyVersion: 2.7
11
10
  Exclude:
12
11
  - vendor/**/*
13
12
  NewCops: disable
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 3.2.0
data/Gemfile CHANGED
@@ -7,6 +7,6 @@ gemspec
7
7
 
8
8
  gem "byebug"
9
9
  gem "rake", ">= 12.3.3"
10
- gem "rspec", "~> 3.7"
10
+ gem "rspec", "~> 3.13"
11
11
  gem "rubocop-shopify", require: false
12
12
  gem "yard", "~> 0.9"
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rubocop-sorbet (0.8.0)
4
+ rubocop-sorbet (0.8.1)
5
5
  rubocop (>= 0.90.0)
6
6
 
7
7
  GEM
@@ -9,48 +9,48 @@ GEM
9
9
  specs:
10
10
  ast (2.4.2)
11
11
  byebug (11.1.3)
12
- diff-lcs (1.3)
13
- json (2.6.3)
12
+ diff-lcs (1.5.1)
13
+ json (2.7.1)
14
14
  language_server-protocol (3.17.0.3)
15
- parallel (1.23.0)
16
- parser (3.2.2.3)
15
+ parallel (1.24.0)
16
+ parser (3.3.0.5)
17
17
  ast (~> 2.4.1)
18
18
  racc
19
- racc (1.7.1)
19
+ racc (1.7.3)
20
20
  rainbow (3.1.1)
21
- rake (13.0.1)
22
- regexp_parser (2.8.1)
21
+ rake (13.2.1)
22
+ regexp_parser (2.9.0)
23
23
  rexml (3.2.6)
24
- rspec (3.8.0)
25
- rspec-core (~> 3.8.0)
26
- rspec-expectations (~> 3.8.0)
27
- rspec-mocks (~> 3.8.0)
28
- rspec-core (3.8.2)
29
- rspec-support (~> 3.8.0)
30
- rspec-expectations (3.8.4)
24
+ rspec (3.13.0)
25
+ rspec-core (~> 3.13.0)
26
+ rspec-expectations (~> 3.13.0)
27
+ rspec-mocks (~> 3.13.0)
28
+ rspec-core (3.13.0)
29
+ rspec-support (~> 3.13.0)
30
+ rspec-expectations (3.13.0)
31
31
  diff-lcs (>= 1.2.0, < 2.0)
32
- rspec-support (~> 3.8.0)
33
- rspec-mocks (3.8.1)
32
+ rspec-support (~> 3.13.0)
33
+ rspec-mocks (3.13.0)
34
34
  diff-lcs (>= 1.2.0, < 2.0)
35
- rspec-support (~> 3.8.0)
36
- rspec-support (3.8.2)
37
- rubocop (1.55.1)
35
+ rspec-support (~> 3.13.0)
36
+ rspec-support (3.13.1)
37
+ rubocop (1.62.1)
38
38
  json (~> 2.3)
39
39
  language_server-protocol (>= 3.17.0)
40
40
  parallel (~> 1.10)
41
- parser (>= 3.2.2.3)
41
+ parser (>= 3.3.0.2)
42
42
  rainbow (>= 2.2.2, < 4.0)
43
43
  regexp_parser (>= 1.8, < 3.0)
44
44
  rexml (>= 3.2.5, < 4.0)
45
- rubocop-ast (>= 1.28.1, < 2.0)
45
+ rubocop-ast (>= 1.31.1, < 2.0)
46
46
  ruby-progressbar (~> 1.7)
47
47
  unicode-display_width (>= 2.4.0, < 3.0)
48
- rubocop-ast (1.29.0)
49
- parser (>= 3.2.1.0)
48
+ rubocop-ast (1.31.2)
49
+ parser (>= 3.3.0.4)
50
50
  rubocop-shopify (2.14.0)
51
51
  rubocop (~> 1.51)
52
52
  ruby-progressbar (1.13.0)
53
- unicode-display_width (2.4.2)
53
+ unicode-display_width (2.5.0)
54
54
  yard (0.9.36)
55
55
 
56
56
  PLATFORMS
@@ -59,7 +59,7 @@ PLATFORMS
59
59
  DEPENDENCIES
60
60
  byebug
61
61
  rake (>= 12.3.3)
62
- rspec (~> 3.7)
62
+ rspec (~> 3.13)
63
63
  rubocop-shopify
64
64
  rubocop-sorbet!
65
65
  yard (~> 0.9)
@@ -0,0 +1,6 @@
1
+ #
2
+ # Configuration for obsoletion.
3
+ #
4
+ # See: https://docs.rubocop.org/rubocop/extensions.html#config-obsoletions
5
+ #
6
+ {}
data/config/rbi.yml CHANGED
@@ -2,7 +2,6 @@ require:
2
2
  - rubocop-sorbet
3
3
 
4
4
  AllCops:
5
- TargetRubyVersion: 3.0
6
5
  DisabledByDefault: true
7
6
  Include:
8
7
  - '**/*.rbi'
data/dev.yml CHANGED
@@ -3,7 +3,7 @@ name: "rubocop-sorbet"
3
3
  type: "ruby"
4
4
 
5
5
  up:
6
- - ruby: "3.2.0"
6
+ - ruby
7
7
  - "bundler"
8
8
 
9
9
  commands:
@@ -32,8 +32,13 @@ module RuboCop
32
32
  # true
33
33
  # end
34
34
  # end
35
- class CallbackConditionalsBinding < RuboCop::Cop::Cop # rubocop:todo InternalAffairs/InheritDeprecatedCopClass
36
- CALLBACKS = [
35
+ class CallbackConditionalsBinding < RuboCop::Cop::Base
36
+ extend AutoCorrector
37
+ include Alignment
38
+
39
+ MSG = "Callback conditionals should be bound to the right type. Use T.bind(self, %{type})"
40
+
41
+ RESTRICT_ON_SEND = [
37
42
  :validate,
38
43
  :validates,
39
44
  :validates_with,
@@ -72,97 +77,56 @@ module RuboCop
72
77
  :append_after_action,
73
78
  ].freeze
74
79
 
75
- def autocorrect(node)
76
- lambda do |corrector|
77
- options = node.each_child_node.find(&:hash_type?)
78
-
79
- conditional = nil
80
- options.each_pair do |keyword, block|
81
- if keyword.value == :if || keyword.value == :unless
82
- conditional = block
83
- break
84
- end
85
- end
86
-
87
- _, _, block = conditional.child_nodes
88
-
89
- # Find the class node and check if it includes a namespace on the
90
- # same line e.g.: Namespace::Class, which will require the fully
91
- # qualified name
92
-
93
- klass = node.ancestors.find(&:class_type?)
94
-
95
- expected_class = if klass.children.first.children.first.nil?
96
- node.parent_module_name.split("::").last
97
- else
98
- klass.identifier.source
99
- end
100
-
101
- do_end_lambda = conditional.source.include?("do") && conditional.source.include?("end")
80
+ # @!method argumentless_unbound_callable_callback_conditional?(node)
81
+ def_node_matcher :argumentless_unbound_callable_callback_conditional?, <<~PATTERN
82
+ (pair (sym {:if :unless}) # callback conditional
83
+ $(block
84
+ (send nil? {:lambda :proc}) # callable
85
+ (args) # argumentless
86
+ !`(send(const {cbase nil?} :T) :bind self $_ ) # unbound
87
+ )
88
+ )
89
+ PATTERN
102
90
 
103
- unless do_end_lambda
104
- # We are converting a one line lambda into a multiline
105
- # Remove the space after the `{`
106
- if /{\s/.match?(conditional.source)
107
- corrector.remove_preceding(block, 1)
91
+ def on_send(node)
92
+ type = immediately_enclosing_module_name(node)
93
+ return unless type
94
+
95
+ node.arguments.each do |arg|
96
+ next unless arg.hash_type? # Skip non-keyword arguments
97
+
98
+ arg.each_child_node do |pair_node|
99
+ argumentless_unbound_callable_callback_conditional?(pair_node) do |block|
100
+ add_offense(pair_node, message: format(MSG, type: type)) do |corrector|
101
+ block_opening_indentation = block.source_range.source_line[/\A */]
102
+ block_body_indentation = block_opening_indentation + SPACE * configured_indentation_width
103
+
104
+ if block.single_line? # then convert to multi-line block first
105
+ # 1. Replace whitespace (if any) between the opening delimiter and the block body,
106
+ # with newline and the correct indentation for the block body.
107
+ preceeding_whitespace_range = block.loc.begin.end.join(block.body.source_range.begin)
108
+ corrector.replace(preceeding_whitespace_range, "\n#{block_body_indentation}")
109
+
110
+ # 2. Replace whitespace (if any) between the block body and the closing delimiter,
111
+ # with newline and the same indentation as the block opening.
112
+ trailing_whitespace_range = block.body.source_range.end.join(block.loc.end.begin)
113
+ corrector.replace(trailing_whitespace_range, "\n#{block_opening_indentation}")
114
+ end
115
+
116
+ # Prepend the binding to the block body
117
+ corrector.insert_before(block.body, "T.bind(self, #{type})\n#{block_body_indentation}")
118
+ end
108
119
  end
109
-
110
- # Remove the last space and `}` and re-add it with a line break
111
- # and the correct indentation
112
- base_indentation = " " * node.loc.column
113
- chars_to_remove = /\s}/.match?(conditional.source) ? 2 : 1
114
- corrector.remove_trailing(conditional, chars_to_remove)
115
- corrector.insert_after(block, "\n#{base_indentation}}")
116
120
  end
117
-
118
- # Add the T.bind
119
- indentation = " " * (node.loc.column + 2)
120
- line_start = do_end_lambda ? "" : "\n#{indentation}"
121
- bind = "#{line_start}T.bind(self, #{expected_class})\n#{indentation}"
122
-
123
- corrector.insert_before(block, bind)
124
121
  end
125
122
  end
126
123
 
127
- def on_send(node)
128
- return unless CALLBACKS.include?(node.method_name)
129
-
130
- options = node.each_child_node.find(&:hash_type?)
131
- return if options.nil?
132
-
133
- conditional = nil
134
- options.each_pair do |keyword, block|
135
- next unless keyword.sym_type?
136
-
137
- if keyword.value == :if || keyword.value == :unless
138
- conditional = block
139
- break
140
- end
141
- end
142
-
143
- return if conditional.nil? || conditional.array_type? || conditional.child_nodes.empty?
144
-
145
- return unless conditional.arguments.empty?
124
+ private
146
125
 
147
- type, _, block = conditional.child_nodes
148
- return unless type.lambda_or_proc? || type.block_literal?
149
-
150
- klass = node.ancestors.find(&:class_type?)
151
-
152
- expected_class = if klass&.children&.first&.children&.first.nil?
153
- node.parent_module_name&.split("::")&.last
154
- else
155
- klass.identifier.source
156
- end
157
-
158
- return if expected_class.nil?
159
-
160
- unless block.source.include?("T.bind(self")
161
- add_offense(
162
- node,
163
- message: "Callback conditionals should be bound to the right type. Use T.bind(self, #{expected_class})",
164
- )
165
- end
126
+ # Find the immediately enclosing class or module name.
127
+ # Returns `nil`` if the immediate parent (skipping begin if present) is not a class or module.
128
+ def immediately_enclosing_module_name(node)
129
+ (node.parent&.begin_type? ? node.parent.parent : node.parent)&.defined_module_name
166
130
  end
167
131
  end
168
132
  end
@@ -33,21 +33,18 @@ module RuboCop
33
33
  #
34
34
  # # good
35
35
  # { "User" => User }.fetch(class_name)
36
- class ConstantsFromStrings < ::RuboCop::Cop::Cop # rubocop:todo InternalAffairs/InheritDeprecatedCopClass
37
- # @!method constant_from_string?(node)
38
- def_node_matcher(:constant_from_string?, <<-PATTERN)
39
- (send _ {:constantize :constants :const_get} ...)
40
- PATTERN
36
+ class ConstantsFromStrings < ::RuboCop::Cop::Base
37
+ MSG = "Don't use `%<method_name>s`, it makes the code harder to understand, less editor-friendly, " \
38
+ "and impossible to analyze. Replace `%<method_name>s` with a case/when or a hash."
41
39
 
42
- def on_send(node)
43
- return unless constant_from_string?(node)
40
+ RESTRICT_ON_SEND = [
41
+ :constantize,
42
+ :constants,
43
+ :const_get,
44
+ ].freeze
44
45
 
45
- add_offense(
46
- node,
47
- location: :selector,
48
- message: "Don't use `#{node.method_name}`, it makes the code harder to understand, less editor-friendly, " \
49
- "and impossible to analyze. Replace `#{node.method_name}` with a case/when or a hash.",
50
- )
46
+ def on_send(node)
47
+ add_offense(node.selector, message: format(MSG, method_name: node.method_name))
51
48
  end
52
49
  end
53
50
  end
@@ -79,7 +79,7 @@ module RuboCop
79
79
  return unless t_struct_prop?(node)
80
80
 
81
81
  kind = node.method?(:const) ? :attr_reader : :attr_accessor
82
- name = node.arguments[0].source.delete_prefix(":")
82
+ name = node.first_argument.source.delete_prefix(":")
83
83
  type = node.arguments[1].source
84
84
  default = nil
85
85
  factory = nil
@@ -29,7 +29,8 @@ module RuboCop
29
29
  strictness = extract_strictness(sigil)
30
30
  return unless check_strictness_not_empty(sigil, strictness)
31
31
  return unless check_strictness_valid(sigil, strictness)
32
- return unless check_strictness_level(sigil, strictness)
32
+
33
+ nil unless check_strictness_level(sigil, strictness)
33
34
  end
34
35
 
35
36
  def autocorrect(_node)
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RuboCop
4
4
  module Sorbet
5
- VERSION = "0.8.0"
5
+ VERSION = "0.8.1"
6
6
  end
7
7
  end
@@ -12,5 +12,7 @@ module RuboCop
12
12
  CONFIG = YAML.safe_load(CONFIG_DEFAULT.read).freeze
13
13
 
14
14
  private_constant(:CONFIG_DEFAULT, :PROJECT_ROOT)
15
+
16
+ ::RuboCop::ConfigObsoletion.files << PROJECT_ROOT.join("config", "obsoletion.yml")
15
17
  end
16
18
  end
@@ -12,6 +12,8 @@ Gem::Specification.new do |spec|
12
12
  spec.homepage = "https://github.com/shopify/rubocop-sorbet"
13
13
  spec.license = "MIT"
14
14
 
15
+ spec.required_ruby_version = ">= 3.0"
16
+
15
17
  spec.metadata["allowed_push_host"] = "https://rubygems.org"
16
18
  spec.metadata["homepage_uri"] = spec.homepage
17
19
  spec.metadata["source_code_uri"] = "https://github.com/shopify/rubocop-sorbet"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop-sorbet
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.8.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ufuk Kayserilioglu
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: exe
13
13
  cert_chain: []
14
- date: 2024-03-18 00:00:00.000000000 Z
14
+ date: 2024-04-09 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: rubocop
@@ -35,6 +35,7 @@ extensions: []
35
35
  extra_rdoc_files: []
36
36
  files:
37
37
  - ".github/CODEOWNERS"
38
+ - ".github/dependabot.yml"
38
39
  - ".github/release.yml"
39
40
  - ".github/workflows/ci.yml"
40
41
  - ".github/workflows/cla.yml"
@@ -42,6 +43,7 @@ files:
42
43
  - ".gitignore"
43
44
  - ".rspec"
44
45
  - ".rubocop.yml"
46
+ - ".ruby-version"
45
47
  - ".travis.yml"
46
48
  - ".yardopts"
47
49
  - CODE_OF_CONDUCT.md
@@ -55,6 +57,7 @@ files:
55
57
  - bin/rubocop
56
58
  - bin/setup
57
59
  - config/default.yml
60
+ - config/obsoletion.yml
58
61
  - config/rbi.yml
59
62
  - dev.yml
60
63
  - lib/rubocop-sorbet.rb
@@ -121,14 +124,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
121
124
  requirements:
122
125
  - - ">="
123
126
  - !ruby/object:Gem::Version
124
- version: '0'
127
+ version: '3.0'
125
128
  required_rubygems_version: !ruby/object:Gem::Requirement
126
129
  requirements:
127
130
  - - ">="
128
131
  - !ruby/object:Gem::Version
129
132
  version: '0'
130
133
  requirements: []
131
- rubygems_version: 3.5.6
134
+ rubygems_version: 3.5.7
132
135
  signing_key:
133
136
  specification_version: 4
134
137
  summary: Automatic Sorbet code style checking tool.