pax_global_header00006660000000000000000000000064136005536160014517gustar00rootroot0000000000000052 comment=a91d882e0515a111811e9aedcefffbe67c1dd17f rubocop-performance-1.5.2/000077500000000000000000000000001360055361600154745ustar00rootroot00000000000000rubocop-performance-1.5.2/.circleci/000077500000000000000000000000001360055361600173275ustar00rootroot00000000000000rubocop-performance-1.5.2/.circleci/config.yml000066400000000000000000000021561360055361600213230ustar00rootroot00000000000000version: 2.1 jobs: rake_default: parameters: image: description: "Name of the Docker image." type: string default: "circleci/ruby" docker: - image: << parameters.image >> environment: # CircleCI container has two cores, but Ruby can see 32 cores. So we # configure test-queue. # See https://github.com/tmm1/test-queue#environment-variables TEST_QUEUE_WORKERS: 2 JRUBY_OPTS: "--dev -J-Xmx1000M" steps: - checkout - run: bundle install - run: bundle exec rake workflows: build: jobs: - rake_default: name: Ruby 2.3 image: circleci/ruby:2.3 - rake_default: name: Ruby 2.4 image: circleci/ruby:2.4 - rake_default: name: Ruby 2.5 image: circleci/ruby:2.5 - rake_default: name: Ruby 2.6 image: circleci/ruby:2.6 - rake_default: name: Ruby HEAD image: rubocophq/circleci-ruby-snapshot:latest # Nightly snapshot build - rake_default: name: JRuby 9.2 image: circleci/jruby:9.2 rubocop-performance-1.5.2/.gitattributes000066400000000000000000000000311360055361600203610ustar00rootroot00000000000000CHANGELOG.md merge=union rubocop-performance-1.5.2/.github/000077500000000000000000000000001360055361600170345ustar00rootroot00000000000000rubocop-performance-1.5.2/.github/FUNDING.yml000066400000000000000000000002641360055361600206530ustar00rootroot00000000000000# These are supported funding model platforms github: [bbatsov, koic] patreon: bbatsov open_collective: rubocop tidelift: "rubygems/rubocop" custom: https://www.paypal.me/bbatsov rubocop-performance-1.5.2/.github/ISSUE_TEMPLATE/000077500000000000000000000000001360055361600212175ustar00rootroot00000000000000rubocop-performance-1.5.2/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000021411360055361600237070ustar00rootroot00000000000000--- name: Bug Report about: Report an issue with RuboCop Performance you've discovered. --- *Be clear, concise and precise in your description of the problem. Open an issue with a descriptive title and a summary in grammatically correct, complete sentences.* *Use the template below when reporting bugs. Please, make sure that you're running the latest stable RuboCop and that the problem you're reporting hasn't been reported (and potentially fixed) already.* *Before filing the ticket you should replace all text above the horizontal rule with your own words.* -------- ## Expected behavior Describe here how you expected RuboCop Performance to behave in this particular situation. ## Actual behavior Describe here what actually happened. ## Steps to reproduce the problem This is extremely important! Providing us with a reliable way to reproduce a problem will expedite its solution. ## RuboCop version Include the output of `rubocop -V` or `bundle exec rubocop -V` if using Bundler. Here's an example: ``` $ [bundle exec] rubocop -V 0.65.0 (using Parser 2.5.1.2, running on ruby 2.5.1 x86_64-linux) ``` rubocop-performance-1.5.2/.github/ISSUE_TEMPLATE/feature_request.md000066400000000000000000000011351360055361600247440ustar00rootroot00000000000000--- name: Feature Request about: Suggest new RuboCop Performance features or improvements to existing features. --- ## Is your feature request related to a problem? Please describe. A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] ## Describe the solution you'd like A clear and concise description of what you want to happen. ## Describe alternatives you've considered A clear and concise description of any alternative solutions or features you've considered. ## Additional context Add any other context or screenshots about the feature request here. rubocop-performance-1.5.2/.github/PULL_REQUEST_TEMPLATE.md000066400000000000000000000020101360055361600226260ustar00rootroot00000000000000**Replace this text with a summary of the changes in your PR. The more detailed you are, the better.** ----------------- Before submitting the PR make sure the following are checked: * [ ] Wrote [good commit messages][1]. * [ ] Commit message starts with `[Fix #issue-number]` (if the related issue exists). * [ ] Feature branch is up-to-date with `master` (if not - rebase it). * [ ] Squashed related commits together. * [ ] Added tests. * [ ] Added an entry to the [Changelog](https://github.com/rubocop-hq/rubocop-performance/blob/master/CHANGELOG.md) if the new code introduces user-observable changes. See [changelog entry format](https://github.com/rubocop-hq/rubocop-performance/blob/master/CONTRIBUTING.md#changelog-entry-format). * [ ] The PR relates to *only* one subject with a clear title and description in grammatically correct, complete sentences. * [ ] Run `bundle exec rake default`. It executes all tests and RuboCop for itself, and generates the documentation. [1]: https://chris.beams.io/posts/git-commit/ rubocop-performance-1.5.2/.gitignore000066400000000000000000000016411360055361600174660ustar00rootroot00000000000000# rcov generated coverage coverage.data # rdoc generated rdoc # yard generated doc .yardoc # bundler .bundle Gemfile.lock Gemfile.local # rbenv .ruby-version # rspec /spec/examples.txt /.test_queue_stats # jeweler generated pkg # etags TAGS # Have editor/IDE/OS specific files you need to ignore? Consider using a global gitignore: # # * Create a file at ~/.gitignore # * Include files you want ignored # * Run: git config --global core.excludesfile ~/.gitignore # # After doing this, these files will be ignored in all your git projects, # saving you from having to 'pollute' every project you touch with them # # Not sure what to needs to be ignored for particular editors/OSes? Here's some ideas to get you started. (Remember, remove the leading # of the line) # # For MacOS: # #.DS_Store # For TextMate #*.tmproj #tmtags # For emacs: #*~ #\#* #.\#* # For vim: *.swp # For redcar: #.redcar # For rubinius: #*.rbc rubocop-performance-1.5.2/.rspec000066400000000000000000000000361360055361600166100ustar00rootroot00000000000000--color --require spec_helper rubocop-performance-1.5.2/.rubocop.yml000066400000000000000000000034261360055361600177530ustar00rootroot00000000000000# This is the configuration used to check the rubocop source code. inherit_from: .rubocop_todo.yml require: - rubocop/cop/internal_affairs - rubocop-performance - rubocop-rspec AllCops: Exclude: - 'vendor/**/*' - 'spec/fixtures/**/*' - 'tmp/**/*' TargetRubyVersion: 2.3 Naming/PredicateName: # Method define macros for dynamically generated method. MethodDefinitionMacros: - define_method - define_singleton_method - def_node_matcher - def_node_search Style/FormatStringToken: # Because we parse a lot of source codes from strings. Percent arrays # look like unannotated format string tokens to this cop. Exclude: - spec/**/* Layout/EndOfLine: EnforcedStyle: lf Layout/ClassStructure: Enabled: true Categories: module_inclusion: - include - prepend - extend ExpectedOrder: - module_inclusion - constants - public_class_methods - initializer - instance_methods - protected_methods - private_methods # Trailing white space is meaningful in code examples Layout/TrailingWhitespace: AllowInHeredoc: true Lint/AmbiguousBlockAssociation: Exclude: - 'spec/**/*.rb' Lint/InterpolationCheck: Exclude: - 'spec/**/*.rb' Lint/UselessAccessModifier: MethodCreatingMethods: - 'def_matcher' - 'def_node_matcher' Lint/BooleanSymbol: Enabled: false Metrics/BlockLength: Exclude: - 'Rakefile' - '**/*.rake' - 'spec/**/*.rb' Naming/FileName: Exclude: - lib/rubocop-performance.rb Metrics/ModuleLength: Exclude: - 'spec/**/*.rb' Performance/Caller: Exclude: - spec/rubocop/cop/performance/caller_spec.rb Performance/ChainArrayAllocation: Enabled: false RSpec/PredicateMatcher: EnforcedStyle: explicit RSpec/NestedGroups: Max: 7 rubocop-performance-1.5.2/.rubocop_todo.yml000066400000000000000000000034061360055361600207760ustar00rootroot00000000000000# This configuration was generated by # `rubocop --auto-gen-config` # on 2019-07-27 22:39:00 +0800 using RuboCop version 0.73.0. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. # Offense count: 12 InternalAffairs/NodeDestructuring: Exclude: - 'lib/rubocop/cop/performance/casecmp.rb' - 'lib/rubocop/cop/performance/detect.rb' - 'lib/rubocop/cop/performance/redundant_block_call.rb' - 'lib/rubocop/cop/performance/regexp_match.rb' - 'lib/rubocop/cop/performance/string_replacement.rb' # Offense count: 8 Metrics/AbcSize: Max: 17 # Offense count: 2 # Configuration parameters: CountComments. Metrics/ClassLength: Max: 164 # Offense count: 14 # Configuration parameters: CountComments, ExcludedMethods. Metrics/MethodLength: Max: 14 # Offense count: 40 # Configuration parameters: Prefixes. # Prefixes: when, with, without RSpec/ContextWording: Exclude: - 'spec/project_spec.rb' - 'spec/rubocop/cop/performance/case_when_splat_spec.rb' - 'spec/rubocop/cop/performance/count_spec.rb' - 'spec/rubocop/cop/performance/detect_spec.rb' - 'spec/rubocop/cop/performance/double_start_end_with_spec.rb' - 'spec/rubocop/cop/performance/fixed_size_spec.rb' - 'spec/rubocop/cop/performance/reverse_each_spec.rb' - 'spec/rubocop/cop/performance/string_replacement_spec.rb' - 'spec/rubocop/cop/performance/times_map_spec.rb' # Offense count: 89 # Configuration parameters: Max. RSpec/ExampleLength: Enabled: false # Offense count: 26 # Configuration parameters: AggregateFailuresByDefault. RSpec/MultipleExpectations: Max: 7 rubocop-performance-1.5.2/CHANGELOG.md000066400000000000000000000063561360055361600173170ustar00rootroot00000000000000# Change log ## master (unreleased) ## 1.5.2 (2019-12-25) ### Bug fixes * [#86](https://github.com/rubocop-hq/rubocop-performance/issues/86): Fix an incorrect autocorrect for `Performance/RedundantMerge` when using an empty hash argument. ([@koic][]) ## 1.5.1 (2019-11-14) ### Bug fixes * [#82](https://github.com/rubocop-hq/rubocop-performance/pull/82): Let `Performance/StartWith` and `Performance/EndWith` correct `Regexp#match?` and `Regexp#=~`. ([@eugeneius][]) ## 1.5.0 (2019-10-01) ### Bug fixes * [#74](https://github.com/rubocop-hq/rubocop-performance/pull/74): Fix an error for `Performance/RedundantMerge` when `MaxKeyValuePairs` option is set to `null`. ([@koic][]) * [#70](https://github.com/rubocop-hq/rubocop-performance/issues/70): This PR fixes a false negative for `Performance/FlatMap` when using symbol to proc operator argument of `map` method. ([@koic][], [@splattael][]) ### Changes * [#69](https://github.com/rubocop-hq/rubocop-performance/issues/69): Remove `SafeMode` from `Performance/Count` and `Performance/Detect`. Set `SafeAutoCorrect` to `false` for these cops by default. ([@rrosenblum][]) ## 1.4.1 (2019-07-29) ### Bug fixes * [#67](https://github.com/rubocop-hq/rubocop-performance/issues/67): Fix an error for `Performance/RedundantMerge` when `MaxKeyValuePairs` option is set to `null`. ([@koic][]) * [#73](https://github.com/rubocop-hq/rubocop-performance/pull/73): Fix a false negative for `Performance/RegexpMatch` when `MatchData` is not detected in `if` branch of guard condition. ([@koic][]) ## 1.4.0 (2019-06-20) ### Bug fixes * [#54](https://github.com/rubocop-hq/rubocop-performance/issues/54): Fix `Performance/FixedSize` to accept const assign with some operation. ([@tejasbubane][]) * [#61](https://github.com/rubocop-hq/rubocop-performance/pull/61): Fix a false negative for `Performance/RegexpMatch` when using RuboCop 0.71 or higher. ([@koic][]) ## 1.3.0 (2019-05-13) ### Bug fixes * [#48](https://github.com/rubocop-hq/rubocop-performance/issues/48): Reduce `Performance/RegexpMatch` false positive by only flagging `match` used with Regexp/String/Symbol literals. ([@dduugg][]) ### Changes * [#52](https://github.com/rubocop-hq/rubocop-performance/issues/52): Drop support for Ruby 2.2. ([@bquorning][]) ## 1.2.0 (2019-05-04) ### Bug fixes * [#47](https://github.com/rubocop-hq/rubocop-performance/pull/47): Fix a false negative for `Performance/RegexpMatch` when using RuboCop 0.68 or higher. ([@koic][]) ## 1.1.0 (2019-04-08) ### Changes * [#39](https://github.com/rubocop-hq/rubocop-performance/pull/39): Remove `Performance/LstripRstrip` cop. ([@koic][]) * [#39](https://github.com/rubocop-hq/rubocop-performance/pull/39): Remove `Performance/RedundantSortBy`, `Performance/UnneededSort` and `Performance/Sample` cops. ([@koic][]) ## 1.0.0 (2019-03-14) ### New features * Extract performance cops from rubocop-hq/rubocop repository. ([@composerinteralia][], [@koic][]) [@composerinteralia]: https://github.com/composerinteralia [@koic]: https://github.com/koic [@bquorning]: https://github.com/bquorning [@dduugg]: https://github.com/dduugg [@tejasbubane]: https://github.com/tejasbubane [@rrosenblum]: https://github.com/rrosenblum [@splattael]: https://github.com/splattael [@eugeneius]: https://github.com/eugeneius rubocop-performance-1.5.2/CONTRIBUTING.md000066400000000000000000000065011360055361600177270ustar00rootroot00000000000000# Contributing If you discover issues, have ideas for improvements or new features, please report them to the [issue tracker][1] of the repository or submit a pull request. Please, try to follow these guidelines when you do so. ## Issue reporting * Check that the issue has not already been reported. * Check that the issue has not already been fixed in the latest code (a.k.a. `master`). * Be clear, concise and precise in your description of the problem. * Open an issue with a descriptive title and a summary in grammatically correct, complete sentences. * Include the output of `rubocop -V`: ``` $ rubocop -V 0.50.0 (using Parser 2.4.0.0, running on ruby 2.4.2 x86_64-linux) ``` * Include any relevant code to the issue summary. ## Pull requests * Read [how to properly contribute to open source projects on GitHub][2]. * Fork the project. * Use a topic/feature branch to easily amend a pull request later, if necessary. * Write [good commit messages][3]. * Use the same coding conventions as the rest of the project. * Commit and push until you are happy with your contribution. * If your change has a corresponding open GitHub issue, prefix the commit message with `[Fix #github-issue-number]`. * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally. * Add an entry to the [Changelog](CHANGELOG.md) accordingly. See [changelog entry format](#changelog-entry-format). * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it. * Make sure the test suite is passing and the code you wrote doesn't produce RuboCop offenses (usually this is as simple as running `bundle exec rake`). * [Squash related commits together][5]. * Open a [pull request][4] that relates to *only* one subject with a clear title and description in grammatically correct, complete sentences. ### Changelog entry format Here are a few examples: ``` * [#716](https://github.com/rubocop-hq/rubocop-performance/issues/716): Fixed a regression in the auto-correction logic of `MethodDefParentheses`. ([@bbatsov][]) * New cop `ElseLayout` checks for odd arrangement of code in the `else` branch of a conditional expression. ([@bbatsov][]) ``` * Mark it up in [Markdown syntax][6]. * The entry line should start with `* ` (an asterisk and a space). * If the change has a related GitHub issue (e.g. a bug fix for a reported issue), put a link to the issue as `[#123](https://github.com/rubocop-hq/rubocop-performance/issues/123): `. * Describe the brief of the change. The sentence should end with a punctuation. * At the end of the entry, add an implicit link to your GitHub user page as `([@username][])`. * If this is your first contribution to RuboCop project, add a link definition for the implicit link to the bottom of the changelog as `[@username]: https://github.com/username`. [1]: https://github.com/rubocop-hq/rubocop-performance/issues [2]: https://www.gun.io/blog/how-to-github-fork-branch-and-pull-request [3]: https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html [4]: https://help.github.com/articles/about-pull-requests [5]: http://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html [6]: https://daringfireball.net/projects/markdown/syntax rubocop-performance-1.5.2/Gemfile000066400000000000000000000007321360055361600167710ustar00rootroot00000000000000# frozen_string_literal: true source 'https://rubygems.org' git_source(:github) { |repo| "https://github.com/#{repo}.git" } gemspec gem 'bump', require: false gem 'rake' gem 'rspec' gem 'rubocop', github: 'rubocop-hq/rubocop' gem 'rubocop-rspec', '~> 1.29.0' # Workaround for YARD 0.9.20 or lower. # It specifies `github` until the release that includes the following changes: # https://github.com/lsegal/yard/pull/1290 gem 'yard', github: 'lsegal/yard', ref: '10a2e5b' rubocop-performance-1.5.2/LICENSE.txt000066400000000000000000000020461360055361600173210ustar00rootroot00000000000000Copyright (c) 2012-18 Bozhidar Batsov 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. rubocop-performance-1.5.2/README.md000066400000000000000000000033211360055361600167520ustar00rootroot00000000000000# RuboCop Performance [![Gem Version](https://badge.fury.io/rb/rubocop-performance.svg)](https://badge.fury.io/rb/rubocop-performance) [![CircleCI](https://circleci.com/gh/rubocop-hq/rubocop-performance.svg?style=svg)](https://circleci.com/gh/rubocop-hq/rubocop-performance) Performance optimization analysis for your projects, as an extension to [RuboCop](https://github.com/rubocop-hq/rubocop). ## Installation Just install the `rubocop-performance` gem ```sh gem install rubocop-performance ``` or if you use bundler put this in your `Gemfile` ```ruby gem 'rubocop-performance' ``` ## Usage You need to tell RuboCop to load the Performance extension. There are three ways to do this: ### RuboCop configuration file Put this into your `.rubocop.yml`. ```yaml require: rubocop-performance ``` Alternatively, use the following array notation when specifying multiple extensions. ```yaml require: - rubocop-other-extension - rubocop-performance ``` Now you can run `rubocop` and it will automatically load the RuboCop Performance cops together with the standard cops. ### Command line ```sh rubocop --require rubocop-performance ``` ### Rake task ```ruby RuboCop::RakeTask.new do |task| task.requires << 'rubocop-performance' end ``` ## The Cops All cops are located under [`lib/rubocop/cop/performance`](lib/rubocop/cop/performance), and contain examples/documentation. In your `.rubocop.yml`, you may treat the Performance cops just like any other cop. For example: ```yaml Performance/Size: Exclude: - lib/example.rb ``` ## Contributing Checkout the [contribution guidelines](CONTRIBUTING.md). ## License `rubocop-performance` is MIT licensed. [See the accompanying file](LICENSE.txt) for the full text. rubocop-performance-1.5.2/Rakefile000066400000000000000000000027641360055361600171520ustar00rootroot00000000000000# frozen_string_literal: true require 'bundler' require 'bundler/gem_tasks' Dir['tasks/**/*.rake'].each { |t| load t } begin Bundler.setup(:default, :development) rescue Bundler::BundlerError => e warn e.message warn 'Run `bundle install` to install missing gems' exit e.status_code end require 'rubocop/rake_task' require 'rspec/core/rake_task' RSpec::Core::RakeTask.new(:spec) do |spec| spec.pattern = FileList['spec/**/*_spec.rb'] end desc 'Run RSpec with code coverage' task :coverage do ENV['COVERAGE'] = 'true' Rake::Task['spec'].execute end desc 'Run RuboCop over itself' RuboCop::RakeTask.new(:internal_investigation).tap do |task| if RUBY_ENGINE == 'ruby' && RbConfig::CONFIG['host_os'] !~ /mswin|msys|mingw|cygwin|bccwin|wince|emc/ task.options = %w[--parallel] end end task default: %i[ documentation_syntax_check generate_cops_documentation spec internal_investigation ] desc 'Generate a new cop template' task :new_cop, [:cop] do |_task, args| require 'rubocop' cop_name = args.fetch(:cop) do warn 'usage: bundle exec rake new_cop[Department/Name]' exit! end github_user = `git config github.user`.chop github_user = 'your_id' if github_user.empty? generator = RuboCop::Cop::Generator.new(cop_name, github_user) generator.write_source generator.write_spec generator.inject_require( root_file_path: 'lib/rubocop/cop/performance_cops.rb' ) generator.inject_config(config_file_path: 'config/default.yml') puts generator.todo end rubocop-performance-1.5.2/config/000077500000000000000000000000001360055361600167415ustar00rootroot00000000000000rubocop-performance-1.5.2/config/default.yml000066400000000000000000000172361360055361600211210ustar00rootroot00000000000000# This is the default configuration file. Performance/Caller: Description: >- Use `caller(n..n)` instead of `caller`. Enabled: true VersionAdded: '0.49' Performance/CaseWhenSplat: Description: >- Reordering `when` conditions with a splat to the end of the `when` branches can improve performance. Enabled: false AutoCorrect: false SafeAutoCorrect: false VersionAdded: '0.34' VersionChanged: '0.59' Performance/Casecmp: Description: >- Use `casecmp` rather than `downcase ==`, `upcase ==`, `== downcase`, or `== upcase`.. Reference: 'https://github.com/JuanitoFatas/fast-ruby#stringcasecmp-vs-stringdowncase---code' Enabled: true VersionAdded: '0.36' Performance/ChainArrayAllocation: Description: >- Instead of chaining array methods that allocate new arrays, mutate an existing array. Reference: 'https://twitter.com/schneems/status/1034123879978029057' Enabled: false VersionAdded: '0.59' Performance/CompareWithBlock: Description: 'Use `sort_by(&:foo)` instead of `sort { |a, b| a.foo <=> b.foo }`.' Enabled: true VersionAdded: '0.46' Performance/Count: Description: >- Use `count` instead of `select...size`, `reject...size`, `select...count`, `reject...count`, `select...length`, and `reject...length`. # This cop has known compatibility issues with `ActiveRecord` and other # frameworks. ActiveRecord's `count` ignores the block that is passed to it. # For more information, see the documentation in the cop itself. SafeAutoCorrect: false Enabled: true VersionAdded: '0.31' VersionChanged: '1.5' Performance/Detect: Description: >- Use `detect` instead of `select.first`, `find_all.first`, `select.last`, and `find_all.last`. Reference: 'https://github.com/JuanitoFatas/fast-ruby#enumerabledetect-vs-enumerableselectfirst-code' # This cop has known compatibility issues with `ActiveRecord` and other # frameworks. `ActiveRecord` does not implement a `detect` method and `find` # has its own meaning. Correcting `ActiveRecord` methods with this cop # should be considered unsafe. SafeAutoCorrect: false Enabled: true VersionAdded: '0.30' VersionChanged: '1.5' Performance/DoubleStartEndWith: Description: >- Use `str.{start,end}_with?(x, ..., y, ...)` instead of `str.{start,end}_with?(x, ...) || str.{start,end}_with?(y, ...)`. Enabled: true VersionAdded: '0.36' VersionChanged: '0.48' # Used to check for `starts_with?` and `ends_with?`. # These methods are defined by `ActiveSupport`. IncludeActiveSupportAliases: false Performance/EndWith: Description: 'Use `end_with?` instead of a regex match anchored to the end of a string.' Reference: 'https://github.com/JuanitoFatas/fast-ruby#stringmatch-vs-stringstart_withstringend_with-code-start-code-end' # This will change to a new method call which isn't guaranteed to be on the # object. Switching these methods has to be done with knowledge of the types # of the variables which rubocop doesn't have. SafeAutoCorrect: false AutoCorrect: false Enabled: true VersionAdded: '0.36' VersionChanged: '0.44' Performance/FixedSize: Description: 'Do not compute the size of statically sized objects except in constants' Enabled: true VersionAdded: '0.35' Performance/FlatMap: Description: >- Use `Enumerable#flat_map` instead of `Enumerable#map...Array#flatten(1)` or `Enumberable#collect..Array#flatten(1)` Reference: 'https://github.com/JuanitoFatas/fast-ruby#enumerablemaparrayflatten-vs-enumerableflat_map-code' Enabled: true VersionAdded: '0.30' EnabledForFlattenWithoutParams: false # If enabled, this cop will warn about usages of # `flatten` being called without any parameters. # This can be dangerous since `flat_map` will only flatten 1 level, and # `flatten` without any parameters can flatten multiple levels. Performance/InefficientHashSearch: Description: 'Use `key?` or `value?` instead of `keys.include?` or `values.include?`' Reference: 'https://github.com/JuanitoFatas/fast-ruby#hashkey-instead-of-hashkeysinclude-code' Enabled: true VersionAdded: '0.56' Safe: false Performance/OpenStruct: Description: 'Use `Struct` instead of `OpenStruct`.' Enabled: false VersionAdded: '0.61' Safe: false Performance/RangeInclude: Description: 'Use `Range#cover?` instead of `Range#include?`.' Reference: 'https://github.com/JuanitoFatas/fast-ruby#cover-vs-include-code' Enabled: true VersionAdded: '0.36' Safe: false Performance/RedundantBlockCall: Description: 'Use `yield` instead of `block.call`.' Reference: 'https://github.com/JuanitoFatas/fast-ruby#proccall-and-block-arguments-vs-yieldcode' Enabled: true VersionAdded: '0.36' Performance/RedundantMatch: Description: >- Use `=~` instead of `String#match` or `Regexp#match` in a context where the returned `MatchData` is not needed. Enabled: true VersionAdded: '0.36' Performance/RedundantMerge: Description: 'Use Hash#[]=, rather than Hash#merge! with a single key-value pair.' Reference: 'https://github.com/JuanitoFatas/fast-ruby#hashmerge-vs-hash-code' Enabled: true VersionAdded: '0.36' # Max number of key-value pairs to consider an offense MaxKeyValuePairs: 2 Performance/RegexpMatch: Description: >- Use `match?` instead of `Regexp#match`, `String#match`, `Symbol#match`, `Regexp#===`, or `=~` when `MatchData` is not used. Reference: 'https://github.com/JuanitoFatas/fast-ruby#regexp-vs-stringmatch-vs-string-vs-stringmatch-code-' Enabled: true VersionAdded: '0.47' Performance/ReverseEach: Description: 'Use `reverse_each` instead of `reverse.each`.' Reference: 'https://github.com/JuanitoFatas/fast-ruby#enumerablereverseeach-vs-enumerablereverse_each-code' Enabled: true VersionAdded: '0.30' Performance/Size: Description: >- Use `size` instead of `count` for counting the number of elements in `Array` and `Hash`. Reference: 'https://github.com/JuanitoFatas/fast-ruby#arraylength-vs-arraysize-vs-arraycount-code' Enabled: true VersionAdded: '0.30' Performance/StartWith: Description: 'Use `start_with?` instead of a regex match anchored to the beginning of a string.' Reference: 'https://github.com/JuanitoFatas/fast-ruby#stringmatch-vs-stringstart_withstringend_with-code-start-code-end' # This will change to a new method call which isn't guaranteed to be on the # object. Switching these methods has to be done with knowledge of the types # of the variables which rubocop doesn't have. SafeAutoCorrect: false AutoCorrect: false Enabled: true VersionAdded: '0.36' VersionChanged: '0.44' Performance/StringReplacement: Description: >- Use `tr` instead of `gsub` when you are replacing the same number of characters. Use `delete` instead of `gsub` when you are deleting characters. Reference: 'https://github.com/JuanitoFatas/fast-ruby#stringgsub-vs-stringtr-code' Enabled: true VersionAdded: '0.33' Performance/TimesMap: Description: 'Checks for .times.map calls.' AutoCorrect: false Enabled: true VersionAdded: '0.36' VersionChanged: '0.50' SafeAutoCorrect: false # see https://github.com/rubocop-hq/rubocop/issues/4658 Performance/UnfreezeString: Description: 'Use unary plus to get an unfrozen string literal.' Enabled: true VersionAdded: '0.50' Performance/UriDefaultParser: Description: 'Use `URI::DEFAULT_PARSER` instead of `URI::Parser.new`.' Enabled: true VersionAdded: '0.50' rubocop-performance-1.5.2/lib/000077500000000000000000000000001360055361600162425ustar00rootroot00000000000000rubocop-performance-1.5.2/lib/rubocop-performance.rb000066400000000000000000000004171360055361600225410ustar00rootroot00000000000000# frozen_string_literal: true require 'rubocop' require_relative 'rubocop/performance' require_relative 'rubocop/performance/version' require_relative 'rubocop/performance/inject' RuboCop::Performance::Inject.defaults! require_relative 'rubocop/cop/performance_cops' rubocop-performance-1.5.2/lib/rubocop/000077500000000000000000000000001360055361600177135ustar00rootroot00000000000000rubocop-performance-1.5.2/lib/rubocop/cop/000077500000000000000000000000001360055361600204745ustar00rootroot00000000000000rubocop-performance-1.5.2/lib/rubocop/cop/performance/000077500000000000000000000000001360055361600227755ustar00rootroot00000000000000rubocop-performance-1.5.2/lib/rubocop/cop/performance/caller.rb000066400000000000000000000034241360055361600245670ustar00rootroot00000000000000# frozen_string_literal: true module RuboCop module Cop module Performance # This cop identifies places where `caller[n]` # can be replaced by `caller(n..n).first`. # # @example # # bad # caller[1] # caller.first # caller_locations[1] # caller_locations.first # # # good # caller(2..2).first # caller(1..1).first # caller_locations(2..2).first # caller_locations(1..1).first class Caller < Cop MSG_BRACE = 'Use `%s(%d..%d).first`' \ ' instead of `%s[%d]`.' MSG_FIRST = 'Use `%s(%d..%d).first`' \ ' instead of `%s.first`.' def_node_matcher :slow_caller?, <<-PATTERN { (send nil? {:caller :caller_locations}) (send nil? {:caller :caller_locations} int) } PATTERN def_node_matcher :caller_with_scope_method?, <<-PATTERN { (send #slow_caller? :first) (send #slow_caller? :[] int) } PATTERN def on_send(node) return unless caller_with_scope_method?(node) add_offense(node) end private def message(node) method_name = node.receiver.method_name caller_arg = node.receiver.first_argument n = caller_arg ? int_value(caller_arg) : 1 if node.method?(:[]) m = int_value(node.first_argument) n += m format(MSG_BRACE, n: n, m: m, method: method_name) else format(MSG_FIRST, n: n, method: method_name) end end def int_value(node) node.children[0] end end end end end rubocop-performance-1.5.2/lib/rubocop/cop/performance/case_when_splat.rb000066400000000000000000000127201360055361600264630ustar00rootroot00000000000000# frozen_string_literal: true module RuboCop module Cop module Performance # Reordering `when` conditions with a splat to the end # of the `when` branches can improve performance. # # Ruby has to allocate memory for the splat expansion every time # that the `case` `when` statement is run. Since Ruby does not support # fall through inside of `case` `when`, like some other languages do, # the order of the `when` branches should not matter. By placing any # splat expansions at the end of the list of `when` branches we will # reduce the number of times that memory has to be allocated for # the expansion. The exception to this is if multiple of your `when` # conditions can be true for any given condition. A likely scenario for # this defining a higher level when condition to override a condition # that is inside of the splat expansion. # # This is not a guaranteed performance improvement. If the data being # processed by the `case` condition is normalized in a manner that favors # hitting a condition in the splat expansion, it is possible that # moving the splat condition to the end will use more memory, # and run slightly slower. # # @example # # bad # case foo # when *condition # bar # when baz # foobar # end # # case foo # when *[1, 2, 3, 4] # bar # when 5 # baz # end # # # good # case foo # when baz # foobar # when *condition # bar # end # # case foo # when 1, 2, 3, 4 # bar # when 5 # baz # end class CaseWhenSplat < Cop include Alignment include RangeHelp MSG = 'Reordering `when` conditions with a splat to the end ' \ 'of the `when` branches can improve performance.' ARRAY_MSG = 'Pass the contents of array literals ' \ 'directly to `when` conditions.' def on_case(case_node) when_conditions = case_node.when_branches.flat_map(&:conditions) splat_offenses(when_conditions).reverse_each do |condition| range = condition.parent.loc.keyword.join(condition.source_range) variable, = *condition message = variable.array_type? ? ARRAY_MSG : MSG add_offense(condition.parent, location: range, message: message) end end def autocorrect(when_node) lambda do |corrector| if needs_reorder?(when_node) reorder_condition(corrector, when_node) else inline_fix_branch(corrector, when_node) end end end private def replacement(conditions) reordered = conditions.partition(&:splat_type?).reverse reordered.flatten.map(&:source).join(', ') end def inline_fix_branch(corrector, when_node) conditions = when_node.conditions range = range_between(conditions[0].loc.expression.begin_pos, conditions[-1].loc.expression.end_pos) corrector.replace(range, replacement(conditions)) end def reorder_condition(corrector, when_node) when_branches = when_node.parent.when_branches return if when_branches.one? corrector.remove(when_branch_range(when_node)) corrector.insert_after(when_branches.last.source_range, reordering_correction(when_node)) end def reordering_correction(when_node) new_condition = replacement(when_node.conditions) if same_line?(when_node, when_node.body) new_condition_with_then(when_node, new_condition) else new_branch_without_then(when_node, new_condition) end end def when_branch_range(when_node) next_branch = when_node.parent.when_branches[when_node.branch_index + 1] range_between(when_node.source_range.begin_pos, next_branch.source_range.begin_pos) end def new_condition_with_then(node, new_condition) "\n#{indent_for(node)}when " \ "#{new_condition} then #{node.body.source}" end def new_branch_without_then(node, new_condition) "\n#{indent_for(node)}when #{new_condition}" \ "\n#{indent_for(node.body)}#{node.body.source}" end def indent_for(node) ' ' * node.loc.column end def splat_offenses(when_conditions) found_non_splat = false offenses = when_conditions.reverse.map do |condition| found_non_splat ||= non_splat?(condition) next if non_splat?(condition) condition if found_non_splat end offenses.compact end def non_splat?(condition) variable, = *condition (condition.splat_type? && variable.array_type?) || !condition.splat_type? end def needs_reorder?(when_node) following_branches = when_node.parent.when_branches[(when_node.branch_index + 1)..-1] following_branches.any? do |when_branch| when_branch.conditions.any? do |condition| non_splat?(condition) end end end end end end end rubocop-performance-1.5.2/lib/rubocop/cop/performance/casecmp.rb000066400000000000000000000061131360055361600247360ustar00rootroot00000000000000# frozen_string_literal: true module RuboCop module Cop module Performance # This cop identifies places where a case-insensitive string comparison # can better be implemented using `casecmp`. # # @example # # bad # str.downcase == 'abc' # str.upcase.eql? 'ABC' # 'abc' == str.downcase # 'ABC'.eql? str.upcase # str.downcase == str.downcase # # # good # str.casecmp('ABC').zero? # 'abc'.casecmp(str).zero? class Casecmp < Cop MSG = 'Use `%s` instead of `%s`.' CASE_METHODS = %i[downcase upcase].freeze def_node_matcher :downcase_eq, <<-PATTERN (send $(send _ ${:downcase :upcase}) ${:== :eql? :!=} ${str (send _ {:downcase :upcase} ...) (begin str)}) PATTERN def_node_matcher :eq_downcase, <<-PATTERN (send {str (send _ {:downcase :upcase} ...) (begin str)} ${:== :eql? :!=} $(send _ ${:downcase :upcase})) PATTERN def_node_matcher :downcase_downcase, <<-PATTERN (send $(send _ ${:downcase :upcase}) ${:== :eql? :!=} $(send _ ${:downcase :upcase})) PATTERN def on_send(node) return unless downcase_eq(node) || eq_downcase(node) return unless (parts = take_method_apart(node)) _, _, arg, variable = parts good_method = build_good_method(arg, variable) add_offense( node, message: format(MSG, good: good_method, bad: node.source) ) end def autocorrect(node) return unless (parts = take_method_apart(node)) receiver, method, arg, variable = parts correction(node, receiver, method, arg, variable) end private def take_method_apart(node) if downcase_downcase(node) receiver, method, rhs = *node arg, = *rhs elsif downcase_eq(node) receiver, method, arg = *node elsif eq_downcase(node) arg, method, receiver = *node else return end variable, = *receiver [receiver, method, arg, variable] end def correction(node, _receiver, method, arg, variable) lambda do |corrector| corrector.insert_before(node.loc.expression, '!') if method == :!= replacement = build_good_method(arg, variable) corrector.replace(node.loc.expression, replacement) end end def build_good_method(arg, variable) # We want resulting call to be parenthesized # if arg already includes one or more sets of parens, don't add more # or if method call already used parens, again, don't add more if arg.send_type? || !parentheses?(arg) "#{variable.source}.casecmp(#{arg.source}).zero?" else "#{variable.source}.casecmp#{arg.source}.zero?" end end end end end end rubocop-performance-1.5.2/lib/rubocop/cop/performance/chain_array_allocation.rb000066400000000000000000000056731360055361600300220ustar00rootroot00000000000000# frozen_string_literal: true module RuboCop module Cop module Performance # This cop is used to identify usages of # @example # # bad # array = ["a", "b", "c"] # array.compact.flatten.map { |x| x.downcase } # # Each of these methods (`compact`, `flatten`, `map`) will generate a # new intermediate array that is promptly thrown away. Instead it is # faster to mutate when we know it's safe. # # @example # # good. # array = ["a", "b", "c"] # array.compact! # array.flatten! # array.map! { |x| x.downcase } # array class ChainArrayAllocation < Cop include RangeHelp # These methods return a new array but only sometimes. They must be # called with an argument. For example: # # [1,2].first # => 1 # [1,2].first(1) # => [1] # RETURN_NEW_ARRAY_WHEN_ARGS = ':first :last :pop :sample :shift ' # These methods return a new array only when called without a block. RETURNS_NEW_ARRAY_WHEN_NO_BLOCK = ':zip :product ' # These methods ALWAYS return a new array # after they're called it's safe to mutate the the resulting array ALWAYS_RETURNS_NEW_ARRAY = ':* :+ :- :collect :compact :drop '\ ':drop_while :flatten :map :reject ' \ ':reverse :rotate :select :shuffle :sort ' \ ':take :take_while :transpose :uniq ' \ ':values_at :| ' # These methods have a mutation alternative. For example :collect # can be called as :collect! HAS_MUTATION_ALTERNATIVE = ':collect :compact :flatten :map :reject '\ ':reverse :rotate :select :shuffle :sort '\ ':uniq ' MSG = 'Use unchained `%s!` and `%s!` '\ '(followed by `return array` if required) instead of chaining '\ '`%s...%s`.' def_node_matcher :flat_map_candidate?, <<-PATTERN { (send (send _ ${#{RETURN_NEW_ARRAY_WHEN_ARGS}} {int lvar ivar cvar gvar}) ${#{HAS_MUTATION_ALTERNATIVE}} $...) (send (block (send _ ${#{ALWAYS_RETURNS_NEW_ARRAY} }) ...) ${#{HAS_MUTATION_ALTERNATIVE}} $...) (send (send _ ${#{ALWAYS_RETURNS_NEW_ARRAY + RETURNS_NEW_ARRAY_WHEN_NO_BLOCK}} ...) ${#{HAS_MUTATION_ALTERNATIVE}} $...) } PATTERN def on_send(node) flat_map_candidate?(node) do |fm, sm, _| range = range_between( node.loc.dot.begin_pos, node.source_range.end_pos ) add_offense( node, location: range, message: format(MSG, method: fm, second_method: sm) ) end end end end end end rubocop-performance-1.5.2/lib/rubocop/cop/performance/compare_with_block.rb000066400000000000000000000072001360055361600271540ustar00rootroot00000000000000# frozen_string_literal: true module RuboCop module Cop module Performance # This cop identifies places where `sort { |a, b| a.foo <=> b.foo }` # can be replaced by `sort_by(&:foo)`. # This cop also checks `max` and `min` methods. # # @example # # bad # array.sort { |a, b| a.foo <=> b.foo } # array.max { |a, b| a.foo <=> b.foo } # array.min { |a, b| a.foo <=> b.foo } # array.sort { |a, b| a[:foo] <=> b[:foo] } # # # good # array.sort_by(&:foo) # array.sort_by { |v| v.foo } # array.sort_by do |var| # var.foo # end # array.max_by(&:foo) # array.min_by(&:foo) # array.sort_by { |a| a[:foo] } class CompareWithBlock < Cop include RangeHelp MSG = 'Use `%s_by%s` instead of ' \ '`%s { |%s, %s| %s ' \ '<=> %s }`.' def_node_matcher :compare?, <<-PATTERN (block $(send _ {:sort :min :max}) (args (arg $_a) (arg $_b)) $send) PATTERN def_node_matcher :replaceable_body?, <<-PATTERN (send (send (lvar %1) $_method $...) :<=> (send (lvar %2) _method $...)) PATTERN def on_block(node) compare?(node) do |send, var_a, var_b, body| replaceable_body?(body, var_a, var_b) do |method, args_a, args_b| return unless slow_compare?(method, args_a, args_b) range = compare_range(send, node) add_offense( node, location: range, message: message(send, method, var_a, var_b, args_a) ) end end end def autocorrect(node) lambda do |corrector| send, var_a, var_b, body = compare?(node) method, arg, = replaceable_body?(body, var_a, var_b) replacement = if method == :[] "#{send.method_name}_by { |a| a[#{arg.first.source}] }" else "#{send.method_name}_by(&:#{method})" end corrector.replace(compare_range(send, node), replacement) end end private def slow_compare?(method, args_a, args_b) return false unless args_a == args_b if method == :[] return false unless args_a.size == 1 key = args_a.first return false unless %i[sym str int].include?(key.type) else return false unless args_a.empty? end true end # rubocop:disable Metrics/MethodLength def message(send, method, var_a, var_b, args) compare_method = send.method_name if method == :[] key = args.first instead = " { |a| a[#{key.source}] }" str_a = "#{var_a}[#{key.source}]" str_b = "#{var_b}[#{key.source}]" else instead = "(&:#{method})" str_a = "#{var_a}.#{method}" str_b = "#{var_b}.#{method}" end format(MSG, compare_method: compare_method, instead: instead, var_a: var_a, var_b: var_b, str_a: str_a, str_b: str_b) end # rubocop:enable Metrics/MethodLength def compare_range(send, node) range_between(send.loc.selector.begin_pos, node.loc.end.end_pos) end end end end end rubocop-performance-1.5.2/lib/rubocop/cop/performance/count.rb000066400000000000000000000062401360055361600244540ustar00rootroot00000000000000# frozen_string_literal: true module RuboCop module Cop module Performance # This cop is used to identify usages of `count` on an `Enumerable` that # follow calls to `select` or `reject`. Querying logic can instead be # passed to the `count` call. # # @example # # bad # [1, 2, 3].select { |e| e > 2 }.size # [1, 2, 3].reject { |e| e > 2 }.size # [1, 2, 3].select { |e| e > 2 }.length # [1, 2, 3].reject { |e| e > 2 }.length # [1, 2, 3].select { |e| e > 2 }.count { |e| e.odd? } # [1, 2, 3].reject { |e| e > 2 }.count { |e| e.even? } # array.select(&:value).count # # # good # [1, 2, 3].count { |e| e > 2 } # [1, 2, 3].count { |e| e < 2 } # [1, 2, 3].count { |e| e > 2 && e.odd? } # [1, 2, 3].count { |e| e < 2 && e.even? } # Model.select('field AS field_one').count # Model.select(:value).count # # `ActiveRecord` compatibility: # `ActiveRecord` will ignore the block that is passed to `count`. # Other methods, such as `select`, will convert the association to an # array and then run the block on the array. A simple work around to # make `count` work with a block is to call `to_a.count {...}`. # # Example: # `Model.where(id: [1, 2, 3]).select { |m| m.method == true }.size` # # becomes: # # `Model.where(id: [1, 2, 3]).to_a.count { |m| m.method == true }` class Count < Cop include RangeHelp MSG = 'Use `count` instead of `%s...%s`.' def_node_matcher :count_candidate?, <<-PATTERN { (send (block $(send _ ${:select :reject}) ...) ${:count :length :size}) (send $(send _ ${:select :reject} (:block_pass _)) ${:count :length :size}) } PATTERN def on_send(node) count_candidate?(node) do |selector_node, selector, counter| return unless eligible_node?(node) range = source_starting_at(node) do selector_node.loc.selector.begin_pos end add_offense(node, location: range, message: format(MSG, selector: selector, counter: counter)) end end def autocorrect(node) selector_node, selector, _counter = count_candidate?(node) selector_loc = selector_node.loc.selector return if selector == :reject range = source_starting_at(node) { |n| n.loc.dot.begin_pos } lambda do |corrector| corrector.remove(range) corrector.replace(selector_loc, 'count') end end private def eligible_node?(node) !(node.parent && node.parent.block_type?) end def source_starting_at(node) begin_pos = if block_given? yield node else node.source_range.begin_pos end range_between(begin_pos, node.source_range.end_pos) end end end end end rubocop-performance-1.5.2/lib/rubocop/cop/performance/detect.rb000066400000000000000000000065721360055361600246040ustar00rootroot00000000000000# frozen_string_literal: true module RuboCop module Cop module Performance # This cop is used to identify usages of # `select.first`, `select.last`, `find_all.first`, and `find_all.last` # and change them to use `detect` instead. # # @example # # bad # [].select { |item| true }.first # [].select { |item| true }.last # [].find_all { |item| true }.first # [].find_all { |item| true }.last # # # good # [].detect { |item| true } # [].reverse.detect { |item| true } # # `ActiveRecord` compatibility: # `ActiveRecord` does not implement a `detect` method and `find` has its # own meaning. Correcting ActiveRecord methods with this cop should be # considered unsafe. class Detect < Cop MSG = 'Use `%s` instead of ' \ '`%s.%s`.' REVERSE_MSG = 'Use `reverse.%s` instead of ' \ '`%s.%s`.' def_node_matcher :detect_candidate?, <<-PATTERN { (send $(block (send _ {:select :find_all}) ...) ${:first :last} $...) (send $(send _ {:select :find_all} ...) ${:first :last} $...) } PATTERN def on_send(node) detect_candidate?(node) do |receiver, second_method, args| return unless args.empty? return unless receiver receiver, _args, body = *receiver if receiver.block_type? return if accept_first_call?(receiver, body) register_offense(node, receiver, second_method) end end def autocorrect(node) receiver, first_method = *node replacement = if first_method == :last "reverse.#{preferred_method}" else preferred_method end first_range = receiver.source_range.end.join(node.loc.selector) receiver, _args, _body = *receiver if receiver.block_type? lambda do |corrector| corrector.remove(first_range) corrector.replace(receiver.loc.selector, replacement) end end private def accept_first_call?(receiver, body) caller, _first_method, args = *receiver # check that we have usual block or block pass return true if body.nil? && (args.nil? || !args.block_pass_type?) lazy?(caller) end def register_offense(node, receiver, second_method) _caller, first_method, _args = *receiver range = receiver.loc.selector.join(node.loc.selector) message = second_method == :last ? REVERSE_MSG : MSG formatted_message = format(message, prefer: preferred_method, first_method: first_method, second_method: second_method) add_offense(node, location: range, message: formatted_message) end def preferred_method config.for_cop('Style/CollectionMethods') \ ['PreferredMethods']['detect'] || 'detect' end def lazy?(node) return false unless node receiver, method, _args = *node method == :lazy && !receiver.nil? end end end end end rubocop-performance-1.5.2/lib/rubocop/cop/performance/double_start_end_with.rb000066400000000000000000000056731360055361600277050ustar00rootroot00000000000000# frozen_string_literal: true module RuboCop module Cop module Performance # This cop checks for double `#start_with?` or `#end_with?` calls # separated by `||`. In some cases such calls can be replaced # with an single `#start_with?`/`#end_with?` call. # # @example # # bad # str.start_with?("a") || str.start_with?(Some::CONST) # str.start_with?("a", "b") || str.start_with?("c") # str.end_with?(var1) || str.end_with?(var2) # # # good # str.start_with?("a", Some::CONST) # str.start_with?("a", "b", "c") # str.end_with?(var1, var2) class DoubleStartEndWith < Cop MSG = 'Use `%s.%s(%s)` ' \ 'instead of `%s`.' def on_or(node) receiver, method, first_call_args, second_call_args = process_source(node) return unless receiver && second_call_args.all?(&:pure?) combined_args = combine_args(first_call_args, second_call_args) add_offense_for_double_call(node, receiver, method, combined_args) end def autocorrect(node) _receiver, _method, first_call_args, second_call_args = process_source(node) combined_args = combine_args(first_call_args, second_call_args) first_argument = first_call_args.first.loc.expression last_argument = second_call_args.last.loc.expression range = first_argument.join(last_argument) lambda do |corrector| corrector.replace(range, combined_args) end end private def process_source(node) if check_for_active_support_aliases? check_with_active_support_aliases(node) else two_start_end_with_calls(node) end end def combine_args(first_call_args, second_call_args) (first_call_args + second_call_args).map(&:source).join(', ') end def add_offense_for_double_call(node, receiver, method, combined_args) msg = format(MSG, receiver: receiver.source, method: method, combined_args: combined_args, original_code: node.source) add_offense(node, message: msg) end def check_for_active_support_aliases? cop_config['IncludeActiveSupportAliases'] end def_node_matcher :two_start_end_with_calls, <<-PATTERN (or (send $_recv [{:start_with? :end_with?} $_method] $...) (send _recv _method $...)) PATTERN def_node_matcher :check_with_active_support_aliases, <<-PATTERN (or (send $_recv [{:start_with? :starts_with? :end_with? :ends_with?} $_method] $...) (send _recv _method $...)) PATTERN end end end end rubocop-performance-1.5.2/lib/rubocop/cop/performance/end_with.rb000066400000000000000000000037101360055361600251240ustar00rootroot00000000000000# frozen_string_literal: true module RuboCop module Cop module Performance # This cop identifies unnecessary use of a regex where `String#end_with?` # would suffice. # # @example # # bad # 'abc'.match?(/bc\Z/) # /bc\Z/.match?('abc') # 'abc' =~ /bc\Z/ # /bc\Z/ =~ 'abc' # 'abc'.match(/bc\Z/) # /bc\Z/.match('abc') # # # good # 'abc'.end_with?('bc') class EndWith < Cop MSG = 'Use `String#end_with?` instead of a regex match anchored to ' \ 'the end of the string.' SINGLE_QUOTE = "'" def_node_matcher :redundant_regex?, <<-PATTERN {(send $!nil? {:match :=~ :match?} (regexp (str $#literal_at_end?) (regopt))) (send (regexp (str $#literal_at_end?) (regopt)) {:match :match?} $_) (match-with-lvasgn (regexp (str $#literal_at_end?) (regopt)) $_)} PATTERN def literal_at_end?(regex_str) # is this regexp 'literal' in the sense of only matching literal # chars, rather than using metachars like . and * and so on? # also, is it anchored at the end of the string? regex_str =~ /\A(?:#{LITERAL_REGEX})+\\z\z/ end def on_send(node) return unless redundant_regex?(node) add_offense(node) end alias on_match_with_lvasgn on_send def autocorrect(node) redundant_regex?(node) do |receiver, regex_str| receiver, regex_str = regex_str, receiver if receiver.is_a?(String) regex_str = regex_str[0..-3] # drop \Z anchor regex_str = interpret_string_escapes(regex_str) lambda do |corrector| new_source = receiver.source + '.end_with?(' + to_string_literal(regex_str) + ')' corrector.replace(node.source_range, new_source) end end end end end end end rubocop-performance-1.5.2/lib/rubocop/cop/performance/fixed_size.rb000066400000000000000000000042301360055361600254520ustar00rootroot00000000000000# frozen_string_literal: true module RuboCop module Cop module Performance # Do not compute the size of statically sized objects. # # @example # # String methods # # bad # 'foo'.size # %q[bar].count # %(qux).length # # # Symbol methods # # bad # :fred.size # :'baz'.length # # # Array methods # # bad # [1, 2, thud].count # %W(1, 2, bar).size # # # Hash methods # # bad # { a: corge, b: grault }.length # # # good # foo.size # bar.count # qux.length # # # good # :"#{fred}".size # CONST = :baz.length # # # good # [1, 2, *thud].count # garply = [1, 2, 3] # garply.size # # # good # { a: corge, **grault }.length # waldo = { a: corge, b: grault } # waldo.size # class FixedSize < Cop MSG = 'Do not compute the size of statically sized objects.' def_node_matcher :counter, <<-MATCHER (send ${array hash str sym} {:count :length :size} $...) MATCHER def on_send(node) return if node.ancestors.any? { |ancestor| allowed_parent?(ancestor) } counter(node) do |var, arg| return if allowed_variable?(var) || allowed_argument?(arg) add_offense(node) end end private def allowed_variable?(var) contains_splat?(var) || contains_double_splat?(var) end def allowed_argument?(arg) arg && non_string_argument?(arg.first) end def allowed_parent?(node) node && (node.casgn_type? || node.block_type?) end def contains_splat?(node) return unless node.array_type? node.each_child_node(:splat).any? end def contains_double_splat?(node) return unless node.hash_type? node.each_child_node(:kwsplat).any? end def non_string_argument?(node) node && !node.str_type? end end end end end rubocop-performance-1.5.2/lib/rubocop/cop/performance/flat_map.rb000066400000000000000000000054111360055361600251060ustar00rootroot00000000000000# frozen_string_literal: true module RuboCop module Cop module Performance # This cop is used to identify usages of # # @example # # bad # [1, 2, 3, 4].map { |e| [e, e] }.flatten(1) # [1, 2, 3, 4].collect { |e| [e, e] }.flatten(1) # # # good # [1, 2, 3, 4].flat_map { |e| [e, e] } # [1, 2, 3, 4].map { |e| [e, e] }.flatten # [1, 2, 3, 4].collect { |e| [e, e] }.flatten class FlatMap < Cop include RangeHelp MSG = 'Use `flat_map` instead of `%s...%s`.' FLATTEN_MULTIPLE_LEVELS = ' Beware, `flat_map` only flattens 1 level ' \ 'and `flatten` can be used to flatten ' \ 'multiple levels.' def_node_matcher :flat_map_candidate?, <<-PATTERN (send { (block $(send _ ${:collect :map}) ...) $(send _ ${:collect :map} (block_pass _)) } ${:flatten :flatten!} $... ) PATTERN def on_send(node) flat_map_candidate?(node) do |map_node, first_method, flatten, params| flatten_level, = *params.first if cop_config['EnabledForFlattenWithoutParams'] && !flatten_level offense_for_levels(node, map_node, first_method, flatten) elsif flatten_level == 1 offense_for_method(node, map_node, first_method, flatten) end end end def autocorrect(node) map_node, _first_method, _flatten, params = flat_map_candidate?(node) flatten_level, = *params.first return unless flatten_level range = range_between(node.loc.dot.begin_pos, node.source_range.end_pos) lambda do |corrector| corrector.remove(range) corrector.replace(map_node.loc.selector, 'flat_map') end end private def offense_for_levels(node, map_node, first_method, flatten) message = MSG + FLATTEN_MULTIPLE_LEVELS register_offense(node, map_node, first_method, flatten, message) end def offense_for_method(node, map_node, first_method, flatten) register_offense(node, map_node, first_method, flatten, MSG) end def register_offense(node, map_node, first_method, flatten, message) range = range_between(map_node.loc.selector.begin_pos, node.loc.expression.end_pos) add_offense(node, location: range, message: format(message, method: first_method, flatten: flatten)) end end end end end rubocop-performance-1.5.2/lib/rubocop/cop/performance/inefficient_hash_search.rb000066400000000000000000000057421360055361600301450ustar00rootroot00000000000000# frozen_string_literal: true module RuboCop module Cop module Performance # This cop checks for inefficient searching of keys and values within # hashes. # # `Hash#keys.include?` is less efficient than `Hash#key?` because # the former allocates a new array and then performs an O(n) search # through that array, while `Hash#key?` does not allocate any array and # performs a faster O(1) search for the key. # # `Hash#values.include?` is less efficient than `Hash#value?`. While they # both perform an O(n) search through all of the values, calling `values` # allocates a new array while using `value?` does not. # # @example # # bad # { a: 1, b: 2 }.keys.include?(:a) # { a: 1, b: 2 }.keys.include?(:z) # h = { a: 1, b: 2 }; h.keys.include?(100) # # # good # { a: 1, b: 2 }.key?(:a) # { a: 1, b: 2 }.has_key?(:z) # h = { a: 1, b: 2 }; h.key?(100) # # # bad # { a: 1, b: 2 }.values.include?(2) # { a: 1, b: 2 }.values.include?('garbage') # h = { a: 1, b: 2 }; h.values.include?(nil) # # # good # { a: 1, b: 2 }.value?(2) # { a: 1, b: 2 }.has_value?('garbage') # h = { a: 1, b: 2 }; h.value?(nil) # class InefficientHashSearch < Cop def_node_matcher :inefficient_include?, <<-PATTERN (send (send $_ {:keys :values}) :include? _) PATTERN def on_send(node) inefficient_include?(node) do |receiver| return if receiver.nil? add_offense(node) end end def autocorrect(node) lambda do |corrector| # Replace `keys.include?` or `values.include?` with the appropriate # `key?`/`value?` method. corrector.replace( node.loc.expression, "#{autocorrect_hash_expression(node)}."\ "#{autocorrect_method(node)}(#{autocorrect_argument(node)})" ) end end private def message(node) "Use `##{autocorrect_method(node)}` instead of "\ "`##{current_method(node)}.include?`." end def autocorrect_method(node) case current_method(node) when :keys then use_long_method ? 'has_key?' : 'key?' when :values then use_long_method ? 'has_value?' : 'value?' end end def current_method(node) node.receiver.method_name end def use_long_method preferred_config = config.for_all_cops['Style/PreferredHashMethods'] preferred_config && preferred_config['EnforcedStyle'] == 'long' && preferred_config['Enabled'] end def autocorrect_argument(node) node.arguments.first.source end def autocorrect_hash_expression(node) node.receiver.receiver.source end end end end end rubocop-performance-1.5.2/lib/rubocop/cop/performance/open_struct.rb000066400000000000000000000024111360055361600256650ustar00rootroot00000000000000# frozen_string_literal: true module RuboCop module Cop module Performance # This cop checks for `OpenStruct.new` calls. # Instantiation of an `OpenStruct` invalidates # Ruby global method cache as it causes dynamic method # definition during program runtime. # This could have an effect on performance, # especially in case of single-threaded # applications with multiple `OpenStruct` instantiations. # # @example # # bad # class MyClass # def my_method # OpenStruct.new(my_key1: 'my_value1', my_key2: 'my_value2') # end # end # # # good # class MyClass # MyStruct = Struct.new(:my_key1, :my_key2) # def my_method # MyStruct.new('my_value1', 'my_value2') # end # end # class OpenStruct < Cop MSG = 'Consider using `Struct` over `OpenStruct` ' \ 'to optimize the performance.' def_node_matcher :open_struct, <<-PATTERN (send (const {nil? cbase} :OpenStruct) :new ...) PATTERN def on_send(node) open_struct(node) do add_offense(node, location: :selector) end end end end end end rubocop-performance-1.5.2/lib/rubocop/cop/performance/range_include.rb000066400000000000000000000031651360055361600261260ustar00rootroot00000000000000# frozen_string_literal: true module RuboCop module Cop module Performance # This cop identifies uses of `Range#include?`, which iterates over each # item in a `Range` to see if a specified item is there. In contrast, # `Range#cover?` simply compares the target item with the beginning and # end points of the `Range`. In a great majority of cases, this is what # is wanted. # # This cop is `Safe: false` by default because `Range#include?` and # `Range#cover?` are not equivalent behaviour. # # @example # # bad # ('a'..'z').include?('b') # => true # # # good # ('a'..'z').cover?('b') # => true # # # Example of a case where `Range#cover?` may not provide # # the desired result: # # ('a'..'z').cover?('yellow') # => true class RangeInclude < Cop MSG = 'Use `Range#cover?` instead of `Range#include?`.' # TODO: If we traced out assignments of variables to their uses, we # might pick up on a few more instances of this issue # Right now, we only detect direct calls on a Range literal # (We don't even catch it if the Range is in double parens) def_node_matcher :range_include, <<-PATTERN (send {irange erange (begin {irange erange})} :include? ...) PATTERN def on_send(node) return unless range_include(node) add_offense(node, location: :selector) end def autocorrect(node) ->(corrector) { corrector.replace(node.loc.selector, 'cover?') } end end end end end rubocop-performance-1.5.2/lib/rubocop/cop/performance/redundant_block_call.rb000066400000000000000000000046311360055361600274570ustar00rootroot00000000000000# frozen_string_literal: true module RuboCop module Cop module Performance # This cop identifies the use of a `&block` parameter and `block.call` # where `yield` would do just as well. # # @example # # bad # def method(&block) # block.call # end # def another(&func) # func.call 1, 2, 3 # end # # # good # def method # yield # end # def another # yield 1, 2, 3 # end class RedundantBlockCall < Cop MSG = 'Use `yield` instead of `%s.call`.' YIELD = 'yield' OPEN_PAREN = '(' CLOSE_PAREN = ')' SPACE = ' ' def_node_matcher :blockarg_def, <<-PATTERN {(def _ (args ... (blockarg $_)) $_) (defs _ _ (args ... (blockarg $_)) $_)} PATTERN def_node_search :blockarg_calls, <<-PATTERN (send (lvar %1) :call ...) PATTERN def_node_search :blockarg_assigned?, <<-PATTERN (lvasgn %1 ...) PATTERN def on_def(node) blockarg_def(node) do |argname, body| next unless body calls_to_report(argname, body).each do |blockcall| add_offense(blockcall, message: format(MSG, argname: argname)) end end end # offenses are registered on the `block.call` nodes def autocorrect(node) _receiver, _method, *args = *node new_source = String.new(YIELD) unless args.empty? new_source += if parentheses?(node) OPEN_PAREN else SPACE end new_source << args.map(&:source).join(', ') end new_source << CLOSE_PAREN if parentheses?(node) && !args.empty? ->(corrector) { corrector.replace(node.source_range, new_source) } end private def calls_to_report(argname, body) return [] if blockarg_assigned?(body, argname) calls = to_enum(:blockarg_calls, body, argname) return [] if calls.any? { |call| args_include_block_pass?(call) } calls end def args_include_block_pass?(blockcall) _receiver, _call, *args = *blockcall args.any?(&:block_pass_type?) end end end end end rubocop-performance-1.5.2/lib/rubocop/cop/performance/redundant_match.rb000066400000000000000000000034321360055361600264640ustar00rootroot00000000000000# frozen_string_literal: true module RuboCop module Cop module Performance # This cop identifies the use of `Regexp#match` or `String#match`, which # returns `#`/`nil`. The return value of `=~` is an integral # index/`nil` and is more performant. # # @example # # bad # do_something if str.match(/regex/) # while regex.match('str') # do_something # end # # # good # method(str =~ /regex/) # return value unless regex =~ 'str' class RedundantMatch < Cop MSG = 'Use `=~` in places where the `MatchData` returned by ' \ '`#match` will not be used.' # 'match' is a fairly generic name, so we don't flag it unless we see # a string or regexp literal on one side or the other def_node_matcher :match_call?, <<-PATTERN {(send {str regexp} :match _) (send !nil? :match {str regexp})} PATTERN def_node_matcher :only_truthiness_matters?, <<-PATTERN ^({if while until case while_post until_post} equal?(%0) ...) PATTERN def on_send(node) return unless match_call?(node) && (!node.value_used? || only_truthiness_matters?(node)) && !(node.parent && node.parent.block_type?) add_offense(node) end def autocorrect(node) # Regexp#match can take a second argument, but this cop doesn't # register an offense in that case return unless node.first_argument.regexp_type? new_source = node.receiver.source + ' =~ ' + node.first_argument.source ->(corrector) { corrector.replace(node.source_range, new_source) } end end end end end rubocop-performance-1.5.2/lib/rubocop/cop/performance/redundant_merge.rb000066400000000000000000000127371360055361600264770ustar00rootroot00000000000000# frozen_string_literal: true module RuboCop module Cop module Performance # This cop identifies places where `Hash#merge!` can be replaced by # `Hash#[]=`. # # @example # hash.merge!(a: 1) # hash.merge!({'key' => 'value'}) # hash.merge!(a: 1, b: 2) class RedundantMerge < Cop AREF_ASGN = '%s[%s] = %s' MSG = 'Use `%s` instead of `%s`.' WITH_MODIFIER_CORRECTION = <<~RUBY %s %s %s%s%s %send RUBY def_node_matcher :redundant_merge_candidate, <<-PATTERN (send $!nil? :merge! [(hash $...) !kwsplat_type?]) PATTERN def_node_matcher :modifier_flow_control?, <<-PATTERN [{if while until} modifier_form?] PATTERN def on_send(node) each_redundant_merge(node) do |redundant_merge_node| add_offense(redundant_merge_node) end end def autocorrect(node) redundant_merge_candidate(node) do |receiver, pairs| new_source = to_assignments(receiver, pairs).join("\n") if node.parent && pairs.size > 1 correct_multiple_elements(node, node.parent, new_source) else correct_single_element(node, new_source) end end end private def message(node) redundant_merge_candidate(node) do |receiver, pairs| assignments = to_assignments(receiver, pairs).join('; ') format(MSG, prefer: assignments, current: node.source) end end def each_redundant_merge(node) redundant_merge_candidate(node) do |receiver, pairs| next if non_redundant_merge?(node, receiver, pairs) yield node end end def non_redundant_merge?(node, receiver, pairs) pairs.empty? || non_redundant_pairs?(receiver, pairs) || kwsplat_used?(pairs) || non_redundant_value_used?(receiver, node) end def non_redundant_pairs?(receiver, pairs) pairs.size > 1 && !receiver.pure? || pairs.size > max_key_value_pairs end def kwsplat_used?(pairs) pairs.any?(&:kwsplat_type?) end def non_redundant_value_used?(receiver, node) node.value_used? && !EachWithObjectInspector.new(node, receiver).value_used? end def correct_multiple_elements(node, parent, new_source) if modifier_flow_control?(parent) new_source = rewrite_with_modifier(node, parent, new_source) node = parent else padding = "\n#{leading_spaces(node)}" new_source.gsub!(/\n/, padding) end ->(corrector) { corrector.replace(node.source_range, new_source) } end def correct_single_element(node, new_source) ->(corrector) { corrector.replace(node.source_range, new_source) } end def to_assignments(receiver, pairs) pairs.map do |pair| key, value = *pair key = key.sym_type? && pair.colon? ? ":#{key.source}" : key.source format(AREF_ASGN, receiver: receiver.source, key: key, value: value.source) end end def rewrite_with_modifier(node, parent, new_source) indent = ' ' * indent_width padding = "\n#{indent + leading_spaces(node)}" new_source.gsub!(/\n/, padding) format(WITH_MODIFIER_CORRECTION, keyword: parent.loc.keyword.source, condition: parent.condition.source, leading_space: leading_spaces(node), indent: indent, body: new_source).chomp end def leading_spaces(node) node.source_range.source_line[/\A\s*/] end def indent_width @config.for_cop('Layout/IndentationWidth')['Width'] || 2 end def max_key_value_pairs Integer(cop_config['MaxKeyValuePairs'] || 2) end # A utility class for checking the use of values within an # `each_with_object` call. class EachWithObjectInspector extend NodePattern::Macros def initialize(node, receiver) @node = node @receiver = unwind(receiver) end def value_used? return false unless eligible_receiver? && second_argument receiver.loc.name.source == second_argument.loc.name.source end private attr_reader :node, :receiver def eligible_receiver? receiver.respond_to?(:lvar_type?) && receiver.lvar_type? end def second_argument parent = node.parent parent = parent.parent if parent.begin_type? @second_argument ||= each_with_object_node(parent) end def unwind(receiver) while receiver.respond_to?(:send_type?) && receiver.send_type? receiver, = *receiver end receiver end def_node_matcher :each_with_object_node, <<-PATTERN (block (send _ :each_with_object _) (args _ $_) ...) PATTERN end end end end end rubocop-performance-1.5.2/lib/rubocop/cop/performance/regexp_match.rb000066400000000000000000000175341360055361600260020ustar00rootroot00000000000000# frozen_string_literal: true module RuboCop module Cop module Performance # In Ruby 2.4, `String#match?`, `Regexp#match?`, and `Symbol#match?` # have been added. The methods are faster than `match`. # Because the methods avoid creating a `MatchData` object or saving # backref. # So, when `MatchData` is not used, use `match?` instead of `match`. # # @example # # bad # def foo # if x =~ /re/ # do_something # end # end # # # bad # def foo # if x !~ /re/ # do_something # end # end # # # bad # def foo # if x.match(/re/) # do_something # end # end # # # bad # def foo # if /re/ === x # do_something # end # end # # # good # def foo # if x.match?(/re/) # do_something # end # end # # # good # def foo # if !x.match?(/re/) # do_something # end # end # # # good # def foo # if x =~ /re/ # do_something(Regexp.last_match) # end # end # # # good # def foo # if x.match(/re/) # do_something($~) # end # end # # # good # def foo # if /re/ === x # do_something($~) # end # end class RegexpMatch < Cop extend TargetRubyVersion minimum_target_ruby_version 2.4 # Constants are included in this list because it is unlikely that # someone will store `nil` as a constant and then use it for comparison TYPES_IMPLEMENTING_MATCH = %i[const regexp str sym].freeze MSG = 'Use `match?` instead of `%s` when `MatchData` ' \ 'is not used.' def_node_matcher :match_method?, <<-PATTERN { (send _recv :match {regexp str sym}) (send {regexp str sym} :match _) } PATTERN def_node_matcher :match_with_int_arg_method?, <<-PATTERN (send _recv :match _ (int ...)) PATTERN def_node_matcher :match_operator?, <<-PATTERN (send !nil? {:=~ :!~} !nil?) PATTERN def_node_matcher :match_threequals?, <<-PATTERN (send (regexp (str _) {(regopt) (regopt _)}) :=== !nil?) PATTERN def match_with_lvasgn?(node) return false unless node.match_with_lvasgn_type? regexp, _rhs = *node regexp.to_regexp.named_captures.empty? end MATCH_NODE_PATTERN = <<-PATTERN { #match_method? #match_with_int_arg_method? #match_operator? #match_threequals? #match_with_lvasgn? } PATTERN def_node_matcher :match_node?, MATCH_NODE_PATTERN def_node_search :search_match_nodes, MATCH_NODE_PATTERN def_node_search :last_matches, <<-PATTERN { (send (const nil? :Regexp) :last_match) (send (const nil? :Regexp) :last_match _) ({back_ref nth_ref} _) (gvar #match_gvar?) } PATTERN def on_if(node) check_condition(node.condition) end def on_case(node) return if node.condition node.each_when do |when_node| when_node.each_condition do |condition| check_condition(condition) end end end def autocorrect(node) lambda do |corrector| if match_method?(node) || match_with_int_arg_method?(node) corrector.replace(node.loc.selector, 'match?') elsif match_operator?(node) || match_threequals?(node) recv, oper, arg = *node correct_operator(corrector, recv, arg, oper) elsif match_with_lvasgn?(node) recv, arg = *node correct_operator(corrector, recv, arg) end end end private def check_condition(cond) match_node?(cond) do return if last_match_used?(cond) add_offense(cond) end end def message(node) format(MSG, current: node.loc.selector.source) end def last_match_used?(match_node) scope_root = scope_root(match_node) body = scope_root ? scope_body(scope_root) : match_node.ancestors.last range = range_to_search_for_last_matches(match_node, body, scope_root) find_last_match(body, range, scope_root) end def range_to_search_for_last_matches(match_node, body, scope_root) expression = if modifier_form?(match_node) match_node.parent.if_branch.loc.expression else match_node.loc.expression end match_node_pos = expression.begin_pos next_match_pos = next_match_pos(body, match_node_pos, scope_root) match_node_pos..next_match_pos end def next_match_pos(body, match_node_pos, scope_root) node = search_match_nodes(body).find do |match| begin_pos = if modifier_form?(match) match.parent.if_branch.loc.expression.begin_pos else match.loc.expression.begin_pos end begin_pos > match_node_pos && scope_root(match) == scope_root end node ? node.loc.expression.begin_pos : Float::INFINITY end def modifier_form?(match_node) match_node.parent.if_type? && match_node.parent.modifier_form? end def find_last_match(body, range, scope_root) last_matches(body).find do |ref| ref_pos = ref.loc.expression.begin_pos range.cover?(ref_pos) && scope_root(ref) == scope_root end end def scope_body(node) children = node.children case node.type when :module children[1] when :defs children[3] else children[2] end end def scope_root(node) node.each_ancestor.find do |ancestor| ancestor.def_type? || ancestor.defs_type? || ancestor.class_type? || ancestor.module_type? end end def match_gvar?(sym) %i[ $~ $MATCH $PREMATCH $POSTMATCH $LAST_PAREN_MATCH $LAST_MATCH_INFO ].include?(sym) end def correct_operator(corrector, recv, arg, oper = nil) op_range = correction_range(recv, arg) if TYPES_IMPLEMENTING_MATCH.include?(recv.type) corrector.replace(op_range, '.match?(') elsif TYPES_IMPLEMENTING_MATCH.include?(arg.type) corrector.replace(op_range, '.match?(') swap_receiver_and_arg(corrector, recv, arg) else corrector.replace(op_range, '&.match?(') end corrector.insert_after(arg.loc.expression, ')') corrector.insert_before(recv.loc.expression, '!') if oper == :!~ end def swap_receiver_and_arg(corrector, recv, arg) corrector.replace(recv.loc.expression, arg.source) corrector.replace(arg.loc.expression, recv.source) end def correction_range(recv, arg) buffer = processed_source.buffer op_begin_pos = recv.loc.expression.end_pos op_end_pos = arg.loc.expression.begin_pos Parser::Source::Range.new(buffer, op_begin_pos, op_end_pos) end end end end end rubocop-performance-1.5.2/lib/rubocop/cop/performance/reverse_each.rb000066400000000000000000000020311360055361600257510ustar00rootroot00000000000000# frozen_string_literal: true module RuboCop module Cop module Performance # This cop is used to identify usages of `reverse.each` and # change them to use `reverse_each` instead. # # @example # # bad # [].reverse.each # # # good # [].reverse_each class ReverseEach < Cop include RangeHelp MSG = 'Use `reverse_each` instead of `reverse.each`.' UNDERSCORE = '_' def_node_matcher :reverse_each?, <<-MATCHER (send $(send _ :reverse) :each) MATCHER def on_send(node) reverse_each?(node) do |receiver| location_of_reverse = receiver.loc.selector.begin_pos end_location = node.loc.selector.end_pos range = range_between(location_of_reverse, end_location) add_offense(node, location: range) end end def autocorrect(node) ->(corrector) { corrector.replace(node.loc.dot, UNDERSCORE) } end end end end end rubocop-performance-1.5.2/lib/rubocop/cop/performance/size.rb000066400000000000000000000034121360055361600242740ustar00rootroot00000000000000# frozen_string_literal: true module RuboCop module Cop module Performance # This cop is used to identify usages of `count` on an # `Array` and `Hash` and change them to `size`. # # @example # # bad # [1, 2, 3].count # # # bad # {a: 1, b: 2, c: 3}.count # # # good # [1, 2, 3].size # # # good # {a: 1, b: 2, c: 3}.size # # # good # [1, 2, 3].count { |e| e > 2 } # TODO: Add advanced detection of variables that could # have been assigned to an array or a hash. class Size < Cop MSG = 'Use `size` instead of `count`.' def on_send(node) return unless eligible_node?(node) add_offense(node, location: :selector) end def autocorrect(node) ->(corrector) { corrector.replace(node.loc.selector, 'size') } end private def eligible_node?(node) return false unless node.method?(:count) && !node.arguments? eligible_receiver?(node.receiver) && !allowed_parent?(node.parent) end def eligible_receiver?(node) return false unless node array?(node) || hash?(node) end def allowed_parent?(node) node&.block_type? end def array?(node) return true if node.array_type? return false unless node.send_type? _, constant = *node.receiver constant == :Array || node.method?(:to_a) end def hash?(node) return true if node.hash_type? return false unless node.send_type? _, constant = *node.receiver constant == :Hash || node.method?(:to_h) end end end end end rubocop-performance-1.5.2/lib/rubocop/cop/performance/start_with.rb000066400000000000000000000042441360055361600255160ustar00rootroot00000000000000# frozen_string_literal: true module RuboCop module Cop module Performance # This cop identifies unnecessary use of a regex where # `String#start_with?` would suffice. # # @example # # bad # 'abc'.match?(/\Aab/) # /\Aab/.match?('abc') # 'abc' =~ /\Aab/ # /\Aab/ =~ 'abc' # 'abc'.match(/\Aab/) # /\Aab/.match('abc') # # # good # 'abc'.start_with?('ab') class StartWith < Cop MSG = 'Use `String#start_with?` instead of a regex match anchored to ' \ 'the beginning of the string.' SINGLE_QUOTE = "'" def_node_matcher :redundant_regex?, <<-PATTERN {(send $!nil? {:match :=~ :match?} (regexp (str $#literal_at_start?) (regopt))) (send (regexp (str $#literal_at_start?) (regopt)) {:match :match?} $_) (match-with-lvasgn (regexp (str $#literal_at_start?) (regopt)) $_)} PATTERN def literal_at_start?(regex_str) # is this regexp 'literal' in the sense of only matching literal # chars, rather than using metachars like `.` and `*` and so on? # also, is it anchored at the start of the string? # (tricky: \s, \d, and so on are metacharacters, but other characters # escaped with a slash are just literals. LITERAL_REGEX takes all # that into account.) regex_str =~ /\A\\A(?:#{LITERAL_REGEX})+\z/ end def on_send(node) return unless redundant_regex?(node) add_offense(node) end alias on_match_with_lvasgn on_send def autocorrect(node) redundant_regex?(node) do |receiver, regex_str| receiver, regex_str = regex_str, receiver if receiver.is_a?(String) regex_str = regex_str[2..-1] # drop \A anchor regex_str = interpret_string_escapes(regex_str) lambda do |corrector| new_source = receiver.source + '.start_with?(' + to_string_literal(regex_str) + ')' corrector.replace(node.source_range, new_source) end end end end end end end rubocop-performance-1.5.2/lib/rubocop/cop/performance/string_replacement.rb000066400000000000000000000122711360055361600272120ustar00rootroot00000000000000# frozen_string_literal: true module RuboCop module Cop module Performance # This cop identifies places where `gsub` can be replaced by # `tr` or `delete`. # # @example # # bad # 'abc'.gsub('b', 'd') # 'abc'.gsub('a', '') # 'abc'.gsub(/a/, 'd') # 'abc'.gsub!('a', 'd') # # # good # 'abc'.gsub(/.*/, 'a') # 'abc'.gsub(/a+/, 'd') # 'abc'.tr('b', 'd') # 'a b c'.delete(' ') class StringReplacement < Cop include RangeHelp MSG = 'Use `%s` instead of `%s`.' DETERMINISTIC_REGEX = /\A(?:#{LITERAL_REGEX})+\Z/.freeze DELETE = 'delete' TR = 'tr' BANG = '!' SINGLE_QUOTE = "'" def_node_matcher :string_replacement?, <<-PATTERN (send _ {:gsub :gsub!} ${regexp str (send (const nil? :Regexp) {:new :compile} _)} $str) PATTERN def on_send(node) string_replacement?(node) do |first_param, second_param| return if accept_second_param?(second_param) return if accept_first_param?(first_param) offense(node, first_param, second_param) end end def autocorrect(node) _string, _method, first_param, second_param = *node first_source, = first_source(first_param) second_source, = *second_param unless first_param.str_type? first_source = interpret_string_escapes(first_source) end replacement_method = replacement_method(node, first_source, second_source) replace_method(node, first_source, second_source, first_param, replacement_method) end def replace_method(node, first, second, first_param, replacement) lambda do |corrector| corrector.replace(node.loc.selector, replacement) unless first_param.str_type? corrector.replace(first_param.source_range, to_string_literal(first)) end if second.empty? && first.length == 1 remove_second_param(corrector, node, first_param) end end end private def accept_second_param?(second_param) second_source, = *second_param second_source.length > 1 end def accept_first_param?(first_param) first_source, options = first_source(first_param) return true if first_source.nil? unless first_param.str_type? return true if options return true unless first_source.is_a?(String) && first_source =~ DETERMINISTIC_REGEX # This must be done after checking DETERMINISTIC_REGEX # Otherwise things like \s will trip us up first_source = interpret_string_escapes(first_source) end first_source.length != 1 end def offense(node, first_param, second_param) first_source, = first_source(first_param) unless first_param.str_type? first_source = interpret_string_escapes(first_source) end second_source, = *second_param message = message(node, first_source, second_source) add_offense(node, location: range(node), message: message) end def first_source(first_param) case first_param.type when :regexp source_from_regex_literal(first_param) when :send source_from_regex_constructor(first_param) when :str first_param.children.first end end def source_from_regex_literal(node) regex, options = *node source, = *regex options, = *options [source, options] end def source_from_regex_constructor(node) _const, _init, regex = *node case regex.type when :regexp source_from_regex_literal(regex) when :str source, = *regex source end end def range(node) range_between(node.loc.selector.begin_pos, node.source_range.end_pos) end def replacement_method(node, first_source, second_source) replacement = if second_source.empty? && first_source.length == 1 DELETE else TR end "#{replacement}#{BANG if node.bang_method?}" end def message(node, first_source, second_source) replacement_method = replacement_method(node, first_source, second_source) format(MSG, prefer: replacement_method, current: node.method_name) end def method_suffix(node) node.loc.end ? node.loc.end.source : '' end def remove_second_param(corrector, node, first_param) end_range = range_between(first_param.source_range.end_pos, node.source_range.end_pos) corrector.replace(end_range, method_suffix(node)) end end end end end rubocop-performance-1.5.2/lib/rubocop/cop/performance/times_map.rb000066400000000000000000000035751360055361600253120ustar00rootroot00000000000000# frozen_string_literal: true module RuboCop module Cop module Performance # This cop checks for .times.map calls. # In most cases such calls can be replaced # with an explicit array creation. # # @example # # bad # 9.times.map do |i| # i.to_s # end # # # good # Array.new(9) do |i| # i.to_s # end class TimesMap < Cop MESSAGE = 'Use `Array.new(%s)` with a block ' \ 'instead of `.times.%s`' MESSAGE_ONLY_IF = 'only if `%s` is always 0 or more' def on_send(node) check(node) end def on_block(node) check(node) end def autocorrect(node) map_or_collect, count = times_map_call(node) replacement = "Array.new(#{count.source}" \ "#{map_or_collect.arguments.map { |arg| ", #{arg.source}" }.join})" lambda do |corrector| corrector.replace(map_or_collect.loc.expression, replacement) end end private def check(node) times_map_call(node) do |map_or_collect, count| add_offense(node, message: message(map_or_collect, count)) end end def message(map_or_collect, count) template = if count.literal? MESSAGE + '.' else "#{MESSAGE} #{MESSAGE_ONLY_IF}." end format(template, count: count.source, map_or_collect: map_or_collect.method_name) end def_node_matcher :times_map_call, <<-PATTERN {(block $(send (send $!nil? :times) {:map :collect}) ...) $(send (send $!nil? :times) {:map :collect} (block_pass ...))} PATTERN end end end end rubocop-performance-1.5.2/lib/rubocop/cop/performance/unfreeze_string.rb000066400000000000000000000026101360055361600265320ustar00rootroot00000000000000# frozen_string_literal: true module RuboCop module Cop module Performance # In Ruby 2.3 or later, use unary plus operator to unfreeze a string # literal instead of `String#dup` and `String.new`. # Unary plus operator is faster than `String#dup`. # # Note: `String.new` (without operator) is not exactly the same as `+''`. # These differ in encoding. `String.new.encoding` is always `ASCII-8BIT`. # However, `(+'').encoding` is the same as script encoding(e.g. `UTF-8`). # So, if you expect `ASCII-8BIT` encoding, disable this cop. # # @example # # bad # ''.dup # "something".dup # String.new # String.new('') # String.new('something') # # # good # +'something' # +'' class UnfreezeString < Cop extend TargetRubyVersion minimum_target_ruby_version 2.3 MSG = 'Use unary plus to get an unfrozen string literal.' def_node_matcher :dup_string?, <<-PATTERN (send {str dstr} :dup) PATTERN def_node_matcher :string_new?, <<-PATTERN { (send (const nil? :String) :new {str dstr}) (send (const nil? :String) :new) } PATTERN def on_send(node) add_offense(node) if dup_string?(node) || string_new?(node) end end end end end rubocop-performance-1.5.2/lib/rubocop/cop/performance/uri_default_parser.rb000066400000000000000000000023051360055361600272010ustar00rootroot00000000000000# frozen_string_literal: true module RuboCop module Cop module Performance # This cop identifies places where `URI::Parser.new` # can be replaced by `URI::DEFAULT_PARSER`. # # @example # # bad # URI::Parser.new # # # good # URI::DEFAULT_PARSER # class UriDefaultParser < Cop MSG = 'Use `%sURI::DEFAULT_PARSER` instead of ' \ '`%sURI::Parser.new`.' def_node_matcher :uri_parser_new?, <<-PATTERN (send (const (const ${nil? cbase} :URI) :Parser) :new) PATTERN def on_send(node) return unless uri_parser_new?(node) do |captured_value| double_colon = captured_value ? '::' : '' message = format(MSG, double_colon: double_colon) add_offense(node, message: message) end end def autocorrect(node) lambda do |corrector| double_colon = uri_parser_new?(node) ? '::' : '' corrector.replace( node.loc.expression, "#{double_colon}URI::DEFAULT_PARSER" ) end end end end end end rubocop-performance-1.5.2/lib/rubocop/cop/performance_cops.rb000066400000000000000000000022001360055361600243400ustar00rootroot00000000000000# frozen_string_literal: true require_relative 'performance/caller' require_relative 'performance/case_when_splat' require_relative 'performance/casecmp' require_relative 'performance/compare_with_block' require_relative 'performance/count' require_relative 'performance/detect' require_relative 'performance/double_start_end_with' require_relative 'performance/end_with' require_relative 'performance/fixed_size' require_relative 'performance/flat_map' require_relative 'performance/inefficient_hash_search' require_relative 'performance/open_struct' require_relative 'performance/range_include' require_relative 'performance/redundant_block_call' require_relative 'performance/redundant_match' require_relative 'performance/redundant_merge' require_relative 'performance/regexp_match' require_relative 'performance/reverse_each' require_relative 'performance/size' require_relative 'performance/start_with' require_relative 'performance/string_replacement' require_relative 'performance/times_map' require_relative 'performance/unfreeze_string' require_relative 'performance/uri_default_parser' require_relative 'performance/chain_array_allocation' rubocop-performance-1.5.2/lib/rubocop/performance.rb000066400000000000000000000005661360055361600225500ustar00rootroot00000000000000# frozen_string_literal: true module RuboCop # RuboCop Performance project namespace module Performance PROJECT_ROOT = Pathname.new(__dir__).parent.parent.expand_path.freeze CONFIG_DEFAULT = PROJECT_ROOT.join('config', 'default.yml').freeze CONFIG = YAML.safe_load(CONFIG_DEFAULT.read).freeze private_constant(:CONFIG_DEFAULT, :PROJECT_ROOT) end end rubocop-performance-1.5.2/lib/rubocop/performance/000077500000000000000000000000001360055361600222145ustar00rootroot00000000000000rubocop-performance-1.5.2/lib/rubocop/performance/inject.rb000066400000000000000000000011221360055361600240110ustar00rootroot00000000000000# frozen_string_literal: true module RuboCop module Performance # Because RuboCop doesn't yet support plugins, we have to monkey patch in a # bit of our configuration. module Inject def self.defaults! path = CONFIG_DEFAULT.to_s hash = ConfigLoader.send(:load_yaml_configuration, path) config = Config.new(hash, path) puts "configuration from #{path}" if ConfigLoader.debug? config = ConfigLoader.merge_with_default(config, path) ConfigLoader.instance_variable_set(:@default_configuration, config) end end end end rubocop-performance-1.5.2/lib/rubocop/performance/version.rb000066400000000000000000000001771360055361600242330ustar00rootroot00000000000000# frozen_string_literal: true module RuboCop module Performance module Version STRING = '1.5.2' end end end rubocop-performance-1.5.2/manual/000077500000000000000000000000001360055361600167515ustar00rootroot00000000000000rubocop-performance-1.5.2/manual/cops.md000066400000000000000000000036171360055361600202460ustar00rootroot00000000000000 #### Department [Performance](cops_performance.md) * [Performance/Caller](cops_performance.md#performancecaller) * [Performance/CaseWhenSplat](cops_performance.md#performancecasewhensplat) * [Performance/Casecmp](cops_performance.md#performancecasecmp) * [Performance/ChainArrayAllocation](cops_performance.md#performancechainarrayallocation) * [Performance/CompareWithBlock](cops_performance.md#performancecomparewithblock) * [Performance/Count](cops_performance.md#performancecount) * [Performance/Detect](cops_performance.md#performancedetect) * [Performance/DoubleStartEndWith](cops_performance.md#performancedoublestartendwith) * [Performance/EndWith](cops_performance.md#performanceendwith) * [Performance/FixedSize](cops_performance.md#performancefixedsize) * [Performance/FlatMap](cops_performance.md#performanceflatmap) * [Performance/InefficientHashSearch](cops_performance.md#performanceinefficienthashsearch) * [Performance/OpenStruct](cops_performance.md#performanceopenstruct) * [Performance/RangeInclude](cops_performance.md#performancerangeinclude) * [Performance/RedundantBlockCall](cops_performance.md#performanceredundantblockcall) * [Performance/RedundantMatch](cops_performance.md#performanceredundantmatch) * [Performance/RedundantMerge](cops_performance.md#performanceredundantmerge) * [Performance/RegexpMatch](cops_performance.md#performanceregexpmatch) * [Performance/ReverseEach](cops_performance.md#performancereverseeach) * [Performance/Size](cops_performance.md#performancesize) * [Performance/StartWith](cops_performance.md#performancestartwith) * [Performance/StringReplacement](cops_performance.md#performancestringreplacement) * [Performance/TimesMap](cops_performance.md#performancetimesmap) * [Performance/UnfreezeString](cops_performance.md#performanceunfreezestring) * [Performance/UriDefaultParser](cops_performance.md#performanceuridefaultparser) rubocop-performance-1.5.2/manual/cops_performance.md000066400000000000000000000470111360055361600226230ustar00rootroot00000000000000# Performance ## Performance/Caller Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged --- | --- | --- | --- | --- Enabled | Yes | No | 0.49 | - This cop identifies places where `caller[n]` can be replaced by `caller(n..n).first`. ### Examples ```ruby # bad caller[1] caller.first caller_locations[1] caller_locations.first # good caller(2..2).first caller(1..1).first caller_locations(2..2).first caller_locations(1..1).first ``` ## Performance/CaseWhenSplat Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged --- | --- | --- | --- | --- Disabled | Yes | Yes (Unsafe) | 0.34 | 0.59 Reordering `when` conditions with a splat to the end of the `when` branches can improve performance. Ruby has to allocate memory for the splat expansion every time that the `case` `when` statement is run. Since Ruby does not support fall through inside of `case` `when`, like some other languages do, the order of the `when` branches should not matter. By placing any splat expansions at the end of the list of `when` branches we will reduce the number of times that memory has to be allocated for the expansion. The exception to this is if multiple of your `when` conditions can be true for any given condition. A likely scenario for this defining a higher level when condition to override a condition that is inside of the splat expansion. This is not a guaranteed performance improvement. If the data being processed by the `case` condition is normalized in a manner that favors hitting a condition in the splat expansion, it is possible that moving the splat condition to the end will use more memory, and run slightly slower. ### Examples ```ruby # bad case foo when *condition bar when baz foobar end case foo when *[1, 2, 3, 4] bar when 5 baz end # good case foo when baz foobar when *condition bar end case foo when 1, 2, 3, 4 bar when 5 baz end ``` ### Configurable attributes Name | Default value | Configurable values --- | --- | --- AutoCorrect | `false` | Boolean ## Performance/Casecmp Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged --- | --- | --- | --- | --- Enabled | Yes | Yes | 0.36 | - This cop identifies places where a case-insensitive string comparison can better be implemented using `casecmp`. ### Examples ```ruby # bad str.downcase == 'abc' str.upcase.eql? 'ABC' 'abc' == str.downcase 'ABC'.eql? str.upcase str.downcase == str.downcase # good str.casecmp('ABC').zero? 'abc'.casecmp(str).zero? ``` ### References * [https://github.com/JuanitoFatas/fast-ruby#stringcasecmp-vs-stringdowncase---code](https://github.com/JuanitoFatas/fast-ruby#stringcasecmp-vs-stringdowncase---code) ## Performance/ChainArrayAllocation Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged --- | --- | --- | --- | --- Disabled | Yes | No | 0.59 | - This cop is used to identify usages of Each of these methods (`compact`, `flatten`, `map`) will generate a new intermediate array that is promptly thrown away. Instead it is faster to mutate when we know it's safe. ### Examples ```ruby # bad array = ["a", "b", "c"] array.compact.flatten.map { |x| x.downcase } ``` ```ruby # good. array = ["a", "b", "c"] array.compact! array.flatten! array.map! { |x| x.downcase } array ``` ### References * [https://twitter.com/schneems/status/1034123879978029057](https://twitter.com/schneems/status/1034123879978029057) ## Performance/CompareWithBlock Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged --- | --- | --- | --- | --- Enabled | Yes | Yes | 0.46 | - This cop identifies places where `sort { |a, b| a.foo <=> b.foo }` can be replaced by `sort_by(&:foo)`. This cop also checks `max` and `min` methods. ### Examples ```ruby # bad array.sort { |a, b| a.foo <=> b.foo } array.max { |a, b| a.foo <=> b.foo } array.min { |a, b| a.foo <=> b.foo } array.sort { |a, b| a[:foo] <=> b[:foo] } # good array.sort_by(&:foo) array.sort_by { |v| v.foo } array.sort_by do |var| var.foo end array.max_by(&:foo) array.min_by(&:foo) array.sort_by { |a| a[:foo] } ``` ## Performance/Count Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged --- | --- | --- | --- | --- Enabled | Yes | Yes (Unsafe) | 0.31 | 1.5 This cop is used to identify usages of `count` on an `Enumerable` that follow calls to `select` or `reject`. Querying logic can instead be passed to the `count` call. `ActiveRecord` compatibility: `ActiveRecord` will ignore the block that is passed to `count`. Other methods, such as `select`, will convert the association to an array and then run the block on the array. A simple work around to make `count` work with a block is to call `to_a.count {...}`. Example: `Model.where(id: [1, 2, 3]).select { |m| m.method == true }.size` becomes: `Model.where(id: [1, 2, 3]).to_a.count { |m| m.method == true }` ### Examples ```ruby # bad [1, 2, 3].select { |e| e > 2 }.size [1, 2, 3].reject { |e| e > 2 }.size [1, 2, 3].select { |e| e > 2 }.length [1, 2, 3].reject { |e| e > 2 }.length [1, 2, 3].select { |e| e > 2 }.count { |e| e.odd? } [1, 2, 3].reject { |e| e > 2 }.count { |e| e.even? } array.select(&:value).count # good [1, 2, 3].count { |e| e > 2 } [1, 2, 3].count { |e| e < 2 } [1, 2, 3].count { |e| e > 2 && e.odd? } [1, 2, 3].count { |e| e < 2 && e.even? } Model.select('field AS field_one').count Model.select(:value).count ``` ## Performance/Detect Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged --- | --- | --- | --- | --- Enabled | Yes | Yes (Unsafe) | 0.30 | 1.5 This cop is used to identify usages of `select.first`, `select.last`, `find_all.first`, and `find_all.last` and change them to use `detect` instead. `ActiveRecord` compatibility: `ActiveRecord` does not implement a `detect` method and `find` has its own meaning. Correcting ActiveRecord methods with this cop should be considered unsafe. ### Examples ```ruby # bad [].select { |item| true }.first [].select { |item| true }.last [].find_all { |item| true }.first [].find_all { |item| true }.last # good [].detect { |item| true } [].reverse.detect { |item| true } ``` ### References * [https://github.com/JuanitoFatas/fast-ruby#enumerabledetect-vs-enumerableselectfirst-code](https://github.com/JuanitoFatas/fast-ruby#enumerabledetect-vs-enumerableselectfirst-code) ## Performance/DoubleStartEndWith Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged --- | --- | --- | --- | --- Enabled | Yes | Yes | 0.36 | 0.48 This cop checks for double `#start_with?` or `#end_with?` calls separated by `||`. In some cases such calls can be replaced with an single `#start_with?`/`#end_with?` call. ### Examples ```ruby # bad str.start_with?("a") || str.start_with?(Some::CONST) str.start_with?("a", "b") || str.start_with?("c") str.end_with?(var1) || str.end_with?(var2) # good str.start_with?("a", Some::CONST) str.start_with?("a", "b", "c") str.end_with?(var1, var2) ``` ### Configurable attributes Name | Default value | Configurable values --- | --- | --- IncludeActiveSupportAliases | `false` | Boolean ## Performance/EndWith Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged --- | --- | --- | --- | --- Enabled | Yes | Yes (Unsafe) | 0.36 | 0.44 This cop identifies unnecessary use of a regex where `String#end_with?` would suffice. ### Examples ```ruby # bad 'abc'.match?(/bc\Z/) /bc\Z/.match?('abc') 'abc' =~ /bc\Z/ /bc\Z/ =~ 'abc' 'abc'.match(/bc\Z/) /bc\Z/.match('abc') # good 'abc'.end_with?('bc') ``` ### Configurable attributes Name | Default value | Configurable values --- | --- | --- AutoCorrect | `false` | Boolean ### References * [https://github.com/JuanitoFatas/fast-ruby#stringmatch-vs-stringstart_withstringend_with-code-start-code-end](https://github.com/JuanitoFatas/fast-ruby#stringmatch-vs-stringstart_withstringend_with-code-start-code-end) ## Performance/FixedSize Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged --- | --- | --- | --- | --- Enabled | Yes | No | 0.35 | - Do not compute the size of statically sized objects. ### Examples ```ruby # String methods # bad 'foo'.size %q[bar].count %(qux).length # Symbol methods # bad :fred.size :'baz'.length # Array methods # bad [1, 2, thud].count %W(1, 2, bar).size # Hash methods # bad { a: corge, b: grault }.length # good foo.size bar.count qux.length # good :"#{fred}".size CONST = :baz.length # good [1, 2, *thud].count garply = [1, 2, 3] garply.size # good { a: corge, **grault }.length waldo = { a: corge, b: grault } waldo.size ``` ## Performance/FlatMap Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged --- | --- | --- | --- | --- Enabled | Yes | Yes | 0.30 | - This cop is used to identify usages of ### Examples ```ruby # bad [1, 2, 3, 4].map { |e| [e, e] }.flatten(1) [1, 2, 3, 4].collect { |e| [e, e] }.flatten(1) # good [1, 2, 3, 4].flat_map { |e| [e, e] } [1, 2, 3, 4].map { |e| [e, e] }.flatten [1, 2, 3, 4].collect { |e| [e, e] }.flatten ``` ### Configurable attributes Name | Default value | Configurable values --- | --- | --- EnabledForFlattenWithoutParams | `false` | Boolean ### References * [https://github.com/JuanitoFatas/fast-ruby#enumerablemaparrayflatten-vs-enumerableflat_map-code](https://github.com/JuanitoFatas/fast-ruby#enumerablemaparrayflatten-vs-enumerableflat_map-code) ## Performance/InefficientHashSearch Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged --- | --- | --- | --- | --- Enabled | No | Yes | 0.56 | - This cop checks for inefficient searching of keys and values within hashes. `Hash#keys.include?` is less efficient than `Hash#key?` because the former allocates a new array and then performs an O(n) search through that array, while `Hash#key?` does not allocate any array and performs a faster O(1) search for the key. `Hash#values.include?` is less efficient than `Hash#value?`. While they both perform an O(n) search through all of the values, calling `values` allocates a new array while using `value?` does not. ### Examples ```ruby # bad { a: 1, b: 2 }.keys.include?(:a) { a: 1, b: 2 }.keys.include?(:z) h = { a: 1, b: 2 }; h.keys.include?(100) # good { a: 1, b: 2 }.key?(:a) { a: 1, b: 2 }.has_key?(:z) h = { a: 1, b: 2 }; h.key?(100) # bad { a: 1, b: 2 }.values.include?(2) { a: 1, b: 2 }.values.include?('garbage') h = { a: 1, b: 2 }; h.values.include?(nil) # good { a: 1, b: 2 }.value?(2) { a: 1, b: 2 }.has_value?('garbage') h = { a: 1, b: 2 }; h.value?(nil) ``` ### References * [https://github.com/JuanitoFatas/fast-ruby#hashkey-instead-of-hashkeysinclude-code](https://github.com/JuanitoFatas/fast-ruby#hashkey-instead-of-hashkeysinclude-code) ## Performance/OpenStruct Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged --- | --- | --- | --- | --- Disabled | No | No | 0.61 | - This cop checks for `OpenStruct.new` calls. Instantiation of an `OpenStruct` invalidates Ruby global method cache as it causes dynamic method definition during program runtime. This could have an effect on performance, especially in case of single-threaded applications with multiple `OpenStruct` instantiations. ### Examples ```ruby # bad class MyClass def my_method OpenStruct.new(my_key1: 'my_value1', my_key2: 'my_value2') end end # good class MyClass MyStruct = Struct.new(:my_key1, :my_key2) def my_method MyStruct.new('my_value1', 'my_value2') end end ``` ## Performance/RangeInclude Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged --- | --- | --- | --- | --- Enabled | No | Yes | 0.36 | - This cop identifies uses of `Range#include?`, which iterates over each item in a `Range` to see if a specified item is there. In contrast, `Range#cover?` simply compares the target item with the beginning and end points of the `Range`. In a great majority of cases, this is what is wanted. This cop is `Safe: false` by default because `Range#include?` and `Range#cover?` are not equivalent behaviour. ### Examples ```ruby # bad ('a'..'z').include?('b') # => true # good ('a'..'z').cover?('b') # => true # Example of a case where `Range#cover?` may not provide # the desired result: ('a'..'z').cover?('yellow') # => true ``` ### References * [https://github.com/JuanitoFatas/fast-ruby#cover-vs-include-code](https://github.com/JuanitoFatas/fast-ruby#cover-vs-include-code) ## Performance/RedundantBlockCall Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged --- | --- | --- | --- | --- Enabled | Yes | Yes | 0.36 | - This cop identifies the use of a `&block` parameter and `block.call` where `yield` would do just as well. ### Examples ```ruby # bad def method(&block) block.call end def another(&func) func.call 1, 2, 3 end # good def method yield end def another yield 1, 2, 3 end ``` ### References * [https://github.com/JuanitoFatas/fast-ruby#proccall-and-block-arguments-vs-yieldcode](https://github.com/JuanitoFatas/fast-ruby#proccall-and-block-arguments-vs-yieldcode) ## Performance/RedundantMatch Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged --- | --- | --- | --- | --- Enabled | Yes | Yes | 0.36 | - This cop identifies the use of `Regexp#match` or `String#match`, which returns `#`/`nil`. The return value of `=~` is an integral index/`nil` and is more performant. ### Examples ```ruby # bad do_something if str.match(/regex/) while regex.match('str') do_something end # good method(str =~ /regex/) return value unless regex =~ 'str' ``` ## Performance/RedundantMerge Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged --- | --- | --- | --- | --- Enabled | Yes | Yes | 0.36 | - This cop identifies places where `Hash#merge!` can be replaced by `Hash#[]=`. ### Examples ```ruby hash.merge!(a: 1) hash.merge!({'key' => 'value'}) hash.merge!(a: 1, b: 2) ``` ### Configurable attributes Name | Default value | Configurable values --- | --- | --- MaxKeyValuePairs | `2` | Integer ### References * [https://github.com/JuanitoFatas/fast-ruby#hashmerge-vs-hash-code](https://github.com/JuanitoFatas/fast-ruby#hashmerge-vs-hash-code) ## Performance/RegexpMatch Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged --- | --- | --- | --- | --- Enabled | Yes | Yes | 0.47 | - In Ruby 2.4, `String#match?`, `Regexp#match?`, and `Symbol#match?` have been added. The methods are faster than `match`. Because the methods avoid creating a `MatchData` object or saving backref. So, when `MatchData` is not used, use `match?` instead of `match`. ### Examples ```ruby # bad def foo if x =~ /re/ do_something end end # bad def foo if x !~ /re/ do_something end end # bad def foo if x.match(/re/) do_something end end # bad def foo if /re/ === x do_something end end # good def foo if x.match?(/re/) do_something end end # good def foo if !x.match?(/re/) do_something end end # good def foo if x =~ /re/ do_something(Regexp.last_match) end end # good def foo if x.match(/re/) do_something($~) end end # good def foo if /re/ === x do_something($~) end end ``` ### References * [https://github.com/JuanitoFatas/fast-ruby#regexp-vs-stringmatch-vs-string-vs-stringmatch-code-](https://github.com/JuanitoFatas/fast-ruby#regexp-vs-stringmatch-vs-string-vs-stringmatch-code-) ## Performance/ReverseEach Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged --- | --- | --- | --- | --- Enabled | Yes | Yes | 0.30 | - This cop is used to identify usages of `reverse.each` and change them to use `reverse_each` instead. ### Examples ```ruby # bad [].reverse.each # good [].reverse_each ``` ### References * [https://github.com/JuanitoFatas/fast-ruby#enumerablereverseeach-vs-enumerablereverse_each-code](https://github.com/JuanitoFatas/fast-ruby#enumerablereverseeach-vs-enumerablereverse_each-code) ## Performance/Size Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged --- | --- | --- | --- | --- Enabled | Yes | Yes | 0.30 | - This cop is used to identify usages of `count` on an `Array` and `Hash` and change them to `size`. TODO: Add advanced detection of variables that could have been assigned to an array or a hash. ### Examples ```ruby # bad [1, 2, 3].count # bad {a: 1, b: 2, c: 3}.count # good [1, 2, 3].size # good {a: 1, b: 2, c: 3}.size # good [1, 2, 3].count { |e| e > 2 } ``` ### References * [https://github.com/JuanitoFatas/fast-ruby#arraylength-vs-arraysize-vs-arraycount-code](https://github.com/JuanitoFatas/fast-ruby#arraylength-vs-arraysize-vs-arraycount-code) ## Performance/StartWith Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged --- | --- | --- | --- | --- Enabled | Yes | Yes (Unsafe) | 0.36 | 0.44 This cop identifies unnecessary use of a regex where `String#start_with?` would suffice. ### Examples ```ruby # bad 'abc'.match?(/\Aab/) /\Aab/.match?('abc') 'abc' =~ /\Aab/ /\Aab/ =~ 'abc' 'abc'.match(/\Aab/) /\Aab/.match('abc') # good 'abc'.start_with?('ab') ``` ### Configurable attributes Name | Default value | Configurable values --- | --- | --- AutoCorrect | `false` | Boolean ### References * [https://github.com/JuanitoFatas/fast-ruby#stringmatch-vs-stringstart_withstringend_with-code-start-code-end](https://github.com/JuanitoFatas/fast-ruby#stringmatch-vs-stringstart_withstringend_with-code-start-code-end) ## Performance/StringReplacement Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged --- | --- | --- | --- | --- Enabled | Yes | Yes | 0.33 | - This cop identifies places where `gsub` can be replaced by `tr` or `delete`. ### Examples ```ruby # bad 'abc'.gsub('b', 'd') 'abc'.gsub('a', '') 'abc'.gsub(/a/, 'd') 'abc'.gsub!('a', 'd') # good 'abc'.gsub(/.*/, 'a') 'abc'.gsub(/a+/, 'd') 'abc'.tr('b', 'd') 'a b c'.delete(' ') ``` ### References * [https://github.com/JuanitoFatas/fast-ruby#stringgsub-vs-stringtr-code](https://github.com/JuanitoFatas/fast-ruby#stringgsub-vs-stringtr-code) ## Performance/TimesMap Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged --- | --- | --- | --- | --- Enabled | Yes | Yes (Unsafe) | 0.36 | 0.50 This cop checks for .times.map calls. In most cases such calls can be replaced with an explicit array creation. ### Examples ```ruby # bad 9.times.map do |i| i.to_s end # good Array.new(9) do |i| i.to_s end ``` ### Configurable attributes Name | Default value | Configurable values --- | --- | --- AutoCorrect | `false` | Boolean ## Performance/UnfreezeString Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged --- | --- | --- | --- | --- Enabled | Yes | No | 0.50 | - In Ruby 2.3 or later, use unary plus operator to unfreeze a string literal instead of `String#dup` and `String.new`. Unary plus operator is faster than `String#dup`. Note: `String.new` (without operator) is not exactly the same as `+''`. These differ in encoding. `String.new.encoding` is always `ASCII-8BIT`. However, `(+'').encoding` is the same as script encoding(e.g. `UTF-8`). So, if you expect `ASCII-8BIT` encoding, disable this cop. ### Examples ```ruby # bad ''.dup "something".dup String.new String.new('') String.new('something') # good +'something' +'' ``` ## Performance/UriDefaultParser Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged --- | --- | --- | --- | --- Enabled | Yes | Yes | 0.50 | - This cop identifies places where `URI::Parser.new` can be replaced by `URI::DEFAULT_PARSER`. ### Examples ```ruby # bad URI::Parser.new # good URI::DEFAULT_PARSER ``` rubocop-performance-1.5.2/manual/index.md000066400000000000000000000001721360055361600204020ustar00rootroot00000000000000Performance optimization analysis for your projects, as an extension to [RuboCop](https://github.com/rubocop-hq/rubocop). rubocop-performance-1.5.2/manual/installation.md000066400000000000000000000002571360055361600220000ustar00rootroot00000000000000Just install the `rubocop-performance` gem ```sh gem install rubocop-performance ``` or if you use bundler put this in your `Gemfile` ```ruby gem 'rubocop-performance' ``` rubocop-performance-1.5.2/manual/usage.md000066400000000000000000000007571360055361600204100ustar00rootroot00000000000000You need to tell RuboCop to load the Performance extension. There are three ways to do this: ### RuboCop configuration file Put this into your `.rubocop.yml`. ```yaml require: rubocop-performance ``` Now you can run `rubocop` and it will automatically load the RuboCop Performance cops together with the standard cops. ### Command line ```sh rubocop --require rubocop-performance ``` ### Rake task ```ruby RuboCop::RakeTask.new do |task| task.requires << 'rubocop-performance' end ``` rubocop-performance-1.5.2/mkdocs.yml000066400000000000000000000007111360055361600174760ustar00rootroot00000000000000site_name: "Performance optimization analysis for your projects, as an extension to RuboCop." repo_url: https://github.com/rubocop-hq/rubocop-performance edit_uri: edit/master/manual/ copyright: "Copyright © 2012-2019 Bozhidar Batsov and RuboCop contributors" docs_dir: manual pages: - Home: index.md - Installation: installation.md - Usage: usage.md - Cops: cops.md - Cops Documentation: - Performance Cops: cops_performance.md theme: readthedocs rubocop-performance-1.5.2/readthedocs.yml000066400000000000000000000000611360055361600205010ustar00rootroot00000000000000build: image: stable formats: - htmlzip rubocop-performance-1.5.2/relnotes/000077500000000000000000000000001360055361600173275ustar00rootroot00000000000000rubocop-performance-1.5.2/relnotes/v1.0.0.md000066400000000000000000000003221360055361600204700ustar00rootroot00000000000000### New features * Extract performance cops from rubocop-hq/rubocop repository. ([@composerinteralia][], [@koic][]) [@composerinteralia]: https://github.com/composerinteralia [@koic]: https://github.com/koic rubocop-performance-1.5.2/relnotes/v1.1.0.md000066400000000000000000000005261360055361600204770ustar00rootroot00000000000000### Changes * [#39](https://github.com/rubocop-hq/rubocop-performance/pull/39): Remove `Performance/LstripRstrip` cop. ([@koic][]) * [#39](https://github.com/rubocop-hq/rubocop-performance/pull/39): Remove `Performance/RedundantSortBy`, `Performance/UnneededSort` and `Performance/Sample` cops. ([@koic][]) [@koic]: https://github.com/koic rubocop-performance-1.5.2/relnotes/v1.2.0.md000066400000000000000000000003271360055361600204770ustar00rootroot00000000000000### Bug fixes * [#47](https://github.com/rubocop-hq/rubocop-performance/pull/47): Fix a false negative for `Performance/RegexpMatch` when using RuboCop 0.68 or higher. ([@koic][]) [@koic]: https://github.com/koic rubocop-performance-1.5.2/relnotes/v1.3.0.md000066400000000000000000000003731360055361600205010ustar00rootroot00000000000000### Bug fixes * [#48](https://github.com/rubocop-hq/rubocop-performance/issues/48): Reduce `Performance/RegexpMatch` false positive by only flagging `match` used with Regexp/String/Symbol literals. ([@dduugg][]) [@dduugg]: https://github.com/dduugg rubocop-performance-1.5.2/relnotes/v1.4.0.md000066400000000000000000000006471360055361600205060ustar00rootroot00000000000000### Bug fixes * [#54](https://github.com/rubocop-hq/rubocop-performance/issues/54): Fix `Performance/FixedSize` to accept const assign with some operation. ([@tejasbubane][]) * [#61](https://github.com/rubocop-hq/rubocop-performance/pull/61): Fix a false negative for `Performance/RegexpMatch` when using RuboCop 0.71 or higher. ([@koic][]) [@tejasbubane]: https://github.com/tejasbubane [@koic]: https://github.com/koic rubocop-performance-1.5.2/relnotes/v1.4.1.md000066400000000000000000000006511360055361600205020ustar00rootroot00000000000000### Bug fixes * [#67](https://github.com/rubocop-hq/rubocop-performance/issues/67): Fix an error for `Performance/RedundantMerge` when `MaxKeyValuePairs` option is set to `null`. ([@koic][]) * [#73](https://github.com/rubocop-hq/rubocop-performance/pull/73): Fix a false negative for `Performance/RegexpMatch` when `MatchData` is not detected in `if` branch of guard condition. ([@koic][]) [@koic]: https://github.com/koic rubocop-performance-1.5.2/relnotes/v1.5.0.md000066400000000000000000000007641360055361600205070ustar00rootroot00000000000000### Bug fixes * [#74](https://github.com/rubocop-hq/rubocop-performance/pull/74): Fix an error for `Performance/RedundantMerge` when `MaxKeyValuePairs` option is set to `null`. ([@koic][]) ### Changes * [#69](https://github.com/rubocop-hq/rubocop-performance/issues/69): Remove `SafeMode` from `Performance/Count` and `Performance/Detect`. Set `SafeAutoCorrect` to `false` for these cops by default. ([@rrosenblum][]) [@koic]: https://github.com/koic [@rrosenblum]: https://github.com/rrosenblum rubocop-performance-1.5.2/relnotes/v1.5.1.md000066400000000000000000000003571360055361600205060ustar00rootroot00000000000000### Bug fixes * [#82](https://github.com/rubocop-hq/rubocop-performance/pull/82): Let `Performance/StartWith` and `Performance/EndWith` correct `Regexp#match?` and `Regexp#=~`. ([@eugeneius][]) [@eugeneius]: https://github.com/eugeneius rubocop-performance-1.5.2/relnotes/v1.5.2.md000066400000000000000000000003441360055361600205030ustar00rootroot00000000000000### Bug fixes * [#86](https://github.com/rubocop-hq/rubocop-performance/issues/86): Fix an incorrect autocorrect for `Performance/RedundantMerge` when using an empty hash argument. ([@koic][]) [@koic]: https://github.com/koic rubocop-performance-1.5.2/rubocop-performance.gemspec000066400000000000000000000025331360055361600230140ustar00rootroot00000000000000# frozen_string_literal: true $LOAD_PATH.unshift File.expand_path('lib', __dir__) require 'rubocop/performance/version' Gem::Specification.new do |s| s.name = 'rubocop-performance' s.version = RuboCop::Performance::Version::STRING s.platform = Gem::Platform::RUBY s.required_ruby_version = '>= 2.3.0' s.authors = ['Bozhidar Batsov', 'Jonas Arvidsson', 'Yuji Nakayama'] s.description = <<-DESCRIPTION A collection of RuboCop cops to check for performance optimizations in Ruby code. DESCRIPTION s.email = 'rubocop@googlegroups.com' s.files = `git ls-files config lib LICENSE.txt README.md`.split($RS) s.extra_rdoc_files = ['LICENSE.txt', 'README.md'] s.homepage = 'https://github.com/rubocop-hq/rubocop-performance' s.licenses = ['MIT'] s.summary = 'Automatic performance checking tool for Ruby code.' s.metadata = { 'homepage_uri' => 'https://docs.rubocop.org/projects/performance', 'changelog_uri' => 'https://github.com/rubocop-hq/rubocop-performance/blob/master/CHANGELOG.md', 'source_code_uri' => 'https://github.com/rubocop-hq/rubocop-performance/', 'documentation_uri' => 'https://docs.rubocop.org/projects/performance', 'bug_tracker_uri' => 'https://github.com/rubocop-hq/rubocop-performance/issues' } s.add_runtime_dependency('rubocop', '>= 0.71.0') s.add_development_dependency('simplecov') end rubocop-performance-1.5.2/spec/000077500000000000000000000000001360055361600164265ustar00rootroot00000000000000rubocop-performance-1.5.2/spec/project_spec.rb000066400000000000000000000063641360055361600214440ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe 'RuboCop Performance Project', type: :feature do describe 'changelog' do subject(:changelog) do path = File.join(File.dirname(__FILE__), '..', 'CHANGELOG.md') File.read(path) end let(:lines) { changelog.each_line } let(:non_reference_lines) do lines.take_while { |line| !line.start_with?('[@') } end it 'has newline at end of file' do expect(changelog.end_with?("\n")).to be true end it 'has either entries, headers, or empty lines' do expect(non_reference_lines).to all(match(/^(\*|#|$)/)) end it 'has link definitions for all implicit links' do implicit_link_names = changelog.scan(/\[([^\]]+)\]\[\]/).flatten.uniq implicit_link_names.each do |name| expect(changelog.include?("[#{name}]: http")) .to be(true), "CHANGELOG.md is missing a link for #{name}. " \ 'Please add this link to the bottom of the file.' end end describe 'entry' do subject(:entries) { lines.grep(/^\*/).map(&:chomp) } it 'has a whitespace between the * and the body' do expect(entries).to all(match(/^\* \S/)) end context 'after version 0.14.0' do let(:lines) do changelog.each_line.take_while do |line| !line.start_with?('## 0.14.0') end end it 'has a link to the contributors at the end' do expect(entries).to all(match(/\(\[@\S+\]\[\](?:, \[@\S+\]\[\])*\)$/)) end end describe 'link to related issue' do let(:issues) do entries.map do |entry| entry.match(/\[(?[#\d]+)\]\((?[^\)]+)\)/) end.compact end it 'has an issue number prefixed with #' do issues.each do |issue| expect(issue[:number]).to match(/^#\d+$/) end end it 'has a valid URL' do issues.each do |issue| number = issue[:number].gsub(/\D/, '') pattern = %r{^https://github\.com/rubocop-hq/rubocop-performance/(?:issues|pull)/#{number}$} # rubocop:disable Layout/LineLength expect(issue[:url]).to match(pattern) end end it 'has a colon and a whitespace at the end' do entries_including_issue_link = entries.select do |entry| entry.match(/^\*\s*\[/) end expect(entries_including_issue_link).to all(include('): ')) end end describe 'contributor name' do subject(:contributor_names) { lines.grep(/\A\[@/).map(&:chomp) } it 'has a unique contributor name' do expect(contributor_names.uniq.size).to eq contributor_names.size end end describe 'body' do let(:bodies) do entries.map do |entry| entry .gsub(/`[^`]+`/, '``') .sub(/^\*\s*(?:\[.+?\):\s*)?/, '') .sub(/\s*\([^\)]+\)$/, '') end end it 'does not start with a lower case' do bodies.each do |body| expect(body).not_to match(/^[a-z]/) end end it 'ends with a punctuation' do expect(bodies).to all(match(/[\.\!]$/)) end end end end end rubocop-performance-1.5.2/spec/rubocop/000077500000000000000000000000001360055361600200775ustar00rootroot00000000000000rubocop-performance-1.5.2/spec/rubocop/cop/000077500000000000000000000000001360055361600206605ustar00rootroot00000000000000rubocop-performance-1.5.2/spec/rubocop/cop/performance/000077500000000000000000000000001360055361600231615ustar00rootroot00000000000000rubocop-performance-1.5.2/spec/rubocop/cop/performance/caller_spec.rb000066400000000000000000000050511360055361600257630ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe RuboCop::Cop::Performance::Caller do subject(:cop) { described_class.new } it 'accepts `caller` without argument and method chain' do expect_no_offenses('caller') end it 'accepts `caller` with arguments' do expect_no_offenses('caller(1, 1).first') end it 'accepts `caller_locations` without argument and method chain' do expect_no_offenses('caller_locations') end it 'registers an offense when :first is called on caller' do expect(caller.first).to eq(caller(1..1).first) expect_offense(<<~RUBY) caller.first ^^^^^^^^^^^^ Use `caller(1..1).first` instead of `caller.first`. RUBY end it 'registers an offense when :first is called on caller with 1' do expect(caller(1).first).to eq(caller(1..1).first) expect_offense(<<~RUBY) caller(1).first ^^^^^^^^^^^^^^^ Use `caller(1..1).first` instead of `caller.first`. RUBY end it 'registers an offense when :first is called on caller with 2' do expect(caller(2).first).to eq(caller(2..2).first) expect_offense(<<~RUBY) caller(2).first ^^^^^^^^^^^^^^^ Use `caller(2..2).first` instead of `caller.first`. RUBY end it 'registers an offense when :[] is called on caller' do expect(caller[1]).to eq(caller(2..2).first) expect_offense(<<~RUBY) caller[1] ^^^^^^^^^ Use `caller(2..2).first` instead of `caller[1]`. RUBY end it 'registers an offense when :[] is called on caller with 1' do expect(caller(1)[1]).to eq(caller(2..2).first) expect_offense(<<~RUBY) caller(1)[1] ^^^^^^^^^^^^ Use `caller(2..2).first` instead of `caller[1]`. RUBY end it 'registers an offense when :[] is called on caller with 2' do expect(caller(2)[1]).to eq(caller(3..3).first) expect_offense(<<~RUBY) caller(2)[1] ^^^^^^^^^^^^ Use `caller(3..3).first` instead of `caller[1]`. RUBY end it 'registers an offense when :first is called on caller_locations also' do expect(caller_locations.first.to_s).to eq(caller_locations(1..1).first.to_s) expect_offense(<<~RUBY) caller_locations.first ^^^^^^^^^^^^^^^^^^^^^^ Use `caller_locations(1..1).first` instead of `caller_locations.first`. RUBY end it 'registers an offense when :[] is called on caller_locations also' do expect(caller_locations[1].to_s).to eq(caller_locations(2..2).first.to_s) expect_offense(<<~RUBY) caller_locations[1] ^^^^^^^^^^^^^^^^^^^ Use `caller_locations(2..2).first` instead of `caller_locations[1]`. RUBY end end rubocop-performance-1.5.2/spec/rubocop/cop/performance/case_when_splat_spec.rb000066400000000000000000000231761360055361600276700ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe RuboCop::Cop::Performance::CaseWhenSplat do subject(:cop) { described_class.new } it 'allows case when without splat' do expect_no_offenses(<<~RUBY) case foo when 1 bar else baz end RUBY end it 'allows splat on a variable in the last when condition' do expect_no_offenses(<<~RUBY) case foo when 4 foobar when *cond bar else baz end RUBY end it 'allows multiple splat conditions on variables at the end' do expect_no_offenses(<<~RUBY) case foo when 4 foobar when *cond1 bar when *cond2 doo else baz end RUBY end it 'registers an offense for case when with a splat in the first condition' do expect_offense(<<~RUBY) case foo when *cond ^^^^^^^^^^ Reordering `when` conditions with a splat to the end of the `when` branches can improve performance. bar when 4 foobar else baz end RUBY end it 'registers an offense for case when with a splat without an else' do expect_offense(<<~RUBY) case foo when *baz ^^^^^^^^^ Reordering `when` conditions with a splat to the end of the `when` branches can improve performance. bar when 4 foobar end RUBY end it 'registers an offense for splat conditions in when then' do expect_offense(<<~RUBY) case foo when *cond then bar ^^^^^^^^^^ Reordering `when` conditions with a splat to the end of the `when` branches can improve performance. when 4 then baz end RUBY end it 'registers an offense for a single when with splat expansion followed ' \ 'by another value' do expect_offense(<<~RUBY) case foo when *Foo, Bar ^^^^^^^^^ Reordering `when` conditions with a splat to the end of the `when` branches can improve performance. nil end RUBY end it 'registers an offense for multiple splat conditions at the beginning' do expect_offense(<<~RUBY) case foo when *cond1 ^^^^^^^^^^^ Reordering `when` conditions with a splat to the end of the `when` branches can improve performance. bar when *cond2 ^^^^^^^^^^^ Reordering `when` conditions with a splat to the end of the `when` branches can improve performance. doo when 4 foobar else baz end RUBY end it 'registers an offense for multiple out of order splat conditions' do expect_offense(<<~RUBY) case foo when *cond1 ^^^^^^^^^^^ Reordering `when` conditions with a splat to the end of the `when` branches can improve performance. bar when 8 barfoo when *SOME_CONSTANT ^^^^^^^^^^^^^^^^^^^ Reordering `when` conditions with a splat to the end of the `when` branches can improve performance. doo when 4 foobar else baz end RUBY end it 'registers an offense for splat condition that do not appear at the end' do expect_offense(<<~RUBY) case foo when *cond1 ^^^^^^^^^^^ Reordering `when` conditions with a splat to the end of the `when` branches can improve performance. bar when 8 barfoo when *cond2 ^^^^^^^^^^^ Reordering `when` conditions with a splat to the end of the `when` branches can improve performance. doo when 4 foobar when *cond3 doofoo else baz end RUBY end it 'allows splat expansion on an array literal' do expect_no_offenses(<<~RUBY) case foo when *[1, 2] bar when *[3, 4] bar when 5 baz end RUBY end it 'allows splat expansion on array literal as the last condition' do expect_no_offenses(<<~RUBY) case foo when *[1, 2] bar end RUBY end it 'registers an offense for a splat on a variable that proceeds a splat ' \ 'on an array literal as the last condition' do expect_offense(<<~RUBY) case foo when *cond ^^^^^^^^^^ Reordering `when` conditions with a splat to the end of the `when` branches can improve performance. bar when *[1, 2] baz end RUBY end it 'registers an offense when splat is part of the condition' do expect_offense(<<~RUBY) case foo when cond1, *cond2 ^^^^^^^^^^^^^^^^^^ Reordering `when` conditions with a splat to the end of the `when` branches can improve performance. bar when cond3 baz end RUBY end context 'autocorrect' do it 'corrects a single when with splat expansion followed by ' \ 'another value' do source = <<~RUBY case foo when *Foo, Bar, Baz nil end RUBY new_source = autocorrect_source(source) expect(new_source).to eq(<<~RUBY) case foo when Bar, Baz, *Foo nil end RUBY end it 'corrects a when with splat expansion followed by another value ' \ 'when there are multiple whens' do source = <<~RUBY case foo when *Foo, Bar nil when FooBar 1 end RUBY new_source = autocorrect_source(source) expect(new_source).to eq(<<~RUBY) case foo when FooBar 1 when Bar, *Foo nil end RUBY end it 'corrects a when with multiple out of order splat expansions ' \ 'followed by other values when there are multiple whens' do source = <<~RUBY case foo when *Foo, Bar, *Baz, Qux nil when FooBar 1 end RUBY new_source = autocorrect_source(source) expect(new_source).to eq(<<~RUBY) case foo when FooBar 1 when Bar, Qux, *Foo, *Baz nil end RUBY end it 'moves a single splat condition to the end of the when conditions' do new_source = autocorrect_source(<<~RUBY) case foo when *cond bar when 3 baz end RUBY expect(new_source).to eq(<<~RUBY) case foo when 3 baz when *cond bar end RUBY end it 'moves multiple splat condition to the end of the when conditions' do new_source = autocorrect_source_with_loop(<<~RUBY) case foo when *cond1 bar when *cond2 foobar when 5 baz end RUBY expect(new_source).to eq(<<~RUBY) case foo when 5 baz when *cond1 bar when *cond2 foobar end RUBY end it 'moves multiple out of order splat condition to the end ' \ 'of the when conditions' do new_source = autocorrect_source_with_loop(<<~RUBY) case foo when *cond1 bar when 3 doo when *cond2 foobar when 6 baz end RUBY expect(new_source).to eq(<<~RUBY) case foo when 3 doo when 6 baz when *cond1 bar when *cond2 foobar end RUBY end it 'corrects splat condition when using when then' do new_source = autocorrect_source(<<~RUBY) case foo when *cond then bar when 4 then baz end RUBY expect(new_source).to eq(<<~RUBY) case foo when 4 then baz when *cond then bar end RUBY end it 'corrects nested case when statements' do new_source = autocorrect_source(<<~RUBY) def check case foo when *cond bar when 3 baz end end RUBY expect(new_source).to eq(<<~RUBY) def check case foo when 3 baz when *cond bar end end RUBY end it 'corrects splat on a variable and leaves an array literal alone' do new_source = autocorrect_source(<<~RUBY) case foo when *cond bar when *[1, 2] baz end RUBY expect(new_source).to eq(<<~RUBY) case foo when *[1, 2] baz when *cond bar end RUBY end it 'corrects a splat as part of the condition' do new_source = autocorrect_source(<<~RUBY) case foo when cond1, *cond2 bar when cond3 baz end RUBY expect(new_source).to eq(<<~RUBY) case foo when cond3 baz when cond1, *cond2 bar end RUBY end it 'corrects an array followed by splat in the same condition' do new_source = autocorrect_source(<<~RUBY) case foo when *[cond1, cond2], *cond3 bar when cond4 baz end RUBY expect(new_source).to eq(<<~RUBY) case foo when cond4 baz when *[cond1, cond2], *cond3 bar end RUBY end it 'corrects a splat followed by array in the same condition' do new_source = autocorrect_source(<<~RUBY) case foo when *cond1, *[cond2, cond3] bar when cond4 baz end RUBY expect(new_source).to eq(<<~RUBY) case foo when cond4 baz when *cond1, *[cond2, cond3] bar end RUBY end end end rubocop-performance-1.5.2/spec/rubocop/cop/performance/casecmp_spec.rb000066400000000000000000000117271360055361600261430ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe RuboCop::Cop::Performance::Casecmp do subject(:cop) { described_class.new } shared_examples 'selectors' do |selector| it "autocorrects str.#{selector} ==" do new_source = autocorrect_source("str.#{selector} == 'string'") expect(new_source).to eq "str.casecmp('string').zero?" end it "autocorrects str.#{selector} == with parens around arg" do new_source = autocorrect_source("str.#{selector} == ('string')") expect(new_source).to eq "str.casecmp('string').zero?" end it "autocorrects str.#{selector} !=" do new_source = autocorrect_source("str.#{selector} != 'string'") expect(new_source).to eq "!str.casecmp('string').zero?" end it "autocorrects str.#{selector} != with parens around arg" do new_source = autocorrect_source("str.#{selector} != ('string')") expect(new_source).to eq "!str.casecmp('string').zero?" end it "autocorrects str.#{selector}.eql? without parens" do new_source = autocorrect_source("str.#{selector}.eql? 'string'") expect(new_source).to eq "str.casecmp('string').zero?" end it "autocorrects str.#{selector}.eql? with parens" do new_source = autocorrect_source("str.#{selector}.eql?('string')") expect(new_source).to eq "str.casecmp('string').zero?" end it "autocorrects str.#{selector}.eql? with parens and funny spacing" do new_source = autocorrect_source("str.#{selector}.eql? ( 'string' )") expect(new_source).to eq "str.casecmp( 'string' ).zero?" end it "autocorrects == str.#{selector}" do new_source = autocorrect_source("'string' == str.#{selector}") expect(new_source).to eq "str.casecmp('string').zero?" end it "autocorrects string with parens == str.#{selector}" do new_source = autocorrect_source("('string') == str.#{selector}") expect(new_source).to eq "str.casecmp('string').zero?" end it "autocorrects string != str.#{selector}" do new_source = autocorrect_source("'string' != str.#{selector}") expect(new_source).to eq "!str.casecmp('string').zero?" end it 'autocorrects string with parens and funny spacing ' \ "eql? str.#{selector}" do new_source = autocorrect_source("( 'string' ).eql? str.#{selector}") expect(new_source).to eq "str.casecmp( 'string' ).zero?" end it "autocorrects string.eql? str.#{selector} without parens " do new_source = autocorrect_source("'string'.eql? str.#{selector}") expect(new_source).to eq "str.casecmp('string').zero?" end it "autocorrects string.eql? str.#{selector} with parens " do new_source = autocorrect_source("'string'.eql?(str.#{selector})") expect(new_source).to eq "str.casecmp('string').zero?" end it "autocorrects obj.#{selector} == str.#{selector}" do new_source = autocorrect_source("obj.#{selector} == str.#{selector}") expect(new_source).to eq 'obj.casecmp(str).zero?' end it "autocorrects obj.#{selector} eql? str.#{selector}" do new_source = autocorrect_source("obj.#{selector}.eql? str.#{selector}") expect(new_source).to eq 'obj.casecmp(str).zero?' end it "formats the error message correctly for str.#{selector} ==" do inspect_source("str.#{selector} == 'string'") expect(cop.highlights).to eq(["str.#{selector} == 'string'"]) expect(cop.messages).to eq( [ "Use `str.casecmp('string').zero?` instead of " \ "`str.#{selector} == 'string'`." ] ) end it "formats the error message correctly for == str.#{selector}" do inspect_source("'string' == str.#{selector}") expect(cop.highlights).to eq(["'string' == str.#{selector}"]) expect(cop.messages).to eq( [ "Use `str.casecmp('string').zero?` instead of " \ "`'string' == str.#{selector}`." ] ) end it 'formats the error message correctly for ' \ "obj.#{selector} == str.#{selector}" do inspect_source("obj.#{selector} == str.#{selector}") expect(cop.highlights).to eq(["obj.#{selector} == str.#{selector}"]) expect(cop.messages).to eq( [ 'Use `obj.casecmp(str).zero?` instead of ' \ "`obj.#{selector} == str.#{selector}`." ] ) end it "doesn't report an offense for variable == str.#{selector}" do expect_no_offenses(<<~RUBY) var = "a" var == str.#{selector} RUBY end it "doesn't report an offense for str.#{selector} == variable" do expect_no_offenses(<<~RUBY) var = "a" str.#{selector} == var RUBY end it "doesn't report an offense for obj.method == str.#{selector}" do expect_no_offenses("obj.method == str.#{selector}") end it "doesn't report an offense for str.#{selector} == obj.method" do expect_no_offenses("str.#{selector} == obj.method") end end it_behaves_like('selectors', 'upcase') it_behaves_like('selectors', 'downcase') end rubocop-performance-1.5.2/spec/rubocop/cop/performance/chain_array_allocation_spec.rb000066400000000000000000000035151360055361600312110ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe RuboCop::Cop::Performance::ChainArrayAllocation, :config do subject(:cop) { described_class.new(config) } def generate_message(method_one, method_two) "Use unchained `#{method_one}!` and `#{method_two}!` "\ '(followed by `return array` if required) instead of '\ "chaining `#{method_one}...#{method_two}`." end shared_examples 'map_and_flat' do |method, method_two| it "registers an offense when calling #{method}...#{method_two}" do inspect_source("[1, 2, 3, 4].#{method} { |e| [e, e] }.#{method_two}") expect(cop.messages) .to eq([generate_message(method, method_two)]) expect(cop.highlights).to eq([".#{method_two}"]) end end describe 'configured to only warn when flattening one level' do it_behaves_like('map_and_flat', 'map', 'flatten') end describe 'Methods that require an argument' do it 'first' do # Yes I know this is not valid Ruby inspect_source('[1, 2, 3, 4].first.uniq') expect(cop.messages.empty?).to be(true) inspect_source('[1, 2, 3, 4].first(10).uniq') expect(cop.messages.empty?).to be(false) expect(cop.messages) .to eq([generate_message('first', 'uniq')]) expect(cop.highlights).to eq(['.uniq']) inspect_source('[1, 2, 3, 4].first(variable).uniq') expect(cop.messages.empty?).to be(false) expect(cop.messages) .to eq([generate_message('first', 'uniq')]) expect(cop.highlights).to eq(['.uniq']) end end describe 'methods that only return an array with no block' do it 'zip' do # Yes I know this is not valid Ruby inspect_source('[1, 2, 3, 4].zip {|f| }.uniq') expect(cop.messages.empty?).to be(true) inspect_source('[1, 2, 3, 4].zip.uniq') expect(cop.messages.empty?).to be(false) end end end rubocop-performance-1.5.2/spec/rubocop/cop/performance/compare_with_block_spec.rb000066400000000000000000000074121360055361600303570ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe RuboCop::Cop::Performance::CompareWithBlock do subject(:cop) { described_class.new } shared_examples 'compare with block' do |method| it "registers an offense for #{method}" do inspect_source("array.#{method} { |a, b| a.foo <=> b.foo }") expect(cop.offenses.size).to eq(1) end it "registers an offense for #{method} with [:foo]" do inspect_source("array.#{method} { |a, b| a[:foo] <=> b[:foo] }") expect(cop.offenses.size).to eq(1) end it "registers an offense for #{method} with ['foo']" do inspect_source("array.#{method} { |a, b| a['foo'] <=> b['foo'] }") expect(cop.offenses.size).to eq(1) end it "registers an offense for #{method} with [1]" do inspect_source("array.#{method} { |a, b| a[1] <=> b[1] }") expect(cop.offenses.size).to eq(1) end it 'highlights compare method' do inspect_source("array.#{method} { |a, b| a.foo <=> b.foo }") expect(cop.highlights).to eq(["#{method} { |a, b| a.foo <=> b.foo }"]) end it "accepts valid #{method} usage" do expect_no_offenses("array.#{method} { |a, b| b <=> a }") end it "accepts #{method}_by" do expect_no_offenses("array.#{method}_by { |a| a.baz }") end it "autocorrects array.#{method} { |a, b| a.foo <=> b.foo }" do new_source = autocorrect_source("array.#{method} { |a, b| a.foo <=> b.foo }") expect(new_source).to eq "array.#{method}_by(&:foo)" end it "autocorrects array.#{method} { |a, b| a.bar <=> b.bar }" do new_source = autocorrect_source("array.#{method} { |a, b| a.bar <=> b.bar }") expect(new_source).to eq "array.#{method}_by(&:bar)" end it "autocorrects array.#{method} { |x, y| x.foo <=> y.foo }" do new_source = autocorrect_source("array.#{method} { |x, y| x.foo <=> y.foo }") expect(new_source).to eq "array.#{method}_by(&:foo)" end it "autocorrects array.#{method} do |a, b| a.foo <=> b.foo end" do new_source = autocorrect_source(<<~RUBY) array.#{method} do |a, b| a.foo <=> b.foo end RUBY expect(new_source).to eq "array.#{method}_by(&:foo)\n" end it "autocorrects array.#{method} { |a, b| a[:foo] <=> b[:foo] }" do new_source = autocorrect_source( "array.#{method} { |a, b| a[:foo] <=> b[:foo] }" ) expect(new_source).to eq "array.#{method}_by { |a| a[:foo] }" end it "autocorrects array.#{method} { |a, b| a['foo'] <=> b['foo'] }" do new_source = autocorrect_source( "array.#{method} { |a, b| a['foo'] <=> b['foo'] }" ) expect(new_source).to eq "array.#{method}_by { |a| a['foo'] }" end it "autocorrects array.#{method} { |a, b| a[1] <=> b[1] }" do new_source = autocorrect_source( "array.#{method} { |a, b| a[1] <=> b[1] }" ) expect(new_source).to eq "array.#{method}_by { |a| a[1] }" end it 'formats the error message correctly for ' \ "array.#{method} { |a, b| a.foo <=> b.foo }" do inspect_source("array.#{method} { |a, b| a.foo <=> b.foo }") expect(cop.messages).to eq(["Use `#{method}_by(&:foo)` instead of " \ "`#{method} { |a, b| a.foo <=> b.foo }`."]) end it 'formats the error message correctly for ' \ "array.#{method} { |a, b| a[:foo] <=> b[:foo] }" do inspect_source("array.#{method} { |a, b| a[:foo] <=> b[:foo] }") expected = ["Use `#{method}_by { |a| a[:foo] }` instead of " \ "`#{method} { |a, b| a[:foo] <=> b[:foo] }`."] expect(cop.messages).to eq(expected) end end include_examples 'compare with block', 'sort' include_examples 'compare with block', 'max' include_examples 'compare with block', 'min' end rubocop-performance-1.5.2/spec/rubocop/cop/performance/count_spec.rb000066400000000000000000000207561360055361600256620ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe RuboCop::Cop::Performance::Count do subject(:cop) { described_class.new } shared_examples 'selectors' do |selector| it "registers an offense for using array.#{selector}...size" do inspect_source("[1, 2, 3].#{selector} { |e| e.even? }.size") expect(cop.messages) .to eq(["Use `count` instead of `#{selector}...size`."]) expect(cop.highlights).to eq(["#{selector} { |e| e.even? }.size"]) end it "registers an offense for using hash.#{selector}...size" do inspect_source("{a: 1, b: 2, c: 3}.#{selector} { |e| e == :a }.size") expect(cop.messages) .to eq(["Use `count` instead of `#{selector}...size`."]) expect(cop.highlights).to eq(["#{selector} { |e| e == :a }.size"]) end it "registers an offense for using array.#{selector}...length" do inspect_source("[1, 2, 3].#{selector} { |e| e.even? }.length") expect(cop.messages) .to eq(["Use `count` instead of `#{selector}...length`."]) expect(cop.highlights).to eq(["#{selector} { |e| e.even? }.length"]) end it "registers an offense for using hash.#{selector}...length" do inspect_source("{a: 1, b: 2}.#{selector} { |e| e == :a }.length") expect(cop.messages) .to eq(["Use `count` instead of `#{selector}...length`."]) expect(cop.highlights).to eq(["#{selector} { |e| e == :a }.length"]) end it "registers an offense for using array.#{selector}...count" do inspect_source("[1, 2, 3].#{selector} { |e| e.even? }.count") expect(cop.messages) .to eq(["Use `count` instead of `#{selector}...count`."]) expect(cop.highlights).to eq(["#{selector} { |e| e.even? }.count"]) end it "registers an offense for using hash.#{selector}...count" do inspect_source("{a: 1, b: 2}.#{selector} { |e| e == :a }.count") expect(cop.messages) .to eq(["Use `count` instead of `#{selector}...count`."]) expect(cop.highlights).to eq(["#{selector} { |e| e == :a }.count"]) end it "allows usage of #{selector}...count with a block on an array" do expect_no_offenses(<<~RUBY) [1, 2, 3].#{selector} { |e| e.odd? }.count { |e| e > 2 } RUBY end it "allows usage of #{selector}...count with a block on a hash" do expect_no_offenses(<<~RUBY) {a: 1, b: 2}.#{selector} { |e| e == :a }.count { |e| e > 2 } RUBY end it "registers an offense for #{selector} with params instead of a block" do inspect_source(<<~RUBY) Data = Struct.new(:value) array = [Data.new(2), Data.new(3), Data.new(2)] puts array.#{selector}(&:value).count RUBY expect(cop.messages) .to eq(["Use `count` instead of `#{selector}...count`."]) expect(cop.highlights).to eq(["#{selector}(&:value).count"]) end it "registers an offense for #{selector}(&:something).count" do inspect_source("foo.#{selector}(&:something).count") expect(cop.messages) .to eq(["Use `count` instead of `#{selector}...count`."]) expect(cop.highlights).to eq(["#{selector}(&:something).count"]) end it "registers an offense for #{selector}(&:something).count " \ 'when called as an instance method on its own class' do source = <<~RUBY class A < Array def count(&block) #{selector}(&block).count end end RUBY inspect_source(source) expect(cop.messages) .to eq(["Use `count` instead of `#{selector}...count`."]) expect(cop.highlights).to eq(["#{selector}(&block).count"]) end it "allows usage of #{selector} without getting the size" do expect_no_offenses("[1, 2, 3].#{selector} { |e| e.even? }") end context 'bang methods' do it "allows usage of #{selector}!...size" do expect_no_offenses("[1, 2, 3].#{selector}! { |e| e.odd? }.size") end it "allows usage of #{selector}!...count" do expect_no_offenses("[1, 2, 3].#{selector}! { |e| e.odd? }.count") end it "allows usage of #{selector}!...length" do expect_no_offenses("[1, 2, 3].#{selector}! { |e| e.odd? }.length") end end end it_behaves_like('selectors', 'select') it_behaves_like('selectors', 'reject') context 'ActiveRecord select' do it 'allows usage of select with a string' do expect_no_offenses("Model.select('field AS field_one').count") end it 'allows usage of select with multiple strings' do expect_no_offenses(<<~RUBY) Model.select('field AS field_one', 'other AS field_two').count RUBY end it 'allows usage of select with a symbol' do expect_no_offenses('Model.select(:field).count') end it 'allows usage of select with multiple symbols' do expect_no_offenses('Model.select(:field, :other_field).count') end end it 'allows usage of another method with size' do expect_no_offenses('[1, 2, 3].map { |e| e + 1 }.size') end it 'allows usage of size on an array' do expect_no_offenses('[1, 2, 3].size') end it 'allows usage of count on an array' do expect_no_offenses('[1, 2, 3].count') end it 'allows usage of count on an interstitial method called on select' do expect_no_offenses(<<~RUBY) Data = Struct.new(:value) array = [Data.new(2), Data.new(3), Data.new(2)] puts array.select(&:value).uniq.count RUBY end it 'allows usage of count on an interstitial method with blocks ' \ 'called on select' do expect_no_offenses(<<~RUBY) Data = Struct.new(:value) array = [Data.new(2), Data.new(3), Data.new(2)] array.select(&:value).uniq { |v| v > 2 }.count RUBY end it 'allows usage of size called on an assigned variable' do expect_no_offenses(<<~RUBY) nodes = [1] nodes.size RUBY end it 'allows usage of methods called on size' do expect_no_offenses('shorter.size.to_f') end context 'properly parses non related code' do it 'will not raise an error for Bundler.setup' do expect { inspect_source('Bundler.setup(:default, :development)') } .not_to raise_error end it 'will not raise an error for RakeTask.new' do expect { inspect_source('RakeTask.new(:spec)') } .not_to raise_error end end context 'autocorrect' do context 'will correct' do it 'select..size to count' do new_source = autocorrect_source('[1, 2].select { |e| e > 2 }.size') expect(new_source).to eq('[1, 2].count { |e| e > 2 }') end it 'select..count without a block to count' do new_source = autocorrect_source('[1, 2].select { |e| e > 2 }.count') expect(new_source).to eq('[1, 2].count { |e| e > 2 }') end it 'select..length to count' do new_source = autocorrect_source('[1, 2].select { |e| e > 2 }.length') expect(new_source).to eq('[1, 2].count { |e| e > 2 }') end it 'select...size when select has parameters' do source = <<~RUBY Data = Struct.new(:value) array = [Data.new(2), Data.new(3), Data.new(2)] puts array.select(&:value).size RUBY new_source = autocorrect_source(source) expect(new_source) .to eq(<<~RUBY) Data = Struct.new(:value) array = [Data.new(2), Data.new(3), Data.new(2)] puts array.count(&:value) RUBY end end describe 'will not correct' do it 'reject...size' do new_source = autocorrect_source('[1, 2].reject { |e| e > 2 }.size') expect(new_source).to eq('[1, 2].reject { |e| e > 2 }.size') end it 'reject...count' do new_source = autocorrect_source('[1, 2].reject { |e| e > 2 }.count') expect(new_source).to eq('[1, 2].reject { |e| e > 2 }.count') end it 'reject...length' do new_source = autocorrect_source('[1, 2].reject { |e| e > 2 }.length') expect(new_source).to eq('[1, 2].reject { |e| e > 2 }.length') end it 'select...count when count has a block' do source = '[1, 2].select { |e| e > 2 }.count { |e| e.even? }' new_source = autocorrect_source(source) expect(new_source).to eq(source) end it 'reject...size when select has parameters' do source = <<~RUBY Data = Struct.new(:value) array = [Data.new(2), Data.new(3), Data.new(2)] puts array.reject(&:value).size RUBY new_source = autocorrect_source(source) expect(new_source).to eq(source) end end end end rubocop-performance-1.5.2/spec/rubocop/cop/performance/detect_spec.rb000066400000000000000000000153201360055361600257710ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe RuboCop::Cop::Performance::Detect do subject(:cop) { described_class.new(config) } let(:collection_method) { nil } let(:config) do RuboCop::Config.new( 'Style/CollectionMethods' => { 'PreferredMethods' => { 'detect' => collection_method } } ) end # rspec will not let you use a variable assigned using let outside # of `it` select_methods = %i[select find_all].freeze select_methods.each do |method| it "registers an offense when first is called on #{method}" do inspect_source("[1, 2, 3].#{method} { |i| i % 2 == 0 }.first") expect(cop.messages) .to eq(["Use `detect` instead of `#{method}.first`."]) end it "doesn't register an offense when first(n) is called on #{method}" do expect_no_offenses("[1, 2, 3].#{method} { |i| i % 2 == 0 }.first(n)") end it "registers an offense when last is called on #{method}" do inspect_source("[1, 2, 3].#{method} { |i| i % 2 == 0 }.last") expect(cop.messages) .to eq(["Use `reverse.detect` instead of `#{method}.last`."]) end it "doesn't register an offense when last(n) is called on #{method}" do expect_no_offenses("[1, 2, 3].#{method} { |i| i % 2 == 0 }.last(n)") end it "registers an offense when first is called on multiline #{method}" do inspect_source(<<~RUBY) [1, 2, 3].#{method} do |i| i % 2 == 0 end.first RUBY expect(cop.messages).to eq(["Use `detect` instead of `#{method}.first`."]) end it "registers an offense when last is called on multiline #{method}" do inspect_source(<<~RUBY) [1, 2, 3].#{method} do |i| i % 2 == 0 end.last RUBY expect(cop.messages) .to eq(["Use `reverse.detect` instead of `#{method}.last`."]) end it "registers an offense when first is called on #{method} short syntax" do inspect_source("[1, 2, 3].#{method}(&:even?).first") expect(cop.messages).to eq(["Use `detect` instead of `#{method}.first`."]) end it "registers an offense when last is called on #{method} short syntax" do inspect_source("[1, 2, 3].#{method}(&:even?).last") expect(cop.messages) .to eq(["Use `reverse.detect` instead of `#{method}.last`."]) end it "registers an offense when #{method} is called" \ 'on `lazy` without receiver' do inspect_source("lazy.#{method}(&:even?).first") expect(cop.messages).to eq(["Use `detect` instead of `#{method}.first`."]) end it "does not register an offense when #{method} is used " \ 'without first or last' do expect_no_offenses("[1, 2, 3].#{method} { |i| i % 2 == 0 }") end it "does not register an offense when #{method} is called" \ 'without block or args' do expect_no_offenses("adapter.#{method}.first") end it "does not register an offense when #{method} is called" \ 'with args but without ampersand syntax' do expect_no_offenses("adapter.#{method}('something').first") end it "does not register an offense when #{method} is called" \ 'on lazy enumerable' do expect_no_offenses("adapter.lazy.#{method} { 'something' }.first") end end it 'does not register an offense when detect is used' do expect_no_offenses('[1, 2, 3].detect { |i| i % 2 == 0 }') end context 'autocorrect' do shared_examples 'detect_autocorrect' do |preferred_method| context "with #{preferred_method}" do let(:collection_method) { preferred_method } select_methods.each do |method| it "corrects #{method}.first to #{preferred_method} (with block)" do source = "[1, 2, 3].#{method} { |i| i % 2 == 0 }.first" new_source = autocorrect_source(source) expect(new_source) .to eq("[1, 2, 3].#{preferred_method} { |i| i % 2 == 0 }") end it "corrects #{method}.last to reverse.#{preferred_method} " \ '(with block)' do source = "[1, 2, 3].#{method} { |i| i % 2 == 0 }.last" new_source = autocorrect_source(source) expect(new_source) .to eq("[1, 2, 3].reverse.#{preferred_method} { |i| i % 2 == 0 }") end it "corrects #{method}.first to #{preferred_method} (short syntax)" do source = "[1, 2, 3].#{method}(&:even?).first" new_source = autocorrect_source(source) expect(new_source).to eq("[1, 2, 3].#{preferred_method}(&:even?)") end it "corrects #{method}.last to reverse.#{preferred_method} " \ '(short syntax)' do source = "[1, 2, 3].#{method}(&:even?).last" new_source = autocorrect_source(source) expect(new_source) .to eq("[1, 2, 3].reverse.#{preferred_method}(&:even?)") end it "corrects #{method}.first to #{preferred_method} (multiline)" do source = <<~RUBY [1, 2, 3].#{method} do |i| i % 2 == 0 end.first RUBY new_source = autocorrect_source(source) expect(new_source).to eq(<<~RUBY) [1, 2, 3].#{preferred_method} do |i| i % 2 == 0 end RUBY end it "corrects #{method}.last to reverse.#{preferred_method} " \ '(multiline)' do source = <<~RUBY [1, 2, 3].#{method} do |i| i % 2 == 0 end.last RUBY new_source = autocorrect_source(source) expect(new_source) .to eq(<<~RUBY) [1, 2, 3].reverse.#{preferred_method} do |i| i % 2 == 0 end RUBY end it "corrects multiline #{method} to #{preferred_method} " \ "with 'first' on the last line" do source = <<~RUBY [1, 2, 3].#{method} { true } .first['x'] RUBY new_source = autocorrect_source(source) expect(new_source) .to eq("[1, 2, 3].#{preferred_method} { true }['x']\n") end it "corrects multiline #{method} to #{preferred_method} " \ "with 'first' on the last line (short syntax)" do source = <<~RUBY [1, 2, 3].#{method}(&:blank?) .first['x'] RUBY new_source = autocorrect_source(source) expect(new_source) .to eq("[1, 2, 3].#{preferred_method}(&:blank?)['x']\n") end end end end it_behaves_like 'detect_autocorrect', 'detect' it_behaves_like 'detect_autocorrect', 'find' end end rubocop-performance-1.5.2/spec/rubocop/cop/performance/double_start_end_with_spec.rb000066400000000000000000000175731360055361600311050ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe RuboCop::Cop::Performance::DoubleStartEndWith do subject(:cop) { described_class.new(config) } context 'IncludeActiveSupportAliases: false' do let(:config) do RuboCop::Config.new( 'Performance/DoubleStartEndWith' => { 'IncludeActiveSupportAliases' => false } ) end context 'two #start_with? calls' do context 'with the same receiver' do context 'all parameters of the second call are pure' do let(:source) { 'x.start_with?(a, b) || x.start_with?("c", D)' } it 'registers an offense' do inspect_source(source) expect(cop.offenses.size).to eq(1) expect(cop.offenses.first.message).to eq( 'Use `x.start_with?(a, b, "c", D)` instead of ' \ '`x.start_with?(a, b) || x.start_with?("c", D)`.' ) expect(cop.highlights).to eq( ['x.start_with?(a, b) || x.start_with?("c", D)'] ) end it 'corrects to a single start_with?' do new_source = autocorrect_source(source) expect(new_source).to eq('x.start_with?(a, b, "c", D)') end end context 'one of the parameters of the second call is not pure' do it "doesn't register an offense" do expect_no_offenses('x.start_with?(a, "b") || x.start_with?(C, d)') end end end context 'with different receivers' do it "doesn't register an offense" do expect_no_offenses('x.start_with?("a") || y.start_with?("b")') end end end context 'two #end_with? calls' do context 'with the same receiver' do context 'all parameters of the second call are pure' do let(:source) { 'x.end_with?(a, b) || x.end_with?("c", D)' } it 'registers an offense' do inspect_source(source) expect(cop.offenses.size).to eq(1) expect(cop.offenses.first.message).to eq( 'Use `x.end_with?(a, b, "c", D)` instead of ' \ '`x.end_with?(a, b) || x.end_with?("c", D)`.' ) expect(cop.highlights).to eq( ['x.end_with?(a, b) || x.end_with?("c", D)'] ) end it 'corrects to a single end_with?' do new_source = autocorrect_source(source) expect(new_source).to eq('x.end_with?(a, b, "c", D)') end end context 'one of the parameters of the second call is not pure' do it "doesn't register an offense" do expect_no_offenses('x.end_with?(a, "b") || x.end_with?(C, d)') end end end context 'with different receivers' do it "doesn't register an offense" do expect_no_offenses('x.end_with?("a") || y.end_with?("b")') end end end context 'a .start_with? and .end_with? call with the same receiver' do it "doesn't register an offense" do expect_no_offenses('x.start_with?("a") || x.end_with?("b")') end end context 'two #starts_with? calls' do it "doesn't register an offense" do expect_no_offenses('x.starts_with?(a, b) || x.starts_with?("c", D)') end end context 'two #ends_with? calls' do it "doesn't register an offense" do expect_no_offenses('x.ends_with?(a, b) || x.ends_with?("c", D)') end end end context 'IncludeActiveSupportAliases: true' do let(:config) do RuboCop::Config.new( 'Performance/DoubleStartEndWith' => { 'IncludeActiveSupportAliases' => true } ) end context 'two #start_with? calls' do context 'with the same receiver' do context 'all parameters of the second call are pure' do let(:source) { 'x.start_with?(a, b) || x.start_with?("c", D)' } it 'registers an offense' do inspect_source(source) expect(cop.offenses.size).to eq(1) expect(cop.offenses.first.message) .to eq('Use `x.start_with?(a, b, "c", D)` instead of ' \ '`x.start_with?(a, b) || x.start_with?("c", D)`.') expect(cop.highlights) .to eq(['x.start_with?(a, b) || x.start_with?("c", D)']) end it 'corrects to a single start_with?' do new_source = autocorrect_source(source) expect(new_source).to eq('x.start_with?(a, b, "c", D)') end end end end context 'two #end_with? calls' do context 'with the same receiver' do context 'all parameters of the second call are pure' do let(:source) { 'x.end_with?(a, b) || x.end_with?("c", D)' } it 'registers an offense' do inspect_source(source) expect(cop.offenses.size).to eq(1) expect(cop.offenses.first.message) .to eq('Use `x.end_with?(a, b, "c", D)` instead of ' \ '`x.end_with?(a, b) || x.end_with?("c", D)`.') expect(cop.highlights) .to eq(['x.end_with?(a, b) || x.end_with?("c", D)']) end it 'corrects to a single end_with?' do new_source = autocorrect_source(source) expect(new_source).to eq('x.end_with?(a, b, "c", D)') end end end end context 'two #starts_with? calls' do context 'with the same receiver' do context 'all parameters of the second call are pure' do let(:source) { 'x.starts_with?(a, b) || x.starts_with?("c", D)' } it 'registers an offense' do inspect_source(source) expect(cop.offenses.size).to eq(1) expect(cop.offenses.first.message).to eq( 'Use `x.starts_with?(a, b, "c", D)` instead of ' \ '`x.starts_with?(a, b) || x.starts_with?("c", D)`.' ) expect(cop.highlights).to eq( ['x.starts_with?(a, b) || x.starts_with?("c", D)'] ) end it 'corrects to a single starts_with?' do new_source = autocorrect_source(source) expect(new_source).to eq('x.starts_with?(a, b, "c", D)') end end context 'one of the parameters of the second call is not pure' do it "doesn't register an offense" do expect_no_offenses(<<~RUBY) x.starts_with?(a, "b") || x.starts_with?(C, d) RUBY end end end context 'with different receivers' do it "doesn't register an offense" do expect_no_offenses('x.starts_with?("a") || y.starts_with?("b")') end end end context 'two #ends_with? calls' do context 'with the same receiver' do context 'all parameters of the second call are pure' do let(:source) { 'x.ends_with?(a, b) || x.ends_with?("c", D)' } it 'registers an offense' do inspect_source(source) expect(cop.offenses.size).to eq(1) expect(cop.offenses.first.message).to eq( 'Use `x.ends_with?(a, b, "c", D)` instead of ' \ '`x.ends_with?(a, b) || x.ends_with?("c", D)`.' ) expect(cop.highlights).to eq( ['x.ends_with?(a, b) || x.ends_with?("c", D)'] ) end it 'corrects to a single ends_with?' do new_source = autocorrect_source(source) expect(new_source).to eq('x.ends_with?(a, b, "c", D)') end end context 'one of the parameters of the second call is not pure' do it "doesn't register an offense" do expect_no_offenses('x.ends_with?(a, "b") || x.ends_with?(C, d)') end end end context 'with different receivers' do it "doesn't register an offense" do expect_no_offenses('x.ends_with?("a") || y.ends_with?("b")') end end end end end rubocop-performance-1.5.2/spec/rubocop/cop/performance/end_with_spec.rb000066400000000000000000000111231360055361600263170ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe RuboCop::Cop::Performance::EndWith do subject(:cop) { described_class.new } shared_examples 'different match methods' do |method| it "autocorrects str#{method} /abc\\z/" do new_source = autocorrect_source("str#{method} /abc\\z/") expect(new_source).to eq "str.end_with?('abc')" end it "autocorrects /abc\\z/#{method} str" do new_source = autocorrect_source("/abc\\z/#{method} str") expect(new_source).to eq "str.end_with?('abc')" end it "autocorrects str#{method} /\\n\\z/" do new_source = autocorrect_source("str#{method} /\\n\\z/") expect(new_source).to eq 'str.end_with?("\n")' end it "autocorrects /\\n\\z/#{method} str" do new_source = autocorrect_source("/\\n\\z/#{method} str") expect(new_source).to eq 'str.end_with?("\n")' end it "autocorrects str#{method} /\\t\\z/" do new_source = autocorrect_source("str#{method} /\\t\\z/") expect(new_source).to eq 'str.end_with?("\t")' end it "autocorrects /\\t\\z/#{method} str" do new_source = autocorrect_source("/\\t\\z/#{method} str") expect(new_source).to eq 'str.end_with?("\t")' end # regexp metacharacters %w[. $ ^ |].each do |str| it "autocorrects str#{method} /\\#{str}\\z/" do new_source = autocorrect_source("str#{method} /\\#{str}\\z/") expect(new_source).to eq "str.end_with?('#{str}')" end it "autocorrects /\\#{str}\\z/#{method} str" do new_source = autocorrect_source("/\\#{str}\\z/#{method} str") expect(new_source).to eq "str.end_with?('#{str}')" end it "doesn't register an error for str#{method} /#{str}\\z/" do expect_no_offenses("str#{method} /#{str}\\z/") end it "doesn't register an error for /#{str}\\z/#{method} str" do expect_no_offenses("/#{str}\\z/#{method} str") end end # escapes like "\n" # note that "\b" is a literal backspace char in a double-quoted string... # but in a regex, it's an anchor on a word boundary %w[a e f r t v].each do |str| it "autocorrects str#{method} /\\#{str}\\z/" do new_source = autocorrect_source("str#{method} /\\#{str}\\z/") expect(new_source).to eq %{str.end_with?("\\#{str}")} end it "autocorrects /\\#{str}\\z/#{method} str" do new_source = autocorrect_source("/\\#{str}\\z/#{method} str") expect(new_source).to eq %{str.end_with?("\\#{str}")} end end # character classes, anchors %w[w W s S d D A Z z G b B h H R X S].each do |str| it "doesn't register an error for str#{method} /\\#{str}\\z/" do expect_no_offenses("str#{method} /\\#{str}\\z/") end it "doesn't register an error for /\\#{str}\\z/#{method} str" do expect_no_offenses("/\\#{str}\\z/#{method} str") end end # characters with no special meaning whatsoever %w[i j l m o q y].each do |str| it "autocorrects str#{method} /\\#{str}\\z/" do new_source = autocorrect_source("str#{method} /\\#{str}\\z/") expect(new_source).to eq "str.end_with?('#{str}')" end it "autocorrects /\\#{str}\\z/#{method} str" do new_source = autocorrect_source("/\\#{str}\\z/#{method} str") expect(new_source).to eq "str.end_with?('#{str}')" end end it "formats the error message correctly for str#{method} /abc\\z/" do inspect_source("str#{method} /abc\\z/") expect(cop.messages).to eq(['Use `String#end_with?` instead of a ' \ 'regex match anchored to the end of ' \ 'the string.']) end it "formats the error message correctly for /abc\\z/#{method} str" do inspect_source("/abc\\z/#{method} str") expect(cop.messages).to eq(['Use `String#end_with?` instead of a ' \ 'regex match anchored to the end of ' \ 'the string.']) end it "autocorrects str#{method} /\\\\\\z/" do new_source = autocorrect_source("str#{method} /\\\\\\z/") expect(new_source).to eq("str.end_with?('\\\\')") end it "autocorrects /\\\\\\z/#{method} str" do new_source = autocorrect_source("/\\\\\\z/#{method} str") expect(new_source).to eq("str.end_with?('\\\\')") end end include_examples('different match methods', '.match?') include_examples('different match methods', ' =~') include_examples('different match methods', '.match') it 'allows match without a receiver' do expect_no_offenses('expect(subject.spin).to match(/\n\z/)') end end rubocop-performance-1.5.2/spec/rubocop/cop/performance/fixed_size_spec.rb000066400000000000000000000142011360055361600266470ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe RuboCop::Cop::Performance::FixedSize do subject(:cop) { described_class.new } let(:message) do 'Do not compute the size of statically sized objects.' end shared_examples 'common functionality' do |method| context 'strings' do it "registers an offense when calling #{method} on a single quoted " \ 'string' do inspect_source("'a'.#{method}") expect(cop.messages).to eq([message]) end it "registers an offense when calling #{method} on a double quoted " \ 'string' do inspect_source("\"a\".#{method}") expect(cop.messages).to eq([message]) end it "registers an offense when calling #{method} on a %q string" do inspect_source("%q(a).#{method}") expect(cop.messages).to eq([message]) end it "registers an offense when calling #{method} on a %Q string" do inspect_source("%Q(a).#{method}") expect(cop.messages).to eq([message]) end it "registers an offense when calling #{method} on a % string" do inspect_source("%(a).#{method}") expect(cop.messages).to eq([message]) end it "accepts calling #{method} on a double quoted string that " \ 'contains interpolation' do expect_no_offenses("\"\#{foo}\".#{method}") end it "accepts calling #{method} on a %Q string that contains " \ 'interpolation' do expect_no_offenses("\%Q(\#{foo}).#{method}") end it "accepts calling #{method} on a % string that contains " \ 'interpolation' do expect_no_offenses("\%(\#{foo}).#{method}") end it "accepts calling #{method} on a single quoted string that " \ 'is assigned to a constant' do expect_no_offenses("CONST = 'a'.#{method}") end it "accepts calling #{method} on a double quoted string that " \ 'is assigned to a constant' do expect_no_offenses("CONST = \"a\".#{method}") end it "accepts calling #{method} on a %q string that is assigned to " \ 'a constant' do expect_no_offenses("CONST = %q(a).#{method}") end it "accepts calling #{method} on a %q string that is assigned to " \ 'a constant along with some arithmetic operations' do expect_no_offenses("CONST = %q(a).#{method} + 1 * 20") end it "accepts calling #{method} on a variable " do expect_no_offenses(<<~RUBY) foo = "abc" foo.#{method} RUBY end end context 'symbols' do it "registers an offense when calling #{method} on a symbol" do inspect_source(":foo.#{method}") expect(cop.messages).to eq([message]) end it "registers an offense when calling #{method} on a quoted symbol" do inspect_source(":'foo-bar'.#{method}") expect(cop.messages).to eq([message]) end it "accepts calling #{method} on an interpolated quoted symbol" do expect_no_offenses(":\"foo-\#{bar}\".#{method}") end it "registers an offense when calling #{method} on %s" do inspect_source("%s(foo-bar).#{method}") expect(cop.messages).to eq([message]) end it "accepts calling #{method} on a symbol that is assigned " \ 'to a constant' do expect_no_offenses("CONST = :foo.#{method}") end end context 'arrays' do it "registers an offense when calling #{method} on an array using []" do inspect_source("[1, 2, foo].#{method}") expect(cop.messages).to eq([message]) end it "registers an offense when calling #{method} on an array using %w" do inspect_source("%w(1, 2, foo).#{method}") expect(cop.messages).to eq([message]) end it "registers an offense when calling #{method} on an array using %W" do inspect_source("%W(1, 2, foo).#{method}") expect(cop.messages).to eq([message]) end it "accepts calling #{method} on an array using [] that contains " \ 'a splat' do expect_no_offenses("[1, 2, *foo].#{method}") end it "accepts calling #{method} on array that is set to a variable" do expect_no_offenses(<<~RUBY) foo = [1, 2, 3] foo.#{method} RUBY end it "accepts calling #{method} on an array that is assigned " \ 'to a constant' do expect_no_offenses("CONST = [1, 2, 3].#{method}") end end context 'hashes' do it "registers an offense when calling #{method} on a hash using {}" do inspect_source("{a: 1, b: 2}.#{method}") expect(cop.messages).to eq([message]) end it "accepts calling #{method} on a hash set to a variable" do expect_no_offenses(<<~RUBY) foo = {a: 1, b: 2} foo.#{method} RUBY end it "accepts calling #{method} on a hash that contains a double splat" do expect_no_offenses("{a: 1, **foo}.#{method}") end it "accepts calling #{method} on an hash that is assigned " \ 'to a constant' do expect_no_offenses("CONST = {a: 1, b: 2}.#{method}") end end end it_behaves_like 'common functionality', 'size' it_behaves_like 'common functionality', 'length' it_behaves_like 'common functionality', 'count' shared_examples 'count with arguments' do |variable| it 'accepts calling count with a variable' do expect_no_offenses("#{variable}.count(bar)") end it 'accepts calling count with an instance variable' do expect_no_offenses("#{variable}.count(@bar)") end it 'registers an offense when calling count with a string' do inspect_source("#{variable}.count('o')") expect(cop.messages).to eq([message]) end it 'accepts calling count with a block' do expect_no_offenses("#{variable}.count { |v| v == 'a' }") end it 'accepts calling count with a symbol proc' do expect_no_offenses("#{variable}.count(&:any?) ") end end it_behaves_like 'count with arguments', '"foo"' it_behaves_like 'count with arguments', '[1, 2, 3]' it_behaves_like 'count with arguments', '{a: 1, b: 2}' end rubocop-performance-1.5.2/spec/rubocop/cop/performance/flat_map_spec.rb000066400000000000000000000115401360055361600263040ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe RuboCop::Cop::Performance::FlatMap, :config do subject(:cop) { described_class.new(config) } shared_examples 'map_and_collect' do |method, flatten| it "registers an offense when calling #{method}...#{flatten}(1)" do inspect_source("[1, 2, 3, 4].#{method} { |e| [e, e] }.#{flatten}(1)") expect(cop.messages) .to eq(["Use `flat_map` instead of `#{method}...#{flatten}`."]) expect(cop.highlights).to eq(["#{method} { |e| [e, e] }.#{flatten}(1)"]) end it "registers an offense when calling #{method}(&:foo).#{flatten}(1)" do inspect_source("[1, 2, 3, 4].#{method}(&:foo).#{flatten}(1)") expect(cop.messages) .to eq(["Use `flat_map` instead of `#{method}...#{flatten}`."]) expect(cop.highlights).to eq(["#{method}(&:foo).#{flatten}(1)"]) end it "registers an offense when calling #{method}(&foo).#{flatten}(1)" do inspect_source("[1, 2, 3, 4].#{method}(&foo).#{flatten}(1)") expect(cop.messages) .to eq(["Use `flat_map` instead of `#{method}...#{flatten}`."]) expect(cop.highlights).to eq(["#{method}(&foo).#{flatten}(1)"]) end it "does not register an offense when calling #{method}...#{flatten} " \ 'with a number greater than 1' do expect_no_offenses("[1, 2, 3, 4].#{method} { |e| [e, e] }.#{flatten}(3)") end it "does not register an offense when calling #{method}!...#{flatten}" do expect_no_offenses("[1, 2, 3, 4].#{method}! { |e| [e, e] }.#{flatten}") end it "corrects #{method}..#{flatten}(1) to flat_map" do source = "[1, 2].#{method} { |e| [e, e] }.#{flatten}(1)" new_source = autocorrect_source(source) expect(new_source).to eq('[1, 2].flat_map { |e| [e, e] }') end it "corrects #{method}(&:foo).#{flatten} to flat_map" do source = "[1, 2].#{method}(&:foo).#{flatten}(1)" new_source = autocorrect_source(source) expect(new_source).to eq('[1, 2].flat_map(&:foo)') end it "corrects #{method}(&foo).#{flatten} to flat_map" do source = "[1, 2].#{method}(&:foo).#{flatten}(1)" new_source = autocorrect_source(source) expect(new_source).to eq('[1, 2].flat_map(&:foo)') end end describe 'configured to only warn when flattening one level' do let(:config) do RuboCop::Config.new('Performance/FlatMap' => { 'Enabled' => true, 'EnabledForFlattenWithoutParams' => false }) end shared_examples 'flatten_with_params_disabled' do |method, flatten| it "does not register an offense when calling #{method}...#{flatten}" do expect_no_offenses("[1, 2, 3, 4].map { |e| [e, e] }.#{flatten}") end end it_behaves_like('map_and_collect', 'map', 'flatten') it_behaves_like('map_and_collect', 'map', 'flatten!') it_behaves_like('map_and_collect', 'collect', 'flatten') it_behaves_like('map_and_collect', 'collect', 'flatten!') it_behaves_like('flatten_with_params_disabled', 'map', 'flatten') it_behaves_like('flatten_with_params_disabled', 'collect', 'flatten') it_behaves_like('flatten_with_params_disabled', 'map', 'flatten!') it_behaves_like('flatten_with_params_disabled', 'collect', 'flatten!') end describe 'configured to warn when flatten is not called with parameters' do let(:config) do RuboCop::Config.new('Performance/FlatMap' => { 'Enabled' => true, 'EnabledForFlattenWithoutParams' => true }) end shared_examples 'flatten_with_params_enabled' do |method, flatten| it "registers an offense when calling #{method}...#{flatten}" do inspect_source("[1, 2, 3, 4].map { |e| [e, e] }.#{flatten}") expect(cop.messages) .to eq(["Use `flat_map` instead of `map...#{flatten}`. " \ 'Beware, `flat_map` only flattens 1 level and `flatten` ' \ 'can be used to flatten multiple levels.']) expect(cop.highlights).to eq(["map { |e| [e, e] }.#{flatten}"]) end it "will not correct #{method}..#{flatten} to flat_map" do source = "[1, 2].map { |e| [e, e] }.#{flatten}" new_source = autocorrect_source(source) expect(new_source).to eq("[1, 2].map { |e| [e, e] }.#{flatten}") end end it_behaves_like('map_and_collect', 'map', 'flatten') it_behaves_like('map_and_collect', 'map', 'flatten!') it_behaves_like('map_and_collect', 'collect', 'flatten') it_behaves_like('map_and_collect', 'collect', 'flatten!') it_behaves_like('flatten_with_params_enabled', 'map', 'flatten') it_behaves_like('flatten_with_params_enabled', 'collect', 'flatten') it_behaves_like('flatten_with_params_enabled', 'map', 'flatten!') it_behaves_like('flatten_with_params_enabled', 'collect', 'flatten!') end end rubocop-performance-1.5.2/spec/rubocop/cop/performance/inefficient_hash_search_spec.rb000066400000000000000000000110211360055361600313260ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe RuboCop::Cop::Performance::InefficientHashSearch do subject(:cop) { described_class.new(config) } shared_examples 'correct behavior' do |expected| let(:expected_key_method) { expected == :short ? 'key?' : 'has_key?' } let(:expected_value_method) { expected == :short ? 'value?' : 'has_value?' } it 'registers an offense when a hash literal receives `keys.include?`' do expect_offense(<<~RUBY) { a: 1 }.keys.include? 1 ^^^^^^^^^^^^^^^^^^^^^^^^ Use `##{expected_key_method}` instead of `#keys.include?`. RUBY end it 'registers an offense when an existing hash receives `keys.include?`' do expect_offense(<<~RUBY) h = { a: 1 }; h.keys.include? 1 ^^^^^^^^^^^^^^^^^ Use `##{expected_key_method}` instead of `#keys.include?`. RUBY end it 'registers an offense when a hash literal receives `values.include?`' do expect_offense(<<~RUBY) { a: 1 }.values.include? 1 ^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `##{expected_value_method}` instead of `#values.include?`. RUBY end it 'registers an offense when a hash variable receives `values.include?`' do expect_offense(<<~RUBY) h = { a: 1 }; h.values.include? 1 ^^^^^^^^^^^^^^^^^^^ Use `##{expected_value_method}` instead of `#values.include?`. RUBY end it 'finds no offense when a `keys` array variable receives `include?`' do expect_no_offenses(<<~RUBY) h = { a: 1 }; keys = h.keys ; keys.include? 1 RUBY end it 'finds no offense when a `values` array variable receives `include?` ' do expect_no_offenses(<<~RUBY) h = { a: 1 }; values = h.values ; values.include? 1 RUBY end it 'does not register an offense when `keys` method defined by itself ' \ 'and `include?` method are method chaining' do expect_no_offenses(<<~RUBY) def my_include?(key) keys.include?(key) end RUBY end describe 'autocorrect' do context 'when using `keys.include?`' do it 'corrects to `key?` or `has_key?`' do new_source = autocorrect_source('{ a: 1 }.keys.include?(1)') expect(new_source).to eq("{ a: 1 }.#{expected_key_method}(1)") end it 'corrects when hash is not a literal' do new_source = autocorrect_source('h = { a: 1 }; h.keys.include?(1)') expect(new_source).to eq("h = { a: 1 }; h.#{expected_key_method}(1)") end it 'gracefully handles whitespace' do new_source = autocorrect_source("{ a: 1 }. keys.\ninclude? 1") expect(new_source).to eq("{ a: 1 }.#{expected_key_method}(1)") end end context 'when using `values.include?`' do it 'corrects to `value?` or `has_value?`' do new_source = autocorrect_source('{ a: 1 }.values.include?(1)') expect(new_source).to eq("{ a: 1 }.#{expected_value_method}(1)") end it 'corrects when hash is not a literal' do new_source = autocorrect_source('h = { a: 1 }; h.values.include?(1)') expect(new_source) .to eq("h = { a: 1 }; h.#{expected_value_method}(1)") end it 'gracefully handles whitespace' do new_source = autocorrect_source("{ a: 1 }. values.\ninclude? 1") expect(new_source).to eq("{ a: 1 }.#{expected_value_method}(1)") end end end end context 'when config is empty' do let(:config) { RuboCop::Config.new } it_behaves_like 'correct behavior', :short end context 'when config enforces short hash methods' do let(:config) do RuboCop::Config.new( 'AllCops' => { 'Style/PreferredHashMethods' => { 'EnforcedStyle' => 'short', 'Enabled' => true } } ) end it_behaves_like 'correct behavior', :short end context 'when config specifies long hash methods but is not enabled' do let(:config) do RuboCop::Config.new( 'AllCops' => { 'Style/PreferredHashMethods' => { 'EnforcedStyle' => 'long', 'Enabled' => false } } ) end it_behaves_like 'correct behavior', :short end context 'when config enforces long hash methods' do let(:config) do RuboCop::Config.new( 'AllCops' => { 'Style/PreferredHashMethods' => { 'EnforcedStyle' => 'long', 'Enabled' => true } } ) end it_behaves_like 'correct behavior', :long end end rubocop-performance-1.5.2/spec/rubocop/cop/performance/open_struct_spec.rb000066400000000000000000000014421360055361600270660ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe RuboCop::Cop::Performance::OpenStruct do subject(:cop) { described_class.new(config) } let(:config) { RuboCop::Config.new } it 'registers an offense for OpenStruct.new' do expect_offense(<<~RUBY) OpenStruct.new(key: "value") ^^^ Consider using `Struct` over `OpenStruct` to optimize the performance. RUBY end it 'registers an offense for a fully qualified ::OpenStruct.new' do expect_offense(<<~RUBY) ::OpenStruct.new(key: "value") ^^^ Consider using `Struct` over `OpenStruct` to optimize the performance. RUBY end it 'does not register offense for Struct' do expect_no_offenses(<<~RUBY) MyStruct = Struct.new(:key) MyStruct.new('value') RUBY end end rubocop-performance-1.5.2/spec/rubocop/cop/performance/range_include_spec.rb000066400000000000000000000017531360055361600273250ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe RuboCop::Cop::Performance::RangeInclude do subject(:cop) { described_class.new } it 'autocorrects (a..b).include? without parens' do new_source = autocorrect_source('(a..b).include? 1') expect(new_source).to eq '(a..b).cover? 1' end it 'autocorrects (a...b).include? without parens' do new_source = autocorrect_source('(a...b).include? 1') expect(new_source).to eq '(a...b).cover? 1' end it 'autocorrects (a..b).include? with parens' do new_source = autocorrect_source('(a..b).include?(1)') expect(new_source).to eq '(a..b).cover?(1)' end it 'autocorrects (a...b).include? with parens' do new_source = autocorrect_source('(a...b).include?(1)') expect(new_source).to eq '(a...b).cover?(1)' end it 'formats the error message correctly for (a..b).include? 1' do expect_offense(<<~RUBY) (a..b).include? 1 ^^^^^^^^ Use `Range#cover?` instead of `Range#include?`. RUBY end end rubocop-performance-1.5.2/spec/rubocop/cop/performance/redundant_block_call_spec.rb000066400000000000000000000073611360055361600306600ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe RuboCop::Cop::Performance::RedundantBlockCall do subject(:cop) { described_class.new } it 'autocorrects block.call without arguments' do new_source = autocorrect_source(<<~RUBY) def method(&block) block.call end RUBY expect(new_source).to eq(<<~RUBY) def method(&block) yield end RUBY end it 'autocorrects block.call with empty parentheses' do new_source = autocorrect_source(<<~RUBY) def method(&block) block.call() end RUBY expect(new_source).to eq(<<~RUBY) def method(&block) yield end RUBY end it 'autocorrects block.call with arguments' do new_source = autocorrect_source(<<~RUBY) def method(&block) block.call 1, 2 end RUBY expect(new_source).to eq(<<~RUBY) def method(&block) yield 1, 2 end RUBY end it 'autocorrects multiple occurrences of block.call with arguments' do new_source = autocorrect_source(<<~RUBY) def method(&block) block.call 1 block.call 2 end RUBY expect(new_source).to eq(<<~RUBY) def method(&block) yield 1 yield 2 end RUBY end it 'autocorrects even when block arg has a different name' do new_source = autocorrect_source(<<~RUBY) def method(&func) func.call end RUBY expect(new_source).to eq(<<~RUBY) def method(&func) yield end RUBY end it 'accepts a block that is not `call`ed' do expect_no_offenses(<<~RUBY) def method(&block) something.call end RUBY end it 'accepts an empty method body' do expect_no_offenses(<<~RUBY) def method(&block) end RUBY end it 'accepts another block being passed as the only arg' do expect_no_offenses(<<~RUBY) def method(&block) block.call(&some_proc) end RUBY end it 'accepts another block being passed along with other args' do expect_no_offenses(<<~RUBY) def method(&block) block.call(1, &some_proc) end RUBY end it 'accepts another block arg in at least one occurrence of block.call' do expect_no_offenses(<<~RUBY) def method(&block) block.call(1, &some_proc) block.call(2) end RUBY end it 'accepts an optional block that is defaulted' do expect_no_offenses(<<~RUBY) def method(&block) block ||= ->(i) { puts i } block.call(1) end RUBY end it 'accepts an optional block that is overridden' do expect_no_offenses(<<~RUBY) def method(&block) block = ->(i) { puts i } block.call(1) end RUBY end it 'formats the error message for func.call(1) correctly' do expect_offense(<<~RUBY) def method(&func) func.call(1) ^^^^^^^^^^^^ Use `yield` instead of `func.call`. end RUBY end it 'autocorrects using parentheses when block.call uses parentheses' do new_source = autocorrect_source(<<~RUBY) def method(&block) block.call(a, b) end RUBY expect(new_source).to eq(<<~RUBY) def method(&block) yield(a, b) end RUBY end it 'autocorrects when the result of the call is used in a scope that ' \ 'requires parentheses' do source = <<~RUBY def method(&block) each_with_object({}) do |(key, value), acc| acc.merge!(block.call(key) => rhs[value]) end end RUBY new_source = autocorrect_source(source) expect(new_source).to eq(<<~RUBY) def method(&block) each_with_object({}) do |(key, value), acc| acc.merge!(yield(key) => rhs[value]) end end RUBY end end rubocop-performance-1.5.2/spec/rubocop/cop/performance/redundant_match_spec.rb000066400000000000000000000057011360055361600276630ustar00rootroot00000000000000# frozen_string_literal: true # do_something if str.match(/regex/) # while regex.match('str') # do_something # end # # @good # method(str.match(/regex/)) # return regex.match('str') RSpec.describe RuboCop::Cop::Performance::RedundantMatch do subject(:cop) { described_class.new } it 'autocorrects .match in if condition' do new_source = autocorrect_source('something if str.match(/regex/)') expect(new_source).to eq 'something if str =~ /regex/' end it 'autocorrects .match in unless condition' do new_source = autocorrect_source('something unless str.match(/regex/)') expect(new_source).to eq 'something unless str =~ /regex/' end it 'autocorrects .match in while condition' do new_source = autocorrect_source(<<~RUBY) while str.match(/regex/) do_something end RUBY expect(new_source).to eq(<<~RUBY) while str =~ /regex/ do_something end RUBY end it 'autocorrects .match in until condition' do new_source = autocorrect_source(<<~RUBY) until str.match(/regex/) do_something end RUBY expect(new_source).to eq(<<~RUBY) until str =~ /regex/ do_something end RUBY end it 'autocorrects .match in method body (but not tail position)' do new_source = autocorrect_source(<<~RUBY) def method(str) str.match(/regex/) true end RUBY expect(new_source).to eq(<<~RUBY) def method(str) str =~ /regex/ true end RUBY end it 'does not autocorrect if .match has a string agrgument' do new_source = autocorrect_source('something if str.match("string")') expect(new_source).to eq 'something if str.match("string")' end it 'does not register an error when return value of .match is passed ' \ 'to another method' do expect_no_offenses(<<~RUBY) def method(str) something(str.match(/regex/)) end RUBY end it 'does not register an error when return value of .match is stored in an ' \ 'instance variable' do expect_no_offenses(<<~RUBY) def method(str) @var = str.match(/regex/) true end RUBY end it 'does not register an error when return value of .match is returned from' \ ' surrounding method' do expect_no_offenses(<<~RUBY) def method(str) str.match(/regex/) end RUBY end it 'does not register an offense when match has a block' do expect_no_offenses(<<~RUBY) /regex/.match(str) do |m| something(m) end RUBY end it 'does not register an error when there is no receiver to the match call' do expect_no_offenses('match("bar")') end it 'formats error message correctly for something if str.match(/regex/)' do expect_offense(<<~RUBY) something if str.match(/regex/) ^^^^^^^^^^^^^^^^^^ Use `=~` in places where the `MatchData` returned by `#match` will not be used. RUBY end end rubocop-performance-1.5.2/spec/rubocop/cop/performance/redundant_merge_spec.rb000066400000000000000000000165461360055361600276770ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe RuboCop::Cop::Performance::RedundantMerge, :config do subject(:cop) { described_class.new(config) } let(:cop_config) do { 'MaxKeyValuePairs' => 2 } end it 'autocorrects hash.merge!(a: 1)' do new_source = autocorrect_source('hash.merge!(a: 1)') expect(new_source).to eq 'hash[:a] = 1' end it 'autocorrects hash.merge!("abc" => "value")' do new_source = autocorrect_source('hash.merge!("abc" => "value")') expect(new_source).to eq 'hash["abc"] = "value"' end context 'when receiver is a local variable' do it 'autocorrects hash.merge!(a: 1, b: 2)' do new_source = autocorrect_source(<<~RUBY) hash = {} hash.merge!(a: 1, b: 2) RUBY expect(new_source).to eq(<<~RUBY) hash = {} hash[:a] = 1 hash[:b] = 2 RUBY end end context 'when receiver is a method call' do it "doesn't autocorrect hash.merge!(a: 1, b: 2)" do new_source = autocorrect_source('hash.merge!(a: 1, b: 2)') expect(new_source).to eq('hash.merge!(a: 1, b: 2)') end end context 'when receiver is implicit' do it "doesn't autocorrect" do new_source = autocorrect_source('merge!(foo: 1, bar: 2)') expect(new_source).to eq('merge!(foo: 1, bar: 2)') end end context 'when any argument is a double splat' do it 'does not register an offense when the only argument is a' \ 'double splat' do expect_no_offenses(<<~RUBY) foo.merge!(**bar) RUBY end it 'does not register an offense when there are multiple arguments ' \ 'and at least one is a double splat' do expect_no_offenses(<<~RUBY) foo.merge!(baz: qux, **bar) RUBY end end context 'when internal to each_with_object' do it 'autocorrects when the receiver is the object being built' do source = <<~RUBY foo.each_with_object({}) do |f, hash| hash.merge!(a: 1, b: 2) end RUBY new_source = autocorrect_source(source) expect(new_source).to eq(<<~RUBY) foo.each_with_object({}) do |f, hash| hash[:a] = 1 hash[:b] = 2 end RUBY end it 'autocorrects when the receiver is the object being built when ' \ 'merge! is the last statement' do source = <<~RUBY foo.each_with_object({}) do |f, hash| some_method hash.merge!(a: 1, b: 2) end RUBY new_source = autocorrect_source(source) expect(new_source).to eq(<<~RUBY) foo.each_with_object({}) do |f, hash| some_method hash[:a] = 1 hash[:b] = 2 end RUBY end it 'autocorrects when the receiver is the object being built when ' \ 'merge! is not the last statement' do source = <<~RUBY foo.each_with_object({}) do |f, hash| hash.merge!(a: 1, b: 2) why_are_you_doing_this? end RUBY new_source = autocorrect_source(source) expect(new_source).to eq(<<~RUBY) foo.each_with_object({}) do |f, hash| hash[:a] = 1 hash[:b] = 2 why_are_you_doing_this? end RUBY end it 'does not register an offense when merge! is being assigned inside ' \ 'each_with_object' do expect_no_offenses(<<~RUBY) foo.each_with_object({}) do |f, hash| changes = hash.merge!(a: 1, b: 2) why_are_you_doing_this? end RUBY end it 'autocorrects when receiver uses element reference to the object ' \ 'built by each_with_object' do source = <<~RUBY foo.each_with_object(bar) do |f, hash| hash[:a].merge!(b: "") end RUBY new_source = autocorrect_source(source) expect(new_source).to eq(<<~RUBY) foo.each_with_object(bar) do |f, hash| hash[:a][:b] = "" end RUBY end it 'autocorrects when receiver uses multiple element references to the ' \ 'object built by each_with_object' do source = <<~RUBY foo.each_with_object(bar) do |f, hash| hash[:a][:b].merge!(c: "") end RUBY new_source = autocorrect_source(source) expect(new_source).to eq(<<~RUBY) foo.each_with_object(bar) do |f, hash| hash[:a][:b][:c] = "" end RUBY end it 'autocorrects merge! called on any method on the object built ' \ 'by each_with_object' do source = <<~RUBY foo.each_with_object(bar) do |f, hash| hash.bar.merge!(c: "") end RUBY new_source = autocorrect_source(source) expect(new_source).to eq(<<~RUBY) foo.each_with_object(bar) do |f, hash| hash.bar[:c] = "" end RUBY end end %w[if unless while until].each do |kw| context "when there is a modifier #{kw}, and more than 1 pair" do it "autocorrects it to an #{kw} block" do new_source = autocorrect_source( <<~RUBY hash = {} hash.merge!(a: 1, b: 2) #{kw} condition1 && condition2 RUBY ) expect(new_source).to eq(<<~RUBY) hash = {} #{kw} condition1 && condition2 hash[:a] = 1 hash[:b] = 2 end RUBY end context 'when original code was indented' do it 'maintains proper indentation' do new_source = autocorrect_source( <<~RUBY hash = {} begin hash.merge!(a: 1, b: 2) #{kw} condition1 end RUBY ) expect(new_source).to eq(<<~RUBY) hash = {} begin #{kw} condition1 hash[:a] = 1 hash[:b] = 2 end end RUBY end end end end context 'when code is indented, and there is more than 1 pair' do it 'indents the autocorrected code properly' do new_source = autocorrect_source(<<~RUBY) hash = {} begin hash.merge!(a: 1, b: 2) end RUBY expect(new_source).to eq(<<~RUBY) hash = {} begin hash[:a] = 1 hash[:b] = 2 end RUBY end end it 'does not register an offense when using an empty hash argument' do expect_no_offenses(<<~RUBY) foo.merge!({}) RUBY end it "doesn't register an error when return value is used" do expect_no_offenses(<<~RUBY) variable = hash.merge!(a: 1) puts variable RUBY end it 'formats the error message correctly for hash.merge!(a: 1)' do expect_offense(<<~RUBY) hash.merge!(a: 1) ^^^^^^^^^^^^^^^^^ Use `hash[:a] = 1` instead of `hash.merge!(a: 1)`. RUBY end context 'with MaxKeyValuePairs of 1' do let(:cop_config) do { 'MaxKeyValuePairs' => 1 } end it "doesn't register errors for multi-value hash merges" do expect_no_offenses(<<~RUBY) hash = {} hash.merge!(a: 1, b: 2) RUBY end end context 'when MaxKeyValuePairs is set to nil' do let(:cop_config) do { 'MaxKeyValuePairs' => nil } end it 'does not raise `TypeError`' do expect_offense(<<~RUBY) hash = {} hash.merge!(a: 1, b: 2) ^^^^^^^^^^^^^^^^^^^^^^^ Use `hash[:a] = 1; hash[:b] = 2` instead of `hash.merge!(a: 1, b: 2)`. RUBY end end end rubocop-performance-1.5.2/spec/rubocop/cop/performance/regexp_match_spec.rb000066400000000000000000000276741360055361600272060ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe RuboCop::Cop::Performance::RegexpMatch, :config do subject(:cop) { described_class.new(config) } shared_examples 'offense' do |name, code, correction| it "registers an offense for #{name}" do inspect_source(code) expect(cop.offenses.size).to eq(1) end it "corrects #{name}" do new_source = autocorrect_source(code) expect(new_source).to eq(correction) end end shared_examples 'all legacy match methods' do |name, cond, correction| include_examples 'offense', "#{name} in if condition", <<-RUBY, <<-RUBY2 if #{cond} do_something end RUBY if #{correction} do_something end RUBY2 include_examples 'offense', "#{name} in unless condition", <<-RUBY, <<-RUBY2 unless #{cond} do_something end RUBY unless #{correction} do_something end RUBY2 include_examples 'offense', "#{name} in if condition", <<-RUBY, <<-RUBY2 do_something if #{cond} RUBY do_something if #{correction} RUBY2 include_examples 'offense', "#{name} in unless condition", <<-RUBY, <<-RUBY2 do_something unless #{cond} RUBY do_something unless #{correction} RUBY2 include_examples 'offense', "#{name} in elsif condition", <<-RUBY, <<-RUBY2 if cond do_something elsif #{cond} do_something2 end RUBY if cond do_something elsif #{correction} do_something2 end RUBY2 include_examples 'offense', "#{name} in case condition", <<-RUBY, <<-RUBY2 case when #{cond} do_something end RUBY case when #{correction} do_something end RUBY2 include_examples 'offense', "#{name} in ternary operator", <<-RUBY, <<-RUBY2 #{cond} ? do_something : do_something2 RUBY #{correction} ? do_something : do_something2 RUBY2 include_examples 'offense', "#{name} in method definition", <<-RUBY, <<-RUBY2 def foo if #{cond} do_something end end RUBY def foo if #{correction} do_something end end RUBY2 %w[ $& $' $` $~ $1 $2 $100 $MATCH Regexp.last_match Regexp.last_match(1) ].each do |var| it "accepts #{name} in method with #{var}" do expect_no_offenses(<<-RUBY) def foo if #{cond} do_something(#{var}) end end RUBY end it "accepts #{name} in a class method with #{var}" do expect_no_offenses(<<-RUBY) def self.foo if #{cond} do_something(#{var}) end end RUBY end it "accepts #{name} in method with #{var} before if" do expect_no_offenses(<<-RUBY) def foo return #{var} if #{cond} end RUBY end it "accepts #{name} in method with #{var} before unless" do expect_no_offenses(<<-RUBY) def foo return #{var} unless #{cond} end RUBY end it "accepts #{name} in guard condition with " \ "#{var} is used in the line after that" do expect_no_offenses(<<-RUBY) def foo return if #{cond} do_something(#{var}) end RUBY end include_examples 'offense', "#{name} in if guard condition with " \ "#{var} is used in another method", <<-RUBY, <<-RUBY2 def foo return if #{cond} end def bar do_something(#{var}) end RUBY def foo return if #{correction} end def bar do_something(#{var}) end RUBY2 it "accepts #{name} in method with #{var} in block" do expect_no_offenses(<<-RUBY) def foo bar do if #{cond} do_something end end puts #{var} end RUBY end include_examples 'offense', "#{name} in method before `#{var}`", <<-RUBY, <<-RUBY2 def foo do_something(#{var}) if #{cond} do_something2 end end RUBY def foo do_something(#{var}) if #{correction} do_something2 end end RUBY2 include_examples 'offense', "#{name} in method, `#{var}` is in other method", <<-RUBY, <<-RUBY2 def foo if #{cond} do_something2 end end def bar do_something(#{var}) end RUBY def foo if #{correction} do_something2 end end def bar do_something(#{var}) end RUBY2 include_examples 'offense', "#{name} in class method, `#{var}` is in other method", <<-RUBY, <<-RUBY2 def self.foo if #{cond} do_something2 end end def self.bar do_something(#{var}) end RUBY def self.foo if #{correction} do_something2 end end def self.bar do_something(#{var}) end RUBY2 include_examples 'offense', "#{name} in class, `#{var}` is in method", <<-RUBY, <<-RUBY2 class Foo if #{cond} do_something end def foo #{var} end end RUBY class Foo if #{correction} do_something end def foo #{var} end end RUBY2 include_examples 'offense', "#{name} in module, `#{var}` is in method", <<-RUBY, <<-RUBY2 module Foo if #{cond} do_something end def foo #{var} end end RUBY module Foo if #{correction} do_something end def foo #{var} end end RUBY2 include_examples 'offense', "#{name}, #{var} reference is overrided", <<-RUBY, <<-RUBY2 if #{cond} do_something #{cond} #{var} end RUBY if #{correction} do_something #{cond} #{var} end RUBY2 end end context 'target ruby version < 2.4', :ruby23 do it 'accepts match method call in if condition' do expect_no_offenses(<<-RUBY) if foo.match(/re/) do_something end RUBY end it 'accepts match method call in elsif condition' do expect_no_offenses(<<-RUBY) if cond do_something elsif foo.match(/re/) do_something2 end RUBY end end context 'target ruby version >= 2.4', :ruby24 do it_behaves_like('all legacy match methods', 'String#match method call', '"foo".match(re)', '"foo".match?(re)') it_behaves_like('all legacy match methods', 'String#match method call with position', '"foo".match(re, 1)', '"foo".match?(re, 1)') it_behaves_like('all legacy match methods', 'Regexp#match method call', '/re/.match(foo)', '/re/.match?(foo)') it_behaves_like('all legacy match methods', 'Regexp#match method call with position', '/re/.match(foo, 1)', '/re/.match?(foo, 1)') it_behaves_like('all legacy match methods', 'Symbol#match method call', ':foo.match(re)', ':foo.match?(re)') it_behaves_like('all legacy match methods', 'Symbol#match method call with position', ':foo.match(re, 1)', ':foo.match?(re, 1)') it_behaves_like('all legacy match methods', 'match method call for a variable', 'foo.match(/re/)', 'foo.match?(/re/)') it_behaves_like('all legacy match methods', 'match method call for a variable with position', 'foo.match(/re/, 1)', 'foo.match?(/re/, 1)') it_behaves_like('all legacy match methods', 'matching by =~`', '/re/ =~ foo', '/re/.match?(foo)') it_behaves_like('all legacy match methods', 'matching by =~`', 'foo =~ /re/', '/re/.match?(foo)') it_behaves_like('all legacy match methods', 'matching by =~`', '"foo" =~ re', '"foo".match?(re)') it_behaves_like('all legacy match methods', 'matching by =~`', 're =~ "foo"', '"foo".match?(re)') it_behaves_like('all legacy match methods', 'matching by =~`', ':foo =~ re', ':foo.match?(re)') it_behaves_like('all legacy match methods', 'matching by =~`', 're =~ :foo', ':foo.match?(re)') it_behaves_like('all legacy match methods', 'matching by =~`', 're =~ foo', 're&.match?(foo)') it_behaves_like('all legacy match methods', 'matching by =~`', 're =~ FOO', 'FOO.match?(re)') it_behaves_like('all legacy match methods', 'matching by =~`', 'FOO =~ re', 'FOO.match?(re)') it_behaves_like('all legacy match methods', 'matching by !~`', '/re/ !~ foo', '!/re/.match?(foo)') it_behaves_like('all legacy match methods', 'matching by !~`', 'foo !~ /re/', '!/re/.match?(foo)') it_behaves_like('all legacy match methods', 'matching by !~`', '"foo" !~ re', '!"foo".match?(re)') it_behaves_like('all legacy match methods', 'matching by !~`', 're !~ "foo"', '!"foo".match?(re)') it_behaves_like('all legacy match methods', 'matching by !~`', ':foo !~ re', '!:foo.match?(re)') it_behaves_like('all legacy match methods', 'matching by !~`', 're !~ :foo', '!:foo.match?(re)') it_behaves_like('all legacy match methods', 'matching by !~`', 're !~ foo', '!re&.match?(foo)') it_behaves_like('all legacy match methods', 'matching by !~`', 're !~ FOO', '!FOO.match?(re)') it_behaves_like('all legacy match methods', 'matching by !~`', 'FOO !~ re', '!FOO.match?(re)') it_behaves_like('all legacy match methods', 'matching by ===`', '/re/ === foo', '/re/.match?(foo)') it_behaves_like('all legacy match methods', 'matching by ===`', '/re/i === foo', '/re/i.match?(foo)') it 'accepts Regexp#match? method call' do expect_no_offenses(<<-RUBY) if /re/.match?(str) do_something end RUBY end it 'accepts String#match? method call' do expect_no_offenses(<<-RUBY) if str.match?(/re/) do_something end RUBY end it 'accepts match without arguments' do expect_no_offenses(<<-RUBY) code if match RUBY end it 'accepts =~ with assignment' do expect_no_offenses(<<-RUBY) if /alias_(?.*)/ =~ something do_something end RUBY end it 'accepts match without explicit regexp/str/sym use' do expect_no_offenses(<<-RUBY) if CONST.match(var) do_something end RUBY end it 'registers an offense when a regexp used independently ' \ 'with a regexp used in `if` are mixed' do expect_offense(<<-RUBY) def foo /re/ === re # A regexp used independently is not yet supported. do_something if /re/ === re ^^^^^^^^^^^ Use `match?` instead of `===` when `MatchData` is not used. end RUBY end end end rubocop-performance-1.5.2/spec/rubocop/cop/performance/reverse_each_spec.rb000066400000000000000000000037701360055361600271620ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe RuboCop::Cop::Performance::ReverseEach do subject(:cop) { described_class.new } it 'registers an offense when each is called on reverse' do expect_offense(<<~RUBY) [1, 2, 3].reverse.each { |e| puts e } ^^^^^^^^^^^^ Use `reverse_each` instead of `reverse.each`. RUBY end it 'registers an offense when each is called on reverse on a variable' do expect_offense(<<~RUBY) arr = [1, 2, 3] arr.reverse.each { |e| puts e } ^^^^^^^^^^^^ Use `reverse_each` instead of `reverse.each`. RUBY end it 'registers an offense when each is called on reverse on a method call' do expect_offense(<<~RUBY) def arr [1, 2, 3] end arr.reverse.each { |e| puts e } ^^^^^^^^^^^^ Use `reverse_each` instead of `reverse.each`. RUBY end it 'does not register an offense when reverse is used without each' do expect_no_offenses('[1, 2, 3].reverse') end it 'does not register an offense when each is used without reverse' do expect_no_offenses('[1, 2, 3].each { |e| puts e }') end context 'autocorrect' do it 'corrects reverse.each to reverse_each' do new_source = autocorrect_source('[1, 2].reverse.each { |e| puts e }') expect(new_source).to eq('[1, 2].reverse_each { |e| puts e }') end it 'corrects reverse.each to reverse_each on a variable' do new_source = autocorrect_source(<<~RUBY) arr = [1, 2] arr.reverse.each { |e| puts e } RUBY expect(new_source).to eq(<<~RUBY) arr = [1, 2] arr.reverse_each { |e| puts e } RUBY end it 'corrects reverse.each to reverse_each on a method call' do new_source = autocorrect_source(<<~RUBY) def arr [1, 2] end arr.reverse.each { |e| puts e } RUBY expect(new_source).to eq(<<~RUBY) def arr [1, 2] end arr.reverse_each { |e| puts e } RUBY end end end rubocop-performance-1.5.2/spec/rubocop/cop/performance/size_spec.rb000066400000000000000000000077251360055361600255050ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe RuboCop::Cop::Performance::Size do subject(:cop) { described_class.new } it 'does not register an offense when calling count ' \ 'as a stand alone method' do expect_no_offenses('count(items)') end it 'does not register an offense when calling count on an object ' \ 'other than an array or a hash' do expect_no_offenses('object.count(items)') end describe 'on array' do it 'registers an offense when calling count' do expect_offense(<<~RUBY) [1, 2, 3].count ^^^^^ Use `size` instead of `count`. RUBY end it 'registers an offense when calling count on to_a' do expect_offense(<<~RUBY) (1..3).to_a.count ^^^^^ Use `size` instead of `count`. RUBY end it 'registers an offense when calling count on Array[]' do expect_offense(<<~RUBY) Array[*1..5].count ^^^^^ Use `size` instead of `count`. RUBY end it 'does not register an offense when calling size' do expect_no_offenses('[1, 2, 3].size') end it 'does not register an offense when calling another method' do expect_no_offenses('[1, 2, 3].each') end it 'does not register an offense when calling count with a block' do expect_no_offenses('[1, 2, 3].count { |e| e > 3 }') end it 'does not register an offense when calling count with a to_proc block' do expect_no_offenses('[1, 2, 3].count(&:nil?)') end it 'does not register an offense when calling count with an argument' do expect_no_offenses('[1, 2, 3].count(1)') end it 'corrects count to size' do new_source = autocorrect_source('[1, 2, 3].count') expect(new_source).to eq('[1, 2, 3].size') end it 'corrects count to size on to_a' do new_source = autocorrect_source('(1..3).to_a.count') expect(new_source).to eq('(1..3).to_a.size') end it 'corrects count to size on Array[]' do new_source = autocorrect_source('Array[*1..5].count') expect(new_source).to eq('Array[*1..5].size') end end describe 'on hash' do it 'registers an offense when calling count' do expect_offense(<<~RUBY) {a: 1, b: 2, c: 3}.count ^^^^^ Use `size` instead of `count`. RUBY end it 'registers an offense when calling count on to_h' do expect_offense(<<~RUBY) [[:foo, :bar], [1, 2]].to_h.count ^^^^^ Use `size` instead of `count`. RUBY end it 'registers an offense when calling count on Hash[]' do expect_offense(<<~RUBY) Hash[*('a'..'z')].count ^^^^^ Use `size` instead of `count`. RUBY end it 'does not register an offense when calling size' do expect_no_offenses('{a: 1, b: 2, c: 3}.size') end it 'does not register an offense when calling another method' do expect_no_offenses('{a: 1, b: 2, c: 3}.each') end it 'does not register an offense when calling count with a block' do expect_no_offenses('{a: 1, b: 2, c: 3}.count { |e| e > 3 }') end it 'does not register an offense when calling count with a to_proc block' do expect_no_offenses('{a: 1, b: 2, c: 3}.count(&:nil?)') end it 'does not register an offense when calling count with an argument' do expect_no_offenses('{a: 1, b: 2, c: 3}.count(1)') end it 'corrects count to size' do new_source = autocorrect_source('{a: 1, b: 2, c: 3}.count') expect(new_source).to eq('{a: 1, b: 2, c: 3}.size') end it 'corrects count to size on to_h' do new_source = autocorrect_source('[[:foo, :bar], [1, 2]].to_h.count') expect(new_source).to eq('[[:foo, :bar], [1, 2]].to_h.size') end it 'corrects count to size on Hash[]' do new_source = autocorrect_source("Hash[*('a'..'z')].count") expect(new_source).to eq("Hash[*('a'..'z')].size") end end end rubocop-performance-1.5.2/spec/rubocop/cop/performance/start_with_spec.rb000066400000000000000000000077151360055361600267220ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe RuboCop::Cop::Performance::StartWith do subject(:cop) { described_class.new } shared_examples 'different match methods' do |method| it "autocorrects str#{method} /\\Aabc/" do new_source = autocorrect_source("str#{method} /\\Aabc/") expect(new_source).to eq "str.start_with?('abc')" end it "autocorrects /\\Aabc/#{method} str" do new_source = autocorrect_source("/\\Aabc/#{method} str") expect(new_source).to eq "str.start_with?('abc')" end # escapes like "\n" # note that "\b" is a literal backspace char in a double-quoted string... # but in a regex, it's an anchor on a word boundary %w[a e f r t v].each do |str| it "autocorrects str#{method} /\\A\\#{str}/" do new_source = autocorrect_source("str#{method} /\\A\\#{str}/") expect(new_source).to eq %{str.start_with?("\\#{str}")} end it "autocorrects /\\A\\#{str}#{method} str/" do new_source = autocorrect_source("/\\A\\#{str}/#{method} str") expect(new_source).to eq %{str.start_with?("\\#{str}")} end end # regexp metacharacters %w[. * ? $ ^ |].each do |str| it "autocorrects str#{method} /\\A\\#{str}/" do new_source = autocorrect_source("str#{method} /\\A\\#{str}/") expect(new_source).to eq "str.start_with?('#{str}')" end it "autocorrects /\\A\\#{str}/#{method} str" do new_source = autocorrect_source("/\\A\\#{str}/#{method} str") expect(new_source).to eq "str.start_with?('#{str}')" end it "doesn't register an error for str#{method} /\\A#{str}/" do expect_no_offenses("str#{method} /\\A#{str}/") end it "doesn't register an error for /\\A#{str}/#{method} str" do expect_no_offenses("/\\A#{str}/#{method} str") end end # character classes, anchors %w[w W s S d D A Z z G b B h H R X S].each do |str| it "doesn't register an error for str#{method} /\\A\\#{str}/" do expect_no_offenses("str#{method} /\\A\\#{str}/") end it "doesn't register an error for /\\A\\#{str}/#{method} str" do expect_no_offenses("/\\A\\#{str}/#{method} str") end end # characters with no special meaning whatsoever %w[i j l m o q y].each do |str| it "autocorrects str#{method} /\\A\\#{str}/" do new_source = autocorrect_source("str#{method} /\\A\\#{str}/") expect(new_source).to eq "str.start_with?('#{str}')" end it "autocorrects /\\A\\#{str}#{method} str/" do new_source = autocorrect_source("/\\A\\#{str}/#{method} str") expect(new_source).to eq "str.start_with?('#{str}')" end end it "formats the error message correctly for str#{method} /\\Aabc/" do inspect_source("str#{method} /\\Aabc/") expect(cop.messages).to eq(['Use `String#start_with?` instead of a ' \ 'regex match anchored to the beginning of ' \ 'the string.']) end it "formats the error message correctly for /\\Aabc/#{method} str" do inspect_source("/\\Aabc/#{method} str") expect(cop.messages).to eq(['Use `String#start_with?` instead of a ' \ 'regex match anchored to the beginning of ' \ 'the string.']) end it "autocorrects str#{method} /\\A\\\\/" do new_source = autocorrect_source("str#{method} /\\A\\\\/") expect(new_source).to eq("str.start_with?('\\\\')") end it "autocorrects /\\A\\\\/#{method} str" do new_source = autocorrect_source("/\\A\\\\/#{method} str") expect(new_source).to eq("str.start_with?('\\\\')") end end include_examples('different match methods', '.match?') include_examples('different match methods', ' =~') include_examples('different match methods', '.match') it 'allows match without a receiver' do expect_no_offenses('expect(subject.spin).to match(/\A\n/)') end end rubocop-performance-1.5.2/spec/rubocop/cop/performance/string_replacement_spec.rb000066400000000000000000000266721360055361600304220ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe RuboCop::Cop::Performance::StringReplacement do subject(:cop) { described_class.new } it 'accepts methods other than gsub' do expect_no_offenses("'abc'.insert(2, 'a')") end shared_examples 'accepts' do |method| context 'non deterministic parameters' do it 'accepts gsub when the length of the pattern is greater than 1' do expect_no_offenses("'abc'.#{method}('ab', 'de')") end it 'accepts the first param being a variable' do expect_no_offenses(<<~RUBY) regex = /a/ 'abc'.#{method}(regex, '1') RUBY end it 'accepts the second param being a variable' do expect_no_offenses(<<~RUBY) replacement = 'e' 'abc'.#{method}('abc', replacement) RUBY end it 'accepts the both params being a variables' do expect_no_offenses(<<~RUBY) regex = /a/ replacement = 'e' 'abc'.#{method}(regex, replacement) RUBY end it 'accepts gsub with only one param' do expect_no_offenses("'abc'.#{method}('a')") end it 'accepts gsub with a block' do expect_no_offenses("'abc'.#{method}('a') { |s| s.upcase } ") end it 'accepts a pattern with string interpolation' do expect_no_offenses(<<~RUBY) foo = 'a' 'abc'.#{method}(\"\#{foo}\", '1') RUBY end it 'accepts a replacement with string interpolation' do expect_no_offenses(<<~RUBY) foo = '1' 'abc'.#{method}('a', \"\#{foo}\") RUBY end it 'allows empty regex literal pattern' do expect_no_offenses("'abc'.#{method}(//, '1')") end it 'allows empty regex pattern from string' do expect_no_offenses("'abc'.#{method}(Regexp.new(''), '1')") end it 'allows empty regex pattern from regex' do expect_no_offenses("'abc'.#{method}(Regexp.new(//), '1')") end it 'allows regex literals with options' do expect_no_offenses("'abc'.#{method}(/a/i, '1')") end it 'allows regex with options' do expect_no_offenses("'abc'.#{method}(Regexp.new(/a/i), '1')") end it 'allows empty string pattern' do expect_no_offenses("'abc'.#{method}('', '1')") end end it 'accepts calls to gsub when the length of the pattern is shorter than ' \ 'the length of the replacement' do expect_no_offenses("'abc'.#{method}('a', 'ab')") end it 'accepts calls to gsub when the length of the pattern is longer than ' \ 'the length of the replacement' do expect_no_offenses("'abc'.#{method}('ab', 'd')") end end it_behaves_like('accepts', 'gsub') it_behaves_like('accepts', 'gsub!') describe 'deterministic regex' do describe 'regex literal' do it 'registers an offense when using space' do expect_offense(<<~RUBY) 'abc'.gsub(/ /, '') ^^^^^^^^^^^^^ Use `delete` instead of `gsub`. RUBY end %w[a b c ' " % ! = < > # & ; : ` ~ 1 2 3 - _ , \r \\\\ \y \u1234 \x65].each do |str| it "registers an offense when replacing #{str} with a literal" do inspect_source("'abc'.gsub(/#{str}/, 'a')") expect(cop.messages).to eq(['Use `tr` instead of `gsub`.']) end it "registers an offense when deleting #{str}" do inspect_source("'abc'.gsub(/#{str}/, '')") expect(cop.messages).to eq(['Use `delete` instead of `gsub`.']) end end it 'allows deterministic regex when the length of the pattern ' \ 'and the length of the replacement do not match' do expect_no_offenses(%('abc'.gsub(/a/, 'def'))) end it 'registers an offense when escape characters in regex' do inspect_source(%('abc'.gsub(/\n/, ','))) expect(cop.messages).to eq(['Use `tr` instead of `gsub`.']) end it 'registers an offense when using %r notation' do expect_offense(<<~RUBY) '/abc'.gsub(%r{a}, 'd') ^^^^^^^^^^^^^^^^ Use `tr` instead of `gsub`. RUBY end end describe 'regex constructor' do it 'registers an offense when only using word characters' do expect_offense(<<~RUBY) 'abc'.gsub(Regexp.new('b'), '2') ^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `tr` instead of `gsub`. RUBY end it 'registers an offense when regex is built from regex' do expect_offense(<<~RUBY) 'abc'.gsub(Regexp.new(/b/), '2') ^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `tr` instead of `gsub`. RUBY end it 'registers an offense when using compile' do expect_offense(<<~RUBY) '123'.gsub(Regexp.compile('1'), 'a') ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `tr` instead of `gsub`. RUBY end end end describe 'non deterministic regex' do it 'allows regex containing a +' do expect_no_offenses("'abc'.gsub(/a+/, 'def')") end it 'allows regex containing a *' do expect_no_offenses("'abc'.gsub(/a*/, 'def')") end it 'allows regex containing a ^' do expect_no_offenses("'abc'.gsub(/^/, '')") end it 'allows regex containing a $' do expect_no_offenses("'abc'.gsub(/$/, '')") end it 'allows regex containing a ?' do expect_no_offenses("'abc'.gsub(/a?/, 'def')") end it 'allows regex containing a .' do expect_no_offenses("'abc'.gsub(/./, 'a')") end it 'allows regex containing a |' do expect_no_offenses("'abc'.gsub(/a|b/, 'd')") end it 'allows regex containing ()' do expect_no_offenses("'abc'.gsub(/(ab)/, 'd')") end it 'allows regex containing escaped ()' do expect_no_offenses("'(abc)'.gsub(/(ab)/, 'd')") end it 'allows regex containing {}' do expect_no_offenses("'abc'.gsub(/a{3,}/, 'd')") end it 'allows regex containing []' do expect_no_offenses("'abc'.gsub(/[a-z]/, 'd')") end it 'allows regex containing a backslash' do expect_no_offenses('"abc".gsub(/\\s/, "d")') end it 'allows regex literal containing interpolations' do expect_no_offenses(<<~'RUBY') foo = 'a' "abc".gsub(/#{foo}/, "d") RUBY end it 'allows regex constructor containing a string with interpolations' do expect_no_offenses(<<~'RUBY') foo = 'a' "abc".gsub(Regexp.new("#{foo}"), "d") RUBY end it 'allows regex constructor containing regex with interpolations' do expect_no_offenses(<<~'RUBY') foo = 'a' "abc".gsub(Regexp.new(/#{foo}/), "d") RUBY end end it 'registers an offense when the pattern has non deterministic regex ' \ 'as a string' do expect_offense(<<~RUBY) 'a + c'.gsub('+', '-') ^^^^^^^^^^^^^^ Use `tr` instead of `gsub`. RUBY end it 'registers an offense when using gsub to find and replace ' \ 'a single character' do expect_offense(<<~RUBY) 'abc'.gsub('a', '1') ^^^^^^^^^^^^^^ Use `tr` instead of `gsub`. RUBY end it 'registers an offense when using gsub! to find and replace ' \ 'a single character ' do expect_offense(<<~RUBY) 'abc'.gsub!('a', '1') ^^^^^^^^^^^^^^^ Use `tr!` instead of `gsub!`. RUBY end it 'registers an offense for gsub! when deleting one characters' do expect_offense(<<~RUBY) 'abc'.gsub!('a', '') ^^^^^^^^^^^^^^ Use `delete!` instead of `gsub!`. RUBY end it 'registers an offense when using escape characters in the replacement' do inspect_source("'abc'.gsub('a', '\n')") expect(cop.messages).to eq(['Use `tr` instead of `gsub`.']) end it 'registers an offense when using escape characters in the pattern' do inspect_source("'abc'.gsub('\n', ',')") expect(cop.messages).to eq(['Use `tr` instead of `gsub`.']) end context 'auto-correct' do describe 'corrects to tr' do it 'corrects when the length of the pattern and replacement are one' do new_source = autocorrect_source("'abc'.gsub('a', 'd')") expect(new_source).to eq("'abc'.tr('a', 'd')") end it 'corrects when the pattern is a regex literal' do new_source = autocorrect_source("'abc'.gsub(/a/, '1')") expect(new_source).to eq("'abc'.tr('a', '1')") end it 'corrects when the pattern is a regex literal using %r' do new_source = autocorrect_source("'abc'.gsub(%r{a}, '1')") expect(new_source).to eq("'abc'.tr('a', '1')") end it 'corrects when the pattern uses Regexp.new' do new_source = autocorrect_source("'abc'.gsub(Regexp.new('a'), '1')") expect(new_source).to eq("'abc'.tr('a', '1')") end it 'corrects when the pattern uses Regexp.compile' do new_source = autocorrect_source("'abc'.gsub(Regexp.compile('a'), '1')") expect(new_source).to eq("'abc'.tr('a', '1')") end it 'corrects when the replacement contains a new line character' do new_source = autocorrect_source("'abc'.gsub('a', '\n')") expect(new_source).to eq("'abc'.tr('a', '\n')") end it 'corrects when the replacement contains escape backslash' do new_source = autocorrect_source("\"\".gsub('/', '\\\\')") expect(new_source).to eq("\"\".tr('/', '\\\\')") end it 'corrects when the pattern contains a new line character' do new_source = autocorrect_source("'abc'.gsub('\n', ',')") expect(new_source).to eq("'abc'.tr('\n', ',')") end it 'corrects when the pattern contains double backslash' do new_source = autocorrect_source("''.gsub('\\\\', '')") expect(new_source).to eq("''.delete('\\\\')") end it 'corrects when replacing to a single quote' do new_source = autocorrect_source('"a`b".gsub("`", "\'")') expect(new_source).to eq('"a`b".tr("`", "\'")') end it 'corrects when replacing to a double quote' do new_source = autocorrect_source('"a`b".gsub("`", "\"")') expect(new_source).to eq('"a`b".tr("`", "\"")') end end describe 'corrects to delete' do it 'corrects when deleting a single character' do new_source = autocorrect_source("'abc'.gsub!('a', '')") expect(new_source).to eq("'abc'.delete!('a')") end it 'corrects when the pattern is a regex literal' do new_source = autocorrect_source("'abc'.gsub(/a/, '')") expect(new_source).to eq("'abc'.delete('a')") end it 'corrects when deleting an escape character' do new_source = autocorrect_source("'abc'.gsub('\n', '')") expect(new_source).to eq("'abc'.delete('\n')") end it 'corrects when the pattern uses Regexp.new' do new_source = autocorrect_source("'abc'.gsub(Regexp.new('a'), '')") expect(new_source).to eq("'abc'.delete('a')") end it 'corrects when the pattern uses Regexp.compile' do new_source = autocorrect_source("'ab'.gsub(Regexp.compile('a'), '')") expect(new_source).to eq("'ab'.delete('a')") end it 'corrects when there are no brackets' do new_source = autocorrect_source("'abc'.gsub! 'a', ''") expect(new_source).to eq("'abc'.delete! 'a'") end it 'corrects when a regexp contains escapes' do new_source = autocorrect_source("'abc'.gsub(/\\n/, '')") expect(new_source).to eq(%('abc'.delete("\\n"))) end end end end rubocop-performance-1.5.2/spec/rubocop/cop/performance/times_map_spec.rb000066400000000000000000000045541360055361600265060ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe RuboCop::Cop::Performance::TimesMap do subject(:cop) { described_class.new } before do inspect_source(source) end shared_examples 'map_or_collect' do |method| context ".times.#{method}" do context 'with a block' do let(:source) { "4.times.#{method} { |i| i.to_s }" } it 'registers an offense' do expect(cop.offenses.size).to eq(1) expect(cop.offenses.first.message).to eq( "Use `Array.new(4)` with a block instead of `.times.#{method}`." ) expect(cop.highlights).to eq(["4.times.#{method} { |i| i.to_s }"]) end it 'auto-corrects' do corrected = autocorrect_source(source) expect(corrected).to eq('Array.new(4) { |i| i.to_s }') end end context 'for non-literal receiver' do let(:source) { "n.times.#{method} { |i| i.to_s }" } it 'registers an offense' do expect(cop.offenses.size).to eq(1) expect(cop.offenses.first.message).to eq( "Use `Array.new(n)` with a block instead of `.times.#{method}` " \ 'only if `n` is always 0 or more.' ) expect(cop.highlights).to eq(["n.times.#{method} { |i| i.to_s }"]) end end context 'with an explicitly passed block' do let(:source) { "4.times.#{method}(&method(:foo))" } it 'registers an offense' do expect(cop.offenses.size).to eq(1) expect(cop.offenses.first.message).to eq( "Use `Array.new(4)` with a block instead of `.times.#{method}`." ) expect(cop.highlights).to eq(["4.times.#{method}(&method(:foo))"]) end it 'auto-corrects' do corrected = autocorrect_source(source) expect(corrected).to eq('Array.new(4, &method(:foo))') end end context 'without a block' do let(:source) { "4.times.#{method}" } it "doesn't register an offense" do expect(cop.offenses.empty?).to be(true) end end context 'called on nothing' do let(:source) { "times.#{method} { |i| i.to_s }" } it "doesn't register an offense" do expect(cop.offenses.empty?).to be(true) end end end end it_behaves_like 'map_or_collect', 'map' it_behaves_like 'map_or_collect', 'collect' end rubocop-performance-1.5.2/spec/rubocop/cop/performance/unfreeze_string_spec.rb000066400000000000000000000041001360055361600277240ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe RuboCop::Cop::Performance::UnfreezeString, :config do subject(:cop) { described_class.new(config) } context 'TargetRubyVersion >= 2.3', :ruby23 do it 'registers an offense for an empty string with `.dup`' do expect_offense(<<~RUBY) "".dup ^^^^^^ Use unary plus to get an unfrozen string literal. RUBY end it 'registers an offense for a string with `.dup`' do expect_offense(<<~RUBY) "foo".dup ^^^^^^^^^ Use unary plus to get an unfrozen string literal. RUBY end it 'registers an offense for a heredoc with `.dup`' do expect_offense(<<~RUBY) <' : val}`" end value.gsub("#{Dir.pwd}/", '').rstrip end def references(config, cop) cop_config = config.for_cop(cop) urls = RuboCop::Cop::MessageAnnotator.new( config, cop.name, cop_config, {} ).urls return '' if urls.empty? content = h3('References') content << urls.map { |url| "* [#{url}](#{url})" }.join("\n") content << "\n" content end def print_cops_of_department(cops, department, config) selected_cops = cops_of_department(cops, department).select do |cop| cop.to_s.start_with?('RuboCop::Cop::Performance') end return if selected_cops.empty? content = +"# #{department}\n" selected_cops.each do |cop| content << print_cop_with_doc(cop, config) end file_name = "#{Dir.pwd}/manual/cops_#{department.downcase}.md" File.open(file_name, 'w') do |file| puts "* generated #{file_name}" file.write(content.strip + "\n") end end def print_cop_with_doc(cop, config) t = config.for_cop(cop) non_display_keys = %w[ Description Enabled StyleGuide Reference Safe SafeAutoCorrect VersionAdded VersionChanged ] pars = t.reject { |k| non_display_keys.include? k } description = 'No documentation' examples_object = [] YARD::Registry.all(:class).detect do |code_object| next unless RuboCop::Cop::Badge.for(code_object.to_s) == cop.badge description = code_object.docstring unless code_object.docstring.blank? examples_object = code_object.tags('example') end cops_body(config, cop, description, examples_object, pars) end # rubocop:disable Metrics/AbcSize def table_of_content_for_department(cops, department) selected_cops = cops_of_department(cops, department.to_sym).select do |cop| cop.to_s.start_with?('RuboCop::Cop::Performance') end return if selected_cops.empty? type_title = department[0].upcase + department[1..-1] filename = "cops_#{department.downcase}.md" content = +"#### Department [#{type_title}](#{filename})\n\n" selected_cops.each do |cop| anchor = cop.cop_name.sub('/', '').downcase content << "* [#{cop.cop_name}](#{filename}##{anchor})\n" end content end # rubocop:enable Metrics/AbcSize def print_table_of_contents(cops) path = "#{Dir.pwd}/manual/cops.md" original = File.read(path) content = +"\n" content << table_contents(cops) content << "\n" content = if original.empty? content else original.sub( /.+/m, content ) end File.write(path, content) end def table_contents(cops) cops .departments .map(&:to_s) .sort .map { |department| table_of_content_for_department(cops, department) } .reject(&:nil?) .join("\n") end def assert_manual_synchronized # Do not print diff and yield whether exit code was zero sh('git diff --quiet manual') do |outcome, _| return if outcome # Output diff before raising error sh('GIT_PAGER=cat git diff manual') warn 'The manual directory is out of sync. ' \ 'Run `rake generate_cops_documentation` and commit the results.' exit! end end def main cops = RuboCop::Cop::Cop.registry config = RuboCop::ConfigLoader.load_file('config/default.yml') YARD::Registry.load! cops.departments.sort!.each do |department| print_cops_of_department(cops, department, config) end print_table_of_contents(cops) assert_manual_synchronized if ENV['CI'] == 'true' ensure RuboCop::ConfigLoader.default_configuration = nil end main end desc 'Syntax check for the documentation comments' task documentation_syntax_check: :yard_for_generate_documentation do require 'parser/ruby25' ok = true YARD::Registry.load! cops = RuboCop::Cop::Cop.registry cops.each do |cop| examples = YARD::Registry.all(:class).find do |code_object| next unless RuboCop::Cop::Badge.for(code_object.to_s) == cop.badge break code_object.tags('example') end examples.to_a.each do |example| begin buffer = Parser::Source::Buffer.new('', 1) buffer.source = example.text parser = Parser::Ruby25.new(RuboCop::AST::Builder.new) parser.diagnostics.all_errors_are_fatal = true parser.parse(buffer) rescue Parser::SyntaxError => e path = example.object.file puts "#{path}: Syntax Error in an example. #{e}" ok = false end end end abort unless ok end rubocop-performance-1.5.2/tasks/cut_release.rake000066400000000000000000000030761360055361600217660ustar00rootroot00000000000000# frozen_string_literal: true require 'bump' namespace :cut_release do %w[major minor patch pre].each do |release_type| desc "Cut a new #{release_type} release, create release notes " \ 'and update documents.' task release_type do run(release_type) end end def add_header_to_changelog(version) changelog = File.read('CHANGELOG.md') head, tail = changelog.split("## master (unreleased)\n\n", 2) File.open('CHANGELOG.md', 'w') do |f| f << head f << "## master (unreleased)\n\n" f << "## #{version} (#{Time.now.strftime('%F')})\n\n" f << tail end end def create_release_notes(version) release_notes = new_version_changes.strip contributor_links = user_links(release_notes) File.open("relnotes/v#{version}.md", 'w') do |file| file << release_notes file << "\n\n" file << contributor_links file << "\n" end end def new_version_changes changelog = File.read('CHANGELOG.md') _, _, new_changes, _older_changes = changelog.split(/^## .*$/, 4) new_changes end def user_links(text) names = text.scan(/\[@(\S+)\]\[\]/).map(&:first).uniq names.map { |name| "[@#{name}]: https://github.com/#{name}" } .join("\n") end def run(release_type) old_version = Bump::Bump.current Bump::Bump.run(release_type, commit: false, bundle: false, tag: false) new_version = Bump::Bump.current add_header_to_changelog(new_version) create_release_notes(new_version) puts "Changed version from #{old_version} to #{new_version}." end end