rubocop-thread_safety 0.6.0 → 0.7.0

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: fa732d8bbefbcfc25dfd2b8adbfcc84788d77d8a21992b152975d9ee92e2f105
4
- data.tar.gz: f2d29b69ad8e94c9c4b5de205b7301d45ffb3fb3a1c69429af27a5000e7350c6
3
+ metadata.gz: 440e483486f3d67ad21fa72aa04eb47535020324672c3e08646894cec3517450
4
+ data.tar.gz: 50fe186da5956ee780dd5c2839d7792fc57f10ed4860d0781d2f0d68180d635d
5
5
  SHA512:
6
- metadata.gz: ec8b96d08f872297a64bbf558f93a27e09bbe61d2a06b7248fdb2a3896a7367c520eceb0ca7f1e88fdb26fc8a03e2eab1772794353b5e12a3724a3371360d689
7
- data.tar.gz: 7479c57f327e71e7fbc7b263ea13af5e4e908d7a29849946329bae072ba454636dca72465b1c7dc64d9150064d5e61ca68a81eb45a4ea247a5af69027afd6fb7
6
+ metadata.gz: bb89cc31949d9803344f78556dc6f3351369bd2936ee8b26f93e6caa477b3bcfa6b7db9e8d78dade90eca9003455af4411c3ef064c9399f63455d6c7cd45c36c
7
+ data.tar.gz: b03de48386d763afe6d7a66aa2f604bcfc1fda637dfd864c2f09b52122fb8637c26f9a675299fe85fae464804e4e8cc2b569ba9fa8104cca2a592ef6fcb6c8c1
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Change log
2
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
+
3
11
  ## 0.6.0
4
12
 
5
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))
data/README.md CHANGED
@@ -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
- require: rubocop-thread_safety
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 -r rubocop-thread_safety --only ThreadSafety,Style/GlobalVars,Style/ClassVars,Style/MutableConstant
38
+ $ rubocop --plugin rubocop-thread_safety --only ThreadSafety,Style/GlobalVars,Style/ClassVars,Style/MutableConstant
36
39
 
37
40
  ### Configuration
38
41
 
data/config/default.yml CHANGED
@@ -35,6 +35,7 @@ ThreadSafety/NewThread:
35
35
  ThreadSafety/DirChdir:
36
36
  Description: Avoid using `Dir.chdir` due to its process-wide effect.
37
37
  Enabled: true
38
+ AllowCallWithBlock: false
38
39
 
39
40
  ThreadSafety/RackMiddlewareInstanceVariable:
40
41
  Description: Avoid instance variables in Rack middleware.
@@ -49,7 +49,7 @@ module RuboCop
49
49
  ...)
50
50
  MATCHER
51
51
 
52
- def on_send(node)
52
+ def on_send(node) # rubocop:disable InternalAffairs/OnSendWithoutOnCSend
53
53
  return unless mattr?(node) || (!class_attribute_allowed? && class_attr?(node)) || singleton_attr?(node)
54
54
 
55
55
  add_offense(node)
@@ -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)
@@ -4,6 +4,8 @@ module RuboCop
4
4
  module Cop
5
5
  module ThreadSafety
6
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.
7
9
  #
8
10
  # @example
9
11
  # # bad
@@ -11,25 +13,51 @@ module RuboCop
11
13
  #
12
14
  # # bad
13
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
+ #
14
29
  class DirChdir < Base
15
- MESSAGE = 'Avoid using `%<module>s.%<method>s` due to its process-wide effect.'
30
+ MESSAGE = 'Avoid using `%<module>s%<dot>s%<method>s` due to its process-wide effect.'
16
31
  RESTRICT_ON_SEND = %i[chdir cd].freeze
17
32
 
18
33
  # @!method chdir?(node)
19
34
  def_node_matcher :chdir?, <<~MATCHER
20
35
  {
21
- (send (const {nil? cbase} {:Dir :FileUtils}) :chdir ...)
22
- (send (const {nil? cbase} :FileUtils) :cd ...)
36
+ (call (const {nil? cbase} {:Dir :FileUtils}) :chdir ...)
37
+ (call (const {nil? cbase} :FileUtils) :cd ...)
23
38
  }
