commit
b21c59f751
@ -163,6 +163,14 @@ Performance/CaseWhenSplat:
|
||||
Performance/Caller:
|
||||
Enabled: false
|
||||
|
||||
# Enable when https://github.com/Homebrew/homebrew-core/pull/64983 is merged.
|
||||
Performance/ConstantRegexp:
|
||||
Enabled: false
|
||||
|
||||
# Makes code less readable for minor performance increases.
|
||||
Performance/MethodObjectAsBlock:
|
||||
Enabled: false
|
||||
|
||||
# Don't allow cops to be disabled in casks and formulae.
|
||||
Style/DisableCopsWithinSourceCodeDirective:
|
||||
Enabled: true
|
||||
|
||||
@ -44,7 +44,7 @@ GEM
|
||||
method_source (1.0.0)
|
||||
mime-types (3.3.1)
|
||||
mime-types-data (~> 3.2015)
|
||||
mime-types-data (3.2020.0512)
|
||||
mime-types-data (3.2020.1104)
|
||||
mini_portile2 (2.4.0)
|
||||
minitest (5.14.2)
|
||||
mustache (1.1.1)
|
||||
@ -98,7 +98,7 @@ GEM
|
||||
rspec-support (3.10.0)
|
||||
rspec-wait (0.0.9)
|
||||
rspec (>= 3, < 4)
|
||||
rubocop (1.3.0)
|
||||
rubocop (1.3.1)
|
||||
parallel (~> 1.10)
|
||||
parser (>= 2.7.1.5)
|
||||
rainbow (>= 2.2.2, < 4.0)
|
||||
@ -109,8 +109,8 @@ GEM
|
||||
unicode-display_width (>= 1.4.0, < 2.0)
|
||||
rubocop-ast (1.1.1)
|
||||
parser (>= 2.7.1.5)
|
||||
rubocop-performance (1.8.1)
|
||||
rubocop (>= 0.87.0)
|
||||
rubocop-performance (1.9.0)
|
||||
rubocop (>= 0.90.0, < 2.0)
|
||||
rubocop-ast (>= 0.4.0)
|
||||
rubocop-rspec (2.0.0)
|
||||
rubocop (~> 1.0)
|
||||
@ -123,11 +123,11 @@ GEM
|
||||
docile (~> 1.1)
|
||||
simplecov-html (~> 0.11)
|
||||
simplecov-html (0.12.3)
|
||||
sorbet (0.5.6060)
|
||||
sorbet-static (= 0.5.6060)
|
||||
sorbet-runtime (0.5.6060)
|
||||
sorbet (0.5.6076)
|
||||
sorbet-static (= 0.5.6076)
|
||||
sorbet-runtime (0.5.6076)
|
||||
sorbet-runtime-stub (0.2.0)
|
||||
sorbet-static (0.5.6060-universal-darwin-14)
|
||||
sorbet-static (0.5.6076-universal-darwin-14)
|
||||
spoom (1.0.4)
|
||||
colorize
|
||||
sorbet (~> 0.5.5)
|
||||
@ -142,14 +142,14 @@ GEM
|
||||
thor (>= 0.19.2)
|
||||
thor (1.0.1)
|
||||
thread_safe (0.3.6)
|
||||
tzinfo (1.2.7)
|
||||
tzinfo (1.2.8)
|
||||
thread_safe (~> 0.1)
|
||||
unf (0.1.4)
|
||||
unf_ext
|
||||
unf_ext (0.0.7.7)
|
||||
unicode-display_width (1.7.0)
|
||||
webrobots (0.1.2)
|
||||
zeitwerk (2.4.0)
|
||||
zeitwerk (2.4.1)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
@ -22,7 +22,7 @@ module Cask
|
||||
attr_reader :token, :sourcefile_path, :config, :default_config
|
||||
|
||||
def self.each(&block)
|
||||
return to_enum unless block_given?
|
||||
return to_enum unless block
|
||||
|
||||
Tap.flat_map(&:cask_files).each do |f|
|
||||
block.call CaskLoader::FromTapPathLoader.new(f).load(config: nil)
|
||||
|
||||
@ -84,7 +84,7 @@ module Cask
|
||||
|
||||
def self.parser(&block)
|
||||
Homebrew::CLI::Parser.new do
|
||||
if block_given?
|
||||
if block
|
||||
instance_eval(&block)
|
||||
else
|
||||
usage_banner <<~EOS
|
||||
|
||||
@ -68,7 +68,7 @@ module Cask
|
||||
Cmd.parser do
|
||||
usage_banner banner
|
||||
|
||||
instance_eval(&block) if block_given?
|
||||
instance_eval(&block) if block
|
||||
|
||||
OPTIONS.each do |option|
|
||||
send(*option)
|
||||
|
||||
@ -34,7 +34,7 @@ module Cask
|
||||
send(*option)
|
||||
end
|
||||
|
||||
instance_eval(&block) if block_given?
|
||||
instance_eval(&block) if block
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@ -125,7 +125,7 @@ module Cask
|
||||
def language(*args, default: false, &block)
|
||||
if args.empty?
|
||||
language_eval
|
||||
elsif block_given?
|
||||
elsif block
|
||||
@language_blocks ||= {}
|
||||
@language_blocks[args] = block
|
||||
|
||||
@ -248,7 +248,7 @@ module Cask
|
||||
|
||||
def caveats(*strings, &block)
|
||||
@caveats ||= DSL::Caveats.new(cask)
|
||||
if block_given?
|
||||
if block
|
||||
@caveats.eval_caveats(&block)
|
||||
elsif strings.any?
|
||||
strings.each do |string|
|
||||
|
||||
@ -136,7 +136,7 @@ module Homebrew
|
||||
switch short, long, description: desc, env: option_to_name(long), method: :on_tail
|
||||
end
|
||||
|
||||
instance_eval(&block) if block_given?
|
||||
instance_eval(&block) if block
|
||||
end
|
||||
|
||||
def switch(*names, description: nil, env: nil, required_for: nil, depends_on: nil, method: :on)
|
||||
|
||||
@ -56,7 +56,7 @@ class CompilerFailure
|
||||
def initialize(name, version, &block)
|
||||
@name = name
|
||||
@version = Version.parse(version.to_s)
|
||||
instance_eval(&block) if block_given?
|
||||
instance_eval(&block) if block
|
||||
end
|
||||
|
||||
def fails_with?(compiler)
|
||||
|
||||
@ -120,9 +120,9 @@ class Dependency
|
||||
@expand_stack.pop
|
||||
end
|
||||
|
||||
def action(dependent, dep, &_block)
|
||||
def action(dependent, dep, &block)
|
||||
catch(:action) do
|
||||
if block_given?
|
||||
if block
|
||||
yield dependent, dep
|
||||
elsif dep.optional? || dep.recommended?
|
||||
prune unless dependent.build.with?(dep)
|
||||
|
||||
@ -4,8 +4,8 @@
|
||||
class Formula
|
||||
undef on_linux
|
||||
|
||||
def on_linux(&_block)
|
||||
raise "No block content defined for on_linux block" unless block_given?
|
||||
def on_linux(&block)
|
||||
raise "No block content defined for on_linux block" unless block
|
||||
|
||||
yield
|
||||
end
|
||||
@ -19,8 +19,8 @@ class Formula
|
||||
class << self
|
||||
undef on_linux
|
||||
|
||||
def on_linux(&_block)
|
||||
raise "No block content defined for on_linux block" unless block_given?
|
||||
def on_linux(&block)
|
||||
raise "No block content defined for on_linux block" unless block
|
||||
|
||||
yield
|
||||
end
|
||||
|
||||
@ -4,8 +4,8 @@
|
||||
class Formula
|
||||
undef on_macos
|
||||
|
||||
def on_macos(&_block)
|
||||
raise "No block content defined for on_macos block" unless block_given?
|
||||
def on_macos(&block)
|
||||
raise "No block content defined for on_macos block" unless block
|
||||
|
||||
yield
|
||||
end
|
||||
@ -13,8 +13,8 @@ class Formula
|
||||
class << self
|
||||
undef on_macos
|
||||
|
||||
def on_macos(&_block)
|
||||
raise "No block content defined for on_macos block" unless block_given?
|
||||
def on_macos(&block)
|
||||
raise "No block content defined for on_macos block" unless block
|
||||
|
||||
yield
|
||||
end
|
||||
|
||||
@ -282,7 +282,7 @@ class Formula
|
||||
# and is specified to this instance.
|
||||
def installed_alias_path
|
||||
path = build.source["path"] if build.is_a?(Tab)
|
||||
return unless path&.match?(%r{#{HOMEBREW_TAP_DIR_REGEX}/Aliases})
|
||||
return unless path&.match?(%r{#{HOMEBREW_TAP_DIR_REGEX}/Aliases}o)
|
||||
return unless File.symlink?(path)
|
||||
|
||||
path
|
||||
@ -1384,16 +1384,16 @@ class Formula
|
||||
# <pre>on_macos do
|
||||
# # Do something Mac-specific
|
||||
# end</pre>
|
||||
def on_macos(&_block)
|
||||
raise "No block content defined for on_macos block" unless block_given?
|
||||
def on_macos(&block)
|
||||
raise "No block content defined for on_macos block" unless block
|
||||
end
|
||||
|
||||
# Block only executed on Linux. No-op on macOS.
|
||||
# <pre>on_linux do
|
||||
# # Do something Linux-specific
|
||||
# end</pre>
|
||||
def on_linux(&_block)
|
||||
raise "No block content defined for on_linux block" unless block_given?
|
||||
def on_linux(&block)
|
||||
raise "No block content defined for on_linux block" unless block
|
||||
end
|
||||
|
||||
# Standard parameters for cargo builds.
|
||||
@ -2118,7 +2118,7 @@ class Formula
|
||||
# a block.
|
||||
def mkdir(name, &block)
|
||||
result = FileUtils.mkdir_p(name)
|
||||
return result unless block_given?
|
||||
return result unless block
|
||||
|
||||
FileUtils.chdir(name, &block)
|
||||
end
|
||||
@ -2458,7 +2458,7 @@ class Formula
|
||||
# end</pre>
|
||||
def stable(&block)
|
||||
@stable ||= SoftwareSpec.new(flags: build_flags)
|
||||
return @stable unless block_given?
|
||||
return @stable unless block
|
||||
|
||||
@stable.instance_eval(&block)
|
||||
end
|
||||
@ -2482,7 +2482,7 @@ class Formula
|
||||
# <pre>head "https://hg.is.awesome.but.git.has.won.example.com/", using: :hg</pre>
|
||||
def head(val = nil, specs = {}, &block)
|
||||
@head ||= HeadSoftwareSpec.new(flags: build_flags)
|
||||
if block_given?
|
||||
if block
|
||||
@head.instance_eval(&block)
|
||||
elsif val
|
||||
@head.url(val, specs)
|
||||
@ -2553,16 +2553,16 @@ class Formula
|
||||
# <pre>on_macos do
|
||||
# depends_on "mac_only_dep"
|
||||
# end</pre>
|
||||
def on_macos(&_block)
|
||||
raise "No block content defined for on_macos block" unless block_given?
|
||||
def on_macos(&block)
|
||||
raise "No block content defined for on_macos block" unless block
|
||||
end
|
||||
|
||||
# Block only executed on Linux. No-op on macOS.
|
||||
# <pre>on_linux do
|
||||
# depends_on "linux_only_dep"
|
||||
# end</pre>
|
||||
def on_linux(&_block)
|
||||
raise "No block content defined for on_linux block" unless block_given?
|
||||
def on_linux(&block)
|
||||
raise "No block content defined for on_linux block" unless block
|
||||
end
|
||||
|
||||
# @!attribute [w] option
|
||||
@ -2757,7 +2757,7 @@ class Formula
|
||||
# end</pre>
|
||||
def livecheck(&block)
|
||||
@livecheck ||= Livecheck.new(self)
|
||||
return @livecheck unless block_given?
|
||||
return @livecheck unless block
|
||||
|
||||
@livecheckable = true
|
||||
@livecheck.instance_eval(&block)
|
||||
|
||||
@ -270,7 +270,7 @@ module Language
|
||||
next unless f.symlink?
|
||||
next unless (rp = f.realpath.to_s).start_with? HOMEBREW_CELLAR
|
||||
|
||||
version = rp.match %r{^#{HOMEBREW_CELLAR}/python@(.*?)/}
|
||||
version = rp.match %r{^#{HOMEBREW_CELLAR}/python@(.*?)/}o
|
||||
version = "@#{version.captures.first}" unless version.nil?
|
||||
|
||||
new_target = rp.sub %r{#{HOMEBREW_CELLAR}/python#{version}/[^/]+}, Formula["python#{version}"].opt_prefix
|
||||
@ -281,7 +281,7 @@ module Language
|
||||
Pathname.glob(@venv_root/"lib/python*/orig-prefix.txt").each do |prefix_file|
|
||||
prefix_path = prefix_file.read
|
||||
|
||||
version = prefix_path.match %r{^#{HOMEBREW_CELLAR}/python@(.*?)/}
|
||||
version = prefix_path.match %r{^#{HOMEBREW_CELLAR}/python@(.*?)/}o
|
||||
version = "@#{version.captures.first}" unless version.nil?
|
||||
|
||||
prefix_path.sub! %r{^#{HOMEBREW_CELLAR}/python#{version}/[^/]+}, Formula["python#{version}"].opt_prefix
|
||||
|
||||
@ -129,7 +129,7 @@ class LinkageChecker
|
||||
private
|
||||
|
||||
def dylib_to_dep(dylib)
|
||||
dylib =~ %r{#{Regexp.escape(HOMEBREW_PREFIX)}/(opt|Cellar)/([\w+-.@]+)/}
|
||||
dylib =~ %r{#{Regexp.escape(HOMEBREW_PREFIX)}/(opt|Cellar)/([\w+-.@]+)/}o
|
||||
Regexp.last_match(2)
|
||||
end
|
||||
|
||||
|
||||
@ -82,7 +82,7 @@ class Requirement
|
||||
return unless @satisfied_result.is_a?(Pathname)
|
||||
|
||||
parent = @satisfied_result.resolved_path.parent
|
||||
if parent.to_s =~ %r{^#{Regexp.escape(HOMEBREW_CELLAR)}/([\w+-.@]+)/[^/]+/(s?bin)/?$}
|
||||
if parent.to_s =~ %r{^#{Regexp.escape(HOMEBREW_CELLAR)}/([\w+-.@]+)/[^/]+/(s?bin)/?$}o
|
||||
parent = HOMEBREW_PREFIX/"opt/#{Regexp.last_match(1)}/#{Regexp.last_match(2)}"
|
||||
end
|
||||
parent
|
||||
@ -168,14 +168,14 @@ class Requirement
|
||||
attr_rw :fatal, :cask, :download
|
||||
|
||||
def satisfy(options = nil, &block)
|
||||
return @satisfied if options.nil? && !block_given?
|
||||
return @satisfied if options.nil? && !block
|
||||
|
||||
options = {} if options.nil?
|
||||
@satisfied = Satisfier.new(options, &block)
|
||||
end
|
||||
|
||||
def env(*settings, &block)
|
||||
if block_given?
|
||||
if block
|
||||
@env_proc = block
|
||||
else
|
||||
super
|
||||
@ -236,9 +236,9 @@ class Requirement
|
||||
reqs
|
||||
end
|
||||
|
||||
def prune?(dependent, req, &_block)
|
||||
def prune?(dependent, req, &block)
|
||||
catch(:prune) do
|
||||
if block_given?
|
||||
if block
|
||||
yield dependent, req
|
||||
elsif req.optional? || req.recommended?
|
||||
prune unless dependent.build.with?(req)
|
||||
|
||||
@ -32,7 +32,7 @@ class Resource
|
||||
@checksum = nil
|
||||
@using = nil
|
||||
@patches = []
|
||||
instance_eval(&block) if block_given?
|
||||
instance_eval(&block) if block
|
||||
end
|
||||
|
||||
def owner=(owner)
|
||||
|
||||
@ -107,7 +107,7 @@ class SoftwareSpec
|
||||
end
|
||||
|
||||
def resource(name, klass = Resource, &block)
|
||||
if block_given?
|
||||
if block
|
||||
raise DuplicateResourceError, name if resource_defined?(name)
|
||||
|
||||
res = klass.new(name, &block)
|
||||
|
||||
@ -586,7 +586,7 @@ class Tap
|
||||
def self.each(&block)
|
||||
return unless TAP_DIRECTORY.directory?
|
||||
|
||||
return to_enum unless block_given?
|
||||
return to_enum unless block
|
||||
|
||||
TAP_DIRECTORY.subdirs.each do |user|
|
||||
user.subdirs.each do |repo|
|
||||
|
||||
@ -10,14 +10,14 @@ end
|
||||
describe "brew --cache", :integration_test do
|
||||
it "prints all cache files for a given Formula" do
|
||||
expect { brew "--cache", testball }
|
||||
.to output(%r{#{HOMEBREW_CACHE}/downloads/[\da-f]{64}--testball-}).to_stdout
|
||||
.to output(%r{#{HOMEBREW_CACHE}/downloads/[\da-f]{64}--testball-}o).to_stdout
|
||||
.and not_to_output.to_stderr
|
||||
.and be_a_success
|
||||
end
|
||||
|
||||
it "prints the cache files for a given Cask" do
|
||||
expect { brew "--cache", cask_path("local-caffeine") }
|
||||
.to output(%r{#{HOMEBREW_CACHE}/downloads/[\da-f]{64}--caffeine\.zip}).to_stdout
|
||||
.to output(%r{#{HOMEBREW_CACHE}/downloads/[\da-f]{64}--caffeine\.zip}o).to_stdout
|
||||
.and not_to_output.to_stderr
|
||||
.and be_a_success
|
||||
end
|
||||
@ -28,7 +28,7 @@ describe "brew --cache", :integration_test do
|
||||
%r{
|
||||
#{HOMEBREW_CACHE}/downloads/[\da-f]{64}--testball-.*\n
|
||||
#{HOMEBREW_CACHE}/downloads/[\da-f]{64}--caffeine\.zip
|
||||
}x,
|
||||
}xo,
|
||||
).to_stdout
|
||||
.and not_to_output.to_stderr
|
||||
.and be_a_success
|
||||
|
||||
@ -10,7 +10,7 @@ end
|
||||
describe "brew --cellar", :integration_test do
|
||||
it "returns the Cellar subdirectory for a given Formula" do
|
||||
expect { brew "--cellar", testball }
|
||||
.to output(%r{#{HOMEBREW_CELLAR}/testball}).to_stdout
|
||||
.to output(%r{#{HOMEBREW_CELLAR}/testball}o).to_stdout
|
||||
.and not_to_output.to_stderr
|
||||
.and be_a_success
|
||||
end
|
||||
|
||||
@ -10,7 +10,7 @@ end
|
||||
describe "brew --prefix", :integration_test do
|
||||
it "prints a given Formula's prefix" do
|
||||
expect { brew "--prefix", testball }
|
||||
.to output(%r{#{HOMEBREW_CELLAR}/testball}).to_stdout
|
||||
.to output(%r{#{HOMEBREW_CELLAR}/testball}o).to_stdout
|
||||
.and not_to_output.to_stderr
|
||||
.and be_a_success
|
||||
end
|
||||
|
||||
@ -10,7 +10,7 @@ end
|
||||
describe "brew --version", :integration_test do
|
||||
it "prints the Homebrew version" do
|
||||
expect { brew "--version" }
|
||||
.to output(/^Homebrew #{Regexp.escape(HOMEBREW_VERSION)}\n/).to_stdout
|
||||
.to output(/^Homebrew #{Regexp.escape(HOMEBREW_VERSION)}\n/o).to_stdout
|
||||
.and not_to_output.to_stderr
|
||||
.and be_a_success
|
||||
end
|
||||
|
||||
@ -22,7 +22,7 @@ describe "brew cleanup", :integration_test do
|
||||
(HOMEBREW_CACHE/"test").write "test"
|
||||
|
||||
expect { brew "cleanup", "--prune=all" }
|
||||
.to output(%r{#{Regexp.escape(HOMEBREW_CACHE)}/test}).to_stdout
|
||||
.to output(%r{#{Regexp.escape(HOMEBREW_CACHE)}/test}o).to_stdout
|
||||
.and not_to_output.to_stderr
|
||||
.and be_a_success
|
||||
end
|
||||
|
||||
@ -10,7 +10,7 @@ end
|
||||
describe "brew config", :integration_test do
|
||||
it "prints information about the current Homebrew configuration" do
|
||||
expect { brew "config" }
|
||||
.to output(/HOMEBREW_VERSION: #{Regexp.escape HOMEBREW_VERSION}/).to_stdout
|
||||
.to output(/HOMEBREW_VERSION: #{Regexp.escape HOMEBREW_VERSION}/o).to_stdout
|
||||
.and not_to_output.to_stderr
|
||||
.and be_a_success
|
||||
end
|
||||
|
||||
@ -12,7 +12,7 @@ describe "brew install", :integration_test do
|
||||
setup_test_formula "testball1"
|
||||
|
||||
expect { brew "install", "testball1" }
|
||||
.to output(%r{#{HOMEBREW_CELLAR}/testball1/0\.1}).to_stdout
|
||||
.to output(%r{#{HOMEBREW_CELLAR}/testball1/0\.1}o).to_stdout
|
||||
.and not_to_output.to_stderr
|
||||
.and be_a_success
|
||||
expect(HOMEBREW_CELLAR/"testball1/0.1/foo/test").not_to be_a_file
|
||||
@ -22,7 +22,7 @@ describe "brew install", :integration_test do
|
||||
setup_test_formula "testball1"
|
||||
|
||||
expect { brew "install", "testball1", "--with-foo" }
|
||||
.to output(%r{#{HOMEBREW_CELLAR}/testball1/0\.1}).to_stdout
|
||||
.to output(%r{#{HOMEBREW_CELLAR}/testball1/0\.1}o).to_stdout
|
||||
.and not_to_output.to_stderr
|
||||
.and be_a_success
|
||||
expect(HOMEBREW_CELLAR/"testball1/0.1/foo/test").to be_a_file
|
||||
@ -36,7 +36,7 @@ describe "brew install", :integration_test do
|
||||
RUBY
|
||||
|
||||
expect { brew "install", "testball1" }
|
||||
.to output(%r{#{HOMEBREW_CELLAR}/testball1/1\.0}).to_stdout
|
||||
.to output(%r{#{HOMEBREW_CELLAR}/testball1/1\.0}o).to_stdout
|
||||
.and not_to_output.to_stderr
|
||||
.and be_a_success
|
||||
expect(HOMEBREW_CELLAR/"testball1/1.0/foo/test").not_to be_a_file
|
||||
@ -69,7 +69,7 @@ describe "brew install", :integration_test do
|
||||
# and there will be the git requirement, but we cannot instantiate git
|
||||
# formula since we only have testball1 formula.
|
||||
expect { brew "install", "testball1", "--HEAD", "--ignore-dependencies" }
|
||||
.to output(%r{#{HOMEBREW_CELLAR}/testball1/HEAD-d5eb689}).to_stdout
|
||||
.to output(%r{#{HOMEBREW_CELLAR}/testball1/HEAD-d5eb689}o).to_stdout
|
||||
.and output(/Cloning into/).to_stderr
|
||||
.and be_a_success
|
||||
expect(HOMEBREW_CELLAR/"testball1/HEAD-d5eb689/foo/test").not_to be_a_file
|
||||
|
||||
@ -10,7 +10,7 @@ end
|
||||
describe "brew command", :integration_test do
|
||||
it "returns the file for a given command" do
|
||||
expect { brew "command", "info" }
|
||||
.to output(%r{#{Regexp.escape(HOMEBREW_LIBRARY_PATH)}/cmd/info.rb}).to_stdout
|
||||
.to output(%r{#{Regexp.escape(HOMEBREW_LIBRARY_PATH)}/cmd/info.rb}o).to_stdout
|
||||
.and be_a_success
|
||||
end
|
||||
end
|
||||
|
||||
@ -66,7 +66,7 @@ describe Homebrew::Diagnostic::Checks do
|
||||
|
||||
specify "#check_user_path_2" do
|
||||
ENV["PATH"] = ENV["PATH"].gsub \
|
||||
%r{(?:^|#{File::PATH_SEPARATOR})#{HOMEBREW_PREFIX}/bin}, ""
|
||||
%r{(?:^|#{File::PATH_SEPARATOR})#{HOMEBREW_PREFIX}/bin}o, ""
|
||||
|
||||
expect(subject.check_user_path_1).to be nil
|
||||
expect(subject.check_user_path_2)
|
||||
|
||||
28
Library/Homebrew/vendor/bundle/bundler/setup.rb
vendored
28
Library/Homebrew/vendor/bundle/bundler/setup.rb
vendored
@ -7,15 +7,15 @@ $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/concurrent-ruby-1.1.7
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/i18n-1.8.5/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/minitest-5.14.2/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/thread_safe-0.3.6/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/tzinfo-1.2.7/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/zeitwerk-2.4.0/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/tzinfo-1.2.8/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/zeitwerk-2.4.1/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/activesupport-6.0.3.4/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/ast-2.4.1/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/bindata-2.4.8/lib"
|
||||
$:.unshift "#{path}/"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/extensions/universal-darwin-19/2.6.0/byebug-11.1.3"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/extensions/universal-darwin-20/2.6.0/byebug-11.1.3"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/byebug-11.1.3/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/extensions/universal-darwin-19/2.6.0/json-2.3.1"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/extensions/universal-darwin-20/2.6.0/json-2.3.1"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/json-2.3.1/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/docile-1.3.2/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/simplecov-html-0.12.3/lib"
|
||||
@ -27,20 +27,20 @@ $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/highline-2.0.3/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/commander-4.5.2/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/connection_pool-2.2.3/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/diff-lcs-1.4.4/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/extensions/universal-darwin-19/2.6.0/unf_ext-0.0.7.7"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/extensions/universal-darwin-20/2.6.0/unf_ext-0.0.7.7"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/unf_ext-0.0.7.7/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/unf-0.1.4/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/domain_name-0.5.20190701/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/elftools-1.1.3/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/extensions/universal-darwin-19/2.6.0/hpricot-0.8.6"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/extensions/universal-darwin-20/2.6.0/hpricot-0.8.6"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/hpricot-0.8.6/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/http-cookie-1.0.3/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/mime-types-data-3.2020.0512/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/mime-types-data-3.2020.1104/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/mime-types-3.3.1/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/net-http-digest_auth-1.4.1/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/net-http-persistent-4.0.0/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/mini_portile2-2.4.0/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/extensions/universal-darwin-19/2.6.0/nokogiri-1.10.10"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/extensions/universal-darwin-20/2.6.0/nokogiri-1.10.10"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/nokogiri-1.10.10/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/ntlm-http-0.1.1/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/webrobots-0.1.2/lib"
|
||||
@ -51,12 +51,12 @@ $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/parallel-1.20.0/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/parallel_tests-3.3.0/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/parser-2.7.2.0/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rainbow-3.0.0/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/sorbet-runtime-0.5.6040/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/sorbet-runtime-0.5.6076/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/parlour-4.0.1/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/patchelf-1.3.0/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/plist-3.5.0/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/pry-0.13.1/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/extensions/universal-darwin-19/2.6.0/rdiscount-2.2.0.2"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/extensions/universal-darwin-20/2.6.0/rdiscount-2.2.0.2"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rdiscount-2.2.0.2/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/regexp_parser-1.8.2/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rexml-3.2.4/lib"
|
||||
@ -72,13 +72,13 @@ $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rspec-wait-0.0.9/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-ast-1.1.1/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/ruby-progressbar-1.10.1/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/unicode-display_width-1.7.0/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-1.3.0/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-performance-1.8.1/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-1.3.1/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-performance-1.9.0/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-rspec-2.0.0/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-sorbet-0.5.1/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/ruby-macho-2.5.0/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/sorbet-static-0.5.6042-universal-darwin-19/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/sorbet-0.5.6042/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/sorbet-static-0.5.6076-universal-darwin-20/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/sorbet-0.5.6076/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/sorbet-runtime-stub-0.2.0/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/thor-1.0.1/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/spoom-1.0.4/lib"
|
||||
|
||||
@ -1,134 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Performance
|
||||
# This cop identifies places where custom code finding the sum of elements
|
||||
# in some Enumerable object can be replaced by `Enumerable#sum` method.
|
||||
#
|
||||
# @example
|
||||
# # bad
|
||||
# [1, 2, 3].inject(:+)
|
||||
# [1, 2, 3].reduce(10, :+)
|
||||
# [1, 2, 3].inject(&:+)
|
||||
# [1, 2, 3].reduce { |acc, elem| acc + elem }
|
||||
#
|
||||
# # good
|
||||
# [1, 2, 3].sum
|
||||
# [1, 2, 3].sum(10)
|
||||
# [1, 2, 3].sum
|
||||
#
|
||||
class Sum < Base
|
||||
include RangeHelp
|
||||
extend AutoCorrector
|
||||
|
||||
MSG = 'Use `%<good_method>s` instead of `%<bad_method>s`.'
|
||||
|
||||
def_node_matcher :sum_candidate?, <<~PATTERN
|
||||
(send _ ${:inject :reduce} $_init ? ${(sym :+) (block_pass (sym :+))})
|
||||
PATTERN
|
||||
|
||||
def_node_matcher :sum_with_block_candidate?, <<~PATTERN
|
||||
(block
|
||||
$(send _ {:inject :reduce} $_init ?)
|
||||
(args (arg $_acc) (arg $_elem))
|
||||
$send)
|
||||
PATTERN
|
||||
|
||||
def_node_matcher :acc_plus_elem?, <<~PATTERN
|
||||
(send (lvar %1) :+ (lvar %2))
|
||||
PATTERN
|
||||
alias elem_plus_acc? acc_plus_elem?
|
||||
|
||||
def on_send(node)
|
||||
sum_candidate?(node) do |method, init, operation|
|
||||
range = sum_method_range(node)
|
||||
message = build_method_message(method, init, operation)
|
||||
|
||||
add_offense(range, message: message) do |corrector|
|
||||
autocorrect(corrector, init, range)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def on_block(node)
|
||||
sum_with_block_candidate?(node) do |send, init, var_acc, var_elem, body|
|
||||
if acc_plus_elem?(body, var_acc, var_elem) || elem_plus_acc?(body, var_elem, var_acc)
|
||||
range = sum_block_range(send, node)
|
||||
message = build_block_message(send, init, var_acc, var_elem, body)
|
||||
|
||||
add_offense(range, message: message) do |corrector|
|
||||
autocorrect(corrector, init, range)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def autocorrect(corrector, init, range)
|
||||
return if init.empty?
|
||||
|
||||
replacement = build_good_method(init)
|
||||
|
||||
corrector.replace(range, replacement)
|
||||
end
|
||||
|
||||
def sum_method_range(node)
|
||||
range_between(node.loc.selector.begin_pos, node.loc.end.end_pos)
|
||||
end
|
||||
|
||||
def sum_block_range(send, node)
|
||||
range_between(send.loc.selector.begin_pos, node.loc.end.end_pos)
|
||||
end
|
||||
|
||||
def build_method_message(method, init, operation)
|
||||
good_method = build_good_method(init)
|
||||
bad_method = build_method_bad_method(init, method, operation)
|
||||
format(MSG, good_method: good_method, bad_method: bad_method)
|
||||
end
|
||||
|
||||
def build_block_message(send, init, var_acc, var_elem, body)
|
||||
good_method = build_good_method(init)
|
||||
bad_method = build_block_bad_method(send.method_name, init, var_acc, var_elem, body)
|
||||
format(MSG, good_method: good_method, bad_method: bad_method)
|
||||
end
|
||||
|
||||
def build_good_method(init)
|
||||
good_method = 'sum'
|
||||
|
||||
unless init.empty?
|
||||
init = init.first
|
||||
good_method += "(#{init.source})" unless init.int_type? && init.value.zero?
|
||||
end
|
||||
good_method
|
||||
end
|
||||
|
||||
def build_method_bad_method(init, method, operation)
|
||||
bad_method = "#{method}("
|
||||
unless init.empty?
|
||||
init = init.first
|
||||
bad_method += "#{init.source}, "
|
||||
end
|
||||
bad_method += if operation.block_pass_type?
|
||||
'&:+)'
|
||||
else
|
||||
':+)'
|
||||
end
|
||||
bad_method
|
||||
end
|
||||
|
||||
def build_block_bad_method(method, init, var_acc, var_elem, body)
|
||||
bad_method = method.to_s
|
||||
|
||||
unless init.empty?
|
||||
init = init.first
|
||||
bad_method += "(#{init.source})"
|
||||
end
|
||||
bad_method += " { |#{var_acc}, #{var_elem}| #{body.source} }"
|
||||
bad_method
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -1,9 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module RuboCop
|
||||
module Performance
|
||||
module Version
|
||||
STRING = '1.8.1'
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -7,6 +7,11 @@ Performance/AncestorsInclude:
|
||||
Safe: false
|
||||
VersionAdded: '1.7'
|
||||
|
||||
Performance/ArraySemiInfiniteRangeSlice:
|
||||
Description: 'Identifies places where slicing arrays with semi-infinite ranges can be replaced by `Array#take` and `Array#drop`.'
|
||||
Enabled: pending
|
||||
VersionAdded: '1.9'
|
||||
|
||||
Performance/BigDecimalWithNumericArgument:
|
||||
Description: 'Convert numeric argument to string before passing to BigDecimal.'
|
||||
Enabled: 'pending'
|
||||
@ -17,11 +22,17 @@ Performance/BindCall:
|
||||
Enabled: true
|
||||
VersionAdded: '1.6'
|
||||
|
||||
Performance/BlockGivenWithExplicitBlock:
|
||||
Description: 'Check block argument explicitly instead of using `block_given?`.'
|
||||
Enabled: pending
|
||||
VersionAdded: '1.9'
|
||||
|
||||
Performance/Caller:
|
||||
Description: >-
|
||||
Use `caller(n..n)` instead of `caller`.
|
||||
Enabled: true
|
||||
VersionAdded: '0.49'
|
||||
VersionChanged: '1.9'
|
||||
|
||||
Performance/CaseWhenSplat:
|
||||
Description: >-
|
||||
@ -51,7 +62,7 @@ Performance/ChainArrayAllocation:
|
||||
|
||||
Performance/CollectionLiteralInLoop:
|
||||
Description: 'Extract Array and Hash literals outside of loops into local variables or constants.'
|
||||
Enabled: true
|
||||
Enabled: 'pending'
|
||||
VersionAdded: '1.8'
|
||||
# Min number of elements to consider an offense
|
||||
MinSize: 1
|
||||
@ -61,6 +72,11 @@ Performance/CompareWithBlock:
|
||||
Enabled: true
|
||||
VersionAdded: '0.46'
|
||||
|
||||
Performance/ConstantRegexp:
|
||||
Description: 'Finds regular expressions with dynamic components that are all constants.'
|
||||
Enabled: pending
|
||||
VersionAdded: '1.9'
|
||||
|
||||
Performance/Count:
|
||||
Description: >-
|
||||
Use `count` instead of `{select,find_all,filter,reject}...{size,count,length}`.
|
||||
@ -154,6 +170,12 @@ Performance/IoReadlines:
|
||||
Enabled: false
|
||||
VersionAdded: '1.7'
|
||||
|
||||
Performance/MethodObjectAsBlock:
|
||||
Description: 'Use block explicitly instead of block-passing a method object.'
|
||||
Reference: 'https://github.com/JuanitoFatas/fast-ruby#normal-way-to-apply-method-vs-method-code'
|
||||
Enabled: pending
|
||||
VersionAdded: '1.9'
|
||||
|
||||
Performance/OpenStruct:
|
||||
Description: 'Use `Struct` instead of `OpenStruct`.'
|
||||
Enabled: false
|
||||
@ -283,7 +305,9 @@ Performance/TimesMap:
|
||||
Performance/UnfreezeString:
|
||||
Description: 'Use unary plus to get an unfrozen string literal.'
|
||||
Enabled: true
|
||||
SafeAutoCorrect: false
|
||||
VersionAdded: '0.50'
|
||||
VersionChanged: '1.9'
|
||||
|
||||
Performance/UriDefaultParser:
|
||||
Description: 'Use `URI::DEFAULT_PARSER` instead of `URI::Parser.new`.'
|
||||
@ -25,7 +25,7 @@ module RuboCop
|
||||
# (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.)
|
||||
/\A\\A(?:#{Util::LITERAL_REGEX})+\z/.match?(regex_str)
|
||||
/\A\\A(?:#{Util::LITERAL_REGEX})+\z/o.match?(regex_str)
|
||||
end
|
||||
|
||||
def literal_at_start_with_caret?(regex_str)
|
||||
@ -35,21 +35,21 @@ module RuboCop
|
||||
# (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.)
|
||||
/\A\^(?:#{Util::LITERAL_REGEX})+\z/.match?(regex_str)
|
||||
/\A\^(?:#{Util::LITERAL_REGEX})+\z/o.match?(regex_str)
|
||||
end
|
||||
|
||||
def literal_at_end_with_backslash_z?(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?
|
||||
/\A(?:#{Util::LITERAL_REGEX})+\\z\z/.match?(regex_str)
|
||||
/\A(?:#{Util::LITERAL_REGEX})+\\z\z/o.match?(regex_str)
|
||||
end
|
||||
|
||||
def literal_at_end_with_dollar?(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?
|
||||
/\A(?:#{Util::LITERAL_REGEX})+\$\z/.match?(regex_str)
|
||||
/\A(?:#{Util::LITERAL_REGEX})+\$\z/o.match?(regex_str)
|
||||
end
|
||||
|
||||
def drop_start_metacharacter(regexp_string)
|
||||
@ -18,6 +18,7 @@ module RuboCop
|
||||
extend AutoCorrector
|
||||
|
||||
MSG = 'Use `<=` instead of `ancestors.include?`.'
|
||||
RESTRICT_ON_SEND = %i[include?].freeze
|
||||
|
||||
def_node_matcher :ancestors_include_candidate?, <<~PATTERN
|
||||
(send (send $_subclass :ancestors) :include? $_superclass)
|
||||
@ -0,0 +1,74 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Performance
|
||||
# This cop identifies places where slicing arrays with semi-infinite ranges
|
||||
# can be replaced by `Array#take` and `Array#drop`.
|
||||
#
|
||||
# @example
|
||||
# # bad
|
||||
# # array[..2]
|
||||
# # array[...2]
|
||||
# # array[2..]
|
||||
# # array[2...]
|
||||
# # array.slice(..2)
|
||||
#
|
||||
# # good
|
||||
# array.take(3)
|
||||
# array.take(2)
|
||||
# array.drop(2)
|
||||
# array.drop(2)
|
||||
# array.take(3)
|
||||
#
|
||||
class ArraySemiInfiniteRangeSlice < Base
|
||||
include RangeHelp
|
||||
extend AutoCorrector
|
||||
extend TargetRubyVersion
|
||||
|
||||
minimum_target_ruby_version 2.7
|
||||
|
||||
MSG = 'Use `%<prefer>s` instead of `%<current>s` with semi-infinite range.'
|
||||
|
||||
SLICE_METHODS = Set[:[], :slice].freeze
|
||||
RESTRICT_ON_SEND = SLICE_METHODS
|
||||
|
||||
def_node_matcher :endless_range_slice?, <<~PATTERN
|
||||
(send $_ $%SLICE_METHODS $#endless_range?)
|
||||
PATTERN
|
||||
|
||||
def_node_matcher :endless_range?, <<~PATTERN
|
||||
{
|
||||
({irange erange} nil? (int positive?))
|
||||
({irange erange} (int positive?) nil?)
|
||||
}
|
||||
PATTERN
|
||||
|
||||
def on_send(node)
|
||||
endless_range_slice?(node) do |receiver, method_name, range_node|
|
||||
prefer = range_node.begin ? :drop : :take
|
||||
message = format(MSG, prefer: prefer, current: method_name)
|
||||
|
||||
add_offense(node, message: message) do |corrector|
|
||||
corrector.replace(node, correction(receiver, range_node))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def correction(receiver, range_node)
|
||||
method_call = if range_node.begin
|
||||
"drop(#{range_node.begin.value})"
|
||||
elsif range_node.irange_type?
|
||||
"take(#{range_node.end.value + 1})"
|
||||
else
|
||||
"take(#{range_node.end.value})"
|
||||
end
|
||||
|
||||
"#{receiver.source}.#{method_call}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -20,6 +20,7 @@ module RuboCop
|
||||
extend AutoCorrector
|
||||
|
||||
MSG = 'Convert numeric argument to string before passing to `BigDecimal`.'
|
||||
RESTRICT_ON_SEND = %i[BigDecimal].freeze
|
||||
|
||||
def_node_matcher :big_decimal_with_numeric_argument?, <<~PATTERN
|
||||
(send nil? :BigDecimal $numeric_type? ...)
|
||||
@ -28,6 +28,7 @@ module RuboCop
|
||||
|
||||
MSG = 'Use `bind_call(%<bind_arg>s%<comma>s%<call_args>s)` ' \
|
||||
'instead of `bind(%<bind_arg>s).call(%<call_args>s)`.'
|
||||
RESTRICT_ON_SEND = %i[call].freeze
|
||||
|
||||
def_node_matcher :bind_with_call_method?, <<~PATTERN
|
||||
(send
|
||||
@ -0,0 +1,52 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Performance
|
||||
# This cop identifies unnecessary use of a `block_given?` where explicit check
|
||||
# of block argument would suffice.
|
||||
#
|
||||
# @example
|
||||
# # bad
|
||||
# def method(&block)
|
||||
# do_something if block_given?
|
||||
# end
|
||||
#
|
||||
# # good
|
||||
# def method(&block)
|
||||
# do_something if block
|
||||
# end
|
||||
#
|
||||
# # good - block is reassigned
|
||||
# def method(&block)
|
||||
# block ||= -> { do_something }
|
||||
# warn "Using default ..." unless block_given?
|
||||
# # ...
|
||||
# end
|
||||
#
|
||||
class BlockGivenWithExplicitBlock < Base
|
||||
extend AutoCorrector
|
||||
|
||||
RESTRICT_ON_SEND = %i[block_given?].freeze
|
||||
MSG = 'Check block argument explicitly instead of using `block_given?`.'
|
||||
|
||||
def_node_matcher :reassigns_block_arg?, '`(lvasgn %1 ...)'
|
||||
|
||||
def on_send(node)
|
||||
def_node = node.each_ancestor(:def, :defs).first
|
||||
return unless def_node
|
||||
|
||||
block_arg = def_node.arguments.find(&:blockarg_type?)
|
||||
return unless block_arg
|
||||
|
||||
block_arg_name = block_arg.loc.name.source.to_sym
|
||||
return if reassigns_block_arg?(def_node, block_arg_name)
|
||||
|
||||
add_offense(node) do |corrector|
|
||||
corrector.replace(node, block_arg_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -19,10 +19,10 @@ module RuboCop
|
||||
# caller_locations(2..2).first
|
||||
# caller_locations(1..1).first
|
||||
class Caller < Base
|
||||
MSG_BRACE = 'Use `%<method>s(%<n>d..%<n>d).first`' \
|
||||
' instead of `%<method>s[%<m>d]`.'
|
||||
MSG_FIRST = 'Use `%<method>s(%<n>d..%<n>d).first`' \
|
||||
' instead of `%<method>s.first`.'
|
||||
extend AutoCorrector
|
||||
|
||||
MSG = 'Use `%<preferred_method>s` instead of `%<current_method>s`.'
|
||||
RESTRICT_ON_SEND = %i[first []].freeze
|
||||
|
||||
def_node_matcher :slow_caller?, <<~PATTERN
|
||||
{
|
||||
@ -41,26 +41,24 @@ module RuboCop
|
||||
def on_send(node)
|
||||
return unless caller_with_scope_method?(node)
|
||||
|
||||
message = message(node)
|
||||
add_offense(node, message: message)
|
||||
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
|
||||
|
||||
preferred_method = "#{method_name}(#{n}..#{n}).first"
|
||||
|
||||
message = format(MSG, preferred_method: preferred_method, current_method: node.source)
|
||||
add_offense(node, message: message) do |corrector|
|
||||
corrector.replace(node, preferred_method)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def int_value(node)
|
||||
node.children[0]
|
||||
end
|
||||
@ -23,6 +23,7 @@ module RuboCop
|
||||
extend AutoCorrector
|
||||
|
||||
MSG = 'Use `%<good>s` instead of `%<bad>s`.'
|
||||
RESTRICT_ON_SEND = %i[== eql? !=].freeze
|
||||
CASE_METHODS = %i[downcase upcase].freeze
|
||||
|
||||
def_node_matcher :downcase_eq, <<~PATTERN
|
||||
@ -0,0 +1,68 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Performance
|
||||
# This cop finds regular expressions with dynamic components that are all constants.
|
||||
#
|
||||
# Ruby allocates a new Regexp object every time it executes a code containing such
|
||||
# a regular expression. It is more efficient to extract it into a constant
|
||||
# or add an `/o` option to perform `#{}` interpolation only once and reuse that
|
||||
# Regexp object.
|
||||
#
|
||||
# @example
|
||||
#
|
||||
# # bad
|
||||
# def tokens(pattern)
|
||||
# pattern.scan(TOKEN).reject { |token| token.match?(/\A#{SEPARATORS}\Z/) }
|
||||
# end
|
||||
#
|
||||
# # good
|
||||
# ALL_SEPARATORS = /\A#{SEPARATORS}\Z/
|
||||
# def tokens(pattern)
|
||||
# pattern.scan(TOKEN).reject { |token| token.match?(ALL_SEPARATORS) }
|
||||
# end
|
||||
#
|
||||
# # good
|
||||
# def tokens(pattern)
|
||||
# pattern.scan(TOKEN).reject { |token| token.match?(/\A#{SEPARATORS}\Z/o) }
|
||||
# end
|
||||
#
|
||||
class ConstantRegexp < Base
|
||||
extend AutoCorrector
|
||||
|
||||
MSG = 'Extract this regexp into a constant or append an `/o` option to its options.'
|
||||
|
||||
def on_regexp(node)
|
||||
return if within_const_assignment?(node) ||
|
||||
!include_interpolated_const?(node) ||
|
||||
node.single_interpolation?
|
||||
|
||||
add_offense(node) do |corrector|
|
||||
corrector.insert_after(node, 'o')
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def within_const_assignment?(node)
|
||||
node.each_ancestor(:casgn).any?
|
||||
end
|
||||
|
||||
def_node_matcher :regexp_escape?, <<~PATTERN
|
||||
(send
|
||||
(const nil? :Regexp) :escape const_type?)
|
||||
PATTERN
|
||||
|
||||
def include_interpolated_const?(node)
|
||||
return false unless node.interpolation?
|
||||
|
||||
node.each_child_node(:begin).all? do |begin_node|
|
||||
inner_node = begin_node.children.first
|
||||
inner_node && (inner_node.const_type? || regexp_escape?(inner_node))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -42,6 +42,7 @@ module RuboCop
|
||||
extend AutoCorrector
|
||||
|
||||
MSG = 'Use `count` instead of `%<selector>s...%<counter>s`.'
|
||||
RESTRICT_ON_SEND = %i[count length size].freeze
|
||||
|
||||
def_node_matcher :count_candidate?, <<~PATTERN
|
||||
{
|
||||
@ -51,6 +51,7 @@ module RuboCop
|
||||
minimum_target_ruby_version 2.5
|
||||
|
||||
MSG = 'Use `%<prefer>s` instead of `%<current>s`.'
|
||||
RESTRICT_ON_SEND = %i[gsub gsub! sub sub!].freeze
|
||||
|
||||
PREFERRED_METHODS = {
|
||||
gsub: :delete_prefix,
|
||||
@ -51,6 +51,7 @@ module RuboCop
|
||||
minimum_target_ruby_version 2.5
|
||||
|
||||
MSG = 'Use `%<prefer>s` instead of `%<current>s`.'
|
||||
RESTRICT_ON_SEND = %i[gsub gsub! sub sub!].freeze
|
||||
|
||||
PREFERRED_METHODS = {
|
||||
gsub: :delete_suffix,
|
||||
@ -4,8 +4,8 @@ module RuboCop
|
||||
module Cop
|
||||
module Performance
|
||||
# This cop is used to identify usages of `first`, `last`, `[0]` or `[-1]`
|
||||
# chained to `select`, `find_all`, or `find_all`
|
||||
# and change them to use `detect` instead.
|
||||
# chained to `select`, `find_all` or `filter` and change them to use
|
||||
# `detect` instead.
|
||||
#
|
||||
# @example
|
||||
# # bad
|
||||
@ -39,6 +39,7 @@ module RuboCop
|
||||
'`%<first_method>s[%<index>i]`.'
|
||||
INDEX_REVERSE_MSG = 'Use `reverse.%<prefer>s` instead of ' \
|
||||
'`%<first_method>s[%<index>i]`.'
|
||||
RESTRICT_ON_SEND = %i[first last []].freeze
|
||||
|
||||
def_node_matcher :detect_candidate?, <<~PATTERN
|
||||
{
|
||||
@ -47,6 +47,7 @@ module RuboCop
|
||||
|
||||
MSG = 'Use `String#end_with?` instead of a regex match anchored to ' \
|
||||
'the end of the string.'
|
||||
RESTRICT_ON_SEND = %i[match =~ match?].freeze
|
||||
|
||||
def_node_matcher :redundant_regex?, <<~PATTERN
|
||||
{(send $!nil? {:match :=~ :match?} (regexp (str $#literal_at_end?) (regopt)))
|
||||
@ -47,6 +47,7 @@ module RuboCop
|
||||
#
|
||||
class FixedSize < Base
|
||||
MSG = 'Do not compute the size of statically sized objects.'
|
||||
RESTRICT_ON_SEND = %i[count length size].freeze
|
||||
|
||||
def_node_matcher :counter, <<~MATCHER
|
||||
(send ${array hash str sym} {:count :length :size} $...)
|
||||
@ -19,6 +19,7 @@ module RuboCop
|
||||
extend AutoCorrector
|
||||
|
||||
MSG = 'Use `flat_map` instead of `%<method>s...%<flatten>s`.'
|
||||
RESTRICT_ON_SEND = %i[flatten flatten!].freeze
|
||||
FLATTEN_MULTIPLE_LEVELS = ' Beware, `flat_map` only flattens 1 level ' \
|
||||
'and `flatten` can be used to flatten ' \
|
||||
'multiple levels.'
|
||||
@ -39,6 +39,8 @@ module RuboCop
|
||||
class InefficientHashSearch < Base
|
||||
extend AutoCorrector
|
||||
|
||||
RESTRICT_ON_SEND = %i[include?].freeze
|
||||
|
||||
def_node_matcher :inefficient_include?, <<~PATTERN
|
||||
(send (send $_ {:keys :values}) :include? _)
|
||||
PATTERN
|
||||
@ -29,14 +29,14 @@ module RuboCop
|
||||
extend AutoCorrector
|
||||
|
||||
MSG = 'Use `%<good>s` instead of `%<bad>s`.'
|
||||
ENUMERABLE_METHODS = (Enumerable.instance_methods + [:each]).freeze
|
||||
RESTRICT_ON_SEND = (Enumerable.instance_methods + [:each]).freeze
|
||||
|
||||
def_node_matcher :readlines_on_class?, <<~PATTERN
|
||||
$(send $(send (const nil? {:IO :File}) :readlines ...) #enumerable_method?)
|
||||
$(send $(send (const nil? {:IO :File}) :readlines ...) _)
|
||||
PATTERN
|
||||
|
||||
def_node_matcher :readlines_on_instance?, <<~PATTERN
|
||||
$(send $(send ${nil? !const_type?} :readlines ...) #enumerable_method? ...)
|
||||
$(send $(send ${nil? !const_type?} :readlines ...) _ ...)
|
||||
PATTERN
|
||||
|
||||
def on_send(node)
|
||||
@ -55,10 +55,6 @@ module RuboCop
|
||||
|
||||
private
|
||||
|
||||
def enumerable_method?(node)
|
||||
ENUMERABLE_METHODS.include?(node.to_sym)
|
||||
end
|
||||
|
||||
def autocorrect(corrector, enumerable_call, readlines_call, receiver)
|
||||
# We cannot safely correct `.readlines` method called on IO/File classes
|
||||
# due to its signature and we are not sure with implicit receiver
|
||||
@ -0,0 +1,32 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Performance
|
||||
# This cop identifies places where methods are converted to blocks, with the
|
||||
# use of `&method`, and passed as arguments to method calls.
|
||||
# It is faster to replace those with explicit blocks, calling those methods inside.
|
||||
#
|
||||
# @example
|
||||
# # bad
|
||||
# array.map(&method(:do_something))
|
||||
# [1, 2, 3].each(&out.method(:puts))
|
||||
#
|
||||
# # good
|
||||
# array.map { |x| do_something(x) }
|
||||
# [1, 2, 3].each { |x| out.puts(x) }
|
||||
#
|
||||
class MethodObjectAsBlock < Base
|
||||
MSG = 'Use block explicitly instead of block-passing a method object.'
|
||||
|
||||
def_node_matcher :method_object_as_argument?, <<~PATTERN
|
||||
(^send (send _ :method sym))
|
||||
PATTERN
|
||||
|
||||
def on_block_pass(node)
|
||||
add_offense(node) if method_object_as_argument?(node)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -30,6 +30,7 @@ module RuboCop
|
||||
class OpenStruct < Base
|
||||
MSG = 'Consider using `Struct` over `OpenStruct` ' \
|
||||
'to optimize the performance.'
|
||||
RESTRICT_ON_SEND = %i[new].freeze
|
||||
|
||||
def_node_matcher :open_struct, <<~PATTERN
|
||||
(send (const {nil? cbase} :OpenStruct) :new ...)
|
||||
@ -28,6 +28,7 @@ module RuboCop
|
||||
extend AutoCorrector
|
||||
|
||||
MSG = 'Use `Range#cover?` instead of `Range#%<bad_method>s`.'
|
||||
RESTRICT_ON_SEND = %i[include? member?].freeze
|
||||
|
||||
# TODO: If we traced out assignments of variables to their uses, we
|
||||
# might pick up on a few more instances of this issue
|
||||
@ -80,11 +80,11 @@ module RuboCop
|
||||
def calls_to_report(argname, body)
|
||||
return [] if blockarg_assigned?(body, argname)
|
||||
|
||||
calls = to_enum(:blockarg_calls, body, argname)
|
||||
blockarg_calls(body, argname).map do |call|
|
||||
return [] if args_include_block_pass?(call)
|
||||
|
||||
return [] if calls.any? { |call| args_include_block_pass?(call) }
|
||||
|
||||
calls
|
||||
call
|
||||
end
|
||||
end
|
||||
|
||||
def args_include_block_pass?(blockcall)
|
||||
@ -22,6 +22,7 @@ module RuboCop
|
||||
|
||||
MSG = 'Use `=~` in places where the `MatchData` returned by ' \
|
||||
'`#match` will not be used.'
|
||||
RESTRICT_ON_SEND = %i[match].freeze
|
||||
|
||||
# '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
|
||||
@ -29,6 +29,7 @@ module RuboCop
|
||||
|
||||
AREF_ASGN = '%<receiver>s[%<key>s] = %<value>s'
|
||||
MSG = 'Use `%<prefer>s` instead of `%<current>s`.'
|
||||
RESTRICT_ON_SEND = %i[merge!].freeze
|
||||
|
||||
WITH_MODIFIER_CORRECTION = <<~RUBY
|
||||
%<keyword>s %<condition>s
|
||||
@ -44,10 +44,10 @@ module RuboCop
|
||||
extend AutoCorrector
|
||||
|
||||
MSG = 'Use `%<good_method>s` instead of `%<bad_method>s`.'
|
||||
REPLACEABLE_METHODS = %i[[] slice first last take drop length size empty?].freeze
|
||||
RESTRICT_ON_SEND = %i[[] slice first last take drop length size empty?].freeze
|
||||
|
||||
def_node_matcher :redundant_chars_call?, <<~PATTERN
|
||||
(send $(send _ :chars) $#replaceable_method? $...)
|
||||
(send $(send _ :chars) $_ $...)
|
||||
PATTERN
|
||||
|
||||
def on_send(node)
|
||||
@ -66,10 +66,6 @@ module RuboCop
|
||||
|
||||
private
|
||||
|
||||
def replaceable_method?(method_name)
|
||||
REPLACEABLE_METHODS.include?(method_name)
|
||||
end
|
||||
|
||||
def offense_range(receiver, node)
|
||||
range_between(receiver.loc.selector.begin_pos, node.loc.expression.end_pos)
|
||||
end
|
||||
@ -17,6 +17,7 @@ module RuboCop
|
||||
extend AutoCorrector
|
||||
|
||||
MSG = 'Use `reverse_each` instead of `reverse.each`.'
|
||||
RESTRICT_ON_SEND = %i[each].freeze
|
||||
UNDERSCORE = '_'
|
||||
|
||||
def_node_matcher :reverse_each?, <<~MATCHER
|
||||
@ -21,6 +21,7 @@ module RuboCop
|
||||
extend AutoCorrector
|
||||
|
||||
MSG = 'Use `%<good_method>s` instead of `%<bad_method>s`.'
|
||||
RESTRICT_ON_SEND = %i[first].freeze
|
||||
|
||||
def_node_matcher :reverse_first_candidate?, <<~PATTERN
|
||||
(send $(send _ :reverse) :first (int _)?)
|
||||
@ -39,6 +39,7 @@ module RuboCop
|
||||
extend AutoCorrector
|
||||
|
||||
MSG = 'Use `size` instead of `count`.'
|
||||
RESTRICT_ON_SEND = %i[count].freeze
|
||||
|
||||
def_node_matcher :array?, <<~PATTERN
|
||||
{
|
||||
@ -22,6 +22,7 @@ module RuboCop
|
||||
extend AutoCorrector
|
||||
|
||||
MSG = 'Use `%<prefer>s` instead of `%<current>s`.'
|
||||
RESTRICT_ON_SEND = %i[gsub gsub!].freeze
|
||||
|
||||
PREFERRED_METHODS = {
|
||||
gsub: :squeeze,
|
||||
@ -58,7 +59,7 @@ module RuboCop
|
||||
private
|
||||
|
||||
def repeating_literal?(regex_str)
|
||||
regex_str.match?(/\A(?:#{Util::LITERAL_REGEX})\+\z/)
|
||||
regex_str.match?(/\A(?:#{Util::LITERAL_REGEX})\+\z/o)
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -47,6 +47,7 @@ module RuboCop
|
||||
|
||||
MSG = 'Use `String#start_with?` instead of a regex match anchored to ' \
|
||||
'the beginning of the string.'
|
||||
RESTRICT_ON_SEND = %i[match =~ match?].freeze
|
||||
|
||||
def_node_matcher :redundant_regex?, <<~PATTERN
|
||||
{(send $!nil? {:match :=~ :match?} (regexp (str $#literal_at_start?) (regopt)))
|
||||
@ -23,6 +23,7 @@ module RuboCop
|
||||
extend AutoCorrector
|
||||
|
||||
MSG = 'Use `String#include?` instead of a regex match with literal-only pattern.'
|
||||
RESTRICT_ON_SEND = %i[match =~ match?].freeze
|
||||
|
||||
def_node_matcher :redundant_regex?, <<~PATTERN
|
||||
{(send $!nil? {:match :=~ :match?} (regexp (str $#literal?) (regopt)))
|
||||
@ -47,7 +48,7 @@ module RuboCop
|
||||
private
|
||||
|
||||
def literal?(regex_str)
|
||||
regex_str.match?(/\A#{Util::LITERAL_REGEX}+\z/)
|
||||
regex_str.match?(/\A#{Util::LITERAL_REGEX}+\z/o)
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -23,6 +23,7 @@ module RuboCop
|
||||
extend AutoCorrector
|
||||
|
||||
MSG = 'Use `%<prefer>s` instead of `%<current>s`.'
|
||||
RESTRICT_ON_SEND = %i[gsub gsub!].freeze
|
||||
DETERMINISTIC_REGEX = /\A(?:#{LITERAL_REGEX})+\Z/.freeze
|
||||
DELETE = 'delete'
|
||||
TR = 'tr'
|
||||
@ -0,0 +1,236 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Performance
|
||||
# This cop identifies places where custom code finding the sum of elements
|
||||
# in some Enumerable object can be replaced by `Enumerable#sum` method.
|
||||
#
|
||||
# This cop can change auto-correction scope depending on the value of
|
||||
# `SafeAutoCorrect`.
|
||||
# Its auto-correction is marked as safe by default (`SafeAutoCorrect: true`)
|
||||
# to prevent `TypeError` in auto-correced code when initial value is not
|
||||
# specified as shown below:
|
||||
#
|
||||
# [source,ruby]
|
||||
# ----
|
||||
# ['a', 'b'].sum # => (String can't be coerced into Integer)
|
||||
# ----
|
||||
#
|
||||
# Therefore if initial value is not specified, unsafe auto-corrected will not occur.
|
||||
#
|
||||
# If you always want to enable auto-correction, you can set `SafeAutoCorrect: false`.
|
||||
#
|
||||
# [source,yaml]
|
||||
# ----
|
||||
# Performance/Sum:
|
||||
# SafeAutoCorrect: false
|
||||
# ----
|
||||
#
|
||||
# Please note that the auto-correction command line option will be changed from
|
||||
# `rubocop -a` to `rubocop -A`, which includes unsafe auto-correction.
|
||||
#
|
||||
# @example
|
||||
# # bad
|
||||
# [1, 2, 3].inject(:+) # These bad cases with no initial value are unsafe and
|
||||
# [1, 2, 3].inject(&:+) # will not be auto-correced by default. If you want to
|
||||
# [1, 2, 3].reduce { |acc, elem| acc + elem } # auto-corrected, you can set `SafeAutoCorrect: false`.
|
||||
# [1, 2, 3].reduce(10, :+)
|
||||
# [1, 2, 3].map { |elem| elem ** 2 }.sum
|
||||
# [1, 2, 3].collect(&:count).sum(10)
|
||||
#
|
||||
# # good
|
||||
# [1, 2, 3].sum
|
||||
# [1, 2, 3].sum(10)
|
||||
# [1, 2, 3].sum { |elem| elem ** 2 }
|
||||
# [1, 2, 3].sum(10, &:count)
|
||||
#
|
||||
class Sum < Base
|
||||
include RangeHelp
|
||||
extend AutoCorrector
|
||||
|
||||
MSG = 'Use `%<good_method>s` instead of `%<bad_method>s`.'
|
||||
MSG_IF_NO_INIT_VALUE =
|
||||
'Use `%<good_method>s` instead of `%<bad_method>s`, unless calling `%<bad_method>s` on an empty array.'
|
||||
RESTRICT_ON_SEND = %i[inject reduce sum].freeze
|
||||
|
||||
def_node_matcher :sum_candidate?, <<~PATTERN
|
||||
(send _ ${:inject :reduce} $_init ? ${(sym :+) (block_pass (sym :+))})
|
||||
PATTERN
|
||||
|
||||
def_node_matcher :sum_map_candidate?, <<~PATTERN
|
||||
(send
|
||||
{
|
||||
(block $(send _ {:map :collect}) ...)
|
||||
$(send _ {:map :collect} (block_pass _))
|
||||
}
|
||||
:sum $_init ?)
|
||||
PATTERN
|
||||
|
||||
def_node_matcher :sum_with_block_candidate?, <<~PATTERN
|
||||
(block
|
||||
$(send _ {:inject :reduce} $_init ?)
|
||||
(args (arg $_acc) (arg $_elem))
|
||||
$send)
|
||||
PATTERN
|
||||
|
||||
def_node_matcher :acc_plus_elem?, <<~PATTERN
|
||||
(send (lvar %1) :+ (lvar %2))
|
||||
PATTERN
|
||||
alias elem_plus_acc? acc_plus_elem?
|
||||
|
||||
def on_send(node)
|
||||
return if empty_array_literal?(node)
|
||||
|
||||
handle_sum_candidate(node)
|
||||
handle_sum_map_candidate(node)
|
||||
end
|
||||
|
||||
def on_block(node)
|
||||
sum_with_block_candidate?(node) do |send, init, var_acc, var_elem, body|
|
||||
if acc_plus_elem?(body, var_acc, var_elem) || elem_plus_acc?(body, var_elem, var_acc)
|
||||
range = sum_block_range(send, node)
|
||||
message = build_block_message(send, init, var_acc, var_elem, body)
|
||||
|
||||
add_offense(range, message: message) do |corrector|
|
||||
autocorrect(corrector, init, range)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def handle_sum_candidate(node)
|
||||
sum_candidate?(node) do |method, init, operation|
|
||||
range = sum_method_range(node)
|
||||
message = build_method_message(node, method, init, operation)
|
||||
|
||||
add_offense(range, message: message) do |corrector|
|
||||
autocorrect(corrector, init, range)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def handle_sum_map_candidate(node)
|
||||
sum_map_candidate?(node) do |map, init|
|
||||
next if node.block_literal? || node.block_argument?
|
||||
|
||||
message = build_sum_map_message(map.method_name, init)
|
||||
|
||||
add_offense(sum_map_range(map, node), message: message) do |corrector|
|
||||
autocorrect_sum_map(corrector, node, map, init)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def empty_array_literal?(node)
|
||||
receiver = node.children.first
|
||||
array_literal?(node) && receiver && receiver.children.empty?
|
||||
end
|
||||
|
||||
def array_literal?(node)
|
||||
receiver = node.children.first
|
||||
receiver&.literal? && receiver&.array_type?
|
||||
end
|
||||
|
||||
def autocorrect(corrector, init, range)
|
||||
return if init.empty? && safe_autocorrect?
|
||||
|
||||
replacement = build_good_method(init)
|
||||
|
||||
corrector.replace(range, replacement)
|
||||
end
|
||||
|
||||
def autocorrect_sum_map(corrector, sum, map, init)
|
||||
sum_range = method_call_with_args_range(sum)
|
||||
map_range = method_call_with_args_range(map)
|
||||
|
||||
block_pass = map.last_argument if map.last_argument&.block_pass_type?
|
||||
replacement = build_good_method(init, block_pass)
|
||||
|
||||
corrector.remove(sum_range)
|
||||
corrector.replace(map_range, ".#{replacement}")
|
||||
end
|
||||
|
||||
def sum_method_range(node)
|
||||
range_between(node.loc.selector.begin_pos, node.loc.end.end_pos)
|
||||
end
|
||||
|
||||
def sum_map_range(map, sum)
|
||||
range_between(map.loc.selector.begin_pos, sum.source_range.end.end_pos)
|
||||
end
|
||||
|
||||
def sum_block_range(send, node)
|
||||
range_between(send.loc.selector.begin_pos, node.loc.end.end_pos)
|
||||
end
|
||||
|
||||
def build_method_message(node, method, init, operation)
|
||||
good_method = build_good_method(init)
|
||||
bad_method = build_method_bad_method(init, method, operation)
|
||||
msg = if init.empty? && !array_literal?(node)
|
||||
MSG_IF_NO_INIT_VALUE
|
||||
else
|
||||
MSG
|
||||
end
|
||||
format(msg, good_method: good_method, bad_method: bad_method)
|
||||
end
|
||||
|
||||
def build_sum_map_message(method, init)
|
||||
sum_method = build_good_method(init)
|
||||
good_method = "#{sum_method} { ... }"
|
||||
bad_method = "#{method} { ... }.#{sum_method}"
|
||||
format(MSG, good_method: good_method, bad_method: bad_method)
|
||||
end
|
||||
|
||||
def build_block_message(send, init, var_acc, var_elem, body)
|
||||
good_method = build_good_method(init)
|
||||
bad_method = build_block_bad_method(send.method_name, init, var_acc, var_elem, body)
|
||||
format(MSG, good_method: good_method, bad_method: bad_method)
|
||||
end
|
||||
|
||||
def build_good_method(init, block_pass = nil)
|
||||
good_method = 'sum'
|
||||
|
||||
args = []
|
||||
unless init.empty?
|
||||
init = init.first
|
||||
args << init.source unless init.int_type? && init.value.zero?
|
||||
end
|
||||
args << block_pass.source if block_pass
|
||||
good_method += "(#{args.join(', ')})" unless args.empty?
|
||||
good_method
|
||||
end
|
||||
|
||||
def build_method_bad_method(init, method, operation)
|
||||
bad_method = "#{method}("
|
||||
unless init.empty?
|
||||
init = init.first
|
||||
bad_method += "#{init.source}, "
|
||||
end
|
||||
bad_method += if operation.block_pass_type?
|
||||
'&:+)'
|
||||
else
|
||||
':+)'
|
||||
end
|
||||
bad_method
|
||||
end
|
||||
|
||||
def build_block_bad_method(method, init, var_acc, var_elem, body)
|
||||
bad_method = method.to_s
|
||||
|
||||
unless init.empty?
|
||||
init = init.first
|
||||
bad_method += "(#{init.source})"
|
||||
end
|
||||
bad_method += " { |#{var_acc}, #{var_elem}| #{body.source} }"
|
||||
bad_method
|
||||
end
|
||||
|
||||
def method_call_with_args_range(node)
|
||||
node.receiver.source_range.end.join(node.source_range.end)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -23,6 +23,7 @@ module RuboCop
|
||||
MESSAGE = 'Use `Array.new(%<count>s)` with a block ' \
|
||||
'instead of `.times.%<map_or_collect>s`'
|
||||
MESSAGE_ONLY_IF = 'only if `%<count>s` is always 0 or more'
|
||||
RESTRICT_ON_SEND = %i[map collect].freeze
|
||||
|
||||
def on_send(node)
|
||||
check(node)
|
||||
@ -10,6 +10,7 @@ module RuboCop
|
||||
# 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`).
|
||||
# Therefore, auto-correction is unsafe.
|
||||
# So, if you expect `ASCII-8BIT` encoding, disable this cop.
|
||||
#
|
||||
# @example
|
||||
@ -24,7 +25,10 @@ module RuboCop
|
||||
# +'something'
|
||||
# +''
|
||||
class UnfreezeString < Base
|
||||
extend AutoCorrector
|
||||
|
||||
MSG = 'Use unary plus to get an unfrozen string literal.'
|
||||
RESTRICT_ON_SEND = %i[dup new].freeze
|
||||
|
||||
def_node_matcher :dup_string?, <<~PATTERN
|
||||
(send {str dstr} :dup)
|
||||
@ -38,7 +42,21 @@ module RuboCop
|
||||
PATTERN
|
||||
|
||||
def on_send(node)
|
||||
add_offense(node) if dup_string?(node) || string_new?(node)
|
||||
return unless dup_string?(node) || string_new?(node)
|
||||
|
||||
add_offense(node) do |corrector|
|
||||
corrector.replace(node, "+#{string_value(node)}")
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def string_value(node)
|
||||
if node.receiver.source == 'String' && node.method?(:new)
|
||||
node.arguments.empty? ? "''" : node.first_argument.source
|
||||
else
|
||||
node.receiver.source
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -18,6 +18,7 @@ module RuboCop
|
||||
|
||||
MSG = 'Use `%<double_colon>sURI::DEFAULT_PARSER` instead of ' \
|
||||
'`%<double_colon>sURI::Parser.new`.'
|
||||
RESTRICT_ON_SEND = %i[new].freeze
|
||||
|
||||
def_node_matcher :uri_parser_new?, <<~PATTERN
|
||||
(send
|
||||
@ -4,13 +4,16 @@ require_relative 'mixin/regexp_metacharacter'
|
||||
require_relative 'mixin/sort_block'
|
||||
|
||||
require_relative 'performance/ancestors_include'
|
||||
require_relative 'performance/array_semi_infinite_range_slice'
|
||||
require_relative 'performance/big_decimal_with_numeric_argument'
|
||||
require_relative 'performance/bind_call'
|
||||
require_relative 'performance/block_given_with_explicit_block'
|
||||
require_relative 'performance/caller'
|
||||
require_relative 'performance/case_when_splat'
|
||||
require_relative 'performance/casecmp'
|
||||
require_relative 'performance/collection_literal_in_loop'
|
||||
require_relative 'performance/compare_with_block'
|
||||
require_relative 'performance/constant_regexp'
|
||||
require_relative 'performance/count'
|
||||
require_relative 'performance/delete_prefix'
|
||||
require_relative 'performance/delete_suffix'
|
||||
@ -20,6 +23,7 @@ require_relative 'performance/end_with'
|
||||
require_relative 'performance/fixed_size'
|
||||
require_relative 'performance/flat_map'
|
||||
require_relative 'performance/inefficient_hash_search'
|
||||
require_relative 'performance/method_object_as_block'
|
||||
require_relative 'performance/open_struct'
|
||||
require_relative 'performance/range_include'
|
||||
require_relative 'performance/io_readlines'
|
||||
@ -0,0 +1,14 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module RuboCop
|
||||
module Performance
|
||||
# This module holds the RuboCop Performance version information.
|
||||
module Version
|
||||
STRING = '1.9.0'
|
||||
|
||||
def self.document_version
|
||||
STRING.match('\d+\.\d+').to_s
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -1,300 +0,0 @@
|
||||
module TZInfo
|
||||
# Use send as a workaround for erroneous 'wrong number of arguments' errors
|
||||
# with JRuby 9.0.5.0 when calling methods with Java implementations. See #114.
|
||||
send(:using, RubyCoreSupport::UntaintExt) if RubyCoreSupport.const_defined?(:UntaintExt)
|
||||
|
||||
# An InvalidZoneinfoFile exception is raised if an attempt is made to load an
|
||||
# invalid zoneinfo file.
|
||||
class InvalidZoneinfoFile < StandardError
|
||||
end
|
||||
|
||||
# Represents a timezone defined by a compiled zoneinfo TZif (\0, 2 or 3) file.
|
||||
#
|
||||
# @private
|
||||
class ZoneinfoTimezoneInfo < TransitionDataTimezoneInfo #:nodoc:
|
||||
|
||||
# Minimum supported timestamp (inclusive).
|
||||
#
|
||||
# Time.utc(1700, 1, 1).to_i
|
||||
MIN_TIMESTAMP = -8520336000
|
||||
|
||||
# Maximum supported timestamp (exclusive).
|
||||
#
|
||||
# Time.utc(2500, 1, 1).to_i
|
||||
MAX_TIMESTAMP = 16725225600
|
||||
|
||||
# Constructs the new ZoneinfoTimezoneInfo with an identifier and path
|
||||
# to the file.
|
||||
def initialize(identifier, file_path)
|
||||
super(identifier)
|
||||
|
||||
File.open(file_path, 'rb') do |file|
|
||||
parse(file)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
# Unpack will return unsigned 32-bit integers. Translate to
|
||||
# signed 32-bit.
|
||||
def make_signed_int32(long)
|
||||
long >= 0x80000000 ? long - 0x100000000 : long
|
||||
end
|
||||
|
||||
# Unpack will return a 64-bit integer as two unsigned 32-bit integers
|
||||
# (most significant first). Translate to signed 64-bit
|
||||
def make_signed_int64(high, low)
|
||||
unsigned = (high << 32) | low
|
||||
unsigned >= 0x8000000000000000 ? unsigned - 0x10000000000000000 : unsigned
|
||||
end
|
||||
|
||||
# Read bytes from file and check that the correct number of bytes could
|
||||
# be read. Raises InvalidZoneinfoFile if the number of bytes didn't match
|
||||
# the number requested.
|
||||
def check_read(file, bytes)
|
||||
result = file.read(bytes)
|
||||
|
||||
unless result && result.length == bytes
|
||||
raise InvalidZoneinfoFile, "Expected #{bytes} bytes reading '#{file.path}', but got #{result ? result.length : 0} bytes"
|
||||
end
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
# Zoneinfo files don't include the offset from standard time (std_offset)
|
||||
# for DST periods. Derive the base offset (utc_offset) where DST is
|
||||
# observed from either the previous or next non-DST period.
|
||||
#
|
||||
# Returns the index of the offset to be used prior to the first
|
||||
# transition.
|
||||
def derive_offsets(transitions, offsets)
|
||||
# The first non-DST offset (if there is one) is the offset observed
|
||||
# before the first transition. Fallback to the first DST offset if there
|
||||
# are no non-DST offsets.
|
||||
first_non_dst_offset_index = offsets.index {|o| !o[:is_dst] }
|
||||
first_offset_index = first_non_dst_offset_index || 0
|
||||
return first_offset_index if transitions.empty?
|
||||
|
||||
# Determine the utc_offset of the next non-dst offset at each transition.
|
||||
utc_offset_from_next = nil
|
||||
|
||||
transitions.reverse_each do |transition|
|
||||
offset = offsets[transition[:offset]]
|
||||
if offset[:is_dst]
|
||||
transition[:utc_offset_from_next] = utc_offset_from_next if utc_offset_from_next
|
||||
else
|
||||
utc_offset_from_next = offset[:utc_total_offset]
|
||||
end
|
||||
end
|
||||
|
||||
utc_offset_from_previous = first_non_dst_offset_index ? offsets[first_non_dst_offset_index][:utc_total_offset] : nil
|
||||
defined_offsets = {}
|
||||
|
||||
transitions.each do |transition|
|
||||
offset_index = transition[:offset]
|
||||
offset = offsets[offset_index]
|
||||
utc_total_offset = offset[:utc_total_offset]
|
||||
|
||||
if offset[:is_dst]
|
||||
utc_offset_from_next = transition[:utc_offset_from_next]
|
||||
|
||||
difference_to_previous = (utc_total_offset - (utc_offset_from_previous || utc_total_offset)).abs
|
||||
difference_to_next = (utc_total_offset - (utc_offset_from_next || utc_total_offset)).abs
|
||||
|
||||
utc_offset = if difference_to_previous == 3600
|
||||
utc_offset_from_previous
|
||||
elsif difference_to_next == 3600
|
||||
utc_offset_from_next
|
||||
elsif difference_to_previous > 0 && difference_to_next > 0
|
||||
difference_to_previous < difference_to_next ? utc_offset_from_previous : utc_offset_from_next
|
||||
elsif difference_to_previous > 0
|
||||
utc_offset_from_previous
|
||||
elsif difference_to_next > 0
|
||||
utc_offset_from_next
|
||||
else
|
||||
# No difference, assume a 1 hour offset from standard time.
|
||||
utc_total_offset - 3600
|
||||
end
|
||||
|
||||
if !offset[:utc_offset]
|
||||
offset[:utc_offset] = utc_offset
|
||||
defined_offsets[offset] = offset_index
|
||||
elsif offset[:utc_offset] != utc_offset
|
||||
# An earlier transition has already derived a different
|
||||
# utc_offset. Define a new offset or reuse an existing identically
|
||||
# defined offset.
|
||||
new_offset = offset.dup
|
||||
new_offset[:utc_offset] = utc_offset
|
||||
|
||||
offset_index = defined_offsets[new_offset]
|
||||
|
||||
unless offset_index
|
||||
offsets << new_offset
|
||||
offset_index = offsets.length - 1
|
||||
defined_offsets[new_offset] = offset_index
|
||||
end
|
||||
|
||||
transition[:offset] = offset_index
|
||||
end
|
||||
else
|
||||
utc_offset_from_previous = utc_total_offset
|
||||
end
|
||||
end
|
||||
|
||||
first_offset_index
|
||||
end
|
||||
|
||||
# Defines an offset for the timezone based on the given index and offset
|
||||
# Hash.
|
||||
def define_offset(index, offset)
|
||||
utc_total_offset = offset[:utc_total_offset]
|
||||
utc_offset = offset[:utc_offset]
|
||||
|
||||
if utc_offset
|
||||
# DST offset with base utc_offset derived by derive_offsets.
|
||||
std_offset = utc_total_offset - utc_offset
|
||||
elsif offset[:is_dst]
|
||||
# DST offset unreferenced by a transition (offset in use before the
|
||||
# first transition). No derived base UTC offset, so assume 1 hour
|
||||
# DST.
|
||||
utc_offset = utc_total_offset - 3600
|
||||
std_offset = 3600
|
||||
else
|
||||
# Non-DST offset.
|
||||
utc_offset = utc_total_offset
|
||||
std_offset = 0
|
||||
end
|
||||
|
||||
offset index, utc_offset, std_offset, offset[:abbr].untaint.to_sym
|
||||
end
|
||||
|
||||
# Parses a zoneinfo file and intializes the DataTimezoneInfo structures.
|
||||
def parse(file)
|
||||
magic, version, ttisgmtcnt, ttisstdcnt, leapcnt, timecnt, typecnt, charcnt =
|
||||
check_read(file, 44).unpack('a4 a x15 NNNNNN')
|
||||
|
||||
if magic != 'TZif'
|
||||
raise InvalidZoneinfoFile, "The file '#{file.path}' does not start with the expected header."
|
||||
end
|
||||
|
||||
if (version == '2' || version == '3') && RubyCoreSupport.time_supports_64bit
|
||||
# Skip the first 32-bit section and read the header of the second 64-bit section
|
||||
file.seek(timecnt * 5 + typecnt * 6 + charcnt + leapcnt * 8 + ttisgmtcnt + ttisstdcnt, IO::SEEK_CUR)
|
||||
|
||||
prev_version = version
|
||||
|
||||
magic, version, ttisgmtcnt, ttisstdcnt, leapcnt, timecnt, typecnt, charcnt =
|
||||
check_read(file, 44).unpack('a4 a x15 NNNNNN')
|
||||
|
||||
unless magic == 'TZif' && (version == prev_version)
|
||||
raise InvalidZoneinfoFile, "The file '#{file.path}' contains an invalid 64-bit section header."
|
||||
end
|
||||
|
||||
using_64bit = true
|
||||
elsif version != '3' && version != '2' && version != "\0"
|
||||
raise InvalidZoneinfoFile, "The file '#{file.path}' contains a version of the zoneinfo format that is not currently supported."
|
||||
else
|
||||
using_64bit = false
|
||||
end
|
||||
|
||||
unless leapcnt == 0
|
||||
raise InvalidZoneinfoFile, "The zoneinfo file '#{file.path}' contains leap second data. TZInfo requires zoneinfo files that omit leap seconds."
|
||||
end
|
||||
|
||||
transitions = []
|
||||
|
||||
if using_64bit
|
||||
timecnt.times do |i|
|
||||
high, low = check_read(file, 8).unpack('NN'.freeze)
|
||||
transition_time = make_signed_int64(high, low)
|
||||
transitions << {:at => transition_time}
|
||||
end
|
||||
else
|
||||
timecnt.times do |i|
|
||||
transition_time = make_signed_int32(check_read(file, 4).unpack('N'.freeze)[0])
|
||||
transitions << {:at => transition_time}
|
||||
end
|
||||
end
|
||||
|
||||
timecnt.times do |i|
|
||||
localtime_type = check_read(file, 1).unpack('C'.freeze)[0]
|
||||
transitions[i][:offset] = localtime_type
|
||||
end
|
||||
|
||||
offsets = []
|
||||
|
||||
typecnt.times do |i|
|
||||
gmtoff, isdst, abbrind = check_read(file, 6).unpack('NCC'.freeze)
|
||||
gmtoff = make_signed_int32(gmtoff)
|
||||
isdst = isdst == 1
|
||||
offset = {:utc_total_offset => gmtoff, :is_dst => isdst, :abbr_index => abbrind}
|
||||
|
||||
unless isdst
|
||||
offset[:utc_offset] = gmtoff
|
||||
offset[:std_offset] = 0
|
||||
end
|
||||
|
||||
offsets << offset
|
||||
end
|
||||
|
||||
abbrev = check_read(file, charcnt)
|
||||
|
||||
offsets.each do |o|
|
||||
abbrev_start = o[:abbr_index]
|
||||
raise InvalidZoneinfoFile, "Abbreviation index is out of range in file '#{file.path}'" unless abbrev_start < abbrev.length
|
||||
|
||||
abbrev_end = abbrev.index("\0", abbrev_start)
|
||||
raise InvalidZoneinfoFile, "Missing abbreviation null terminator in file '#{file.path}'" unless abbrev_end
|
||||
|
||||
o[:abbr] = RubyCoreSupport.force_encoding(abbrev[abbrev_start...abbrev_end], 'UTF-8')
|
||||
end
|
||||
|
||||
transitions.each do |t|
|
||||
if t[:offset] < 0 || t[:offset] >= offsets.length
|
||||
raise InvalidZoneinfoFile, "Invalid offset referenced by transition in file '#{file.path}'."
|
||||
end
|
||||
end
|
||||
|
||||
# Derive the offsets from standard time (std_offset).
|
||||
first_offset_index = derive_offsets(transitions, offsets)
|
||||
|
||||
define_offset(first_offset_index, offsets[first_offset_index])
|
||||
|
||||
offsets.each_with_index do |o, i|
|
||||
define_offset(i, o) unless i == first_offset_index
|
||||
end
|
||||
|
||||
if !using_64bit && !RubyCoreSupport.time_supports_negative
|
||||
# Filter out transitions that are not supported by Time on this
|
||||
# platform.
|
||||
|
||||
# Move the last transition before the epoch up to the epoch. This
|
||||
# allows for accurate conversions for all supported timestamps on the
|
||||
# platform.
|
||||
|
||||
before_epoch, after_epoch = transitions.partition {|t| t[:at] < 0}
|
||||
|
||||
if before_epoch.length > 0 && after_epoch.length > 0 && after_epoch.first[:at] != 0
|
||||
last_before = before_epoch.last
|
||||
last_before[:at] = 0
|
||||
transitions = [last_before] + after_epoch
|
||||
else
|
||||
transitions = after_epoch
|
||||
end
|
||||
end
|
||||
|
||||
# Ignore transitions that occur outside of a defined window. The
|
||||
# transition index cannot handle a large range of transition times.
|
||||
#
|
||||
# This is primarily intended to ignore the far in the past transition
|
||||
# added in zic 2014c (at timestamp -2**63 in zic 2014c and at the
|
||||
# approximate time of the big bang from zic 2014d).
|
||||
transitions.each do |t|
|
||||
at = t[:at]
|
||||
if at >= MIN_TIMESTAMP && at < MAX_TIMESTAMP
|
||||
time = Time.at(at).utc
|
||||
transition time.year, time.mon, t[:offset], at
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -10,6 +10,8 @@ require 'tzinfo/timezone_definition'
|
||||
|
||||
require 'tzinfo/timezone_offset'
|
||||
require 'tzinfo/timezone_transition'
|
||||
require 'tzinfo/transition_rule'
|
||||
require 'tzinfo/annual_rules'
|
||||
require 'tzinfo/timezone_transition_definition'
|
||||
|
||||
require 'tzinfo/timezone_index_definition'
|
||||
@ -22,6 +24,7 @@ require 'tzinfo/zoneinfo_timezone_info'
|
||||
|
||||
require 'tzinfo/data_source'
|
||||
require 'tzinfo/ruby_data_source'
|
||||
require 'tzinfo/posix_time_zone_parser'
|
||||
require 'tzinfo/zoneinfo_data_source'
|
||||
|
||||
require 'tzinfo/timezone_period'
|
||||
51
Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/tzinfo-1.2.8/lib/tzinfo/annual_rules.rb
vendored
Normal file
51
Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/tzinfo-1.2.8/lib/tzinfo/annual_rules.rb
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
module TZInfo
|
||||
# A set of rules that define when transitions occur in time zones with
|
||||
# annually occurring daylight savings time.
|
||||
#
|
||||
# @private
|
||||
class AnnualRules #:nodoc:
|
||||
# Returned by #transitions. #offset is the TimezoneOffset that applies
|
||||
# from the UTC TimeOrDateTime #at. #previous_offset is the prior
|
||||
# TimezoneOffset.
|
||||
Transition = Struct.new(:offset, :previous_offset, :at)
|
||||
|
||||
# The standard offset that applies when daylight savings time is not in
|
||||
# force.
|
||||
attr_reader :std_offset
|
||||
|
||||
# The offset that applies when daylight savings time is in force.
|
||||
attr_reader :dst_offset
|
||||
|
||||
# The rule that determines when daylight savings time starts.
|
||||
attr_reader :dst_start_rule
|
||||
|
||||
# The rule that determines when daylight savings time ends.
|
||||
attr_reader :dst_end_rule
|
||||
|
||||
# Initializes a new {AnnualRules} instance.
|
||||
def initialize(std_offset, dst_offset, dst_start_rule, dst_end_rule)
|
||||
@std_offset = std_offset
|
||||
@dst_offset = dst_offset
|
||||
@dst_start_rule = dst_start_rule
|
||||
@dst_end_rule = dst_end_rule
|
||||
end
|
||||
|
||||
# Returns the transitions between standard and daylight savings time for a
|
||||
# given year. The results are ordered by time of occurrence (earliest to
|
||||
# latest).
|
||||
def transitions(year)
|
||||
start_dst = apply_rule(@dst_start_rule, @std_offset, @dst_offset, year)
|
||||
end_dst = apply_rule(@dst_end_rule, @dst_offset, @std_offset, year)
|
||||
|
||||
end_dst.at < start_dst.at ? [end_dst, start_dst] : [start_dst, end_dst]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Applies a given rule between offsets on a year.
|
||||
def apply_rule(rule, from_offset, to_offset, year)
|
||||
at = rule.at(from_offset, year)
|
||||
Transition.new(to_offset, from_offset, at)
|
||||
end
|
||||
end
|
||||
end
|
||||
136
Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/tzinfo-1.2.8/lib/tzinfo/posix_time_zone_parser.rb
vendored
Normal file
136
Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/tzinfo-1.2.8/lib/tzinfo/posix_time_zone_parser.rb
vendored
Normal file
@ -0,0 +1,136 @@
|
||||
# encoding: UTF-8
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'strscan'
|
||||
|
||||
module TZInfo
|
||||
# An {InvalidPosixTimeZone} exception is raised if an invalid POSIX-style
|
||||
# time zone string is encountered.
|
||||
#
|
||||
# @private
|
||||
class InvalidPosixTimeZone < StandardError #:nodoc:
|
||||
end
|
||||
|
||||
# A parser for POSIX-style TZ strings used in zoneinfo files and specified
|
||||
# by tzfile.5 and tzset.3.
|
||||
#
|
||||
# @private
|
||||
class PosixTimeZoneParser #:nodoc:
|
||||
# Parses a POSIX-style TZ string, returning either a TimezoneOffset or
|
||||
# an AnnualRules instance.
|
||||
def parse(tz_string)
|
||||
raise InvalidPosixTimeZone unless tz_string.kind_of?(String)
|
||||
return nil if tz_string.empty?
|
||||
|
||||
s = StringScanner.new(tz_string)
|
||||
check_scan(s, /([^-+,\d<][^-+,\d]*) | <([^>]+)>/x)
|
||||
std_abbrev = s[1] || s[2]
|
||||
check_scan(s, /([-+]?\d+)(?::(\d+)(?::(\d+))?)?/)
|
||||
std_offset = get_offset_from_hms(s[1], s[2], s[3])
|
||||
|
||||
if s.scan(/([^-+,\d<][^-+,\d]*) | <([^>]+)>/x)
|
||||
dst_abbrev = s[1] || s[2]
|
||||
|
||||
if s.scan(/([-+]?\d+)(?::(\d+)(?::(\d+))?)?/)
|
||||
dst_offset = get_offset_from_hms(s[1], s[2], s[3])
|
||||
else
|
||||
# POSIX is negative for ahead of UTC.
|
||||
dst_offset = std_offset - 3600
|
||||
end
|
||||
|
||||
dst_difference = std_offset - dst_offset
|
||||
|
||||
start_rule = parse_rule(s, 'start')
|
||||
end_rule = parse_rule(s, 'end')
|
||||
|
||||
raise InvalidPosixTimeZone, "Expected the end of a POSIX-style time zone string but found '#{s.rest}'." if s.rest?
|
||||
|
||||
if start_rule.is_always_first_day_of_year? && start_rule.transition_at == 0 &&
|
||||
end_rule.is_always_last_day_of_year? && end_rule.transition_at == 86400 + dst_difference
|
||||
# Constant daylight savings time.
|
||||
# POSIX is negative for ahead of UTC.
|
||||
TimezoneOffset.new(-std_offset, dst_difference, dst_abbrev.to_sym)
|
||||
else
|
||||
AnnualRules.new(
|
||||
TimezoneOffset.new(-std_offset, 0, std_abbrev.to_sym),
|
||||
TimezoneOffset.new(-std_offset, dst_difference, dst_abbrev.to_sym),
|
||||
start_rule,
|
||||
end_rule)
|
||||
end
|
||||
elsif !s.rest?
|
||||
# Constant standard time.
|
||||
# POSIX is negative for ahead of UTC.
|
||||
TimezoneOffset.new(-std_offset, 0, std_abbrev.to_sym)
|
||||
else
|
||||
raise InvalidPosixTimeZone, "Expected the end of a POSIX-style time zone string but found '#{s.rest}'."
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Parses the rule from the TZ string, returning a TransitionRule.
|
||||
def parse_rule(s, type)
|
||||
check_scan(s, /,(?: (?: J(\d+) ) | (\d+) | (?: M(\d+)\.(\d)\.(\d) ) )/x)
|
||||
julian_day_of_year = s[1]
|
||||
absolute_day_of_year = s[2]
|
||||
month = s[3]
|
||||
week = s[4]
|
||||
day_of_week = s[5]
|
||||
|
||||
if s.scan(/\//)
|
||||
check_scan(s, /([-+]?\d+)(?::(\d+)(?::(\d+))?)?/)
|
||||
transition_at = get_seconds_after_midnight_from_hms(s[1], s[2], s[3])
|
||||
else
|
||||
transition_at = 7200
|
||||
end
|
||||
|
||||
begin
|
||||
if julian_day_of_year
|
||||
JulianDayOfYearTransitionRule.new(julian_day_of_year.to_i, transition_at)
|
||||
elsif absolute_day_of_year
|
||||
AbsoluteDayOfYearTransitionRule.new(absolute_day_of_year.to_i, transition_at)
|
||||
elsif week == '5'
|
||||
LastDayOfMonthTransitionRule.new(month.to_i, day_of_week.to_i, transition_at)
|
||||
else
|
||||
DayOfMonthTransitionRule.new(month.to_i, week.to_i, day_of_week.to_i, transition_at)
|
||||
end
|
||||
rescue ArgumentError => e
|
||||
raise InvalidPosixTimeZone, "Invalid #{type} rule in POSIX-style time zone string: #{e}"
|
||||
end
|
||||
end
|
||||
|
||||
# Returns an offset in seconds from hh:mm:ss values. The value can be
|
||||
# negative. -02:33:12 would represent 2 hours, 33 minutes and 12 seconds
|
||||
# ahead of UTC.
|
||||
def get_offset_from_hms(h, m, s)
|
||||
h = h.to_i
|
||||
m = m.to_i
|
||||
s = s.to_i
|
||||
raise InvalidPosixTimeZone, "Invalid minute #{m} in offset for POSIX-style time zone string." if m > 59
|
||||
raise InvalidPosixTimeZone, "Invalid second #{s} in offset for POSIX-style time zone string." if s > 59
|
||||
magnitude = (h.abs * 60 + m) * 60 + s
|
||||
h < 0 ? -magnitude : magnitude
|
||||
end
|
||||
|
||||
# Returns the seconds from midnight from hh:mm:ss values. Hours can exceed
|
||||
# 24 for a time on the following day. Hours can be negative to subtract
|
||||
# hours from midnight on the given day. -02:33:12 represents 22:33:12 on
|
||||
# the prior day.
|
||||
def get_seconds_after_midnight_from_hms(h, m, s)
|
||||
h = h.to_i
|
||||
m = m.to_i
|
||||
s = s.to_i
|
||||
raise InvalidPosixTimeZone, "Invalid minute #{m} in time for POSIX-style time zone string." if m > 59
|
||||
raise InvalidPosixTimeZone, "Invalid second #{s} in time for POSIX-style time zone string." if s > 59
|
||||
(h * 3600) + m * 60 + s
|
||||
end
|
||||
|
||||
# Scans for a pattern and raises an exception if the pattern does not
|
||||
# match the input.
|
||||
def check_scan(s, pattern)
|
||||
result = s.scan(pattern)
|
||||
raise InvalidPosixTimeZone, "Expected '#{s.rest}' to match #{pattern} in POSIX-style time zone string." unless result
|
||||
result
|
||||
end
|
||||
end
|
||||
end
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user