rubocop-thread_safety 0.4.3 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/dependabot.yml +6 -0
- data/.github/workflows/ci.yml +26 -0
- data/.github/workflows/lint.yml +19 -0
- data/.rubocop.yml +10 -1
- data/.rubocop_todo.yml +12 -0
- data/Appraisals +4 -18
- data/LICENSE.txt +2 -1
- data/README.md +7 -5
- data/config/default.yml +1 -0
- data/gemfiles/{rubocop_0.53.gemfile → rubocop_0.90.gemfile} +1 -1
- data/lib/rubocop/cop/thread_safety/class_and_module_attributes.rb +12 -2
- data/lib/rubocop/cop/thread_safety/instance_variable_in_class_method.rb +86 -12
- data/lib/rubocop/cop/thread_safety/mutable_class_instance_variable.rb +43 -18
- data/lib/rubocop/cop/thread_safety/new_thread.rb +4 -2
- data/lib/rubocop/thread_safety/version.rb +1 -1
- data/rubocop-thread_safety.gemspec +3 -3
- metadata +12 -11
- data/.travis.yml +0 -64
- data/gemfiles/rubocop_0.81.gemfile +0 -7
- data/gemfiles/rubocop_0.86.gemfile +0 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6186a05451917191f601a118b61cfd9e14fe59a4a187daa09af557450fee9935
|
4
|
+
data.tar.gz: 2a161a1fabc1fd5cdee9d13b3cd7729e497a4e8c2862427d002baaa1943b0bf0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: aaa04443787ec115fa6dc9e8aa3b069fe2bf7967e38c9eec9ea5c00c9c3d32208dc161a5cb082888279dd183f25acf935a3ccd3aac854e78b1b727ccb3c9121e
|
7
|
+
data.tar.gz: 7957032ddf36e744c0b1dd640bfcf1f3f9e1f899cb73bb1566c8e88ce858e008637a8e3502baaa266617100c89e62be8a852cea471a0c358de1e7c62b7fada86
|
@@ -0,0 +1,26 @@
|
|
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
|
@@ -0,0 +1,19 @@
|
|
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/.rubocop.yml
CHANGED
@@ -1,6 +1,11 @@
|
|
1
|
+
inherit_from: .rubocop_todo.yml
|
2
|
+
|
3
|
+
require:
|
4
|
+
- rubocop/cop/internal_affairs
|
5
|
+
|
1
6
|
AllCops:
|
2
7
|
DisplayCopNames: true
|
3
|
-
TargetRubyVersion: 2.
|
8
|
+
TargetRubyVersion: 2.5
|
4
9
|
|
5
10
|
Lint/RaiseException:
|
6
11
|
Enabled: true
|
@@ -31,6 +36,10 @@ Style/AutoResourceCleanup:
|
|
31
36
|
Style/CollectionMethods:
|
32
37
|
Enabled: true
|
33
38
|
|
39
|
+
Style/FormatStringToken:
|
40
|
+
Exclude:
|
41
|
+
- spec/**/*
|
42
|
+
|
34
43
|
Style/FrozenStringLiteralComment:
|
35
44
|
Exclude:
|
36
45
|
- "gemfiles/*.gemfile"
|
data/.rubocop_todo.yml
ADDED
@@ -0,0 +1,12 @@
|
|
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
CHANGED
@@ -1,23 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
appraise 'rubocop-0.
|
4
|
-
gem 'rubocop', '~> 0.
|
3
|
+
appraise 'rubocop-0.90' do
|
4
|
+
gem 'rubocop', '~> 0.90.0'
|
5
5
|
end
|
6
6
|
|
7
|
-
appraise 'rubocop-
|
8
|
-
gem 'rubocop', '~>
|
9
|
-
end
|
10
|
-
|
11
|
-
if Gem::Requirement.new('>= 2.4.0')
|
12
|
-
.satisfied_by?(Gem::Version.new(RUBY_VERSION))
|
13
|
-
appraise 'rubocop-0.86' do
|
14
|
-
gem 'rubocop', '~> 0.86.0'
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
if Gem::Requirement.new('>= 2.5.0')
|
19
|
-
.satisfied_by?(Gem::Version.new(RUBY_VERSION))
|
20
|
-
appraise 'rubocop-1.20' do
|
21
|
-
gem 'rubocop', '~> 1.20.0'
|
22
|
-
end
|
7
|
+
appraise 'rubocop-1.20' do
|
8
|
+
gem 'rubocop', '~> 1.20.0'
|
23
9
|
end
|
data/LICENSE.txt
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
Copyright 2016-
|
1
|
+
Portions Copyright 2016-2023 Michael Gee and contributors
|
2
|
+
Portions Copyright 2016-2023 CoverMyMeds
|
2
3
|
|
3
4
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
4
5
|
|
data/README.md
CHANGED
@@ -7,10 +7,10 @@ Thread-safety analysis for your projects, as an extension to
|
|
7
7
|
|
8
8
|
### Installation into an application
|
9
9
|
|
10
|
-
Add this line to your application's Gemfile:
|
10
|
+
Add this line to your application's Gemfile (using `require: false` as it's a standalone tool):
|
11
11
|
|
12
12
|
```ruby
|
13
|
-
gem 'rubocop-thread_safety'
|
13
|
+
gem 'rubocop-thread_safety', require: false
|
14
14
|
```
|
15
15
|
|
16
16
|
Install it with Bundler by invoking:
|
@@ -36,7 +36,7 @@ Scan the application for just thread-safety issues:
|
|
36
36
|
|
37
37
|
### Configuration
|
38
38
|
|
39
|
-
There are some added [configuration options](https://github.com/
|
39
|
+
There are some added [configuration options](https://github.com/rubocop/rubocop-thread_safety/blob/master/config/default.yml) that can be tweaked to modify the behaviour of these thread-safety cops.
|
40
40
|
|
41
41
|
### Correcting code for thread-safety
|
42
42
|
|
@@ -68,9 +68,11 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
68
68
|
|
69
69
|
## Contributing
|
70
70
|
|
71
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
71
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/rubocop/rubocop-thread_safety.
|
72
72
|
|
73
73
|
## Copyright
|
74
74
|
|
75
|
-
Copyright (c) 2016-
|
75
|
+
Portions Copyright (c) 2016-2023 Michael Gee and [contributors](https://github.com/rubocop/rubocop-thread_safety/graphs/contributors).
|
76
|
+
Portions Copyright (c) 2016-2023 CoverMyMeds.
|
77
|
+
|
76
78
|
See [LICENSE.txt](LICENSE.txt) for further details.
|
data/config/default.yml
CHANGED
@@ -14,6 +14,7 @@ ThreadSafety/MutableClassInstanceVariable:
|
|
14
14
|
Description: 'Do not assign mutable objects to class instance variables.'
|
15
15
|
Enabled: true
|
16
16
|
EnforcedStyle: literals
|
17
|
+
SafeAutoCorrect: false
|
17
18
|
SupportedStyles:
|
18
19
|
# literals: freeze literals assigned to constants
|
19
20
|
# strict: freeze all constants
|
@@ -12,27 +12,37 @@ module RuboCop
|
|
12
12
|
# class User
|
13
13
|
# cattr_accessor :current_user
|
14
14
|
# end
|
15
|
-
class ClassAndModuleAttributes <
|
15
|
+
class ClassAndModuleAttributes < Base
|
16
16
|
MSG = 'Avoid mutating class and module attributes.'
|
17
|
+
RESTRICT_ON_SEND = %i[
|
18
|
+
mattr_writer mattr_accessor cattr_writer cattr_accessor
|
19
|
+
class_attribute
|
20
|
+
attr attr_accessor attr_writer
|
21
|
+
attr_internal attr_internal_accessor attr_internal_writer
|
22
|
+
].freeze
|
17
23
|
|
24
|
+
# @!method mattr?(node)
|
18
25
|
def_node_matcher :mattr?, <<~MATCHER
|
19
26
|
(send nil?
|
20
27
|
{:mattr_writer :mattr_accessor :cattr_writer :cattr_accessor}
|
21
28
|
...)
|
22
29
|
MATCHER
|
23
30
|
|
31
|
+
# @!method attr?(node)
|
24
32
|
def_node_matcher :attr?, <<~MATCHER
|
25
33
|
(send nil?
|
26
34
|
{:attr :attr_accessor :attr_writer}
|
27
35
|
...)
|
28
36
|
MATCHER
|
29
37
|
|
38
|
+
# @!method attr_internal?(node)
|
30
39
|
def_node_matcher :attr_internal?, <<~MATCHER
|
31
40
|
(send nil?
|
32
41
|
{:attr_internal :attr_internal_accessor :attr_internal_writer}
|
33
42
|
...)
|
34
43
|
MATCHER
|
35
44
|
|
45
|
+
# @!method class_attr?(node)
|
36
46
|
def_node_matcher :class_attr?, <<~MATCHER
|
37
47
|
(send nil?
|
38
48
|
:class_attribute
|
@@ -43,7 +53,7 @@ module RuboCop
|
|
43
53
|
return unless mattr?(node) || class_attr?(node) ||
|
44
54
|
singleton_attr?(node)
|
45
55
|
|
46
|
-
add_offense(node
|
56
|
+
add_offense(node)
|
47
57
|
end
|
48
58
|
|
49
59
|
private
|
@@ -37,59 +37,101 @@ module RuboCop
|
|
37
37
|
# end
|
38
38
|
# end
|
39
39
|
# end
|
40
|
-
|
40
|
+
#
|
41
|
+
# module Example
|
42
|
+
# class_methods do
|
43
|
+
# def test(params)
|
44
|
+
# @params = params
|
45
|
+
# end
|
46
|
+
# end
|
47
|
+
# end
|
48
|
+
#
|
49
|
+
# module Example
|
50
|
+
# module_function
|
51
|
+
#
|
52
|
+
# def test(params)
|
53
|
+
# @params = params
|
54
|
+
# end
|
55
|
+
# end
|
56
|
+
#
|
57
|
+
# module Example
|
58
|
+
# def test(params)
|
59
|
+
# @params = params
|
60
|
+
# end
|
61
|
+
#
|
62
|
+
# module_function :test
|
63
|
+
# end
|
64
|
+
class InstanceVariableInClassMethod < Base
|
41
65
|
MSG = 'Avoid instance variables in class methods.'
|
66
|
+
RESTRICT_ON_SEND = %i[
|
67
|
+
instance_variable_set
|
68
|
+
instance_variable_get
|
69
|
+
].freeze
|
42
70
|
|
71
|
+
# @!method instance_variable_set_call?(node)
|
43
72
|
def_node_matcher :instance_variable_set_call?, <<~MATCHER
|
44
73
|
(send nil? :instance_variable_set (...) (...))
|
45
74
|
MATCHER
|
46
75
|
|
76
|
+
# @!method instance_variable_get_call?(node)
|
47
77
|
def_node_matcher :instance_variable_get_call?, <<~MATCHER
|
48
78
|
(send nil? :instance_variable_get (...))
|
49
79
|
MATCHER
|
50
80
|
|
51
81
|
def on_ivar(node)
|
52
82
|
return unless class_method_definition?(node)
|
83
|
+
return if method_definition?(node)
|
53
84
|
return if synchronized?(node)
|
54
85
|
|
55
|
-
add_offense(node
|
86
|
+
add_offense(node.loc.name)
|
56
87
|
end
|
57
88
|
alias on_ivasgn on_ivar
|
58
89
|
|
59
90
|
def on_send(node)
|
60
91
|
return unless instance_variable_call?(node)
|
61
92
|
return unless class_method_definition?(node)
|
93
|
+
return if method_definition?(node)
|
62
94
|
return if synchronized?(node)
|
63
95
|
|
64
|
-
add_offense(node
|
96
|
+
add_offense(node)
|
65
97
|
end
|
66
98
|
|
67
99
|
private
|
68
100
|
|
69
101
|
def class_method_definition?(node)
|
102
|
+
return false if method_definition?(node)
|
103
|
+
|
70
104
|
in_defs?(node) ||
|
71
105
|
in_def_sclass?(node) ||
|
72
106
|
in_def_class_methods?(node) ||
|
107
|
+
in_def_module_function?(node) ||
|
73
108
|
singleton_method_definition?(node)
|
74
109
|
end
|
75
110
|
|
76
111
|
def in_defs?(node)
|
77
|
-
node.ancestors.any?
|
78
|
-
ancestor.type == :defs
|
79
|
-
end
|
112
|
+
node.ancestors.any?(&:defs_type?)
|
80
113
|
end
|
81
114
|
|
82
115
|
def in_def_sclass?(node)
|
83
|
-
defn = node.ancestors.find
|
84
|
-
ancestor.type == :def
|
85
|
-
end
|
116
|
+
defn = node.ancestors.find(&:def_type?)
|
86
117
|
|
87
|
-
defn&.ancestors&.any?
|
88
|
-
ancestor.type == :sclass
|
89
|
-
end
|
118
|
+
defn&.ancestors&.any?(&:sclass_type?)
|
90
119
|
end
|
91
120
|
|
92
121
|
def in_def_class_methods?(node)
|
122
|
+
in_def_class_methods_dsl?(node) || in_def_class_methods_module?(node)
|
123
|
+
end
|
124
|
+
|
125
|
+
def in_def_class_methods_dsl?(node)
|
126
|
+
node.ancestors.any? do |ancestor|
|
127
|
+
next unless ancestor.block_type?
|
128
|
+
next unless ancestor.children.first.is_a? AST::SendNode
|
129
|
+
|
130
|
+
ancestor.children.first.command? :class_methods
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def in_def_class_methods_module?(node)
|
93
135
|
defn = node.ancestors.find(&:def_type?)
|
94
136
|
return unless defn
|
95
137
|
|
@@ -101,6 +143,14 @@ module RuboCop
|
|
101
143
|
class_methods_module?(mod)
|
102
144
|
end
|
103
145
|
|
146
|
+
def in_def_module_function?(node)
|
147
|
+
defn = node.ancestors.find(&:def_type?)
|
148
|
+
return unless defn
|
149
|
+
|
150
|
+
defn.left_siblings.any? { |sibling| module_function_bare_access_modifier?(sibling) } ||
|
151
|
+
defn.right_siblings.any? { |sibling| module_function_for?(sibling, defn.method_name) }
|
152
|
+
end
|
153
|
+
|
104
154
|
def singleton_method_definition?(node)
|
105
155
|
node.ancestors.any? do |ancestor|
|
106
156
|
next unless ancestor.children.first.is_a? AST::SendNode
|
@@ -109,6 +159,14 @@ module RuboCop
|
|
109
159
|
end
|
110
160
|
end
|
111
161
|
|
162
|
+
def method_definition?(node)
|
163
|
+
node.ancestors.any? do |ancestor|
|
164
|
+
next unless ancestor.children.first.is_a? AST::SendNode
|
165
|
+
|
166
|
+
ancestor.children.first.command? :define_method
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
112
170
|
def synchronized?(node)
|
113
171
|
node.ancestors.find do |ancestor|
|
114
172
|
next unless ancestor.block_type?
|
@@ -122,9 +180,25 @@ module RuboCop
|
|
122
180
|
instance_variable_set_call?(node) || instance_variable_get_call?(node)
|
123
181
|
end
|
124
182
|
|
183
|
+
def module_function_bare_access_modifier?(node)
|
184
|
+
return false unless node
|
185
|
+
|
186
|
+
node.send_type? && node.bare_access_modifier? && node.method?(:module_function)
|
187
|
+
end
|
188
|
+
|
189
|
+
def match_name?(arg_name, method_name)
|
190
|
+
arg_name.to_sym == method_name.to_sym
|
191
|
+
end
|
192
|
+
|
193
|
+
# @!method class_methods_module?(node)
|
125
194
|
def_node_matcher :class_methods_module?, <<~PATTERN
|
126
195
|
(module (const _ :ClassMethods) ...)
|
127
196
|
PATTERN
|
197
|
+
|
198
|
+
# @!method module_function_for?(node)
|
199
|
+
def_node_matcher :module_function_for?, <<~PATTERN
|
200
|
+
(send nil? {:module_function} ({sym str} #match_name?(%1)))
|
201
|
+
PATTERN
|
128
202
|
end
|
129
203
|
end
|
130
204
|
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module ThreadSafety
|
6
|
-
#
|
6
|
+
# Checks whether some class instance variable isn't a
|
7
7
|
# mutable literal (e.g. array or hash).
|
8
8
|
#
|
9
9
|
# It is based on Style/MutableConstant from RuboCop.
|
@@ -72,11 +72,14 @@ module RuboCop
|
|
72
72
|
# end
|
73
73
|
# end.freeze
|
74
74
|
# end
|
75
|
-
class MutableClassInstanceVariable <
|
75
|
+
class MutableClassInstanceVariable < Base
|
76
|
+
extend AutoCorrector
|
76
77
|
include FrozenStringLiteral
|
77
78
|
include ConfigurableEnforcedStyle
|
78
79
|
|
79
80
|
MSG = 'Freeze mutable objects assigned to class instance variables.'
|
81
|
+
FROZEN_STRING_LITERAL_TYPES_RUBY27 = %i[str dstr].freeze
|
82
|
+
FROZEN_STRING_LITERAL_TYPES_RUBY30 = %i[str].freeze
|
80
83
|
|
81
84
|
def on_ivasgn(node)
|
82
85
|
return unless in_class?(node)
|
@@ -106,27 +109,34 @@ module RuboCop
|
|
106
109
|
end
|
107
110
|
end
|
108
111
|
|
109
|
-
def autocorrect(node)
|
112
|
+
def autocorrect(corrector, node)
|
110
113
|
expr = node.source_range
|
111
114
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
corrector.insert_after(expr, ')')
|
122
|
-
end
|
123
|
-
|
124
|
-
corrector.insert_after(expr, '.freeze')
|
115
|
+
splat_value = splat_value(node)
|
116
|
+
if splat_value
|
117
|
+
correct_splat_expansion(corrector, expr, splat_value)
|
118
|
+
elsif node.array_type? && !node.bracketed?
|
119
|
+
corrector.insert_before(expr, '[')
|
120
|
+
corrector.insert_after(expr, ']')
|
121
|
+
elsif requires_parentheses?(node)
|
122
|
+
corrector.insert_before(expr, '(')
|
123
|
+
corrector.insert_after(expr, ')')
|
125
124
|
end
|
125
|
+
|
126
|
+
corrector.insert_after(expr, '.freeze')
|
126
127
|
end
|
127
128
|
|
128
129
|
private
|
129
130
|
|
131
|
+
def frozen_string_literal?(node)
|
132
|
+
literal_types = if target_ruby_version >= 3.0
|
133
|
+
FROZEN_STRING_LITERAL_TYPES_RUBY30
|
134
|
+
else
|
135
|
+
FROZEN_STRING_LITERAL_TYPES_RUBY27
|
136
|
+
end
|
137
|
+
literal_types.include?(node.type) && frozen_string_literals_enabled?
|
138
|
+
end
|
139
|
+
|
130
140
|
def on_assignment(value)
|
131
141
|
if style == :strict
|
132
142
|
strict_check(value)
|
@@ -141,7 +151,9 @@ module RuboCop
|
|
141
151
|
return if operation_produces_threadsafe_object?(value)
|
142
152
|
return if frozen_string_literal?(value)
|
143
153
|
|
144
|
-
add_offense(value)
|
154
|
+
add_offense(value) do |corrector|
|
155
|
+
autocorrect(corrector, value)
|
156
|
+
end
|
145
157
|
end
|
146
158
|
|
147
159
|
def check(value)
|
@@ -149,7 +161,9 @@ module RuboCop
|
|
149
161
|
range_enclosed_in_parentheses?(value)
|
150
162
|
return if frozen_string_literal?(value)
|
151
163
|
|
152
|
-
add_offense(value)
|
164
|
+
add_offense(value) do |corrector|
|
165
|
+
autocorrect(corrector, value)
|
166
|
+
end
|
153
167
|
end
|
154
168
|
|
155
169
|
def in_class?(node)
|
@@ -163,6 +177,7 @@ module RuboCop
|
|
163
177
|
|
164
178
|
def container?(node)
|
165
179
|
return true if define_singleton_method?(node)
|
180
|
+
return true if define_method?(node)
|
166
181
|
|
167
182
|
%i[def defs class module].include?(node.type)
|
168
183
|
end
|
@@ -194,16 +209,24 @@ module RuboCop
|
|
194
209
|
end
|
195
210
|
end
|
196
211
|
|
212
|
+
# @!method define_singleton_method?(node)
|
197
213
|
def_node_matcher :define_singleton_method?, <<~PATTERN
|
198
214
|
(block (send nil? :define_singleton_method ...) ...)
|
199
215
|
PATTERN
|
200
216
|
|
217
|
+
# @!method define_method?(node)
|
218
|
+
def_node_matcher :define_method?, <<~PATTERN
|
219
|
+
(block (send nil? :define_method ...) ...)
|
220
|
+
PATTERN
|
221
|
+
|
222
|
+
# @!method splat_value(node)
|
201
223
|
def_node_matcher :splat_value, <<~PATTERN
|
202
224
|
(array (splat $_))
|
203
225
|
PATTERN
|
204
226
|
|
205
227
|
# NOTE: Some of these patterns may not actually return an immutable
|
206
228
|
# object but we will consider them immutable for this cop.
|
229
|
+
# @!method operation_produces_immutable_object?(node)
|
207
230
|
def_node_matcher :operation_produces_immutable_object?, <<~PATTERN
|
208
231
|
{
|
209
232
|
(const _ _)
|
@@ -220,6 +243,7 @@ module RuboCop
|
|
220
243
|
}
|
221
244
|
PATTERN
|
222
245
|
|
246
|
+
# @!method operation_produces_threadsafe_object?(node)
|
223
247
|
def_node_matcher :operation_produces_threadsafe_object?, <<~PATTERN
|
224
248
|
{
|
225
249
|
(send (const {nil? cbase} :Queue) :new ...)
|
@@ -252,6 +276,7 @@ module RuboCop
|
|
252
276
|
}
|
253
277
|
PATTERN
|
254
278
|
|
279
|
+
# @!method range_enclosed_in_parentheses?(node)
|
255
280
|
def_node_matcher :range_enclosed_in_parentheses?, <<~PATTERN
|
256
281
|
(begin ({irange erange} _ _))
|
257
282
|
PATTERN
|
@@ -10,9 +10,11 @@ module RuboCop
|
|
10
10
|
# @example
|
11
11
|
# # bad
|
12
12
|
# Thread.new { do_work }
|
13
|
-
class NewThread <
|
13
|
+
class NewThread < Base
|
14
14
|
MSG = 'Avoid starting new threads.'
|
15
|
+
RESTRICT_ON_SEND = %i[new].freeze
|
15
16
|
|
17
|
+
# @!method new_thread?(node)
|
16
18
|
def_node_matcher :new_thread?, <<~MATCHER
|
17
19
|
(send (const {nil? cbase} :Thread) :new)
|
18
20
|
MATCHER
|
@@ -20,7 +22,7 @@ module RuboCop
|
|
20
22
|
def on_send(node)
|
21
23
|
return unless new_thread?(node)
|
22
24
|
|
23
|
-
add_offense(node
|
25
|
+
add_offense(node)
|
24
26
|
end
|
25
27
|
end
|
26
28
|
end
|
@@ -15,7 +15,7 @@ Gem::Specification.new do |spec|
|
|
15
15
|
Thread-safety checks via static analysis.
|
16
16
|
A plugin for the RuboCop code style enforcing & linting tool.
|
17
17
|
DESCRIPTION
|
18
|
-
spec.homepage = 'https://github.com/
|
18
|
+
spec.homepage = 'https://github.com/rubocop/rubocop-thread_safety'
|
19
19
|
spec.licenses = ['MIT']
|
20
20
|
|
21
21
|
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
@@ -26,9 +26,9 @@ Gem::Specification.new do |spec|
|
|
26
26
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
27
27
|
spec.require_paths = ['lib']
|
28
28
|
|
29
|
-
spec.required_ruby_version = '>= 2.
|
29
|
+
spec.required_ruby_version = '>= 2.5.0'
|
30
30
|
|
31
|
-
spec.add_runtime_dependency 'rubocop', '>= 0.
|
31
|
+
spec.add_runtime_dependency 'rubocop', '>= 0.90.0'
|
32
32
|
|
33
33
|
spec.add_development_dependency 'appraisal'
|
34
34
|
spec.add_development_dependency 'bundler', '>= 1.10', '< 3'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
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.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Gee
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-03-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rubocop
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0.
|
19
|
+
version: 0.90.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 0.
|
26
|
+
version: 0.90.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: appraisal
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -109,10 +109,13 @@ executables: []
|
|
109
109
|
extensions: []
|
110
110
|
extra_rdoc_files: []
|
111
111
|
files:
|
112
|
+
- ".github/dependabot.yml"
|
113
|
+
- ".github/workflows/ci.yml"
|
114
|
+
- ".github/workflows/lint.yml"
|
112
115
|
- ".gitignore"
|
113
116
|
- ".rspec"
|
114
117
|
- ".rubocop.yml"
|
115
|
-
- ".
|
118
|
+
- ".rubocop_todo.yml"
|
116
119
|
- Appraisals
|
117
120
|
- Gemfile
|
118
121
|
- LICENSE.txt
|
@@ -121,9 +124,7 @@ files:
|
|
121
124
|
- bin/console
|
122
125
|
- bin/setup
|
123
126
|
- config/default.yml
|
124
|
-
- gemfiles/rubocop_0.
|
125
|
-
- gemfiles/rubocop_0.81.gemfile
|
126
|
-
- gemfiles/rubocop_0.86.gemfile
|
127
|
+
- gemfiles/rubocop_0.90.gemfile
|
127
128
|
- gemfiles/rubocop_1.20.gemfile
|
128
129
|
- lib/rubocop-thread_safety.rb
|
129
130
|
- lib/rubocop/cop/thread_safety/class_and_module_attributes.rb
|
@@ -134,7 +135,7 @@ files:
|
|
134
135
|
- lib/rubocop/thread_safety/inject.rb
|
135
136
|
- lib/rubocop/thread_safety/version.rb
|
136
137
|
- rubocop-thread_safety.gemspec
|
137
|
-
homepage: https://github.com/
|
138
|
+
homepage: https://github.com/rubocop/rubocop-thread_safety
|
138
139
|
licenses:
|
139
140
|
- MIT
|
140
141
|
metadata: {}
|
@@ -146,14 +147,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
146
147
|
requirements:
|
147
148
|
- - ">="
|
148
149
|
- !ruby/object:Gem::Version
|
149
|
-
version: 2.
|
150
|
+
version: 2.5.0
|
150
151
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
151
152
|
requirements:
|
152
153
|
- - ">="
|
153
154
|
- !ruby/object:Gem::Version
|
154
155
|
version: '0'
|
155
156
|
requirements: []
|
156
|
-
rubygems_version: 3.
|
157
|
+
rubygems_version: 3.2.33
|
157
158
|
signing_key:
|
158
159
|
specification_version: 4
|
159
160
|
summary: Thread-safety checks via static analysis
|
data/.travis.yml
DELETED
@@ -1,64 +0,0 @@
|
|
1
|
-
cache: bundler
|
2
|
-
language: ruby
|
3
|
-
rvm:
|
4
|
-
- jruby-9.2.9.0
|
5
|
-
- 2.3.0
|
6
|
-
- 2.4
|
7
|
-
- 2.5
|
8
|
-
- 2.6
|
9
|
-
- 2.7
|
10
|
-
- 3.0
|
11
|
-
|
12
|
-
gemfile:
|
13
|
-
- gemfiles/rubocop_0.53.gemfile
|
14
|
-
- gemfiles/rubocop_0.81.gemfile
|
15
|
-
- gemfiles/rubocop_0.86.gemfile
|
16
|
-
- gemfiles/rubocop_1.20.gemfile
|
17
|
-
|
18
|
-
script_rubocop: &script_rubocop
|
19
|
-
- bundle exec rspec
|
20
|
-
- bundle exec rubocop
|
21
|
-
|
22
|
-
jobs:
|
23
|
-
fast_finish: true
|
24
|
-
exclude:
|
25
|
-
- rvm: 2.3.0
|
26
|
-
gemfile: gemfiles/rubocop_0.86.gemfile
|
27
|
-
- rvm: 2.3.0
|
28
|
-
gemfile: gemfiles/rubocop_1.20.gemfile
|
29
|
-
- rvm: 2.5
|
30
|
-
gemfile: gemfiles/rubocop_0.53.gemfile
|
31
|
-
- rvm: 2.6
|
32
|
-
gemfile: gemfiles/rubocop_0.53.gemfile
|
33
|
-
- rvm: 2.7
|
34
|
-
gemfile: gemfiles/rubocop_0.53.gemfile
|
35
|
-
- rvm: 3.0
|
36
|
-
gemfile: gemfiles/rubocop_0.53.gemfile
|
37
|
-
include:
|
38
|
-
- rvm: jruby-9.2.9.0
|
39
|
-
gemfile: gemfiles/rubocop_0.81.gemfile
|
40
|
-
script: *script_rubocop
|
41
|
-
- rvm: 2.3.0
|
42
|
-
gemfile: gemfiles/rubocop_0.81.gemfile
|
43
|
-
script: *script_rubocop
|
44
|
-
- rvm: 2.4
|
45
|
-
gemfile: gemfiles/rubocop_0.81.gemfile
|
46
|
-
script: *script_rubocop
|
47
|
-
- rvm: 2.5
|
48
|
-
gemfile: gemfiles/rubocop_0.81.gemfile
|
49
|
-
script: *script_rubocop
|
50
|
-
- rvm: 2.6
|
51
|
-
gemfile: gemfiles/rubocop_0.81.gemfile
|
52
|
-
script: *script_rubocop
|
53
|
-
- rvm: 2.7
|
54
|
-
gemfile: gemfiles/rubocop_0.81.gemfile
|
55
|
-
script: *script_rubocop
|
56
|
-
- rvm: 3.0
|
57
|
-
gemfile: gemfiles/rubocop_0.81.gemfile
|
58
|
-
script: *script_rubocop
|
59
|
-
|
60
|
-
before_install: gem install --remote bundler
|
61
|
-
install:
|
62
|
-
- bundle install --retry=3
|
63
|
-
script:
|
64
|
-
- bundle exec rspec
|