rubocop-thread_safety 0.4.4 → 0.5.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/.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 +32 -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,7 +72,8 @@ 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 |  | 
| @@ -108,23 +109,21 @@ module RuboCop | |
| 108 109 | 
             
                      end
         | 
| 109 110 | 
             
                    end
         | 
| 110 111 |  | 
| 111 | 
            -
                    def autocorrect(node)
         | 
| 112 | 
            +
                    def autocorrect(corrector, node)
         | 
| 112 113 | 
             
                      expr = node.source_range
         | 
| 113 114 |  | 
| 114 | 
            -
                       | 
| 115 | 
            -
             | 
| 116 | 
            -
                         | 
| 117 | 
            -
             | 
| 118 | 
            -
                         | 
| 119 | 
            -
             | 
| 120 | 
            -
             | 
| 121 | 
            -
                         | 
| 122 | 
            -
             | 
| 123 | 
            -
                          corrector.insert_after(expr, ')')
         | 
| 124 | 
            -
                        end
         | 
| 125 | 
            -
             | 
| 126 | 
            -
                        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, ')')
         | 
| 127 124 | 
             
                      end
         | 
| 125 | 
            +
             | 
| 126 | 
            +
                      corrector.insert_after(expr, '.freeze')
         | 
| 128 127 | 
             
                    end
         | 
| 129 128 |  | 
| 130 129 | 
             
                    private
         | 
| @@ -152,7 +151,9 @@ module RuboCop | |
| 152 151 | 
             
                      return if operation_produces_threadsafe_object?(value)
         | 
| 153 152 | 
             
                      return if frozen_string_literal?(value)
         | 
| 154 153 |  | 
| 155 | 
            -
                      add_offense(value)
         | 
| 154 | 
            +
                      add_offense(value) do |corrector|
         | 
| 155 | 
            +
                        autocorrect(corrector, value)
         | 
| 156 | 
            +
                      end
         | 
| 156 157 | 
             
                    end
         | 
| 157 158 |  | 
| 158 159 | 
             
                    def check(value)
         | 
| @@ -160,7 +161,9 @@ module RuboCop | |
| 160 161 | 
             
                                    range_enclosed_in_parentheses?(value)
         | 
| 161 162 | 
             
                      return if frozen_string_literal?(value)
         | 
| 162 163 |  | 
| 163 | 
            -
                      add_offense(value)
         | 
| 164 | 
            +
                      add_offense(value) do |corrector|
         | 
| 165 | 
            +
                        autocorrect(corrector, value)
         | 
| 166 | 
            +
                      end
         | 
| 164 167 | 
             
                    end
         | 
| 165 168 |  | 
| 166 169 | 
             
                    def in_class?(node)
         | 
| @@ -174,6 +177,7 @@ module RuboCop | |
| 174 177 |  | 
| 175 178 | 
             
                    def container?(node)
         | 
| 176 179 | 
             
                      return true if define_singleton_method?(node)
         | 
| 180 | 
            +
                      return true if define_method?(node)
         | 
| 177 181 |  | 
| 178 182 | 
             
                      %i[def defs class module].include?(node.type)
         | 
| 179 183 | 
             
                    end
         | 
| @@ -205,16 +209,24 @@ module RuboCop | |
| 205 209 | 
             
                      end
         | 
| 206 210 | 
             
                    end
         | 
| 207 211 |  | 
| 212 | 
            +
                    # @!method define_singleton_method?(node)
         | 
| 208 213 | 
             
                    def_node_matcher :define_singleton_method?, <<~PATTERN
         | 
| 209 214 | 
             
                      (block (send nil? :define_singleton_method ...) ...)
         | 
| 210 215 | 
             
                    PATTERN
         | 
| 211 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)
         | 
| 212 223 | 
             
                    def_node_matcher :splat_value, <<~PATTERN
         | 
| 213 224 | 
             
                      (array (splat $_))
         | 
| 214 225 | 
             
                    PATTERN
         | 
| 215 226 |  | 
| 216 227 | 
             
                    # NOTE: Some of these patterns may not actually return an immutable
         | 
| 217 228 | 
             
                    # object but we will consider them immutable for this cop.
         | 
| 229 | 
            +
                    # @!method operation_produces_immutable_object?(node)
         | 
| 218 230 | 
             
                    def_node_matcher :operation_produces_immutable_object?, <<~PATTERN
         | 
| 219 231 | 
             
                      {
         | 
| 220 232 | 
             
                        (const _ _)
         | 
| @@ -231,6 +243,7 @@ module RuboCop | |
| 231 243 | 
             
                      }
         | 
| 232 244 | 
             
                    PATTERN
         | 
| 233 245 |  | 
| 246 | 
            +
                    # @!method operation_produces_threadsafe_object?(node)
         | 
| 234 247 | 
             
                    def_node_matcher :operation_produces_threadsafe_object?, <<~PATTERN
         | 
| 235 248 | 
             
                      {
         | 
| 236 249 | 
             
                        (send (const {nil? cbase} :Queue) :new ...)
         | 
| @@ -263,6 +276,7 @@ module RuboCop | |
| 263 276 | 
             
                      }
         | 
| 264 277 | 
             
                    PATTERN
         | 
| 265 278 |  | 
| 279 | 
            +
                    # @!method range_enclosed_in_parentheses?(node)
         | 
| 266 280 | 
             
                    def_node_matcher :range_enclosed_in_parentheses?, <<~PATTERN
         | 
| 267 281 | 
             
                      (begin ({irange erange} _ _))
         | 
| 268 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
         |