24
39
  MATCHER
25
40
 
26
41
  def on_send(node)
27
- chdir?(node) do
28
- add_offense(
29
- node,
30
- message: format(MESSAGE, module: node.receiver.short_name, method: node.method_name)
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
31
52
  )
32
- end
53
+ )
54
+ end
55
+ alias on_csend on_send
56
+
57
+ private
58
+
59
+ def allow_call_with_block?
60
+ !!cop_config['AllowCallWithBlock']
33
61
  end
34
62
  end
35
63
  end
@@ -198,7 +198,7 @@ module RuboCop
198
198
  end
199
199
 
200
200
  def range_type?(node)
201
- node.erange_type? || node.irange_type?
201
+ node.type?(:erange, :irange)
202
202
  end
203
203
 
204
204
  def correct_splat_expansion(corrector, expr, splat_value)
@@ -245,7 +245,7 @@ module RuboCop
245
245
 
246
246
  # @!method range_enclosed_in_parentheses?(node)
247
247
  def_node_matcher :range_enclosed_in_parentheses?, <<~PATTERN
248
- (begin ({irange erange} _ _))
248
+ (begin (range _ _))
249
249
  PATTERN
250
250
  end
251
251
  end
@@ -16,12 +16,13 @@ module RuboCop
16
16
 
17
17
  # @!method new_thread?(node)
18
18
  def_node_matcher :new_thread?, <<~MATCHER
19
- (send (const {nil? cbase} :Thread) {:new :fork :start} ...)
19
+ (call (const {nil? cbase} :Thread) {:new :fork :start} ...)
20
20
  MATCHER
21
21
 
22
22
  def on_send(node)
23
23
  new_thread?(node) { add_offense(node) }
24
24
  end
25
+ alias on_csend on_send
25
26
  end
26
27
  end
27
28
  end
@@ -88,11 +88,12 @@ module RuboCop
88
88
  def on_send(node)
89
89
  argument = node.first_argument
90
90
 
91
- return unless argument&.sym_type? || argument&.str_type?
91
+ return unless argument&.type?(:sym, :str)
92
92
  return if allowed_identifier?(argument.value)
93
93
 
94
94
  add_offense node
95
95
  end
96
+ alias on_csend on_send
96
97
 
97
98
  private
98
99
 
@@ -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
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RuboCop
4
4
  module ThreadSafety
5
- VERSION = '0.6.0'
5
+ VERSION = '0.7.0'
6
6
  end
7
7
  end
@@ -3,12 +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
-
12
- ::RuboCop::ConfigObsoletion.files << PROJECT_ROOT.join('config', 'obsoletion.yml')
13
6
  end
14
7
  end
@@ -4,9 +4,7 @@ require 'rubocop'
4
4
 
5
5
  require 'rubocop/thread_safety'
6
6
  require 'rubocop/thread_safety/version'
7
- require 'rubocop/thread_safety/inject'
8
-
9
- RuboCop::ThreadSafety::Inject.defaults!
7
+ require 'rubocop/thread_safety/plugin'
10
8
 
11
9
  require 'rubocop/cop/mixin/operation_with_threadsafe_result'
12
10
 
metadata CHANGED
@@ -1,29 +1,48 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop-thread_safety
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.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: 2024-11-11 00:00:00.000000000 Z
10
+ date: 2025-02-22 00:00:00.000000000 Z
12
11
  dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: lint_roller
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '1.1'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '1.1'
13
26
  - !ruby/object:Gem::Dependency
14
27
  name: rubocop
15
28
  requirement: !ruby/object:Gem::Requirement
16
29
  requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '1.72'
17
33
  - - ">="
18
34
  - !ruby/object:Gem::Version
19
- version: 1.48.1
35
+ version: 1.72.1
20
36
  type: :runtime
21
37
  prerelease: false
22
38
  version_requirements: !ruby/object:Gem::Requirement
23
39
  requirements:
40
+ - - "~>"
41
+ - !ruby/object:Gem::Version
42
+ version: '1.72'
24
43
  - - ">="
