rubocop-thread_safety 0.5.1 → 0.7.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +22 -0
- data/README.md +8 -3
- data/config/default.yml +16 -0
- data/config/obsoletion.yml +2 -0
- data/lib/rubocop/cop/mixin/operation_with_threadsafe_result.rb +44 -0
- data/lib/rubocop/cop/thread_safety/class_and_module_attributes.rb +6 -3
- data/lib/rubocop/cop/thread_safety/{instance_variable_in_class_method.rb → class_instance_variable.rb} +67 -13
- data/lib/rubocop/cop/thread_safety/dir_chdir.rb +65 -0
- data/lib/rubocop/cop/thread_safety/mutable_class_instance_variable.rb +10 -43
- data/lib/rubocop/cop/thread_safety/new_thread.rb +4 -5
- data/lib/rubocop/cop/thread_safety/rack_middleware_instance_variable.rb +121 -0
- data/lib/rubocop/thread_safety/plugin.rb +38 -0
- data/lib/rubocop/thread_safety/version.rb +1 -1
- data/lib/rubocop/thread_safety.rb +0 -5
- data/lib/rubocop-thread_safety.rb +5 -3
- metadata +30 -94
- data/.github/dependabot.yml +0 -6
- data/.github/workflows/ci.yml +0 -26
- data/.github/workflows/lint.yml +0 -19
- data/.gitignore +0 -10
- data/.rspec +0 -2
- data/.rubocop.yml +0 -79
- data/.rubocop_todo.yml +0 -12
- data/Appraisals +0 -9
- data/Gemfile +0 -6
- data/Rakefile +0 -8
- data/bin/console +0 -15
- data/bin/setup +0 -6
- data/gemfiles/rubocop_0.90.gemfile +0 -7
- data/gemfiles/rubocop_1.20.gemfile +0 -7
- data/lib/rubocop/thread_safety/inject.rb +0 -20
- data/rubocop-thread_safety.gemspec +0 -38
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 440e483486f3d67ad21fa72aa04eb47535020324672c3e08646894cec3517450
|
4
|
+
data.tar.gz: 50fe186da5956ee780dd5c2839d7792fc57f10ed4860d0781d2f0d68180d635d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bb89cc31949d9803344f78556dc6f3351369bd2936ee8b26f93e6caa477b3bcfa6b7db9e8d78dade90eca9003455af4411c3ef064c9399f63455d6c7cd45c36c
|
7
|
+
data.tar.gz: b03de48386d763afe6d7a66aa2f604bcfc1fda637dfd864c2f09b52122fb8637c26f9a675299fe85fae464804e4e8cc2b569ba9fa8104cca2a592ef6fcb6c8c1
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# Change log
|
2
|
+
|
3
|
+
## master
|
4
|
+
|
5
|
+
## 0.7.0
|
6
|
+
|
7
|
+
- [#80](https://github.com/rubocop/rubocop-thread_safety/pull/80) Make RuboCop ThreadSafety work as a RuboCop plugin. ([@bquorning](https://github.com/bquorning))
|
8
|
+
- [#76](https://github.com/rubocop/rubocop-thread_safety/pull/76): Detect offenses when using safe navigation for `ThreadSafety/DirChdir`, `ThreadSafety/NewThread` and `ThreadSafety/RackMiddlewareInstanceVariable` cops. ([@viralpraxis](https://github.com/viralpraxis))
|
9
|
+
- [#73](https://github.com/rubocop/rubocop-thread_safety/pull/73): Add `AllowCallWithBlock` option to `ThreadSafety/DirChdir` cop. ([@viralpraxis](https://github.com/viralpraxis))
|
10
|
+
|
11
|
+
## 0.6.0
|
12
|
+
|
13
|
+
* [#59](https://github.com/rubocop/rubocop-thread_safety/pull/59): Rename `ThreadSafety::InstanceVariableInClassMethod` cop to `ThreadSafety::ClassInstanceVariable` to better reflect its purpose. ([@viralpraxis](https://github.com/viralpraxis))
|
14
|
+
* [#55](https://github.com/rubocop/rubocop-thread_safety/pull/55): Enhance `ThreadSafety::InstanceVariableInClassMethod` cop to detect offenses within `class_eval/exec` blocks. ([@viralpraxis](https://github.com/viralpraxis))
|
15
|
+
* [#54](https://github.com/rubocop/rubocop-thread_safety/pull/54): Drop support for RuboCop older than 1.48. ([@viralpraxis](https://github.com/viralpraxis))
|
16
|
+
* [#52](https://github.com/rubocop/rubocop-thread_safety/pull/52): Add new `RackMiddlewareInstanceVariable` cop to detect instance variables in Rack middleware. ([@viralpraxis](https://github.com/viralpraxis))
|
17
|
+
* [#48](https://github.com/rubocop/rubocop-thread_safety/pull/48): Do not report instance variables in `ActionDispatch` callbacks in singleton methods. ([@viralpraxis](https://github.com/viralpraxis))
|
18
|
+
* [#43](https://github.com/rubocop/rubocop-thread_safety/pull/43): Make detection of ActiveSupport's `class_attribute` configurable. ([@viralpraxis](https://github.com/viralpraxis))
|
19
|
+
* [#42](https://github.com/rubocop/rubocop-thread_safety/pull/42): Fix some `InstanceVariableInClassMethod` cop false positive offenses. ([@viralpraxis](https://github.com/viralpraxis))
|
20
|
+
* [#41](https://github.com/rubocop/rubocop-thread_safety/pull/41): Drop support for MRI older than 2.7. ([@viralpraxis](https://github.com/viralpraxis))
|
21
|
+
* [#38](https://github.com/rubocop/rubocop-thread_safety/pull/38): Fix `NewThread` cop detection is case of `Thread.start`, `Thread.fork`, or `Thread.new` with arguments. ([@viralpraxis](https://github.com/viralpraxis))
|
22
|
+
* [#36](https://github.com/rubocop/rubocop-thread_safety/pull/36): Add new `DirChdir` cop to detect `Dir.chdir` calls. ([@viralpraxis](https://github.com/viralpraxis))
|
data/README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# RuboCop::ThreadSafety
|
2
2
|
|
3
3
|
Thread-safety analysis for your projects, as an extension to
|
4
|
-
[RuboCop](https://github.com/
|
4
|
+
[RuboCop](https://github.com/rubocop/rubocop).
|
5
5
|
|
6
6
|
## Installation and Usage
|
7
7
|
|
@@ -19,11 +19,14 @@ Install it with Bundler by invoking:
|
|
19
19
|
|
20
20
|
Add this line to your application's `.rubocop.yml`:
|
21
21
|
|
22
|
-
|
22
|
+
plugins: rubocop-thread_safety
|
23
23
|
|
24
24
|
Now you can run `rubocop` and it will automatically load the RuboCop
|
25
25
|
Thread-Safety cops together with the standard cops.
|
26
26
|
|
27
|
+
> [!NOTE]
|
28
|
+
> The plugin system is supported in RuboCop 1.72+. In earlier versions, use `require` instead of `plugins`.
|
29
|
+
|
27
30
|
### Scanning an application without adding it to the Gemfile
|
28
31
|
|
29
32
|
Install the gem:
|
@@ -32,7 +35,7 @@ Install the gem:
|
|
32
35
|
|
33
36
|
Scan the application for just thread-safety issues:
|
34
37
|
|
35
|
-
$ rubocop
|
38
|
+
$ rubocop --plugin rubocop-thread_safety --only ThreadSafety,Style/GlobalVars,Style/ClassVars,Style/MutableConstant
|
36
39
|
|
37
40
|
### Configuration
|
38
41
|
|
@@ -60,6 +63,8 @@ Improvements that would make shared state thread-safe include:
|
|
60
63
|
* Use [`RequestStore`](https://github.com/steveklabnik/request_store)
|
61
64
|
* Use `Thread.current[:name]`
|
62
65
|
|
66
|
+
Certain system calls, such as `chdir`, affect the entire process. To avoid potential thread-safety issues, it's preferable to use (if possible) the `chdir` option in methods like `Kernel.system` and `IO.popen` rather than relying on `Dir.chdir`.
|
67
|
+
|
63
68
|
## Development
|
64
69
|
|
65
70
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
data/config/default.yml
CHANGED
@@ -5,6 +5,7 @@
|
|
5
5
|
ThreadSafety/ClassAndModuleAttributes:
|
6
6
|
Description: 'Avoid mutating class and module attributes.'
|
7
7
|
Enabled: true
|
8
|
+
ActiveSupportClassAttributeAllowed: false
|
8
9
|
|
9
10
|
ThreadSafety/InstanceVariableInClassMethod:
|
10
11
|
Description: 'Avoid using instance variables in class methods.'
|
@@ -30,3 +31,18 @@ ThreadSafety/NewThread:
|
|
30
31
|
Avoid starting new threads.
|
31
32
|
Let a framework like Sidekiq handle the threads.
|
32
33
|
Enabled: true
|
34
|
+
|
35
|
+
ThreadSafety/DirChdir:
|
36
|
+
Description: Avoid using `Dir.chdir` due to its process-wide effect.
|
37
|
+
Enabled: true
|
38
|
+
AllowCallWithBlock: false
|
39
|
+
|
40
|
+
ThreadSafety/RackMiddlewareInstanceVariable:
|
41
|
+
Description: Avoid instance variables in Rack middleware.
|
42
|
+
Enabled: true
|
43
|
+
Include:
|
44
|
+
- 'app/middleware/**/*.rb'
|
45
|
+
- 'lib/middleware/**/*.rb'
|
46
|
+
- 'app/middlewares/**/*.rb'
|
47
|
+
- 'lib/middlewares/**/*.rb'
|
48
|
+
AllowedIdentifiers: []
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
# Common functionality for checking if a well-known operation
|
6
|
+
# produces an object with thread-safe semantics.
|
7
|
+
module OperationWithThreadsafeResult
|
8
|
+
extend NodePattern::Macros
|
9
|
+
|
10
|
+
# @!method operation_produces_threadsafe_object?(node)
|
11
|
+
def_node_matcher :operation_produces_threadsafe_object?, <<~PATTERN
|
12
|
+
{
|
13
|
+
(send (const {nil? cbase} :Queue) :new ...)
|
14
|
+
(send
|
15
|
+
(const (const {nil? cbase} :ThreadSafe) {:Hash :Array})
|
16
|
+
:new ...)
|
17
|
+
(block
|
18
|
+
(send
|
19
|
+
(const (const {nil? cbase} :ThreadSafe) {:Hash :Array})
|
20
|
+
:new ...)
|
21
|
+
...)
|
22
|
+
(send (const (const {nil? cbase} :Concurrent) _) :new ...)
|
23
|
+
(block
|
24
|
+
(send (const (const {nil? cbase} :Concurrent) _) :new ...)
|
25
|
+
...)
|
26
|
+
(send (const (const (const {nil? cbase} :Concurrent) _) _) :new ...)
|
27
|
+
(block
|
28
|
+
(send
|
29
|
+
(const (const (const {nil? cbase} :Concurrent) _) _)
|
30
|
+
:new ...)
|
31
|
+
...)
|
32
|
+
(send
|
33
|
+
(const (const (const (const {nil? cbase} :Concurrent) _) _) _)
|
34
|
+
:new ...)
|
35
|
+
(block
|
36
|
+
(send
|
37
|
+
(const (const (const (const {nil? cbase} :Concurrent) _) _) _)
|
38
|
+
:new ...)
|
39
|
+
...)
|
40
|
+
}
|
41
|
+
PATTERN
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -49,9 +49,8 @@ module RuboCop
|
|
49
49
|
...)
|
50
50
|
MATCHER
|
51
51
|
|
52
|
-
def on_send(node)
|
53
|
-
return unless mattr?(node) || class_attr?(node) ||
|
54
|
-
singleton_attr?(node)
|
52
|
+
def on_send(node) # rubocop:disable InternalAffairs/OnSendWithoutOnCSend
|
53
|
+
return unless mattr?(node) || (!class_attribute_allowed? && class_attr?(node)) || singleton_attr?(node)
|
55
54
|
|
56
55
|
add_offense(node)
|
57
56
|
end
|
@@ -74,6 +73,10 @@ module RuboCop
|
|
74
73
|
|
75
74
|
false
|
76
75
|
end
|
76
|
+
|
77
|
+
def class_attribute_allowed?
|
78
|
+
cop_config['ActiveSupportClassAttributeAllowed']
|
79
|
+
end
|
77
80
|
end
|
78
81
|
end
|
79
82
|
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module ThreadSafety
|
6
|
-
# Avoid instance variables
|
6
|
+
# Avoid class instance variables.
|
7
7
|
#
|
8
8
|
# @example
|
9
9
|
# # bad
|
@@ -61,8 +61,8 @@ module RuboCop
|
|
61
61
|
#
|
62
62
|
# module_function :test
|
63
63
|
# end
|
64
|
-
class
|
65
|
-
MSG = 'Avoid instance variables
|
64
|
+
class ClassInstanceVariable < Base
|
65
|
+
MSG = 'Avoid class instance variables.'
|
66
66
|
RESTRICT_ON_SEND = %i[
|
67
67
|
instance_variable_set
|
68
68
|
instance_variable_get
|
@@ -87,7 +87,7 @@ module RuboCop
|
|
87
87
|
end
|
88
88
|
alias on_ivasgn on_ivar
|
89
89
|
|
90
|
-
def on_send(node)
|
90
|
+
def on_send(node) # rubocop:disable InternalAffairs/OnSendWithoutOnCSend
|
91
91
|
return unless instance_variable_call?(node)
|
92
92
|
return unless class_method_definition?(node)
|
93
93
|
return if method_definition?(node)
|
@@ -99,21 +99,28 @@ module RuboCop
|
|
99
99
|
private
|
100
100
|
|
101
101
|
def class_method_definition?(node)
|
102
|
-
return false if method_definition?(node)
|
103
|
-
|
104
102
|
in_defs?(node) ||
|
105
103
|
in_def_sclass?(node) ||
|
106
104
|
in_def_class_methods?(node) ||
|
107
105
|
in_def_module_function?(node) ||
|
106
|
+
in_class_eval?(node) ||
|
108
107
|
singleton_method_definition?(node)
|
109
108
|
end
|
110
109
|
|
111
110
|
def in_defs?(node)
|
112
|
-
node.ancestors.any?
|
111
|
+
node.ancestors.any? do |ancestor|
|
112
|
+
break false if new_lexical_scope?(ancestor)
|
113
|
+
|
114
|
+
ancestor.defs_type?
|
115
|
+
end
|
113
116
|
end
|
114
117
|
|
115
118
|
def in_def_sclass?(node)
|
116
|
-
defn = node.ancestors.find
|
119
|
+
defn = node.ancestors.find do |ancestor|
|
120
|
+
break if new_lexical_scope?(ancestor)
|
121
|
+
|
122
|
+
ancestor.def_type?
|
123
|
+
end
|
117
124
|
|
118
125
|
defn&.ancestors&.any?(&:sclass_type?)
|
119
126
|
end
|
@@ -124,35 +131,51 @@ module RuboCop
|
|
124
131
|
|
125
132
|
def in_def_class_methods_dsl?(node)
|
126
133
|
node.ancestors.any? do |ancestor|
|
134
|
+
break if new_lexical_scope?(ancestor)
|
127
135
|
next unless ancestor.block_type?
|
128
|
-
next unless ancestor.children.first.is_a? AST::SendNode
|
129
136
|
|
130
137
|
ancestor.children.first.command? :class_methods
|
131
138
|
end
|
132
139
|
end
|
133
140
|
|
134
141
|
def in_def_class_methods_module?(node)
|
135
|
-
defn = node.ancestors.find
|
136
|
-
|
142
|
+
defn = node.ancestors.find do |ancestor|
|
143
|
+
break if new_lexical_scope?(ancestor)
|
144
|
+
|
145
|
+
ancestor.def_type?
|
146
|
+
end
|
147
|
+
return false unless defn
|
137
148
|
|
138
149
|
mod = defn.ancestors.find do |ancestor|
|
139
150
|
%i[class module].include?(ancestor.type)
|
140
151
|
end
|
141
|
-
return unless mod
|
152
|
+
return false unless mod
|
142
153
|
|
143
154
|
class_methods_module?(mod)
|
144
155
|
end
|
145
156
|
|
146
157
|
def in_def_module_function?(node)
|
147
158
|
defn = node.ancestors.find(&:def_type?)
|
148
|
-
return unless defn
|
159
|
+
return false unless defn
|
149
160
|
|
150
161
|
defn.left_siblings.any? { |sibling| module_function_bare_access_modifier?(sibling) } ||
|
151
162
|
defn.right_siblings.any? { |sibling| module_function_for?(sibling, defn.method_name) }
|
152
163
|
end
|
153
164
|
|
165
|
+
def in_class_eval?(node)
|
166
|
+
defn = node.ancestors.find do |ancestor|
|
167
|
+
break if ancestor.def_type? || new_lexical_scope?(ancestor)
|
168
|
+
|
169
|
+
ancestor.block_type?
|
170
|
+
end
|
171
|
+
return false unless defn
|
172
|
+
|
173
|
+
class_eval_scope?(defn)
|
174
|
+
end
|
175
|
+
|
154
176
|
def singleton_method_definition?(node)
|
155
177
|
node.ancestors.any? do |ancestor|
|
178
|
+
break if new_lexical_scope?(ancestor)
|
156
179
|
next unless ancestor.children.first.is_a? AST::SendNode
|
157
180
|
|
158
181
|
ancestor.children.first.command? :define_singleton_method
|
@@ -161,6 +184,7 @@ module RuboCop
|
|
161
184
|
|
162
185
|
def method_definition?(node)
|
163
186
|
node.ancestors.any? do |ancestor|
|
187
|
+
break if new_lexical_scope?(ancestor)
|
164
188
|
next unless ancestor.children.first.is_a? AST::SendNode
|
165
189
|
|
166
190
|
ancestor.children.first.command? :define_method
|
@@ -199,6 +223,36 @@ module RuboCop
|
|
199
223
|
def_node_matcher :module_function_for?, <<~PATTERN
|
200
224
|
(send nil? {:module_function} ({sym str} #match_name?(%1)))
|
201
225
|
PATTERN
|
226
|
+
|
227
|
+
# @!method new_lexical_scope?(node)
|
228
|
+
def_node_matcher :new_lexical_scope?, <<~PATTERN
|
229
|
+
{
|
230
|
+
(block (send (const nil? :Struct) :new ...) _ ({def defs} ...))
|
231
|
+
(block (send (const nil? :Class) :new ...) _ ({def defs} ...))
|
232
|
+
(block (send (const nil? :Data) :define ...) _ ({def defs} ...))
|
233
|
+
(block
|
234
|
+
(send nil?
|
235
|
+
{
|
236
|
+
:prepend_around_action
|
237
|
+
:prepend_before_action
|
238
|
+
:before_action
|
239
|
+
:append_before_action
|
240
|
+
:around_action
|
241
|
+
:append_around_action
|
242
|
+
:append_after_action
|
243
|
+
:after_action
|
244
|
+
:prepend_after_action
|
245
|
+
}
|
246
|
+
)
|
247
|
+
...
|
248
|
+
)
|
249
|
+
}
|
250
|
+
PATTERN
|
251
|
+
|
252
|
+
# @!method class_eval_scope?(node)
|
253
|
+
def_node_matcher :class_eval_scope?, <<~PATTERN
|
254
|
+
(block (send (const {nil? cbase} _) {:class_eval :class_exec}) ...)
|
255
|
+
PATTERN
|
202
256
|
end
|
203
257
|
end
|
204
258
|
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module ThreadSafety
|
6
|
+
# Avoid using `Dir.chdir` due to its process-wide effect.
|
7
|
+
# If `AllowCallWithBlock` (disabled by default) option is enabled,
|
8
|
+
# calling `Dir.chdir` with block will be allowed.
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# # bad
|
12
|
+
# Dir.chdir("/var/run")
|
13
|
+
#
|
14
|
+
# # bad
|
15
|
+
# FileUtils.chdir("/var/run")
|
16
|
+
#
|
17
|
+
# @example AllowCallWithBlock: false (default)
|
18
|
+
# # good
|
19
|
+
# Dir.chdir("/var/run") do
|
20
|
+
# puts Dir.pwd
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# @example AllowCallWithBlock: true
|
24
|
+
# # bad
|
25
|
+
# Dir.chdir("/var/run") do
|
26
|
+
# puts Dir.pwd
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
class DirChdir < Base
|
30
|
+
MESSAGE = 'Avoid using `%<module>s%<dot>s%<method>s` due to its process-wide effect.'
|
31
|
+
RESTRICT_ON_SEND = %i[chdir cd].freeze
|
32
|
+
|
33
|
+
# @!method chdir?(node)
|
34
|
+
def_node_matcher :chdir?, <<~MATCHER
|
35
|
+
{
|
36
|
+
(call (const {nil? cbase} {:Dir :FileUtils}) :chdir ...)
|
37
|
+
(call (const {nil? cbase} :FileUtils) :cd ...)
|
38
|
+
}
|
39
|
+
MATCHER
|
40
|
+
|
41
|
+
def on_send(node)
|
42
|
+
return unless chdir?(node)
|
43
|
+
return if allow_call_with_block? && (node.block_argument? || node.parent&.block_type?)
|
44
|
+
|
45
|
+
add_offense(
|
46
|
+
node,
|
47
|
+
message: format(
|
48
|
+
MESSAGE,
|
49
|
+
module: node.receiver.short_name,
|
50
|
+
method: node.method_name,
|
51
|
+
dot: node.loc.dot.source
|
52
|
+
)
|
53
|
+
)
|
54
|
+
end
|
55
|
+
alias on_csend on_send
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def allow_call_with_block?
|
60
|
+
!!cop_config['AllowCallWithBlock']
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -7,7 +7,7 @@ module RuboCop
|
|
7
7
|
# mutable literal (e.g. array or hash).
|
8
8
|
#
|
9
9
|
# It is based on Style/MutableConstant from RuboCop.
|
10
|
-
# See https://github.com/rubocop
|
10
|
+
# See https://github.com/rubocop/rubocop/blob/master/lib/rubocop/cop/style/mutable_constant.rb
|
11
11
|
#
|
12
12
|
# Class instance variables are a risk to threaded code as they are shared
|
13
13
|
# between threads. A mutable object such as an array or hash may be
|
@@ -74,8 +74,10 @@ module RuboCop
|
|
74
74
|
# end
|
75
75
|
class MutableClassInstanceVariable < Base
|
76
76
|
extend AutoCorrector
|
77
|
+
|
77
78
|
include FrozenStringLiteral
|
78
79
|
include ConfigurableEnforcedStyle
|
80
|
+
include OperationWithThreadsafeResult
|
79
81
|
|
80
82
|
MSG = 'Freeze mutable objects assigned to class instance variables.'
|
81
83
|
FROZEN_STRING_LITERAL_TYPES_RUBY27 = %i[str dstr].freeze
|
@@ -84,22 +86,20 @@ module RuboCop
|
|
84
86
|
def on_ivasgn(node)
|
85
87
|
return unless in_class?(node)
|
86
88
|
|
87
|
-
|
88
|
-
on_assignment(value)
|
89
|
+
on_assignment(node.expression)
|
89
90
|
end
|
90
91
|
|
91
92
|
def on_or_asgn(node)
|
92
|
-
|
93
|
-
return unless lhs&.ivasgn_type?
|
93
|
+
return unless node.assignment_node.ivasgn_type?
|
94
94
|
return unless in_class?(node)
|
95
95
|
|
96
|
-
on_assignment(
|
96
|
+
on_assignment(node.expression)
|
97
97
|
end
|
98
98
|
|
99
99
|
def on_masgn(node)
|
100
100
|
return unless in_class?(node)
|
101
101
|
|
102
|
-
mlhs, values = *node
|
102
|
+
mlhs, values = *node # rubocop:disable InternalAffairs/NodeDestructuring
|
103
103
|
return unless values.array_type?
|
104
104
|
|
105
105
|
mlhs.to_a.zip(values.to_a).each do |lhs, value|
|
@@ -183,7 +183,7 @@ module RuboCop
|
|
183
183
|
end
|
184
184
|
|
185
185
|
def mutable_literal?(node)
|
186
|
-
return if node.nil?
|
186
|
+
return false if node.nil?
|
187
187
|
|
188
188
|
node.mutable_literal? || range_type?(node)
|
189
189
|
end
|
@@ -198,7 +198,7 @@ module RuboCop
|
|
198
198
|
end
|
199
199
|
|
200
200
|
def range_type?(node)
|
201
|
-
node.
|
201
|
+
node.type?(:erange, :irange)
|
202
202
|
end
|
203
203
|
|
204
204
|
def correct_splat_expansion(corrector, expr, splat_value)
|
@@ -243,42 +243,9 @@ module RuboCop
|
|
243
243
|
}
|
244
244
|
PATTERN
|
245
245
|
|
246
|
-
# @!method operation_produces_threadsafe_object?(node)
|
247
|
-
def_node_matcher :operation_produces_threadsafe_object?, <<~PATTERN
|
248
|
-
{
|
249
|
-
(send (const {nil? cbase} :Queue) :new ...)
|
250
|
-
(send
|
251
|
-
(const (const {nil? cbase} :ThreadSafe) {:Hash :Array})
|
252
|
-
:new ...)
|
253
|
-
(block
|
254
|
-
(send
|
255
|
-
(const (const {nil? cbase} :ThreadSafe) {:Hash :Array})
|
256
|
-
:new ...)
|
257
|
-
...)
|
258
|
-
(send (const (const {nil? cbase} :Concurrent) _) :new ...)
|
259
|
-
(block
|
260
|
-
(send (const (const {nil? cbase} :Concurrent) _) :new ...)
|
261
|
-
...)
|
262
|
-
(send (const (const (const {nil? cbase} :Concurrent) _) _) :new ...)
|
263
|
-
(block
|
264
|
-
(send
|
265
|
-
(const (const (const {nil? cbase} :Concurrent) _) _)
|
266
|
-
:new ...)
|
267
|
-
...)
|
268
|
-
(send
|
269
|
-
(const (const (const (const {nil? cbase} :Concurrent) _) _) _)
|
270
|
-
:new ...)
|
271
|
-
(block
|
272
|
-
(send
|
273
|
-
(const (const (const (const {nil? cbase} :Concurrent) _) _) _)
|
274
|
-
:new ...)
|
275
|
-
...)
|
276
|
-
}
|
277
|
-
PATTERN
|
278
|
-
|
279
246
|
# @!method range_enclosed_in_parentheses?(node)
|
280
247
|
def_node_matcher :range_enclosed_in_parentheses?, <<~PATTERN
|
281
|
-
(begin (
|
248
|
+
(begin (range _ _))
|
282
249
|
PATTERN
|
283
250
|
end
|
284
251
|
end
|
@@ -12,18 +12,17 @@ module RuboCop
|
|
12
12
|
# Thread.new { do_work }
|
13
13
|
class NewThread < Base
|
14
14
|
MSG = 'Avoid starting new threads.'
|
15
|
-
RESTRICT_ON_SEND = %i[new].freeze
|
15
|
+
RESTRICT_ON_SEND = %i[new fork start].freeze
|
16
16
|
|
17
17
|
# @!method new_thread?(node)
|
18
18
|
def_node_matcher :new_thread?, <<~MATCHER
|
19
|
-
(
|
19
|
+
(call (const {nil? cbase} :Thread) {:new :fork :start} ...)
|
20
20
|
MATCHER
|
21
21
|
|
22
22
|
def on_send(node)
|
23
|
-
|
24
|
-
|
25
|
-
add_offense(node)
|
23
|
+
new_thread?(node) { add_offense(node) }
|
26
24
|
end
|
25
|
+
alias on_csend on_send
|
27
26
|
end
|
28
27
|
end
|
29
28
|
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module ThreadSafety
|
6
|
+
# Avoid instance variables in rack middleware.
|
7
|
+
#
|
8
|
+
# Middlewares are initialized once, meaning any instance variables are shared between executor threads.
|
9
|
+
# To avoid potential race conditions, it's recommended to design middlewares to be stateless
|
10
|
+
# or to implement proper synchronization mechanisms.
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# # bad
|
14
|
+
# class CounterMiddleware
|
15
|
+
# def initialize(app)
|
16
|
+
# @app = app
|
17
|
+
# @counter = 0
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# def call(env)
|
21
|
+
# app.call(env)
|
22
|
+
# ensure
|
23
|
+
# @counter += 1
|
24
|
+
# end
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# # good
|
28
|
+
# class CounterMiddleware
|
29
|
+
# def initialize(app)
|
30
|
+
# @app = app
|
31
|
+
# @counter = Concurrent::AtomicReference.new(0)
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# def call(env)
|
35
|
+
# app.call(env)
|
36
|
+
# ensure
|
37
|
+
# @counter.update { |ref| ref + 1 }
|
38
|
+
# end
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# class IdentityMiddleware
|
42
|
+
# def initialize(app)
|
43
|
+
# @app = app
|
44
|
+
# end
|
45
|
+
#
|
46
|
+
# def call(env)
|
47
|
+
# app.call(env)
|
48
|
+
# end
|
49
|
+
# end
|
50
|
+
class RackMiddlewareInstanceVariable < Base
|
51
|
+
include AllowedIdentifiers
|
52
|
+
include OperationWithThreadsafeResult
|
53
|
+
|
54
|
+
MSG = 'Avoid instance variables in Rack middleware.'
|
55
|
+
|
56
|
+
RESTRICT_ON_SEND = %i[instance_variable_get instance_variable_set].freeze
|
57
|
+
|
58
|
+
# @!method rack_middleware_like_class?(node)
|
59
|
+
def_node_matcher :rack_middleware_like_class?, <<~MATCHER
|
60
|
+
(class (const nil? _) nil? (begin <(def :initialize (args (arg _)+) ...) (def :call (args (arg _)) ...) ...>))
|
61
|
+
MATCHER
|
62
|
+
|
63
|
+
# @!method app_variable(node)
|
64
|
+
def_node_search :app_variable, <<~MATCHER
|
65
|
+
(def :initialize (args (arg $_) ...) `(ivasgn $_ (lvar $_)))
|
66
|
+
MATCHER
|
67
|
+
|
68
|
+
def on_class(node)
|
69
|
+
return unless rack_middleware_like_class?(node)
|
70
|
+
|
71
|
+
constructor_method = find_constructor_method(node)
|
72
|
+
return unless (application_variable = extract_application_variable_from_contructor_method(constructor_method))
|
73
|
+
|
74
|
+
safe_variables = extract_safe_variables_from_constructor_method(constructor_method)
|
75
|
+
|
76
|
+
node.each_node(:def) do |def_node|
|
77
|
+
def_node.each_node(:ivasgn, :ivar) do |ivar_node|
|
78
|
+
variable, = ivar_node.to_a
|
79
|
+
if variable == application_variable || safe_variables.include?(variable) || allowed_identifier?(variable)
|
80
|
+
next
|
81
|
+
end
|
82
|
+
|
83
|
+
add_offense ivar_node
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def on_send(node)
|
89
|
+
argument = node.first_argument
|
90
|
+
|
91
|
+
return unless argument&.type?(:sym, :str)
|
92
|
+
return if allowed_identifier?(argument.value)
|
93
|
+
|
94
|
+
add_offense node
|
95
|
+
end
|
96
|
+
alias on_csend on_send
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
def find_constructor_method(class_node)
|
101
|
+
class_node
|
102
|
+
.each_node(:def)
|
103
|
+
.find { |node| node.method?(:initialize) && node.arguments.size >= 1 }
|
104
|
+
end
|
105
|
+
|
106
|
+
def extract_application_variable_from_contructor_method(constructor_method)
|
107
|
+
constructor_method
|
108
|
+
.then { |node| app_variable(node) }
|
109
|
+
.then { |variables| variables.first[1] if variables.first }
|
110
|
+
end
|
111
|
+
|
112
|
+
def extract_safe_variables_from_constructor_method(constructor_method)
|
113
|
+
constructor_method
|
114
|
+
.each_node(:ivasgn)
|
115
|
+
.select { |ivasgn_node| operation_produces_threadsafe_object?(ivasgn_node.to_a[1]) }
|
116
|
+
.map { _1.to_a[0] }
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'lint_roller'
|
4
|
+
|
5
|
+
module RuboCop
|
6
|
+
module ThreadSafety
|
7
|
+
# A plugin that integrates RuboCop ThreadSafety with RuboCop's plugin system.
|
8
|
+
class Plugin < LintRoller::Plugin
|
9
|
+
# :nocov:
|
10
|
+
def about
|
11
|
+
LintRoller::About.new(
|
12
|
+
name: 'rubocop-thread_safety',
|
13
|
+
version: Version::STRING,
|
14
|
+
homepage: 'https://github.com/rubocop/rubocop-thread_safety',
|
15
|
+
description: 'Thread-safety checks via static analysis.'
|
16
|
+
)
|
17
|
+
end
|
18
|
+
# :nocov:
|
19
|
+
|
20
|
+
def supported?(context)
|
21
|
+
context.engine == :rubocop
|
22
|
+
end
|
23
|
+
|
24
|
+
def rules(_context)
|
25
|
+
project_root = Pathname.new(__dir__).join('../../..')
|
26
|
+
|
27
|
+
obsoletion = project_root.join('config', 'obsoletion.yml')
|
28
|
+
ConfigObsoletion.files << obsoletion
|
29
|
+
|
30
|
+
LintRoller::Rules.new(
|
31
|
+
type: :path,
|
32
|
+
config_format: :rubocop,
|
33
|
+
value: project_root.join('config/default.yml')
|
34
|
+
)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -3,10 +3,5 @@
|
|
3
3
|
module RuboCop
|
4
4
|
# RuboCop::ThreadSafety detects some potential thread safety issues.
|
5
5
|
module ThreadSafety
|
6
|
-
PROJECT_ROOT = Pathname.new(File.expand_path('../../', __dir__))
|
7
|
-
CONFIG_DEFAULT = PROJECT_ROOT.join('config', 'default.yml').freeze
|
8
|
-
CONFIG = YAML.safe_load(CONFIG_DEFAULT.read).freeze
|
9
|
-
|
10
|
-
private_constant(:CONFIG_DEFAULT, :PROJECT_ROOT)
|
11
6
|
end
|
12
7
|
end
|
@@ -4,11 +4,13 @@ require 'rubocop'
|
|
4
4
|
|
5
5
|
require 'rubocop/thread_safety'
|
6
6
|
require 'rubocop/thread_safety/version'
|
7
|
-
require 'rubocop/thread_safety/
|
7
|
+
require 'rubocop/thread_safety/plugin'
|
8
8
|
|
9
|
-
|
9
|
+
require 'rubocop/cop/mixin/operation_with_threadsafe_result'
|
10
10
|
|
11
|
-
require 'rubocop/cop/thread_safety/
|
11
|
+
require 'rubocop/cop/thread_safety/class_instance_variable'
|
12
12
|
require 'rubocop/cop/thread_safety/class_and_module_attributes'
|
13
13
|
require 'rubocop/cop/thread_safety/mutable_class_instance_variable'
|
14
14
|
require 'rubocop/cop/thread_safety/new_thread'
|
15
|
+
require 'rubocop/cop/thread_safety/dir_chdir'
|
16
|
+
require 'rubocop/cop/thread_safety/rack_middleware_instance_variable'
|
metadata
CHANGED
@@ -1,105 +1,48 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rubocop-thread_safety
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Gee
|
8
|
-
autorequire:
|
9
8
|
bindir: exe
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 2025-02-22 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
13
|
+
name: lint_roller
|
15
14
|
requirement: !ruby/object:Gem::Requirement
|
16
15
|
requirements:
|
17
|
-
- - "
|
16
|
+
- - "~>"
|
18
17
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
18
|
+
version: '1.1'
|
20
19
|
type: :runtime
|
21
20
|
prerelease: false
|
22
21
|
version_requirements: !ruby/object:Gem::Requirement
|
23
22
|
requirements:
|
24
|
-
- - "
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: 0.90.0
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: appraisal
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - ">="
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '0'
|
34
|
-
type: :development
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - ">="
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '0'
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: bundler
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - ">="
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '1.10'
|
48
|
-
- - "<"
|
49
|
-
- !ruby/object:Gem::Version
|
50
|
-
version: '3'
|
51
|
-
type: :development
|
52
|
-
prerelease: false
|
53
|
-
version_requirements: !ruby/object:Gem::Requirement
|
54
|
-
requirements:
|
55
|
-
- - ">="
|
56
|
-
- !ruby/object:Gem::Version
|
57
|
-
version: '1.10'
|
58
|
-
- - "<"
|
23
|
+
- - "~>"
|
59
24
|
- !ruby/object:Gem::Version
|
60
|
-
version: '
|
25
|
+
version: '1.1'
|
61
26
|
- !ruby/object:Gem::Dependency
|
62
|
-
name:
|
27
|
+
name: rubocop
|
63
28
|
requirement: !ruby/object:Gem::Requirement
|
64
29
|
requirements:
|
65
|
-
- - "
|
66
|
-
- !ruby/object:Gem::Version
|
67
|
-
version: '0'
|
68
|
-
type: :development
|
69
|
-
prerelease: false
|
70
|
-
version_requirements: !ruby/object:Gem::Requirement
|
71
|
-
requirements:
|
72
|
-
- - ">="
|
30
|
+
- - "~>"
|
73
31
|
- !ruby/object:Gem::Version
|
74
|
-
version: '
|
75
|
-
- !ruby/object:Gem::Dependency
|
76
|
-
name: rake
|
77
|
-
requirement: !ruby/object:Gem::Requirement
|
78
|
-
requirements:
|
32
|
+
version: '1.72'
|
79
33
|
- - ">="
|
80
34
|
- !ruby/object:Gem::Version
|
81
|
-
version:
|
82
|
-
type: :
|
35
|
+
version: 1.72.1
|
36
|
+
type: :runtime
|
83
37
|
prerelease: false
|
84
38
|
version_requirements: !ruby/object:Gem::Requirement
|
85
|
-
requirements:
|
86
|
-
- - ">="
|
87
|
-
- !ruby/object:Gem::Version
|
88
|
-
version: '10.0'
|
89
|
-
- !ruby/object:Gem::Dependency
|
90
|
-
name: rspec
|
91
|
-
requirement: !ruby/object:Gem::Requirement
|
92
39
|
requirements:
|
93
40
|
- - "~>"
|
94
41
|
- !ruby/object:Gem::Version
|
95
|
-
version: '
|
96
|
-
|
97
|
-
prerelease: false
|
98
|
-
version_requirements: !ruby/object:Gem::Requirement
|
99
|
-
requirements:
|
100
|
-
- - "~>"
|
42
|
+
version: '1.72'
|
43
|
+
- - ">="
|
101
44
|
- !ruby/object:Gem::Version
|
102
|
-
version:
|
45
|
+
version: 1.72.1
|
103
46
|
description: |2
|
104
47
|
Thread-safety checks via static analysis.
|
105
48
|
A plugin for the RuboCop code style enforcing & linting tool.
|
@@ -109,37 +52,31 @@ executables: []
|
|
109
52
|
extensions: []
|
110
53
|
extra_rdoc_files: []
|
111
54
|
files:
|
112
|
-
-
|
113
|
-
- ".github/workflows/ci.yml"
|
114
|
-
- ".github/workflows/lint.yml"
|
115
|
-
- ".gitignore"
|
116
|
-
- ".rspec"
|
117
|
-
- ".rubocop.yml"
|
118
|
-
- ".rubocop_todo.yml"
|
119
|
-
- Appraisals
|
120
|
-
- Gemfile
|
55
|
+
- CHANGELOG.md
|
121
56
|
- LICENSE.txt
|
122
57
|
- README.md
|
123
|
-
- Rakefile
|
124
|
-
- bin/console
|
125
|
-
- bin/setup
|
126
58
|
- config/default.yml
|
127
|
-
-
|
128
|
-
- gemfiles/rubocop_1.20.gemfile
|
59
|
+
- config/obsoletion.yml
|
129
60
|
- lib/rubocop-thread_safety.rb
|
61
|
+
- lib/rubocop/cop/mixin/operation_with_threadsafe_result.rb
|
130
62
|
- lib/rubocop/cop/thread_safety/class_and_module_attributes.rb
|
131
|
-
- lib/rubocop/cop/thread_safety/
|
63
|
+
- lib/rubocop/cop/thread_safety/class_instance_variable.rb
|
64
|
+
- lib/rubocop/cop/thread_safety/dir_chdir.rb
|
132
65
|
- lib/rubocop/cop/thread_safety/mutable_class_instance_variable.rb
|
133
66
|
- lib/rubocop/cop/thread_safety/new_thread.rb
|
67
|
+
- lib/rubocop/cop/thread_safety/rack_middleware_instance_variable.rb
|
134
68
|
- lib/rubocop/thread_safety.rb
|
135
|
-
- lib/rubocop/thread_safety/
|
69
|
+
- lib/rubocop/thread_safety/plugin.rb
|
136
70
|
- lib/rubocop/thread_safety/version.rb
|
137
|
-
- rubocop-thread_safety.gemspec
|
138
71
|
homepage: https://github.com/rubocop/rubocop-thread_safety
|
139
72
|
licenses:
|
140
73
|
- MIT
|
141
|
-
metadata:
|
142
|
-
|
74
|
+
metadata:
|
75
|
+
changelog_uri: https://github.com/rubocop/rubocop-thread_safety/blob/master/CHANGELOG.md
|
76
|
+
source_code_uri: https://github.com/rubocop/rubocop-thread_safety
|
77
|
+
bug_tracker_uri: https://github.com/rubocop/rubocop-thread_safety/issues
|
78
|
+
rubygems_mfa_required: 'true'
|
79
|
+
default_lint_roller_plugin: RuboCop::ThreadSafety::Plugin
|
143
80
|
rdoc_options: []
|
144
81
|
require_paths:
|
145
82
|
- lib
|
@@ -147,15 +84,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
147
84
|
requirements:
|
148
85
|
- - ">="
|
149
86
|
- !ruby/object:Gem::Version
|
150
|
-
version: 2.
|
87
|
+
version: 2.7.0
|
151
88
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
152
89
|
requirements:
|
153
90
|
- - ">="
|
154
91
|
- !ruby/object:Gem::Version
|
155
92
|
version: '0'
|
156
93
|
requirements: []
|
157
|
-
rubygems_version: 3.3
|
158
|
-
signing_key:
|
94
|
+
rubygems_version: 3.6.3
|
159
95
|
specification_version: 4
|
160
96
|
summary: Thread-safety checks via static analysis
|
161
97
|
test_files: []
|
data/.github/dependabot.yml
DELETED
data/.github/workflows/ci.yml
DELETED
@@ -1,26 +0,0 @@
|
|
1
|
-
name: CI
|
2
|
-
|
3
|
-
on: [push, pull_request]
|
4
|
-
|
5
|
-
jobs:
|
6
|
-
test:
|
7
|
-
|
8
|
-
runs-on: ubuntu-latest
|
9
|
-
|
10
|
-
strategy:
|
11
|
-
fail-fast: false
|
12
|
-
matrix:
|
13
|
-
ruby: ["2.5", "2.6", "2.7", "3.0", "3.1", "3.2", ruby-head, jruby-9.2, jruby-9.3]
|
14
|
-
rubocop_version: ["0.90", "1.20"]
|
15
|
-
env:
|
16
|
-
BUNDLE_GEMFILE: "gemfiles/rubocop_${{ matrix.rubocop_version }}.gemfile"
|
17
|
-
steps:
|
18
|
-
- uses: actions/checkout@v3
|
19
|
-
- name: Set up Ruby
|
20
|
-
uses: ruby/setup-ruby@v1
|
21
|
-
with:
|
22
|
-
bundler-cache: true # 'bundle install' and cache gems
|
23
|
-
ruby-version: ${{ matrix.ruby }}
|
24
|
-
bundler: 2.3.26
|
25
|
-
- name: Run tests
|
26
|
-
run: bundle exec rspec
|
data/.github/workflows/lint.yml
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
name: Lint
|
2
|
-
|
3
|
-
on: [push, pull_request]
|
4
|
-
|
5
|
-
jobs:
|
6
|
-
lint:
|
7
|
-
|
8
|
-
runs-on: ubuntu-latest
|
9
|
-
name: Rubocop
|
10
|
-
|
11
|
-
steps:
|
12
|
-
- uses: actions/checkout@v3
|
13
|
-
- name: Set up Ruby
|
14
|
-
uses: ruby/setup-ruby@v1
|
15
|
-
with:
|
16
|
-
bundler-cache: true # 'bundle install' and cache gems
|
17
|
-
ruby-version: "2.7"
|
18
|
-
- name: Run Rubocop
|
19
|
-
run: bundle exec rubocop
|
data/.gitignore
DELETED
data/.rspec
DELETED
data/.rubocop.yml
DELETED
@@ -1,79 +0,0 @@
|
|
1
|
-
inherit_from: .rubocop_todo.yml
|
2
|
-
|
3
|
-
require:
|
4
|
-
- rubocop/cop/internal_affairs
|
5
|
-
|
6
|
-
AllCops:
|
7
|
-
DisplayCopNames: true
|
8
|
-
TargetRubyVersion: 2.5
|
9
|
-
|
10
|
-
Lint/RaiseException:
|
11
|
-
Enabled: true
|
12
|
-
|
13
|
-
Lint/StructNewOverride:
|
14
|
-
Enabled: true
|
15
|
-
|
16
|
-
Metrics/BlockLength:
|
17
|
-
Exclude:
|
18
|
-
- "spec/**/*"
|
19
|
-
|
20
|
-
Metrics/ClassLength:
|
21
|
-
Enabled: false
|
22
|
-
|
23
|
-
Metrics/MethodLength:
|
24
|
-
Max: 14
|
25
|
-
|
26
|
-
Naming/FileName:
|
27
|
-
Exclude:
|
28
|
-
- lib/rubocop-thread_safety.rb
|
29
|
-
- rubocop-thread_safety.gemspec
|
30
|
-
|
31
|
-
# Enable more cops that are disabled by default:
|
32
|
-
|
33
|
-
Style/AutoResourceCleanup:
|
34
|
-
Enabled: true
|
35
|
-
|
36
|
-
Style/CollectionMethods:
|
37
|
-
Enabled: true
|
38
|
-
|
39
|
-
Style/FormatStringToken:
|
40
|
-
Exclude:
|
41
|
-
- spec/**/*
|
42
|
-
|
43
|
-
Style/FrozenStringLiteralComment:
|
44
|
-
Exclude:
|
45
|
-
- "gemfiles/*.gemfile"
|
46
|
-
|
47
|
-
Style/HashEachMethods:
|
48
|
-
Enabled: true
|
49
|
-
|
50
|
-
Style/HashTransformKeys:
|
51
|
-
Enabled: false
|
52
|
-
|
53
|
-
Style/HashTransformValues:
|
54
|
-
Enabled: false
|
55
|
-
|
56
|
-
Style/MethodCalledOnDoEndBlock:
|
57
|
-
Enabled: true
|
58
|
-
Exclude:
|
59
|
-
- spec/**/*
|
60
|
-
|
61
|
-
Style/MissingElse:
|
62
|
-
Enabled: true
|
63
|
-
EnforcedStyle: case
|
64
|
-
|
65
|
-
Style/OptionHash:
|
66
|
-
Enabled: true
|
67
|
-
|
68
|
-
Style/Send:
|
69
|
-
Enabled: true
|
70
|
-
|
71
|
-
Style/StringLiterals:
|
72
|
-
Exclude:
|
73
|
-
- "gemfiles/*.gemfile"
|
74
|
-
|
75
|
-
Style/StringMethods:
|
76
|
-
Enabled: true
|
77
|
-
|
78
|
-
Style/SymbolArray:
|
79
|
-
Enabled: true
|
data/.rubocop_todo.yml
DELETED
@@ -1,12 +0,0 @@
|
|
1
|
-
# This configuration was generated by
|
2
|
-
# `rubocop --auto-gen-config --no-auto-gen-timestamp`
|
3
|
-
# using RuboCop version 1.12.1.
|
4
|
-
# The point is for the user to remove these configuration records
|
5
|
-
# one by one as the offenses are removed from the code base.
|
6
|
-
# Note that changes in the inspected code, or installation of new
|
7
|
-
# versions of RuboCop, may require this file to be generated again.
|
8
|
-
|
9
|
-
# Offense count: 3
|
10
|
-
InternalAffairs/NodeDestructuring:
|
11
|
-
Exclude:
|
12
|
-
- 'lib/rubocop/cop/thread_safety/mutable_class_instance_variable.rb'
|
data/Appraisals
DELETED
data/Gemfile
DELETED
data/Rakefile
DELETED
data/bin/console
DELETED
@@ -1,15 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
# frozen_string_literal: true
|
4
|
-
|
5
|
-
require 'bundler/setup'
|
6
|
-
require 'rubocop-thread_safety'
|
7
|
-
|
8
|
-
# You can add fixtures and/or initialization code here to make experimenting
|
9
|
-
# with your gem easier. You can also use a different console, if you like.
|
10
|
-
|
11
|
-
require 'pry'
|
12
|
-
Pry.start
|
13
|
-
|
14
|
-
# require "irb"
|
15
|
-
# IRB.start
|
data/bin/setup
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# The original code is from https://github.com/rubocop-hq/rubocop-rspec/blob/master/lib/rubocop/rspec/inject.rb
|
4
|
-
# See https://github.com/rubocop-hq/rubocop-rspec/blob/master/MIT_LICENSE.md
|
5
|
-
module RuboCop
|
6
|
-
module ThreadSafety
|
7
|
-
# Because RuboCop doesn't yet support plugins, we have to monkey patch in a
|
8
|
-
# bit of our configuration.
|
9
|
-
module Inject
|
10
|
-
def self.defaults!
|
11
|
-
path = CONFIG_DEFAULT.to_s
|
12
|
-
hash = ConfigLoader.__send__(:load_yaml_configuration, path)
|
13
|
-
config = Config.new(hash, path).tap(&:make_excludes_absolute)
|
14
|
-
puts "configuration from \#{path}" if ConfigLoader.debug?
|
15
|
-
config = ConfigLoader.merge_with_default(config, path)
|
16
|
-
ConfigLoader.instance_variable_set(:@default_configuration, config)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
@@ -1,38 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
lib = File.expand_path('lib', __dir__)
|
4
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
-
require 'rubocop/thread_safety/version'
|
6
|
-
|
7
|
-
Gem::Specification.new do |spec|
|
8
|
-
spec.name = 'rubocop-thread_safety'
|
9
|
-
spec.version = RuboCop::ThreadSafety::VERSION
|
10
|
-
spec.authors = ['Michael Gee']
|
11
|
-
spec.email = ['michaelpgee@gmail.com']
|
12
|
-
|
13
|
-
spec.summary = 'Thread-safety checks via static analysis'
|
14
|
-
spec.description = <<-DESCRIPTION
|
15
|
-
Thread-safety checks via static analysis.
|
16
|
-
A plugin for the RuboCop code style enforcing & linting tool.
|
17
|
-
DESCRIPTION
|
18
|
-
spec.homepage = 'https://github.com/rubocop/rubocop-thread_safety'
|
19
|
-
spec.licenses = ['MIT']
|
20
|
-
|
21
|
-
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
22
|
-
f.match(%r{^(test|spec|features)/})
|
23
|
-
end
|
24
|
-
|
25
|
-
spec.bindir = 'exe'
|
26
|
-
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
27
|
-
spec.require_paths = ['lib']
|
28
|
-
|
29
|
-
spec.required_ruby_version = '>= 2.5.0'
|
30
|
-
|
31
|
-
spec.add_runtime_dependency 'rubocop', '>= 0.90.0'
|
32
|
-
|
33
|
-
spec.add_development_dependency 'appraisal'
|
34
|
-
spec.add_development_dependency 'bundler', '>= 1.10', '< 3'
|
35
|
-
spec.add_development_dependency 'pry' unless ENV['CI']
|
36
|
-
spec.add_development_dependency 'rake', '>= 10.0'
|
37
|
-
spec.add_development_dependency 'rspec', '~> 3.0'
|
38
|
-
end
|