25
44
  - !ruby/object:Gem::Version
26
- version: 1.48.1
45
+ version: 1.72.1
27
46
  description: |2
28
47
  Thread-safety checks via static analysis.
29
48
  A plugin for the RuboCop code style enforcing & linting tool.
@@ -33,26 +52,11 @@ executables: []
33
52
  extensions: []
34
53
  extra_rdoc_files: []
35
54
  files:
36
- - ".github/dependabot.yml"
37
- - ".github/workflows/ci.yml"
38
- - ".github/workflows/lint.yml"
39
- - ".gitignore"
40
- - ".rspec"
41
- - ".rubocop.yml"
42
- - Appraisals
43
55
  - CHANGELOG.md
44
- - Gemfile
45
56
  - LICENSE.txt
46
57
  - README.md
47
- - Rakefile
48
- - bin/console
49
- - bin/setup
50
58
  - config/default.yml
51
59
  - config/obsoletion.yml
52
- - docs/modules/ROOT/pages/cops.adoc
53
- - docs/modules/ROOT/pages/cops_threadsafety.adoc
54
- - gemfiles/rubocop_1.48.gemfile
55
- - gemfiles/rubocop_1.66.gemfile
56
60
  - lib/rubocop-thread_safety.rb
57
61
  - lib/rubocop/cop/mixin/operation_with_threadsafe_result.rb
58
62
  - lib/rubocop/cop/thread_safety/class_and_module_attributes.rb
@@ -62,10 +66,8 @@ files:
62
66
  - lib/rubocop/cop/thread_safety/new_thread.rb
63
67
  - lib/rubocop/cop/thread_safety/rack_middleware_instance_variable.rb
64
68
  - lib/rubocop/thread_safety.rb
65
- - lib/rubocop/thread_safety/inject.rb
69
+ - lib/rubocop/thread_safety/plugin.rb
66
70
  - lib/rubocop/thread_safety/version.rb
67
- - rubocop-thread_safety.gemspec
68
- - tasks/cops_documentation.rake
69
71
  homepage: https://github.com/rubocop/rubocop-thread_safety
70
72
  licenses:
71
73
  - MIT
@@ -74,7 +76,7 @@ metadata:
74
76
  source_code_uri: https://github.com/rubocop/rubocop-thread_safety
75
77
  bug_tracker_uri: https://github.com/rubocop/rubocop-thread_safety/issues
76
78
  rubygems_mfa_required: 'true'
77
- post_install_message:
79
+ default_lint_roller_plugin: RuboCop::ThreadSafety::Plugin
78
80
  rdoc_options: []
79
81
  require_paths:
80
82
  - lib
@@ -89,8 +91,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
89
91
  - !ruby/object:Gem::Version
90
92
  version: '0'
91
93
  requirements: []
92
- rubygems_version: 3.5.11
93
- signing_key:
94
+ rubygems_version: 3.6.3
94
95
  specification_version: 4
95
96
  summary: Thread-safety checks via static analysis
96
97
  test_files: []
@@ -1,6 +0,0 @@
1
- version: 2
2
- updates:
3
- - package-ecosystem: 'github-actions'
4
- directory: '/'
5
- schedule:
6
- interval: 'weekly'
@@ -1,55 +0,0 @@
1
- name: CI
2
-
3
- on:
4
- push:
5
- branches:
6
- - master
7
- pull_request:
8
-
9
- jobs:
10
- confirm_documentation:
11
- runs-on: ubuntu-latest
12
- name: Confirm documentation
13
- steps:
14
- - uses: actions/checkout@v4
15
- - uses: ruby/setup-ruby@v1
16
- with:
17
- ruby-version: "3.2"
18
- bundler-cache: true
19
- - run: bundle exec rake documentation_syntax_check confirm_documentation
20
-
21
- test:
22
- runs-on: ubuntu-latest
23
- strategy:
24
- fail-fast: false
25
- matrix:
26
- ruby: ["2.7", "3.0", "3.1", "3.2", "3.3", ruby-head, jruby-9.4]
27
- rubocop_version: ["1.48", "1.66"]
28
- env:
29
- BUNDLE_GEMFILE: "gemfiles/rubocop_${{ matrix.rubocop_version }}.gemfile"
30
- steps:
31
- - uses: actions/checkout@v4
32
- - name: Set up Ruby
33
- uses: ruby/setup-ruby@v1
34
- with:
35
- bundler-cache: true # 'bundle install' and cache gems
36
- ruby-version: ${{ matrix.ruby }}
37
- bundler: 2.3.26
38
- - name: Run tests
39
- run: bundle exec rspec
40
-
41
- test-prism:
42
- runs-on: ubuntu-latest
43
- env:
44
- BUNDLE_GEMFILE: "gemfiles/rubocop_1.66.gemfile"
45
- PARSER_ENGINE: parser_prism
46
- steps:
47
- - uses: actions/checkout@v4
48
- - name: Set up Ruby
49
- uses: ruby/setup-ruby@v1
50
- with:
51
- bundler-cache: true # 'bundle install' and cache gems
52
- ruby-version: 3.3
53
- bundler: 2.3.26
54
- - name: Run tests
55
- run: bundle exec rspec
@@ -1,23 +0,0 @@
1
- name: Lint
2
-
3
- on:
4
- push:
5
- branches:
6
- - master
7
- pull_request:
8
-
9
- jobs:
10
- lint:
11
-
12
- runs-on: ubuntu-latest
13
- name: Rubocop
14
-
15
- steps:
16
- - uses: actions/checkout@v4
17
- - name: Set up Ruby
18
- uses: ruby/setup-ruby@v1
19
- with:
20
- bundler-cache: true # 'bundle install' and cache gems
21
- ruby-version: "2.7"
22
- - name: Run Rubocop
23
- run: bundle exec rubocop
data/.gitignore DELETED
@@ -1,10 +0,0 @@
1
- /.bundle/
2
- /.yardoc
3
- /Gemfile.lock
4
- /gemfiles/*.lock
5
- /_yardoc/
6
- /coverage/
7
- /doc/
8
- /pkg/
9
- /spec/reports/
10
- /tmp/
data/.rspec DELETED
@@ -1,3 +0,0 @@
1
- --color
2
- --require spec_helper
3
- --warnings
data/.rubocop.yml DELETED
@@ -1,81 +0,0 @@
1
- require:
2
- - rubocop/cop/internal_affairs
3
- - rubocop-rake
4
- - rubocop-rspec
5
-
6
- AllCops:
7
- DisplayCopNames: true
8
- TargetRubyVersion: 2.7
9
- NewCops: enable
10
-
11
- Lint/RaiseException:
12
- Enabled: true
13
-
14
- Lint/StructNewOverride:
15
- Enabled: true
16
-
17
- Metrics/BlockLength:
18
- Exclude:
19
- - "spec/**/*"
20
-
21
- Metrics/ClassLength:
22
- Enabled: false
23
-
24
- Metrics/MethodLength:
25
- Max: 14
26
-
27
- Naming/FileName:
28
- Exclude:
29
- - lib/rubocop-thread_safety.rb
30
- - rubocop-thread_safety.gemspec
31
-
32
- # Enable more cops that are disabled by default:
33
-
34
- Style/AutoResourceCleanup:
35
- Enabled: true
36
-
37
- Style/CollectionMethods:
38
- Enabled: true
39
-
40
- Style/FormatStringToken:
41
- Exclude:
42
- - spec/**/*
43
-
44
- Style/HashEachMethods:
45
- Enabled: true
46
-
47
- Style/HashTransformKeys:
48
- Enabled: false
49
-
50
- Style/HashTransformValues:
51
- Enabled: false
52
-
53
- Style/MethodCalledOnDoEndBlock:
54
- Enabled: true
55
- Exclude:
56
- - spec/**/*
57
-
58
- Style/MissingElse:
59
- Enabled: true
60
- EnforcedStyle: case
61
-
62
- Style/OptionHash:
63
- Enabled: true
64
-
65
- Style/Send:
66
- Enabled: true
67
-
68
- Style/StringMethods:
69
- Enabled: true
70
-
71
- Style/SymbolArray:
72
- Enabled: true
73
-
74
- RSpec/ExampleLength:
75
- Max: 11
76
- Exclude:
77
- - spec/rubocop/cop/thread_safety/rack_middleware_instance_variable_spec.rb
78
-
79
- RSpec/ContextWording:
80
- Exclude:
81
- - spec/shared_contexts.rb
data/Appraisals DELETED
@@ -1,21 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- customize_gemfiles do
4
- {
5
- single_quotes: true,
6
- heading: <<~HEADING.strip
7
- frozen_string_literal: true
8
-
9
- This file was generated by Appraisal
10
- HEADING
11
- }
12
- end
13
-
14
- appraise 'rubocop-1.48' do
15
- gem 'base64', '~> 0.1.1'
16
- gem 'rubocop', '~> 1.48.0'
17
- end
18
-
19
- appraise 'rubocop-1.66' do
20
- gem 'rubocop', '~> 1.66.0'
21
- end
data/Gemfile DELETED
@@ -1,17 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- source 'https://rubygems.org'
4
-
5
- gemspec
6
-
7
- gem 'appraisal'
8
- gem 'bundler', '>= 1.10', '< 3'
9
- gem 'prism', '~> 1.2.0'
10
- gem 'pry' unless ENV['CI']
11
- gem 'rake', '>= 10.0'
12
- gem 'rspec', '~> 3.0'
13
- gem 'rubocop', github: 'rubocop/rubocop'
14
- gem 'rubocop-rake', '~> 0.6.0'
15
- gem 'rubocop-rspec'
16
- gem 'simplecov'
17
- gem 'yard'
data/Rakefile DELETED
@@ -1,22 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'bundler/gem_tasks'
4
- require 'open3'
5
- require 'rspec/core/rake_task'
6
-
7
- Dir['tasks/**/*.rake'].each { |t| load t }
8
-
9
- RSpec::Core::RakeTask.new(:spec)
10
-
11
- desc 'Confirm documentation is up to date'
12
- task confirm_documentation: :generate_cops_documentation do
13
- _, _, _, process =
14
- Open3.popen3('git diff --exit-code docs/')
15
-
16
- unless process.value.success?
17
- abort 'Please run `rake generate_cops_documentation` ' \
18
- 'and add docs/ to the commit.'
19
- end
20
- end
21
-
22
- task default: :spec
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,6 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
- IFS=$'\n\t'
4
- set -vx
5
-
6
- bundle install
@@ -1,8 +0,0 @@
1
- === Department xref:cops_threadsafety.adoc[ThreadSafety]
2
-
3
- * xref:cops_threadsafety.adoc#threadsafetyclassandmoduleattributes[ThreadSafety/ClassAndModuleAttributes]
4
- * xref:cops_threadsafety.adoc#threadsafetyclassinstancevariable[ThreadSafety/ClassInstanceVariable]
5
- * xref:cops_threadsafety.adoc#threadsafetymutableclassinstancevariable[ThreadSafety/MutableClassInstanceVariable]
6
- * xref:cops_threadsafety.adoc#threadsafetynewthread[ThreadSafety/NewThread]
7
- * xref:cops_threadsafety.adoc#threadsafetydirchdir[ThreadSafety/DirChdir]
8
- * xref:cops_threadsafety.adoc#threadsafetyrackmiddlewareinstancevariable[ThreadSafety/RackMiddlewareInstanceVariable]
@@ -1,361 +0,0 @@
1
- ////
2
- Do NOT edit this file by hand directly, as it is automatically generated.
3
-
4
- Please make any necessary changes to the cop documentation within the source files themselves.
5
- ////
6
-
7
- = ThreadSafety
8
-
9
- [#threadsafetyclassandmoduleattributes]
10
- == ThreadSafety/ClassAndModuleAttributes
11
-
12
- |===
13
- | Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed
14
-
15
- | Enabled
16
- | Yes
17
- | No
18
- | -
19
- | -
20
- |===
21
-
22
- Avoid mutating class and module attributes.
23
-
24
- They are implemented by class variables, which are not thread-safe.
25
-
26
- [#examples-threadsafetyclassandmoduleattributes]
27
- === Examples
28
-
29
- [source,ruby]
30
- ----
31
- # bad
32
- class User
33
- cattr_accessor :current_user
34
- end
35
- ----
36
-
37
- [#configurable-attributes-threadsafetyclassandmoduleattributes]
38
- === Configurable attributes
39
-
40
- |===
41
- | Name | Default value | Configurable values
42
-
43
- | ActiveSupportClassAttributeAllowed
44
- | `false`
45
- | Boolean
46
- |===
47
-
48
- [#threadsafetyclassinstancevariable]
49
- == ThreadSafety/ClassInstanceVariable
50
-
51
- |===
52
- | Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed
53
-
54
- | Enabled
55
- | Yes
56
- | No
57
- | -
58
- | -
59
- |===
60
-
61
- Avoid class instance variables.
62
-
63
- [#examples-threadsafetyclassinstancevariable]
64
- === Examples
65
-
66
- [source,ruby]
67
- ----
68
- # bad
69
- class User
70
- def self.notify(info)
71
- @info = validate(info)
72
- Notifier.new(@info).deliver
73
- end
74
- end
75
-
76
- class Model
77
- class << self
78
- def table_name(name)
79
- @table_name = name
80
- end
81
- end
82
- end
83
-
84
- class Host
85
- %i[uri port].each do |key|
86
- define_singleton_method("#{key}=") do |value|
87
- instance_variable_set("@#{key}", value)
88
- end
89
- end
90
- end
91
-
92
- module Example
93
- module ClassMethods
94
- def test(params)
95
- @params = params
96
- end
97
- end
98
- end
99
-
100
- module Example
101
- class_methods do
102
- def test(params)
103
- @params = params
104
- end
105
- end
106
- end
107
-
108
- module Example
109
- module_function
110
-
111
- def test(params)
112
- @params = params
113
- end
114
- end
115
-
116
- module Example
117
- def test(params)
118
- @params = params
119
- end
120
-
121
- module_function :test
122
- end
123
- ----
124
-
125
- [#threadsafetydirchdir]
126
- == ThreadSafety/DirChdir
127
-
128
- |===
129
- | Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed
130
-
131
- | Enabled
132
- | Yes
133
- | No
134
- | -
135
- | -
136
- |===
137
-
138
- Avoid using `Dir.chdir` due to its process-wide effect.
139
-
140
- [#examples-threadsafetydirchdir]
141
- === Examples
142
-
143
- [source,ruby]
144
- ----
145
- # bad
146
- Dir.chdir("/var/run")
147
-
148
- # bad
149
- FileUtils.chdir("/var/run")
150
- ----
151
-
152
- [#threadsafetymutableclassinstancevariable]
153
- == ThreadSafety/MutableClassInstanceVariable
154
-
155
- |===
156
- | Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed
157
-
158
- | Enabled
159
- | Yes
160
- | Always (Unsafe)
161
- | -
162
- | -
163
- |===
164
-
165
- Checks whether some class instance variable isn't a
166
- mutable literal (e.g. array or hash).
167
-
168
- It is based on Style/MutableConstant from RuboCop.
169
- See https://github.com/rubocop/rubocop/blob/master/lib/rubocop/cop/style/mutable_constant.rb
170
-
171
- Class instance variables are a risk to threaded code as they are shared
172
- between threads. A mutable object such as an array or hash may be
173
- updated via an attr_reader so would not be detected by the
174
- ThreadSafety/ClassAndModuleAttributes cop.
175
-
176
- Strict mode can be used to freeze all class instance variables, rather
177
- than just literals.
178
- Strict mode is considered an experimental feature. It has not been
179
- updated with an exhaustive list of all methods that will produce frozen
180
- objects so there is a decent chance of getting some false positives.
181
- Luckily, there is no harm in freezing an already frozen object.
182
-
183
- [#examples-threadsafetymutableclassinstancevariable]
184
- === Examples
185
-
186
- [#enforcedstyle_-literals-_default_-threadsafetymutableclassinstancevariable]
187
- ==== EnforcedStyle: literals (default)
188
-
189
- [source,ruby]
190
- ----
191
- # bad
192
- class Model
193
- @list = [1, 2, 3]
194
- end
195
-
196
- # good
197
- class Model
198
- @list = [1, 2, 3].freeze
199
- end
200
-
201
- # good
202
- class Model
203
- @var = <<~TESTING.freeze
204
- This is a heredoc
205
- TESTING
206
- end
207
-
208
- # good
209
- class Model
210
- @var = Something.new
211
- end
212
- ----
213
-
214
- [#enforcedstyle_-strict-threadsafetymutableclassinstancevariable]
215
- ==== EnforcedStyle: strict
216
-
217
- [source,ruby]
218
- ----
219
- # bad
220
- class Model
221
- @var = Something.new
222
- end
223
-
224
- # bad
225
- class Model
226
- @var = Struct.new do
227
- def foo
228
- puts 1
229
- end
230
- end
231
- end
232
-
233
- # good
234
- class Model
235
- @var = Something.new.freeze
236
- end
237
-
238
- # good
239
- class Model
240
- @var = Struct.new do
241
- def foo
242
- puts 1
243
- end
244
- end.freeze
245
- end
246
- ----
247
-
248
- [#configurable-attributes-threadsafetymutableclassinstancevariable]
249
- === Configurable attributes
250
-
251
- |===
252
- | Name | Default value | Configurable values
253
-
254
- | EnforcedStyle
255
- | `literals`
256
- | `literals`, `strict`
257
- |===
258
-
259
- [#threadsafetynewthread]
260
- == ThreadSafety/NewThread
261
-
262
- |===
263
- | Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed
264
-
265
- | Enabled
266
- | Yes
267
- | No
268
- | -
269
- | -
270
- |===
271
-
272
- Avoid starting new threads.
273
-
274
- Let a framework like Sidekiq handle the threads.
275
-
276
- [#examples-threadsafetynewthread]
277
- === Examples
278
-
279
- [source,ruby]
280
- ----
281
- # bad
282
- Thread.new { do_work }
283
- ----
284
-
285
- [#threadsafetyrackmiddlewareinstancevariable]
286
- == ThreadSafety/RackMiddlewareInstanceVariable
287
-
288
- |===
289
- | Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed
290
-
291
- | Enabled
292
- | Yes
293
- | No
294
- | -
295
- | -
296
- |===
297
-
298
- Avoid instance variables in rack middleware.
299
-
300
- Middlewares are initialized once, meaning any instance variables are shared between executor threads.
301
- To avoid potential race conditions, it's recommended to design middlewares to be stateless
302
- or to implement proper synchronization mechanisms.
303
-
304
- [#examples-threadsafetyrackmiddlewareinstancevariable]
305
- === Examples
306
-
307
- [source,ruby]
308
- ----
309
- # bad
310
- class CounterMiddleware
311
- def initialize(app)
312
- @app = app
313
- @counter = 0
314
- end
315
-
316
- def call(env)
317
- app.call(env)
318
- ensure
319
- @counter += 1
320
- end
321
- end
322
-
323
- # good
324
- class CounterMiddleware
325
- def initialize(app)
326
- @app = app
327
- @counter = Concurrent::AtomicReference.new(0)
328
- end
329
-
330
- def call(env)
331
- app.call(env)
332
- ensure
333
- @counter.update { |ref| ref + 1 }
334
- end
335
- end
336
-
337
- class IdentityMiddleware
338
- def initialize(app)
339
- @app = app
340
- end
341
-
342
- def call(env)
343
- app.call(env)
344
- end
345
- end
346
- ----
347
-
348
- [#configurable-attributes-threadsafetyrackmiddlewareinstancevariable]
349
- === Configurable attributes
350
-
351
- |===
352
- | Name | Default value | Configurable values
353
-
354
- | Include
355
- | `+app/middleware/**/*.rb+`, `+lib/middleware/**/*.rb+`, `+app/middlewares/**/*.rb+`, `+lib/middlewares/**/*.rb+`
356
- | Array
357
-
358
- | AllowedIdentifiers
359
- | `[]`
360
- | Array
361
- |===
@@ -1,20 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # This file was generated by Appraisal
4
-
5
- source 'https://rubygems.org'
6
-
7
- gem 'appraisal'
8
- gem 'base64', '~> 0.1.1'
9
- gem 'bundler', '>= 1.10', '< 3'
10
- gem 'prism', '~> 1.2.0'
11
- gem 'pry'
12
- gem 'rake', '>= 10.0'
13
- gem 'rspec', '~> 3.0'
14
- gem 'rubocop', '~> 1.48.0'
15
- gem 'rubocop-rake', '~> 0.6.0'
16
- gem 'rubocop-rspec'
17
- gem 'simplecov'
18
- gem 'yard'
19
-
20
- gemspec path: '../'
@@ -1,19 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # This file was generated by Appraisal
4
-
5
- source 'https://rubygems.org'
6
-
7
- gem 'appraisal'
8
- gem 'bundler', '>= 1.10', '< 3'
9
- gem 'prism', '~> 1.2.0'
10
- gem 'pry'
11
- gem 'rake', '>= 10.0'
12
- gem 'rspec', '~> 3.0'
13
- gem 'rubocop', '~> 1.66.0'
14
- gem 'rubocop-rake', '~> 0.6.0'
15
- gem 'rubocop-rspec'
16
- gem 'simplecov'
17
- gem 'yard'
18
-
19
- gemspec path: '../'
@@ -1,20 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # The original code is from https://github.com/rubocop/rubocop-rspec/blob/master/lib/rubocop/rspec/inject.rb
4
- # See https://github.com/rubocop/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,39 +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.metadata = {
26
- 'changelog_uri' => 'https://github.com/rubocop/rubocop-thread_safety/blob/master/CHANGELOG.md',
27
- 'source_code_uri' => 'https://github.com/rubocop/rubocop-thread_safety',
28
- 'bug_tracker_uri' => 'https://github.com/rubocop/rubocop-thread_safety/issues',
29
- 'rubygems_mfa_required' => 'true'
30
- }
31
-
32
- spec.bindir = 'exe'
33
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
34
- spec.require_paths = ['lib']
35
-
36
- spec.required_ruby_version = '>= 2.7.0'
37
-
38
- spec.add_dependency 'rubocop', '>= 1.48.1'
39
- end
@@ -1,46 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'rubocop'
4
- require 'rubocop-thread_safety'
5
- require 'rubocop/cops_documentation_generator'
6
- require 'yard'
7
-
8
- YARD::Rake::YardocTask.new(:yard_for_generate_documentation) do |task|
9
- task.files = ['lib/rubocop/cop/**/*.rb']
10
- task.options = ['--no-output']
11
- end
12
-
13
- desc 'Generate docs of all cops departments'
14
- task generate_cops_documentation: :yard_for_generate_documentation do
15
- deps = ['ThreadSafety']
16
- CopsDocumentationGenerator.new(departments: deps).call
17
- end
18
-
19
- desc 'Syntax check for the documentation comments'
20
- task documentation_syntax_check: :yard_for_generate_documentation do
21
- require 'parser/ruby31'
22
-
23
- ok = true
24
- YARD::Registry.load!
25
- cops = RuboCop::Cop::Registry.global
26
- cops.each do |cop|
27
- examples = YARD::Registry.all(:class).find do |code_object|
28
- next unless RuboCop::Cop::Badge.for(code_object.to_s) == cop.badge
29
-
30
- break code_object.tags('example')
31
- end
32
-
33
- examples.to_a.each do |example|
34
- buffer = Parser::Source::Buffer.new('<code>', 1)
35
- buffer.source = example.text
36
- parser = Parser::Ruby31.new(RuboCop::AST::Builder.new)
37
- parser.diagnostics.all_errors_are_fatal = true
38
- parser.parse(buffer)
39
- rescue Parser::SyntaxError => e
40
- path = example.object.file
41
- puts "#{path}: Syntax Error in an example. #{e}"
42
- ok = false
43
- end
44
- end
45
- abort unless ok
46
- end