Warn about undocumented non-private APIs.

This commit is contained in:
Markus Reiter 2024-04-26 20:55:51 +02:00
parent 7c0b989740
commit caf87c0336
No known key found for this signature in database
GPG Key ID: 245293B51702655B
108 changed files with 1530 additions and 731 deletions

View File

@ -70,6 +70,11 @@ Style/Documentation:
- version.rb
- tap.rb
Homebrew/NegateInclude:
Exclude:
# YARD runs stand-alone.
- yard/docstring_parser.rb
Style/DocumentationMethod:
Include:
- "formula.rb"

View File

@ -18,6 +18,7 @@ end
group :doc, optional: true do
gem "yard", require: false
gem "yard-sorbet", require: false
gem "redcarpet", require: false
end
group :ast, optional: true do
gem "rubocop-ast", require: false

View File

@ -60,7 +60,6 @@ class PATH
@paths.join(File::PATH_SEPARATOR)
end
# @!visibility private
sig { returns(String) }
def to_s = to_str

View File

@ -39,8 +39,8 @@ module Homebrew
sig { returns(Pathname) }
def self.gh_executable
# NOTE: We disable HOMEBREW_VERIFY_ATTESTATIONS when installing `gh` itself,
# to prevent a cycle during bootstrapping. This can eventually be resolved
# by vendoring a pure-Ruby Sigstore verifier client.
# to prevent a cycle during bootstrapping. This can eventually be resolved
# by vendoring a pure-Ruby Sigstore verifier client.
@gh_executable ||= T.let(with_env("HOMEBREW_VERIFY_ATTESTATIONS" => nil) do
ensure_executable!("gh")
end, T.nilable(Pathname))

View File

@ -265,7 +265,7 @@ EOS
fi
}
# NOTE: the members of the array in the second arg must not have spaces!
# NOTE: The members of the array in the second arg must not have spaces!
check-array-membership() {
local item=$1
shift

View File

@ -1,6 +1,6 @@
# Note: that we use a non-standard config file name to reduce
# name clashes with other IRB config files like `.irbrc`.
# Note #2: This doesn't work with system Ruby for some reason.
# NOTE: We use a non-standard config file name to reduce name clashes with
# other IRB config files like `.irbrc`.
# NOTE: This doesn't work with system Ruby for some reason.
require 'irb/completion'

View File

@ -9,16 +9,26 @@ class BuildOptions
end
# True if a {Formula} is being built with a specific option.
# <pre>args << "--i-want-spam" if build.with? "spam"
#
# ### Examples
#
# ```ruby
# args << "--i-want-spam" if build.with? "spam"
# ```
#
# ```ruby
# args << "--qt-gui" if build.with? "qt" # "--with-qt" ==> build.with? "qt"
# ```
#
# # If a formula presents a user with a choice, but the choice must be fulfilled:
# If a formula presents a user with a choice, but the choice must be fulfilled:
#
# ```ruby
# if build.with? "example2"
# args << "--with-example2"
# else
# args << "--with-example1"
# end</pre>
# end
# ```
def with?(val)
option_names = val.respond_to?(:option_names) ? val.option_names : [val]
@ -34,7 +44,12 @@ class BuildOptions
end
# True if a {Formula} is being built without a specific option.
# <pre>args << "--no-spam-plz" if build.without? "spam"</pre>
#
# ### Example
#
# ```ruby
# args << "--no-spam-plz" if build.without? "spam"
# ```
def without?(val)
!with?(val)
end
@ -45,19 +60,33 @@ class BuildOptions
end
# True if a {Formula} is being built with {Formula.head} instead of {Formula.stable}.
# <pre>args << "--some-new-stuff" if build.head?</pre>
# <pre># If there are multiple conditional arguments use a block instead of lines.
#
# ### Examples
#
# ```ruby
# args << "--some-new-stuff" if build.head?
# ```
#
# If there are multiple conditional arguments use a block instead of lines.
#
# ```ruby
# if build.head?
# args << "--i-want-pizza"
# args << "--and-a-cold-beer" if build.with? "cold-beer"
# end</pre>
# end
# ```
def head?
include? "HEAD"
end
# True if a {Formula} is being built with {Formula.stable} instead of {Formula.head}.
# This is the default.
# <pre>args << "--some-beta" if build.head?</pre>
#
# ### Example
#
# ```ruby
# args << "--some-beta" if build.head?
# ```
def stable?
!head?
end

View File

@ -147,7 +147,6 @@ module Cask
cask.config
end
# @!visibility private
sig { returns(String) }
def to_s
"#{summarize} (#{self.class.english_name})"

View File

@ -78,7 +78,8 @@ module Cask
@allow_reassignment = allow_reassignment
@loaded_from_api = loaded_from_api
@loader = loader
# Sorbet has trouble with bound procs assigned to ivars: https://github.com/sorbet/sorbet/issues/6843
# Sorbet has trouble with bound procs assigned to instance variables:
# https://github.com/sorbet/sorbet/issues/6843
instance_variable_set(:@block, block)
@default_config = config || Config.new
@ -323,11 +324,9 @@ module Cask
end
# @api public
# @!visibility private
sig { returns(String) }
def to_s = token
# @!visibility private
sig { returns(String) }
def inspect
"#<Cask #{token}#{sourcefile_path&.to_s&.prepend(" ")}>"

View File

@ -112,6 +112,16 @@ module Cask
@token = cask.token
end
# Specifies the cask's name.
#
# NOTE: Multiple names can be specified.
#
# ### Example
#
# ```ruby
# name "Visual Studio Code"
# ```
#
# @api public
def name(*args)
@name ||= []
@ -120,6 +130,14 @@ module Cask
@name.concat(args.flatten)
end
# Describes the cask.
#
# ### Example
#
# ```ruby
# desc "Open-source code editor"
# ```
#
# @api public
def desc(description = nil)
set_unique_stanza(:desc, description.nil?) { description }
@ -146,6 +164,14 @@ module Cask
raise CaskInvalidError.new(cask, "'#{stanza}' stanza failed with: #{e}")
end
# Sets the cask's homepage.
#
# ### Example
#
# ```ruby
# homepage "https://code.visualstudio.com/"
# ```
#
# @api public
def homepage(homepage = nil)
set_unique_stanza(:homepage, homepage.nil?) { homepage }
@ -201,6 +227,14 @@ module Cask
@language_blocks.keys.flatten
end
# Sets the cask's download URL.
#
# ### Example
#
# ```ruby
# url "https://update.code.visualstudio.com/#{version}/#{arch}/stable"
# ```
#
# @api public
def url(*args, **options, &block)
caller_location = T.must(caller_locations).fetch(0)
@ -214,6 +248,8 @@ module Cask
end
end
# Sets the cask's appcast URL.
#
# @api public
def appcast(*args, **kwargs)
set_unique_stanza(:appcast, args.empty? && kwargs.empty?) do
@ -222,6 +258,22 @@ module Cask
end
end
# Sets the cask's container type or nested container path.
#
# ### Examples
#
# The container is a nested disk image:
#
# ```ruby
# container nested: "orca-#{version}.dmg"
# ```
#
# The container should not be unarchived:
#
# ```ruby
# container type: :naked
# ```
#
# @api public
def container(**kwargs)
set_unique_stanza(:container, kwargs.empty?) do
@ -229,6 +281,15 @@ module Cask
end
end
# Sets the cask's version.
#
# ### Example
#
# ```ruby
# version "1.88.1"
# ```
#
# @see DSL::Version
# @api public
def version(arg = nil)
set_unique_stanza(:version, arg.nil?) do
@ -240,6 +301,23 @@ module Cask
end
end
# Sets the cask's download checksum.
#
# ### Example
#
# For universal or single-architecture downloads:
#
# ```ruby
# sha256 "7bdb497080ffafdfd8cc94d8c62b004af1be9599e865e5555e456e2681e150ca"
# ```
#
# For architecture-dependent downloads:
#
# ```ruby
# sha256 arm: "7bdb497080ffafdfd8cc94d8c62b004af1be9599e865e5555e456e2681e150ca",
# intel: "b3c1c2442480a0219b9e05cf91d03385858c20f04b764ec08a3fa83d1b27e7b2"
# ```
#
# @api public
def sha256(arg = nil, arm: nil, intel: nil)
should_return = arg.nil? && arm.nil? && intel.nil?
@ -259,6 +337,14 @@ module Cask
end
end
# Sets the cask's architecture strings.
#
# ### Example
#
# ```ruby
# arch arm: "darwin-arm64", intel: "darwin"
# ```
#
# @api public
def arch(arm: nil, intel: nil)
should_return = arm.nil? && intel.nil?
@ -270,7 +356,10 @@ module Cask
end
end
# `depends_on` uses a load method so that multiple stanzas can be merged.
# Declare dependencies and requirements for a cask.
#
# NOTE: Multiple dependencies can be specified.
#
# @api public
def depends_on(**kwargs)
@depends_on ||= DSL::DependsOn.new
@ -284,9 +373,11 @@ module Cask
@depends_on
end
# Declare conflicts that keep a cask from installing or working correctly.
#
# @api public
def conflicts_with(**kwargs)
# TODO: remove this constraint, and instead merge multiple conflicts_with stanzas
# TODO: Remove this constraint and instead merge multiple `conflicts_with` stanzas
set_unique_stanza(:conflicts_with, kwargs.empty?) { DSL::ConflictsWith.new(**kwargs) }
end
@ -298,6 +389,8 @@ module Cask
cask.caskroom_path
end
# The staged location for this cask, including version number.
#
# @api public
def staged_path
return @staged_path if @staged_path
@ -306,6 +399,8 @@ module Cask
@staged_path = caskroom_path.join(cask_version.to_s)
end
# Provide the user with cask-specific information at install time.
#
# @api public
def caveats(*strings, &block)
@caveats ||= DSL::Caveats.new(cask)
@ -326,11 +421,15 @@ module Cask
@caveats&.discontinued? == true
end
# Asserts that the cask artifacts auto-update.
#
# @api public
def auto_updates(auto_updates = nil)
set_unique_stanza(:auto_updates, auto_updates.nil?) { auto_updates }
end
# Automatically fetch the latest version of a cask from changelogs.
#
# @api public
def livecheck(&block)
@livecheck ||= Livecheck.new(cask)
@ -344,6 +443,10 @@ module Cask
@livecheck.instance_eval(&block)
end
# Declare that a cask is no longer functional or supported.
#
# NOTE: A warning will be shown when trying to install this cask.
#
# @api public
def deprecate!(date:, because:)
@deprecation_date = Date.parse(date)
@ -353,6 +456,10 @@ module Cask
@deprecated = true
end
# Declare that a cask is no longer functional or supported.
#
# NOTE: An error will be thrown when trying to install this cask.
#
# @api public
def disable!(date:, because:)
@disable_date = Date.parse(date)
@ -405,6 +512,8 @@ module Cask
true
end
# The directory `app`s are installed into.
#
# @api public
def appdir
return HOMEBREW_CASK_APPDIR_PLACEHOLDER if Cask.generating_hash?

View File

@ -37,7 +37,6 @@ module Cask
private_class_method :caveat
# @!visibility private
sig { returns(String) }
def to_s
(@custom_caveats + @built_in_caveats.values).join("\n")

View File

@ -27,7 +27,6 @@ module Cask
pairs.to_yaml
end
# @!visibility private
sig { returns(String) }
def to_s = pairs.inspect
end

View File

@ -9,8 +9,8 @@ module Cask
class Postflight < Base
include Staged
def suppress_move_to_applications(_options = {})
odisabled "Cask::DSL#suppress_move_to_applications"
def suppress_move_to_applications(**_options)
odisabled "`Cask::DSL::Postflight#suppress_move_to_applications`"
end
end
end

View File

@ -94,70 +94,94 @@ module Cask
to_s == "latest"
end
# The major version.
#
# @api public
sig { returns(T.self_type) }
def major
version { slice(MAJOR_MINOR_PATCH_REGEX, 1) }
end
# The minor version.
#
# @api public
sig { returns(T.self_type) }
def minor
version { slice(MAJOR_MINOR_PATCH_REGEX, 2) }
end
# The patch version.
#
# @api public
sig { returns(T.self_type) }
def patch
version { slice(MAJOR_MINOR_PATCH_REGEX, 3) }
end
# The major and minor version.
#
# @api public
sig { returns(T.self_type) }
def major_minor
version { [major, minor].reject(&:empty?).join(".") }
end
# The major, minor and patch version.
#
# @api public
sig { returns(T.self_type) }
def major_minor_patch
version { [major, minor, patch].reject(&:empty?).join(".") }
end
# The minor and patch version.
#
# @api public
sig { returns(T.self_type) }
def minor_patch
version { [minor, patch].reject(&:empty?).join(".") }
end
# The comma separated values of the version as array.
#
# @api public
sig { returns(T::Array[Version]) } # Only top-level T.self_type is supported https://sorbet.org/docs/self-type
def csv
split(",").map { self.class.new(_1) }
end
# The version part before the first comma.
#
# @api public
sig { returns(T.self_type) }
def before_comma
version { split(",", 2).first }
end
# The version part after the first comma.
#
# @api public
sig { returns(T.self_type) }
def after_comma
version { split(",", 2).second }
end
# The version without any dividers.
#
# @see DIVIDER_REGEX
# @api public
sig { returns(T.self_type) }
def no_dividers
version { gsub(DIVIDER_REGEX, "") }
end
# The version with the given record separator removed from the end.
#
# @see String#chomp
# @api public
sig { params(separator: T.nilable(String)).returns(T.self_type) }
def chomp(separator = nil)
version { to_s.chomp(T.unsafe(separator)) }
sig { params(separator: String).returns(T.self_type) }
def chomp(separator = T.unsafe(nil))
version { to_s.chomp(separator) }
end
private

View File

@ -13,7 +13,6 @@ module Cask
@errors = errors
end
# @!visibility private
sig { returns(String) }
def to_s
<<~EOS
@ -41,7 +40,6 @@ module Cask
# Error when a cask is not installed.
class CaskNotInstalledError < AbstractCaskErrorWithToken
# @!visibility private
sig { returns(String) }
def to_s
"Cask '#{token}' is not installed."
@ -57,7 +55,6 @@ module Cask
@message = message
end
# @!visibility private
sig { returns(String) }
def to_s
"Cask '#{token}' has been #{message}"
@ -73,7 +70,6 @@ module Cask
@conflicting_cask = conflicting_cask
end
# @!visibility private
sig { returns(String) }
def to_s
"Cask '#{token}' conflicts with '#{conflicting_cask}'."
@ -82,7 +78,6 @@ module Cask
# Error when a cask is not available.
class CaskUnavailableError < AbstractCaskErrorWithToken
# @!visibility private
sig { returns(String) }
def to_s
"Cask '#{token}' is unavailable#{reason.empty? ? "." : ": #{reason}"}"
@ -91,7 +86,6 @@ module Cask
# Error when a cask is unreadable.
class CaskUnreadableError < CaskUnavailableError
# @!visibility private
sig { returns(String) }
def to_s
"Cask '#{token}' is unreadable#{reason.empty? ? "." : ": #{reason}"}"
@ -107,7 +101,6 @@ module Cask
@tap = tap
end
# @!visibility private
sig { returns(String) }
def to_s
s = super
@ -142,7 +135,6 @@ module Cask
# Error when a cask already exists.
class CaskAlreadyCreatedError < AbstractCaskErrorWithToken
# @!visibility private
sig { returns(String) }
def to_s
%Q(Cask '#{token}' already exists. Run #{Formatter.identifier("brew edit --cask #{token}")} to edit it.)
@ -151,7 +143,6 @@ module Cask
# Error when there is a cyclic cask dependency.
class CaskCyclicDependencyError < AbstractCaskErrorWithToken
# @!visibility private
sig { returns(String) }
def to_s
"Cask '#{token}' includes cyclic dependencies on other Casks#{reason.empty? ? "." : ": #{reason}"}"
@ -160,7 +151,6 @@ module Cask
# Error when a cask depends on itself.
class CaskSelfReferencingDependencyError < CaskCyclicDependencyError
# @!visibility private
sig { returns(String) }
def to_s
"Cask '#{token}' depends on itself."
@ -169,7 +159,6 @@ module Cask
# Error when no cask is specified.
class CaskUnspecifiedError < CaskError
# @!visibility private
sig { returns(String) }
def to_s
"This command requires a Cask token."
@ -178,7 +167,6 @@ module Cask
# Error when a cask is invalid.
class CaskInvalidError < AbstractCaskErrorWithToken
# @!visibility private
sig { returns(String) }
def to_s
"Cask '#{token}' definition is invalid#{reason.empty? ? "." : ": #{reason}"}"
@ -203,7 +191,6 @@ module Cask
@reason = reason
end
# @!visibility private
sig { returns(String) }
def to_s
s = +"Failed to quarantine #{path}."
@ -220,7 +207,6 @@ module Cask
# Error while propagating quarantine information to subdirectories.
class CaskQuarantinePropagationError < CaskQuarantineError
# @!visibility private
sig { returns(String) }
def to_s
s = +"Failed to quarantine one or more files within #{path}."
@ -237,7 +223,6 @@ module Cask
# Error while removing quarantine information.
class CaskQuarantineReleaseError < CaskQuarantineError
# @!visibility private
sig { returns(String) }
def to_s
s = +"Failed to release #{path} from quarantine."

View File

@ -17,22 +17,32 @@ module Cask
extend Forwardable
def_delegators :uri, :path, :scheme, :to_s
# @api public
sig {
params(
uri: T.any(URI::Generic, String),
# @api public
verified: T.nilable(String),
# @api public
using: T.any(Class, Symbol, NilClass),
# @api public
tag: T.nilable(String),
# @api public
branch: T.nilable(String),
# @api public
revisions: T.nilable(T::Array[String]),
# @api public
revision: T.nilable(String),
# @api public
trust_cert: T.nilable(T::Boolean),
# @api public
cookies: T.nilable(T::Hash[String, String]),
referer: T.nilable(T.any(URI::Generic, String)),
# @api public
header: T.nilable(T.any(String, T::Array[String])),
user_agent: T.nilable(T.any(Symbol, String)),
# @api public
data: T.nilable(T::Hash[String, String]),
# @api public
only_path: T.nilable(String),
).void
}
@ -77,8 +87,19 @@ module Cask
end
class BlockDSL
# To access URL associated with page contents.
# Allow accessing the URL associated with page contents.
module PageWithURL
# Get the URL of the fetched page.
#
# ### Example
#
# ```ruby
# url "https://example.org/download" do |page|
# file = page[/href="([^"]+.dmg)"/, 1]
# URL.join(page.url, file)
# end
# ```
#
# @api public
sig { returns(URI::Generic) }
attr_accessor :url
@ -87,12 +108,12 @@ module Cask
sig {
params(
uri: T.nilable(T.any(URI::Generic, String)),
dsl: T.nilable(::Cask::DSL),
dsl: ::Cask::DSL,
block: T.proc.params(arg0: T.all(String, PageWithURL))
.returns(T.any(T.any(URI::Generic, String), [T.any(URI::Generic, String), Hash])),
).void
}
def initialize(uri, dsl: nil, &block)
def initialize(uri, dsl:, &block)
@uri = uri
@dsl = dsl
@block = block
@ -114,6 +135,8 @@ module Cask
end
end
# Allows calling a nested `url` stanza in a `url do` block.
#
# @api public
sig {
params(
@ -123,10 +146,12 @@ module Cask
).returns(T.any(T.any(URI::Generic, String), [T.any(URI::Generic, String), Hash]))
}
def url(uri, &block)
self.class.new(uri, &block).call
self.class.new(uri, dsl: @dsl, &block).call
end
private :url
# This allows calling DSL methods from inside a `url` block.
#
# @api public
def method_missing(method, *args, &block)
if @dsl.respond_to?(method)
@ -135,10 +160,12 @@ module Cask
super
end
end
private :method_missing
def respond_to_missing?(method, include_all)
@dsl.respond_to?(method, include_all) || super
end
private :respond_to_missing?
end
sig {
@ -187,7 +214,7 @@ module Cask
super(
if block
LazyObject.new do
uri2, options = *BlockDSL.new(uri, dsl:, &block).call
uri2, options = *BlockDSL.new(uri, dsl: T.must(dsl), &block).call
options ||= {}
DSL.new(uri2, **options)
end

View File

@ -68,9 +68,9 @@ module Cask
unless tried_permissions
print_stderr = Context.current.debug? || Context.current.verbose?
# TODO: Better handling for the case where path is a symlink.
# The -h and -R flags cannot be combined, and behavior is
# The `-h` and `-R` flags cannot be combined and behavior is
# dependent on whether the file argument has a trailing
# slash. This should do the right thing, but is fragile.
# slash. This should do the right thing, but is fragile.
command.run("/usr/bin/chflags",
print_stderr:,
args: command_args + ["--", "000", path])
@ -87,7 +87,7 @@ module Cask
unless tried_ownership
# in case of ownership problems
# TODO: Further examine files to see if ownership is the problem
# before using sudo+chown
# before using `sudo` and `chown`.
ohai "Using sudo to gain ownership of path '#{path}'"
command.run("/usr/sbin/chown",
args: command_args + ["--", User.current, path],

View File

@ -756,7 +756,7 @@ class ReporterHub
def dump(auto_update: false)
report_all = ENV["HOMEBREW_UPDATE_REPORT_ALL_FORMULAE"].present?
if report_all && !Homebrew::EnvConfig.no_install_from_api?
odisabled "HOMEBREW_UPDATE_REPORT_ALL_FORMULAE"
odisabled "`HOMEBREW_UPDATE_REPORT_ALL_FORMULAE`"
opoo "This will not report all formulae because Homebrew cannot get this data from the API."
report_all = false
end

View File

@ -65,7 +65,6 @@ class CompilerFailure
type == compiler.type && version_matched
end
# @!visibility private
sig { returns(String) }
def inspect
"#<#{self.class.name}: #{type} #{version}>"

View File

@ -22,7 +22,6 @@ class CxxStdlib
type.to_s.gsub(/cxx$/, "c++")
end
# @!visibility private
sig { returns(String) }
def inspect
"#<#{self.class.name}: #{compiler} #{type}>"

View File

@ -35,7 +35,6 @@ class Dependencies < SimpleDelegator
self.class.new(*__getobj__.reject { |dep| dep.uses_from_macos? && dep.use_macos_install? })
end
# @!visibility private
sig { returns(String) }
def inspect
"#<#{self.class.name}: #{__getobj__}>"
@ -62,7 +61,6 @@ class Requirements < SimpleDelegator
self
end
# @!visibility private
sig { returns(String) }
def inspect
"#<#{self.class.name}: {#{__getobj__.to_a.join(", ")}}>"

View File

@ -4,6 +4,8 @@
require "dependable"
# A dependency on another Homebrew formula.
#
# @api internal
class Dependency
extend Forwardable
include Dependable
@ -94,11 +96,9 @@ class Dependency
false
end
# @!visibility private
sig { returns(String) }
def to_s = name
# @!visibility private
sig { returns(String) }
def inspect
"#<#{self.class.name}: #{name.inspect} #{tags.inspect}>"
@ -115,6 +115,8 @@ class Dependency
# the list.
# The default filter, which is applied when a block is not given, omits
# optionals and recommends based on what the dependent has asked for
#
# @api internal
def expand(dependent, deps = dependent.deps, cache_key: nil, &block)
# Keep track dependencies to avoid infinite cyclic dependency recursion.
@expand_stack ||= []
@ -181,6 +183,8 @@ class Dependency
end
# Keep a dependency, but prune its dependencies.
#
# @api internal
sig { void }
def keep_but_prune_recursive_deps
throw(:action, :keep_but_prune_recursive_deps)
@ -282,7 +286,6 @@ class UsesFromMacOSDependency < Dependency
self.class.new(formula.full_name.to_s, tags, bounds:)
end
# @!visibility private
sig { returns(String) }
def inspect
"#<#{self.class.name}: #{name.inspect} #{tags.inspect} #{bounds.inspect}>"

View File

@ -98,7 +98,7 @@ module Homebrew
end
odie "Could not find #{name}! The formula or version may not have existed." if test_formula.nil?
else
# Search in the root directory of <repo> as well as recursively in all of its subdirectories
# Search in the root directory of `repo` as well as recursively in all of its subdirectories.
files = Dir[repo/"{,**/}"].filter_map do |dir|
Pathname.glob("#{dir}/#{name}.rb").find(&:file?)
end

View File

@ -20,16 +20,18 @@ module Homebrew
def run
Homebrew.install_bundler_gems!(groups: ["doc"])
HOMEBREW_LIBRARY_PATH.cd do
HOMEBREW_LIBRARY_PATH.cd do |dir|
no_api_args = if args.only_public?
["--hide-api", "private", "--hide-api", "internal"]
else
[]
end
system "bundle", "exec", "yard", "doc", "--output", "doc", *no_api_args
output_dir = dir/"doc"
exec_browser "file://#{HOMEBREW_LIBRARY_PATH}/doc/index.html" if args.open?
safe_system "bundle", "exec", "yard", "doc", "--fail-on-warning", *no_api_args, "--output", output_dir
exec_browser "file://#{output_dir}/index.html" if args.open?
end
end
end

View File

@ -77,11 +77,9 @@ class AbstractDownloadStrategy
end
# Disable any output during downloading.
#
# @deprecated
sig { void }
def shutup!
odeprecated "AbstractDownloadStrategy#shutup!", "AbstractDownloadStrategy#quiet!"
odeprecated "`AbstractDownloadStrategy#shutup!`", "`AbstractDownloadStrategy#quiet!`"
quiet!
end
@ -125,7 +123,6 @@ class AbstractDownloadStrategy
end
private :chdir
# @!attribute [r] source_modified_time
# Returns the most recent modified time for all files in the current working directory after stage.
#
# @api public
@ -236,9 +233,7 @@ class VCSDownloadStrategy < AbstractDownloadStrategy
version.respond_to?(:head?) && version.head?
end
# @!attribute [r] last_commit
# Return last commit's unique identifier for the repository.
# Return most recent modified timestamp unless overridden.
# Return the most recent modified timestamp.
#
# @api public
sig { returns(String) }
@ -733,7 +728,8 @@ class SubversionDownloadStrategy < VCSDownloadStrategy
super
end
# @see AbstractDownloadStrategy#source_modified_time
# Returns the most recent modified time for all files in the current working directory after stage.
#
# @api public
sig { returns(Time) }
def source_modified_time
@ -747,7 +743,8 @@ class SubversionDownloadStrategy < VCSDownloadStrategy
Time.parse time
end
# @see VCSDownloadStrategy#last_commit
# Return last commit's unique identifier for the repository.
#
# @api public
sig { returns(String) }
def last_commit
@ -847,7 +844,8 @@ class GitDownloadStrategy < VCSDownloadStrategy
@ref ||= "master"
end
# @see AbstractDownloadStrategy#source_modified_time
# Returns the most recent modified time for all files in the current working directory after stage.
#
# @api public
sig { returns(Time) }
def source_modified_time
@ -855,7 +853,8 @@ class GitDownloadStrategy < VCSDownloadStrategy
Time.parse(out)
end
# @see VCSDownloadStrategy#last_commit
# Return last commit's unique identifier for the repository.
#
# @api public
sig { returns(String) }
def last_commit
@ -1165,7 +1164,8 @@ class CVSDownloadStrategy < VCSDownloadStrategy
end
end
# @see AbstractDownloadStrategy#source_modified_time
# Returns the most recent modified time for all files in the current working directory after stage.
#
# @api public
sig { returns(Time) }
def source_modified_time
@ -1240,7 +1240,8 @@ class MercurialDownloadStrategy < VCSDownloadStrategy
@url = @url.sub(%r{^hg://}, "")
end
# @see AbstractDownloadStrategy#source_modified_time
# Returns the most recent modified time for all files in the current working directory after stage.
#
# @api public
sig { returns(Time) }
def source_modified_time
@ -1250,7 +1251,8 @@ class MercurialDownloadStrategy < VCSDownloadStrategy
Time.parse(out)
end
# @see VCSDownloadStrategy#last_commit
# Return last commit's unique identifier for the repository.
#
# @api public
sig { returns(String) }
def last_commit
@ -1327,7 +1329,8 @@ class BazaarDownloadStrategy < VCSDownloadStrategy
@url = @url.sub(%r{^bzr://}, "")
end
# @see AbstractDownloadStrategy#source_modified_time
# Returns the most recent modified time for all files in the current working directory after stage.
#
# @api public
sig { returns(Time) }
def source_modified_time
@ -1338,7 +1341,8 @@ class BazaarDownloadStrategy < VCSDownloadStrategy
Time.parse(timestamp)
end
# @see VCSDownloadStrategy#last_commit
# Return last commit's unique identifier for the repository.
#
# @api public
sig { returns(String) }
def last_commit
@ -1390,7 +1394,8 @@ class FossilDownloadStrategy < VCSDownloadStrategy
@url = @url.sub(%r{^fossil://}, "")
end
# @see AbstractDownloadStrategy#source_modified_time
# Returns the most recent modified time for all files in the current working directory after stage.
#
# @api public
sig { returns(Time) }
def source_modified_time
@ -1398,7 +1403,8 @@ class FossilDownloadStrategy < VCSDownloadStrategy
Time.parse(out[/^uuid: +\h+ (.+)$/, 1])
end
# @see VCSDownloadStrategy#last_commit
# Return last commit's unique identifier for the repository.
#
# @api public
sig { returns(String) }
def last_commit

View File

@ -16,7 +16,6 @@ class UsageError < RuntimeError
@reason = reason
end
# @!visibility private
sig { returns(String) }
def to_s
s = "Invalid usage"
@ -109,7 +108,6 @@ class FormulaOrCaskUnavailableError < RuntimeError
"Did you mean #{similar_formula_names.to_sentence two_words_connector: " or ", last_word_connector: " or "}?"
end
# @!visibility private
sig { returns(String) }
def to_s
s = "No available formula or cask with the name \"#{name}\". #{did_you_mean}".strip
@ -129,7 +127,6 @@ class TapFormulaOrCaskUnavailableError < FormulaOrCaskUnavailableError
@tap = tap
end
# @!visibility private
sig { returns(String) }
def to_s
s = super
@ -149,7 +146,6 @@ class FormulaUnavailableError < FormulaOrCaskUnavailableError
" (dependency of #{dependent})" if dependent && dependent != name
end
# @!visibility private
sig { returns(String) }
def to_s
"No available formula with the name \"#{name}\"#{dependent_s}. #{did_you_mean}".strip
@ -160,7 +156,6 @@ end
module FormulaClassUnavailableErrorModule
attr_reader :path, :class_name, :class_list
# @!visibility private
sig { returns(String) }
def to_s
s = super
@ -204,7 +199,6 @@ end
module FormulaUnreadableErrorModule
attr_reader :formula_error
# @!visibility private
sig { returns(String) }
def to_s
"#{name}: " + formula_error.to_s
@ -233,7 +227,6 @@ class TapFormulaUnavailableError < FormulaUnavailableError
super "#{tap}/#{name}"
end
# @!visibility private
sig { returns(String) }
def to_s
s = super
@ -572,7 +565,7 @@ class UnbottledError < RuntimeError
end
end
# Raised by Homebrew.install, Homebrew.reinstall, and Homebrew.upgrade
# Raised by `Homebrew.install`, `Homebrew.reinstall` and `Homebrew.upgrade`
# if the user passes any flags/environment that would case a bottle-only
# installation on a system without build tools to fail.
class BuildFlagsError < RuntimeError

View File

@ -1,5 +1,6 @@
# typed: strict
# @!visibility private
module EnvMethods
include Kernel
@ -39,6 +40,7 @@ module EnvActivation
include Superenv
end
# @!visibility private
class Sorbet
module Private
module Static

View File

@ -4,6 +4,7 @@ module SharedEnvExtension
include EnvMethods
end
# @!visibility private
class Sorbet
module Private
module Static

View File

@ -2,59 +2,72 @@
# frozen_string_literal: true
class Array
# Equal to <tt>self[1]</tt>.
# Equal to `self[1]`.
#
# %w( a b c d e ).second # => "b"
# ### Example
#
# ```ruby
# %w( a b c d e ).second # => "b"
# ```
def second = self[1]
# Equal to <tt>self[2]</tt>.
# Equal to `self[2]`.
#
# %w( a b c d e ).third # => "c"
# ### Example
#
# ```ruby
# %w( a b c d e ).third # => "c"
# ```
def third = self[2]
# Equal to <tt>self[3]</tt>.
# Equal to `self[3]`.
#
# %w( a b c d e ).fourth # => "d"
# ### Example
#
# ```ruby
# %w( a b c d e ).fourth # => "d"
# ```
def fourth = self[3]
# Equal to <tt>self[4]</tt>.
# Equal to `self[4]`.
#
# %w( a b c d e ).fifth # => "e"
# ### Example
#
# ```ruby
# %w( a b c d e ).fifth # => "e"
# ```
def fifth = self[4]
# Converts the array to a comma-separated sentence where the last element is
# joined by the connector word.
#
# You can pass the following kwargs to change the default behavior:
# ### Examples
#
# * <tt>:words_connector</tt> - The sign or word used to join all but the last
# element in arrays with three or more elements (default: ", ").
# * <tt>:last_word_connector</tt> - The sign or word used to join the last element
# in arrays with three or more elements (default: ", and ").
# * <tt>:two_words_connector</tt> - The sign or word used to join the elements
# in arrays with two elements (default: " and ").
# ```ruby
# [].to_sentence # => ""
# ['one'].to_sentence # => "one"
# ['one', 'two'].to_sentence # => "one and two"
# ['one', 'two', 'three'].to_sentence # => "one, two and three"
# ['one', 'two'].to_sentence(two_words_connector: '-')
# # => "one-two"
# ```
#
# ==== Examples
# ```
# ['one', 'two', 'three'].to_sentence(words_connector: ' or ', last_word_connector: ' or at least ')
# # => "one or two or at least three"
# ```
#
# [].to_sentence # => ""
# ['one'].to_sentence # => "one"
# ['one', 'two'].to_sentence # => "one and two"
# ['one', 'two', 'three'].to_sentence # => "one, two, and three"
# ['one', 'two'].to_sentence(two_words_connector: '-')
# # => "one-two"
#
# ['one', 'two', 'three'].to_sentence(words_connector: ' or ', last_word_connector: ' or at least ')
# # => "one or two or at least three"
# @see https://github.com/rails/rails/blob/v7.0.4.2/activesupport/lib/active_support/core_ext/array/conversions.rb#L8-L84
# ActiveSupport Array#to_sentence monkey-patch
#
#
# Copyright (c) David Heinemeier Hansson
#
# 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
# 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:
#
@ -68,6 +81,14 @@ class Array
# 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.
#
#
# @param [String] words_connector The sign or word used to join all but the last
# element in arrays with three or more elements (default: `", "`).
# @param [String] last_word_connector The sign or word used to join the last element
# in arrays with three or more elements (default: `" and "`).
# @param [String] two_words_connector The sign or word used to join the elements
# in arrays with two elements (default: `" and "`).
sig { params(words_connector: String, two_words_connector: String, last_word_connector: String).returns(String) }
def to_sentence(words_connector: ", ", two_words_connector: " and ", last_word_connector: " and ")
case length

View File

@ -3,15 +3,20 @@
class Object
# An object is blank if it's false, empty, or a whitespace string.
# For example, +nil+, '', ' ', [], {}, and +false+ are all blank.
#
# This simplifies
# For example, `nil`, `''`, `' '`, `[]`, `{}` and `false` are all blank.
#
# !address || address.empty?
# ### Example
#
# to
# ```ruby
# !address || address.empty?
# ```
#
# address.blank?
# can be simplified to
#
# ```ruby
# address.blank?
# ```
sig { returns(T::Boolean) }
def blank?
respond_to?(:empty?) ? !!T.unsafe(self).empty? : false
@ -21,20 +26,23 @@ class Object
sig { returns(T::Boolean) }
def present? = !blank?
# Returns the receiver if it's present otherwise returns +nil+.
# <tt>object.presence</tt> is equivalent to
# Returns the receiver if it's present, otherwise returns `nil`.
#
# object.present? ? object : nil
# `object.presence` is equivalent to `object.present? ? object : nil`.
#
# For example, something like
# ### Example
#
# state = params[:state] if params[:state].present?
# country = params[:country] if params[:country].present?
# region = state || country || 'US'
# ```ruby
# state = params[:state] if params[:state].present?
# country = params[:country] if params[:country].present?
# region = state || country || 'US'
# ```
#
# becomes
# can be simplified to
#
# region = params[:state].presence || params[:country].presence || 'US'
# ```ruby
# region = params[:state].presence || params[:country].presence || 'US'
# ```
sig { returns(T.nilable(T.self_type)) }
def presence
self if present?
@ -42,9 +50,11 @@ class Object
end
class NilClass
# +nil+ is blank:
# `nil` is blank:
#
# nil.blank? # => true
# ```ruby
# nil.blank? # => true
# ```
sig { returns(TrueClass) }
def blank? = true
@ -53,9 +63,11 @@ class NilClass
end
class FalseClass
# +false+ is blank:
# `false` is blank:
#
# false.blank? # => true
# ```ruby
# false.blank? # => true
# ```
sig { returns(TrueClass) }
def blank? = true
@ -64,9 +76,11 @@ class FalseClass
end
class TrueClass
# +true+ is not blank:
# `true` is not blank:
#
# true.blank? # => false
# ```ruby
# true.blank? # => false
# ```
sig { returns(FalseClass) }
def blank? = false
@ -77,11 +91,12 @@ end
class Array
# An array is blank if it's empty:
#
# [].blank? # => true
# [1,2,3].blank? # => false
#
# @return [true, false]
alias blank? empty?
# ```ruby
# [].blank? # => true
# [1,2,3].blank? # => false
# ```
sig { returns(T::Boolean) }
def blank? = empty?
sig { returns(T::Boolean) }
def present? = !empty? # :nodoc:
@ -90,11 +105,13 @@ end
class Hash
# A hash is blank if it's empty:
#
# {}.blank? # => true
# { key: 'value' }.blank? # => false
#
# @return [true, false]
alias blank? empty?
# ```ruby
# {}.blank? # => true
# { key: 'value' }.blank? # => false
# ```
sig { returns(T::Boolean) }
def blank? = empty?
sig { returns(T::Boolean) }
def present? = !empty? # :nodoc:
@ -103,9 +120,12 @@ end
class Symbol
# A Symbol is blank if it's empty:
#
# :''.blank? # => true
# :symbol.blank? # => false
alias blank? empty?
# ```ruby
# :''.blank? # => true
# :symbol.blank? # => false
# ```
sig { returns(T::Boolean) }
def blank? = empty?
sig { returns(T::Boolean) }
def present? = !empty? # :nodoc:
@ -122,14 +142,18 @@ class String
# A string is blank if it's empty or contains whitespaces only:
#
# ''.blank? # => true
# ' '.blank? # => true
# "\t\n\r".blank? # => true
# ' blah '.blank? # => false
# ```ruby
# ''.blank? # => true
# ' '.blank? # => true
# "\t\n\r".blank? # => true
# ' blah '.blank? # => false
# ```
#
# Unicode whitespace is supported:
#
# "\u00a0".blank? # => true
# ```ruby
# "\u00a0".blank? # => true
# ```
sig { returns(T::Boolean) }
def blank?
# The regexp that matches blank strings is expensive. For the case of empty
@ -150,8 +174,10 @@ end
class Numeric # :nodoc:
# No number is blank:
#
# 1.blank? # => false
# 0.blank? # => false
# ```ruby
# 1.blank? # => false
# 0.blank? # => false
# ```
sig { returns(FalseClass) }
def blank? = false
@ -162,7 +188,9 @@ end
class Time # :nodoc:
# No Time is blank:
#
# Time.now.blank? # => false
# ```ruby
# Time.now.blank? # => false
# ```
sig { returns(FalseClass) }
def blank? = false

View File

@ -2,7 +2,7 @@
# frozen_string_literal: true
module Enumerable
# The negative of the <tt>Enumerable#include?</tt>. Returns +true+ if the
# The negative of the {Enumerable#include?}. Returns `true` if the
# collection does not include the object.
sig { params(object: T.untyped).returns(T::Boolean) }
def exclude?(object) = !include?(object)
@ -10,21 +10,29 @@ module Enumerable
# Returns a new +Array+ without the blank items.
# Uses Object#blank? for determining if an item is blank.
#
# [1, "", nil, 2, " ", [], {}, false, true].compact_blank
# # => [1, 2, true]
# ### Examples
#
# Set.new([nil, "", 1, false]).compact_blank
# # => [1]
# ```
# [1, "", nil, 2, " ", [], {}, false, true].compact_blank
# # => [1, 2, true]
# ```
#
# When called on a +Hash+, returns a new +Hash+ without the blank values.
# ```ruby
# Set.new([nil, "", 1, false]).compact_blank
# # => [1]
# ```
#
# { a: "", b: 1, c: nil, d: [], e: false, f: true }.compact_blank
# # => { b: 1, f: true }
# When called on a {Hash}, returns a new {Hash} without the blank values.
#
# ```ruby
# { a: "", b: 1, c: nil, d: [], e: false, f: true }.compact_blank
# # => { b: 1, f: true }
# ```
sig { returns(T.self_type) }
def compact_blank = T.unsafe(self).reject(&:blank?)
end
class Hash
# Hash#reject has its own definition, so this needs one too.
# {Hash#reject} has its own definition, so this needs one too.
def compact_blank = reject { |_k, v| T.unsafe(v).blank? }
end

View File

@ -2,25 +2,31 @@
# frozen_string_literal: true
class Hash
# Returns a new hash with +self+ and +other_hash+ merged recursively.
# Returns a new hash with `self` and `other_hash` merged recursively.
#
# h1 = { a: true, b: { c: [1, 2, 3] } }
# h2 = { a: false, b: { x: [3, 4, 5] } }
# ### Examples
#
# h1.deep_merge(h2) # => { a: false, b: { c: [1, 2, 3], x: [3, 4, 5] } }
# ```ruby
# h1 = { a: true, b: { c: [1, 2, 3] } }
# h2 = { a: false, b: { x: [3, 4, 5] } }
#
# h1.deep_merge(h2) # => { a: false, b: { c: [1, 2, 3], x: [3, 4, 5] } }
# ```
#
# Like with Hash#merge in the standard library, a block can be provided
# to merge values:
#
# h1 = { a: 100, b: 200, c: { c1: 100 } }
# h2 = { b: 250, c: { c1: 200 } }
# h1.deep_merge(h2) { |key, this_val, other_val| this_val + other_val }
# # => { a: 100, b: 450, c: { c1: 300 } }
# ```ruby
# h1 = { a: 100, b: 200, c: { c1: 100 } }
# h2 = { b: 250, c: { c1: 200 } }
# h1.deep_merge(h2) { |key, this_val, other_val| this_val + other_val }
# # => { a: 100, b: 450, c: { c1: 300 } }
# ```
def deep_merge(other_hash, &block)
dup.deep_merge!(other_hash, &block)
end
# Same as +deep_merge+, but modifies +self+.
# Same as {#deep_merge}, but modifies `self`.
def deep_merge!(other_hash, &block)
merge!(other_hash) do |key, this_val, other_val|
if T.unsafe(this_val).is_a?(Hash) && other_val.is_a?(Hash)

View File

@ -6,11 +6,14 @@ class Hash
# This includes the values from the root hash and from all
# nested hashes and arrays.
#
# @example
# hash = { person: { name: 'Rob', age: '28' } }
# ### Example
#
# hash.deep_transform_values{ |value| value.to_s.upcase }
# # => {person: {name: "ROB", age: "28"}}
# ```ruby
# hash = { person: { name: 'Rob', age: '28' } }
#
# hash.deep_transform_values{ |value| value.to_s.upcase }
# # => {person: {name: "ROB", age: "28"}}
# ```
def deep_transform_values(&block) = _deep_transform_values_in_object(self, &block)
# Destructively converts all values by using the block operation.

View File

@ -5,14 +5,18 @@ class Hash
# Validates all keys in a hash match `*valid_keys`, raising
# `ArgumentError` on a mismatch.
#
# Note that keys are treated differently than HashWithIndifferentAccess,
# Note that keys are treated differently than `HashWithIndifferentAccess`,
# meaning that string and symbol keys will not match.
#
# { name: 'Rob', years: '28' }.assert_valid_keys(:name, :age)
# # => raises "ArgumentError: Unknown key: :years. Valid keys are: :name, :age"
# { name: 'Rob', age: '28' }.assert_valid_keys('name', 'age')
# # => raises "ArgumentError: Unknown key: :name. Valid keys are: 'name', 'age'"
# { name: 'Rob', age: '28' }.assert_valid_keys(:name, :age) # => passes, raises nothing
# ### Example#
#
# ```ruby
# { name: 'Rob', years: '28' }.assert_valid_keys(:name, :age)
# # => raises "ArgumentError: Unknown key: :years. Valid keys are: :name, :age"
# { name: 'Rob', age: '28' }.assert_valid_keys('name', 'age')
# # => raises "ArgumentError: Unknown key: :name. Valid keys are: 'name', 'age'"
# { name: 'Rob', age: '28' }.assert_valid_keys(:name, :age) # => passes, raises nothing
# ```
sig { params(valid_keys: T.untyped).void }
def assert_valid_keys(*valid_keys)
valid_keys.flatten!
@ -28,10 +32,14 @@ class Hash
# This includes the keys from the root hash and from all
# nested hashes and arrays.
#
# hash = { person: { name: 'Rob', age: '28' } }
# ### Example
#
# hash.deep_transform_keys{ |key| key.to_s.upcase }
# # => {"PERSON"=>{"NAME"=>"Rob", "AGE"=>"28"}}
# ```ruby
# hash = { person: { name: 'Rob', age: '28' } }
#
# hash.deep_transform_keys{ |key| key.to_s.upcase }
# # => {"PERSON"=>{"NAME"=>"Rob", "AGE"=>"28"}}
# ```
def deep_transform_keys(&block) = _deep_transform_keys_in_object(self, &block)
# Destructively converts all keys by using the block operation.
@ -43,10 +51,14 @@ class Hash
# This includes the keys from the root hash and from all
# nested hashes and arrays.
#
# hash = { person: { name: 'Rob', age: '28' } }
# ### Example
#
# hash.deep_stringify_keys
# # => {"person"=>{"name"=>"Rob", "age"=>"28"}}
# ```ruby
# hash = { person: { name: 'Rob', age: '28' } }
#
# hash.deep_stringify_keys
# # => {"person"=>{"name"=>"Rob", "age"=>"28"}}
# ```
def deep_stringify_keys = T.unsafe(self).deep_transform_keys(&:to_s)
# Destructively converts all keys to strings.
@ -55,13 +67,17 @@ class Hash
def deep_stringify_keys! = T.unsafe(self).deep_transform_keys!(&:to_s)
# Returns a new hash with all keys converted to symbols, as long as
# they respond to +to_sym+. This includes the keys from the root hash
# they respond to `to_sym`. This includes the keys from the root hash
# and from all nested hashes and arrays.
#
# hash = { 'person' => { 'name' => 'Rob', 'age' => '28' } }
# ### Example
#
# hash.deep_symbolize_keys
# # => {:person=>{:name=>"Rob", :age=>"28"}}
# ```ruby
# hash = { 'person' => { 'name' => 'Rob', 'age' => '28' } }
#
# hash.deep_symbolize_keys
# # => {:person=>{:name=>"Rob", :age=>"28"}}
# ```
def deep_symbolize_keys
deep_transform_keys do |key|
T.unsafe(key).to_sym
@ -71,7 +87,7 @@ class Hash
end
# Destructively converts all keys to symbols, as long as they respond
# to +to_sym+. This includes the keys from the root hash and from all
# to `to_sym`. This includes the keys from the root hash and from all
# nested hashes and arrays.
def deep_symbolize_keys!
deep_transform_keys! do |key|
@ -102,7 +118,7 @@ class Hash
def _deep_transform_keys_in_object!(object, &block)
case object
when Hash
# We can't use `each_key` here because we're updating the hash in-place
# We can't use `each_key` here because we're updating the hash in-place.
object.keys.each do |key| # rubocop:disable Style/HashEachMethods
value = object.delete(key)
object[yield(key)] = _deep_transform_keys_in_object!(value, &block)

View File

@ -493,14 +493,20 @@ module Kernel
end
# Calls the given block with the passed environment variables
# added to ENV, then restores ENV afterwards.
# <pre>with_env(PATH: "/bin") do
# system "echo $PATH"
# end</pre>
# added to `ENV`, then restores `ENV` afterwards.
#
# NOTE: This method is **not** thread-safe other threads
# which happen to be scheduled during the block will also
# see these environment variables.
#
# ### Example
#
# ```ruby
# with_env(PATH: "/bin") do
# system "echo $PATH"
# end
# ```
#
# @note This method is *not* thread-safe - other threads
# which happen to be scheduled during the block will also
# see these environment variables.
# @api public
def with_env(hash)
old_values = {}

View File

@ -1,24 +1,30 @@
# typed: strict
# frozen_string_literal: true
#--
# Most objects are cloneable, but not all. For example you can't dup methods:
#
# method(:puts).dup # => TypeError: allocator undefined for Method
# ```ruby
# method(:puts).dup # => TypeError: allocator undefined for Method
# ```
#
# Classes may signal their instances are not duplicable removing +dup+/+clone+
# or raising exceptions from them. So, to dup an arbitrary object you normally
# use an optimistic approach and are ready to catch an exception, say:
#
# arbitrary_object.dup rescue object
# ```ruby
# arbitrary_object.dup rescue object
# ```
#
# Rails dups objects in a few critical spots where they are not that arbitrary.
# That rescue is very expensive (like 40 times slower than a predicate), and it
# That `rescue` is very expensive (like 40 times slower than a predicate) and it
# is often triggered.
#
# That's why we hardcode the following cases and check duplicable? instead of
# using that rescue idiom.
#++
# rubocop:disable Layout/EmptyLines
# rubocop:enable Layout/EmptyLines
class Object
# Can you safely dup this object?
#
@ -31,8 +37,10 @@ end
class Method
# Methods are not duplicable:
#
# method(:puts).duplicable? # => false
# method(:puts).dup # => TypeError: allocator undefined for Method
# ```ruby
# method(:puts).duplicable? # => false
# method(:puts).dup # => TypeError: allocator undefined for Method
# ```
sig { returns(FalseClass) }
def duplicable? = false
end
@ -40,8 +48,10 @@ end
class UnboundMethod
# Unbound methods are not duplicable:
#
# method(:puts).unbind.duplicable? # => false
# method(:puts).unbind.dup # => TypeError: allocator undefined for UnboundMethod
# ```ruby
# method(:puts).unbind.duplicable? # => false
# method(:puts).unbind.dup # => TypeError: allocator undefined for UnboundMethod
# ```
sig { returns(FalseClass) }
def duplicable? = false
end
@ -51,7 +61,9 @@ require "singleton"
module Singleton
# Singleton instances are not duplicable:
#
# Class.new.include(Singleton).instance.dup # TypeError (can't dup instance of singleton
# ```ruby
# Class.new.include(Singleton).instance.dup # TypeError (can't dup instance of singleton
# ```
sig { returns(FalseClass) }
def duplicable? = false
end

View File

@ -16,7 +16,7 @@ module Homebrew
return unless @args.cask?
# NOTE: We don't raise an error here because we don't want
# to print the help page or a stack trace.
# to print the help page or a stack trace.
odie "Invalid `--cask` usage: Casks do not work on Linux"
end
end

View File

@ -27,12 +27,11 @@ module Stdenv
append "LDFLAGS", "-Wl,-headerpad_max_install_names"
# sed is strict, and errors out when it encounters files with
# mixed character sets
# `sed` is strict and errors out when it encounters files with mixed character sets.
delete("LC_ALL")
self["LC_CTYPE"] = "C"
# Add lib and include etc. from the current macosxsdk to compiler flags:
# Add `lib` and `include` etc. from the current `macosxsdk` to compiler flags:
macosxsdk(formula: @formula, testing_formula:)
return unless MacOS::Xcode.without_clt?
@ -42,8 +41,8 @@ module Stdenv
end
def remove_macosxsdk(version = nil)
# Clear all lib and include dirs from CFLAGS, CPPFLAGS, LDFLAGS that were
# previously added by macosxsdk
# Clear all `lib` and `include` dirs from `CFLAGS`, `CPPFLAGS`, `LDFLAGS` that were
# previously added by `macosxsdk`.
remove_from_cflags(/ ?-mmacosx-version-min=\d+\.\d+/)
delete("CPATH")
remove "LDFLAGS", "-L#{HOMEBREW_PREFIX}/lib"
@ -58,14 +57,14 @@ module Stdenv
if HOMEBREW_PREFIX.to_s == "/usr/local"
delete("CMAKE_PREFIX_PATH")
else
# It was set in setup_build_environment, so we have to restore it here.
# It was set in `setup_build_environment`, so we have to restore it here.
self["CMAKE_PREFIX_PATH"] = HOMEBREW_PREFIX.to_s
end
remove "CMAKE_FRAMEWORK_PATH", "#{sdk}/System/Library/Frameworks"
end
def macosxsdk(version = nil, formula: nil, testing_formula: false)
# Sets all needed lib and include dirs to CFLAGS, CPPFLAGS, LDFLAGS.
# Sets all needed `lib` and `include` dirs to `CFLAGS`, `CPPFLAGS`, `LDFLAGS`.
remove_macosxsdk
min_version = version || MacOS.version
append_to_cflags("-mmacosx-version-min=#{min_version}")

View File

@ -83,8 +83,8 @@ module Hardware
sysctl_bool("hw.optional.sse4_2")
end
# NOTE: this is more reliable than checking uname.
# `sysctl` returns the right answer even when running in Rosetta 2.
# NOTE: This is more reliable than checking `uname`. `sysctl` returns
# the right answer even when running in Rosetta 2.
def physical_cpu_arm64?
sysctl_bool("hw.optional.arm64")
end

View File

@ -126,10 +126,11 @@ class Pathname
mkpath
# Use FileUtils.mv over File.rename to handle filesystem boundaries. If src
# is a symlink, and its target is moved first, FileUtils.mv will fail:
# https://bugs.ruby-lang.org/issues/7707
# In that case, use the system "mv" command.
# Use `FileUtils.mv` over `File.rename` to handle filesystem boundaries. If `src`
# is a symlink and its target is moved first, `FileUtils.mv` will fail
# (https://bugs.ruby-lang.org/issues/7707).
#
# In that case, use the system `mv` command.
if src.symlink?
raise unless Kernel.system "mv", src.to_s, dst
else
@ -178,7 +179,9 @@ class Pathname
T.unsafe(self).open("a", **open_args) { |f| f.puts(content) }
end
# @note This always overwrites.
# Write to a file atomically.
#
# NOTE: This always overwrites.
#
# @api public
sig { params(content: String).void }
@ -201,6 +204,7 @@ class Pathname
# Changing file ownership failed, moving on.
nil
end
begin
# This operation will affect filesystem ACL's
chmod(old_stat.mode)

File diff suppressed because it is too large Load Diff

View File

@ -37,7 +37,6 @@ class KegOnlyReason
!by_macos?
end
# @!visibility private
sig { returns(String) }
def to_s
return @explanation unless @explanation.empty?

View File

@ -25,7 +25,6 @@ module Homebrew
@text.include? string
end
# @!visibility private
sig { returns(String) }
def to_s
@text

View File

@ -106,7 +106,6 @@ class GitRepository
popen_git("log", "-1", "--pretty=%B", commit, "--", safe:, err: :out)&.strip
end
# @!visibility private
sig { returns(String) }
def to_s = pathname.to_s

View File

@ -48,7 +48,6 @@ class Keg
EOS
end
# @!visibility private
sig { returns(String) }
def to_s
s = []
@ -67,7 +66,6 @@ class Keg
# Error for when a directory is not writable.
class DirectoryNotWritableError < LinkError
# @!visibility private
sig { returns(String) }
def to_s
<<~EOS
@ -176,11 +174,9 @@ class Keg
path.parent
end
# @!visibility private
sig { returns(String) }
def to_s = path.to_s
# @!visibility private
sig { returns(String) }
def inspect
"#<#{self.class.name}:#{path}>"

View File

@ -14,7 +14,7 @@ module Language
# e.g. `resource "github.com/foo/bar"`.
sig { params(resources: T::Array[Resource], target: T.any(String, Pathname)).void }
def self.stage_deps(resources, target)
# odeprecated "Language::Go::stage_deps", "Go modules"
# odeprecated "`Language::Go.stage_deps`", "Go modules"
if resources.empty?
if Homebrew::EnvConfig.developer?
odie "Tried to stage empty Language::Go resources array"

View File

@ -150,8 +150,8 @@ module Language
#
# @param venv_root [Pathname, String] the path to the root of the virtualenv
# (often `libexec/"venv"`)
# @param python [String, Pathname] which interpreter to use (e.g. "python3"
# or "python3.x")
# @param python [String, Pathname] which interpreter to use (e.g. `"python3"`
# or `"python3.x"`)
# @param formula [Formula] the active {Formula}
# @return [Virtualenv] a {Virtualenv} instance
sig {
@ -166,7 +166,7 @@ module Language
def virtualenv_create(venv_root, python = "python", formula = T.cast(self, Formula),
system_site_packages: true, without_pip: true)
# Limit deprecation to 3.12+ for now (or if we can't determine the version).
# Some used this argument for setuptools, which we no longer bundle since 3.12.
# Some used this argument for `setuptools`, which we no longer bundle since 3.12.
unless without_pip
python_version = Language::Python.major_minor_version(python)
if python_version.nil? || python_version.null? || python_version >= "3.12"
@ -198,8 +198,8 @@ module Language
# Returns true if a formula option for the specified python is currently
# active or if the specified python is required by the formula. Valid
# inputs are "python", "python2", and :python3. Note that
# "with-python", "without-python", "with-python@2", and "without-python@2"
# inputs are `"python"`, `"python2"` and `:python3`. Note that
# `"with-python"`, `"without-python"`, `"with-python@2"` and `"without-python@2"`
# formula options are handled correctly even if not associated with any
# corresponding depends_on statement.
sig { params(python: String).returns(T::Boolean) }

View File

@ -16,15 +16,20 @@ class LazyObject < Delegator
@__delegate__ = @__callable__.call
# rubocop:enable Naming/MemoizedInstanceVariableName
end
private :__getobj__
def __setobj__(callable)
@__callable__ = callable
end
private :__setobj__
# Forward to the inner object to make lazy objects type-checkable.
#
# @!visibility private
def is_a?(klass)
# see https://sorbet.org/docs/faq#how-can-i-fix-type-errors-that-arise-from-super
T.bind(self, T.untyped)
__getobj__.is_a?(klass) || super
end
end

View File

@ -39,9 +39,11 @@ module Homebrew
) do
extend Forwardable
# @!attribute [r] version
# @api public
delegate version: :bundle_version
# @!attribute [r] short_version
# @api public
delegate short_version: :bundle_version
end

View File

@ -54,12 +54,15 @@ module Homebrew
) do
extend Forwardable
# @!attribute [r] version
# @api public
delegate version: :bundle_version
# @!attribute [r] short_version
# @api public
delegate short_version: :bundle_version
# @!attribute [r] nice_version
# @api public
delegate nice_version: :bundle_version
end

View File

@ -106,7 +106,6 @@ class Locale
locale_groups.find { |locales| locales.any? { |locale| include?(locale) } }
end
# @!visibility private
sig { returns(String) }
def to_s
[@language, @script, @region].compact.join("-")

View File

@ -132,6 +132,7 @@ class MacOSVersion < Version
alias requires_popcnt? requires_nehalem_cpu?
# Represents the absence of a version.
#
# NOTE: Constructor needs to called with an arbitrary macOS-like version which is then set to `nil`.
NULL = MacOSVersion.new("10.0").tap { |v| v.instance_variable_set(:@version, nil) }.freeze
end
@ -141,7 +142,7 @@ require "lazy_object"
module MacOSVersionErrorCompat
def const_missing(name)
if name == :MacOSVersionError
odisabled "MacOSVersionError", "MacOSVersion::Error"
odisabled "`MacOSVersionError`", "`MacOSVersion::Error`"
return MacOSVersion::Error
end
@ -158,7 +159,7 @@ end
module MacOSVersions
SYMBOLS = LazyObject.new do # rubocop:disable Style/MutableConstant
odisabled "MacOSVersions::SYMBOLS", "MacOSVersion::SYMBOLS"
odisabled "`MacOSVersions::SYMBOLS`", "`MacOSVersion::SYMBOLS`"
MacOSVersion::SYMBOLS
end
end
@ -167,7 +168,7 @@ module OS
module Mac
# TODO: Replace `::Version` with `Version` when this is removed.
Version = LazyObject.new do # rubocop:disable Style/MutableConstant
odisabled "OS::Mac::Version", "MacOSVersion"
odisabled "`OS::Mac::Version`", "`MacOSVersion`"
MacOSVersion
end
end

View File

@ -287,17 +287,18 @@ class Migrator
def repin
return unless pinned?
# old_pin_record is a relative symlink and when we try to to read it
# `old_pin_record` is a relative symlink and when we try to to read it
# from <dir> we actually try to find file
# <dir>/../<...>/../Cellar/name/version.
# To repin formula we need to update the link thus that it points to
# the right directory.
# NOTE: old_pin_record.realpath.sub(oldname, newname) is unacceptable
# here, because it resolves every symlink for old_pin_record and then
#
# NOTE: `old_pin_record.realpath.sub(oldname, newname)` is unacceptable
# here, because it resolves every symlink for `old_pin_record` and then
# substitutes oldname with newname. It breaks things like
# Pathname#make_relative_symlink, where Pathname#relative_path_from
# is used to find relative path from source to destination parent and
# it assumes no symlinks.
# `Pathname#make_relative_symlink`, where `Pathname#relative_path_from`
# is used to find the relative path from source to destination parent
# and it assumes no symlinks.
src_oldname = (old_pin_record.dirname/old_pin_link_record).expand_path
new_pin_record.make_relative_symlink(src_oldname.sub(oldname, newname))
old_pin_record.delete

View File

@ -38,7 +38,6 @@ class Mktemp
@quiet = true
end
# @!visibility private
sig { returns(String) }
def to_s
"[Mktemp: #{tmpdir} retain=#{@retain} quiet=#{@quiet}]"

View File

@ -11,7 +11,6 @@ class Option
@description = description
end
# @!visibility private
sig { returns(String) }
def to_s = flag
@ -30,7 +29,6 @@ class Option
name.hash
end
# @!visibility private
sig { returns(String) }
def inspect
"#<#{self.class.name}: #{flag.inspect}>"
@ -134,13 +132,11 @@ class Options
alias to_ary to_a
# @!visibility private
sig { returns(String) }
def to_s
@options.map(&:to_s).join(" ")
end
# @!visibility private
sig { returns(String) }
def inspect
"#<#{self.class.name}: #{to_a.inspect}>"

View File

@ -51,7 +51,6 @@ class EmbeddedPatch
Utils.safe_popen_write("patch", *args) { |p| p.write(data) }
end
# @!visibility private
sig { returns(String) }
def inspect
"#<#{self.class.name}: #{strip.inspect}>"
@ -151,7 +150,6 @@ class ExternalPatch
raise BuildError.new(f, cmd, args, ENV.to_hash)
end
# @!visibility private
sig { returns(String) }
def inspect
"#<#{self.class.name}: #{strip.inspect} #{url.inspect}>"

View File

@ -39,7 +39,6 @@ class PkgVersion
end
end
# @!visibility private
sig { returns(String) }
def to_s = to_str

View File

@ -140,7 +140,6 @@ class Requirement
[self.class, name, tags].hash
end
# @!visibility private
sig { returns(String) }
def inspect
"#<#{self.class.name}: #{tags.inspect}>"

View File

@ -27,7 +27,6 @@ class ArchRequirement < Requirement
"The #{@arch} architecture is required for this software."
end
# @!visibility private
sig { returns(String) }
def inspect
"#<#{self.class.name}: arch=#{@arch.to_s.inspect} #{tags.inspect}>"

View File

@ -96,7 +96,6 @@ class MacOSRequirement < Requirement
[super, comparator, version].hash
end
# @!visibility private
sig { returns(String) }
def inspect
"#<#{self.class.name}: version#{@comparator}#{@version.to_s.inspect} #{tags.inspect}>"

View File

@ -48,7 +48,6 @@ class XcodeRequirement < Requirement
end
end
# @!visibility private
sig { returns(String) }
def inspect
"#<#{self.class.name}: version>=#{@version.inspect} #{tags.inspect}>"

View File

@ -146,17 +146,22 @@ class Resource < Downloadable
super(verify_download_integrity:)
end
# @!attribute [w] livecheck
# {Livecheck} can be used to check for newer versions of the software.
# This method evaluates the DSL specified in the livecheck block of the
# {Resource} (if it exists) and sets the instance variables of a {Livecheck}
# object accordingly. This is used by `brew livecheck` to check for newer
# versions of the software.
#
# <pre>livecheck do
# ### Example
#
# ```ruby
# livecheck do
# url "https://example.com/foo/releases"
# regex /foo-(\d+(?:\.\d+)+)\.tar/
# end</pre>
# end
# ```
#
# @!attribute [w] livecheck
def livecheck(&block)
return @livecheck unless block
@ -302,7 +307,6 @@ class ResourceStageContext
@staging = staging
end
# @!visibility private
sig { returns(String) }
def to_s
"<#{self.class}: resource=#{resource} staging=#{staging}>"

View File

@ -4,27 +4,26 @@
module RuboCop
module Cop
module Homebrew
# Checks for code that can be written with simpler conditionals
# using `Object#blank?`.
# Checks for code that can be simplified using `Object#blank?`.
#
# @note
# This cop is unsafe autocorrection, because `' '.empty?` returns false,
# but `' '.blank?` returns true. Therefore, autocorrection is not compatible
# if the receiver is a non-empty blank string, tab, or newline meta characters.
# NOTE: Auto-correction for this cop is unsafe because `' '.empty?` returns `false`,
# but `' '.blank?` returns `true`. Therefore, auto-correction is not compatible
# if the receiver is a non-empty blank string.
#
# @example
# # Converts usages of `nil? || empty?` to `blank?`
# ### Example
#
# # bad
# foo.nil? || foo.empty?
# foo == nil || foo.empty?
# ```ruby
# # bad
# foo.nil? || foo.empty?
# foo == nil || foo.empty?
#
# # good
# foo.blank?
# # good
# foo.blank?
# ```
class Blank < Base
extend AutoCorrector
MSG_NIL_OR_EMPTY = "Use `%<prefer>s` instead of `%<current>s`."
MSG = "Use `%<prefer>s` instead of `%<current>s`."
# `(send nil $_)` is not actually a valid match for an offense. Nodes
# that have a single method call on the left hand side
@ -49,7 +48,7 @@ module RuboCop
nil_or_empty?(node) do |var1, var2|
return if var1 != var2
message = format(MSG_NIL_OR_EMPTY, prefer: replacement(var1), current: node.source)
message = format(MSG, prefer: replacement(var1), current: node.source)
add_offense(node, message:) do |corrector|
autocorrect(corrector, node)
end

View File

@ -9,20 +9,23 @@ module RuboCop
module Cask
# This cop makes sure that OS conditionals are consistent.
#
# @example
# # bad
# cask 'foo' do
# if MacOS.version == :high_sierra
# sha256 "..."
# end
# end
# ### Example
#
# # good
# cask 'foo' do
# on_high_sierra do
# sha256 "..."
# end
# ```ruby
# # bad
# cask 'foo' do
# if MacOS.version == :high_sierra
# sha256 "..."
# end
# end
#
# # good
# cask 'foo' do
# on_high_sierra do
# sha256 "..."
# end
# end
# ```
class OnSystemConditionals < Base
extend Forwardable
extend AutoCorrector

View File

@ -6,16 +6,17 @@ module RuboCop
module Cask
# This cop checks that a cask's `url` stanza is formatted correctly.
#
# @example
# # bad
# url "https://example.com/download/foo.dmg",
# verified: "https://example.com/download"
# ### Example
#
# ```ruby
# # bad
# url "https://example.com/download/foo.dmg",
# verified: "https://example.com/download"
#
# # good
# url "https://example.com/download/foo.dmg",
# verified: "example.com/download/"
#
# # good
# url "https://example.com/download/foo.dmg",
# verified: "example.com/download/"
# ```
class Url < Base
extend AutoCorrector
extend Forwardable

View File

@ -8,16 +8,19 @@ module RuboCop
module Cask
# This cop audits variables in casks.
#
# @example
# # bad
# cask do
# arch = Hardware::CPU.intel? ? "darwin" : "darwin-arm64"
# end
# ### Example
#
# # good
# cask 'foo' do
# arch arm: "darwin-arm64", intel: "darwin"
# end
# ```ruby
# # bad
# cask do
# arch = Hardware::CPU.intel? ? "darwin" : "darwin-arm64"
# end
#
# # good
# cask 'foo' do
# arch arm: "darwin-arm64", intel: "darwin"
# end
# ```
class Variables < Base
extend Forwardable
extend AutoCorrector

View File

@ -6,27 +6,30 @@ require "rubocops/extend/formula_cop"
module RuboCop
module Cop
module FormulaAudit
# This cop makes sure that caveats don't recommend unsupported or unsafe operations.
# This cop ensures that caveats don't recommend unsupported or unsafe operations.
#
# @example
# # bad
# def caveats
# <<~EOS
# Use `setuid` to allow running the exeutable by non-root users.
# EOS
# end
# ### Example
#
# # good
# def caveats
# <<~EOS
# Use `sudo` to run the executable.
# EOS
# end
# ```ruby
# # bad
# def caveats
# <<~EOS
# Use `setuid` to allow running the exeutable by non-root users.
# EOS
# end
#
# # good
# def caveats
# <<~EOS
# Use `sudo` to run the executable.
# EOS
# end
# ```
class Caveats < FormulaCop
def audit_formula(_node, _class_node, _parent_class_node, _body_node)
caveats_strings.each do |n|
if regex_match_group(n, /\bsetuid\b/i)
problem "Don't recommend setuid in the caveats, suggest sudo instead."
problem "Don't recommend `setuid` in the caveats, suggest `sudo` instead."
end
problem "Don't use ANSI escape codes in the caveats." if regex_match_group(n, /\e/)

View File

@ -6,43 +6,46 @@ module RuboCop
module Homebrew
# Checks if collection can be blank-compacted with `compact_blank`.
#
# @note
# It is unsafe by default because false positives may occur in the
# blank check of block arguments to the receiver object.
# NOTE: It is unsafe by default because false positives may occur in the
# blank check of block arguments to the receiver object.
#
# For example, `[[1, 2], [3, nil]].reject { |first, second| second.blank? }` and
# `[[1, 2], [3, nil]].compact_blank` are not compatible. The same is true for `blank?`.
# This will work fine when the receiver is a hash object.
# For example, `[[1, 2], [3, nil]].reject { |first, second| second.blank? }` and
# `[[1, 2], [3, nil]].compact_blank` are not compatible. The same is true for `blank?`.
# This will work fine when the receiver is a hash object.
#
# And `compact_blank!` has different implementations for `Array`, `Hash`, and
# `ActionController::Parameters`.
# `Array#compact_blank!`, `Hash#compact_blank!` are equivalent to `delete_if(&:blank?)`.
# `ActionController::Parameters#compact_blank!` is equivalent to `reject!(&:blank?)`.
# If the cop makes a mistake, autocorrected code may get unexpected behavior.
# And `compact_blank!` has different implementations for `Array`, `Hash`, and
# `ActionController::Parameters`.
# `Array#compact_blank!`, `Hash#compact_blank!` are equivalent to `delete_if(&:blank?)`.
# `ActionController::Parameters#compact_blank!` is equivalent to `reject!(&:blank?)`.
# If the cop makes a mistake, autocorrected code may get unexpected behavior.
#
# @example
# ### Examples
#
# # bad
# collection.reject(&:blank?)
# collection.reject { |_k, v| v.blank? }
# ```ruby
# # bad
# collection.reject(&:blank?)
# collection.reject { |_k, v| v.blank? }
#
# # good
# collection.compact_blank
# # good
# collection.compact_blank
# ```
#
# # bad
# collection.delete_if(&:blank?) # Same behavior as `Array#compact_blank!` and `Hash#compact_blank!`
# collection.delete_if { |_, v| v.blank? } # Same behavior as `Array#compact_blank!` and `Hash#compact_blank!`
# collection.reject!(&:blank?) # Same behavior as `ActionController::Parameters#compact_blank!`
# collection.reject! { |_k, v| v.blank? } # Same behavior as `ActionController::Parameters#compact_blank!`
#
# # good
# collection.compact_blank!
# ```ruby
# # bad
# collection.delete_if(&:blank?) # Same behavior as `Array#compact_blank!` and `Hash#compact_blank!`
# collection.delete_if { |_, v| v.blank? } # Same behavior as `Array#compact_blank!` and `Hash#compact_blank!`
# collection.reject!(&:blank?) # Same behavior as `ActionController::Parameters#compact_blank!`
# collection.reject! { |_k, v| v.blank? } # Same behavior as `ActionController::Parameters#compact_blank!`
#
# # good
# collection.compact_blank!
# ```
class CompactBlank < Base
include RangeHelp
extend AutoCorrector
MSG = "Use `%<preferred_method>s` instead."
RESTRICT_ON_SEND = [:reject, :delete_if, :reject!].freeze
def_node_matcher :reject_with_block?, <<~PATTERN

View File

@ -70,7 +70,7 @@ module RuboCop
# Compact the above into this list as we're able to remove detailed notations, etc over time.
when
# Check for http:// GitHub homepage URLs, https:// is preferred.
# Note: only check homepages that are repo pages, not *.github.com hosts
# NOTE: Only check homepages that are repo pages, not *.github.com hosts.
%r{^http://github\.com/},
%r{^http://[^/]*\.github\.io/},

View File

@ -4,9 +4,10 @@
module RuboCop
module Cop
module Homebrew
# This cop restricts usage of IO.read functions for security reasons.
# This cop restricts usage of `IO.read` functions for security reasons.
class IORead < Base
MSG = "The use of `IO.%<method>s` is a security risk."
RESTRICT_ON_SEND = [:read, :readlines].freeze
def on_send(node)

View File

@ -7,23 +7,25 @@ module RuboCop
# Enforces the use of `collection.exclude?(obj)`
# over `!collection.include?(obj)`.
#
# @note
# This cop is unsafe because false positive will occur for
# receiver objects that do not have an `exclude?` method. (e.g. `IPAddr`)
# NOTE: This cop is unsafe because false positives will occur for
# receiver objects that do not have an `#exclude?` method (e.g. `IPAddr`).
#
# @example
# # bad
# !array.include?(2)
# !hash.include?(:key)
# ### Example
#
# # good
# array.exclude?(2)
# hash.exclude?(:key)
# ```ruby
# # bad
# !array.include?(2)
# !hash.include?(:key)
#
# # good
# array.exclude?(2)
# hash.exclude?(:key)
# ```
class NegateInclude < Base
extend AutoCorrector
MSG = "Use `.exclude?` and remove the negation part."
RESTRICT_ON_SEND = [:!].freeze
def_node_matcher :negate_include_call?, <<~PATTERN

View File

@ -7,37 +7,41 @@ module RuboCop
# Checks code that can be written more easily using
# `Object#presence` defined by Active Support.
#
# @example
# # bad
# a.present? ? a : nil
# ### Examples
#
# # bad
# !a.present? ? nil : a
# ```ruby
# # bad
# a.present? ? a : nil
#
# # bad
# a.blank? ? nil : a
# # bad
# !a.present? ? nil : a
#
# # bad
# !a.blank? ? a : nil
# # bad
# a.blank? ? nil : a
#
# # good
# a.presence
# # bad
# !a.blank? ? a : nil
#
# @example
# # bad
# a.present? ? a : b
# # good
# a.presence
# ```
#
# # bad
# !a.present? ? b : a
# ```ruby
# # bad
# a.present? ? a : b
#
# # bad
# a.blank? ? b : a
# # bad
# !a.present? ? b : a
#
# # bad
# !a.blank? ? a : b
# # bad
# a.blank? ? b : a
#
# # good
# a.presence || b
# # bad
# !a.blank? ? a : b
#
# # good
# a.presence || b
# ```
class Presence < Base
include RangeHelp
extend AutoCorrector

View File

@ -4,24 +4,24 @@
module RuboCop
module Cop
module Homebrew
# Checks for code that can be written with simpler conditionals
# using `Object#present?`.
# Checks for code that can be simplified using `Object#present?`.
#
# @example
# # Converts usages of `!nil? && !empty?` to `present?`
# ### Example
#
# # bad
# !foo.nil? && !foo.empty?
# ```ruby
# # bad
# !foo.nil? && !foo.empty?
#
# # bad
# foo != nil && !foo.empty?
# # bad
# foo != nil && !foo.empty?
#
# # good
# foo.present?
# # good
# foo.present?
# ```
class Present < Base
extend AutoCorrector
MSG_EXISTS_AND_NOT_EMPTY = "Use `%<prefer>s` instead of `%<current>s`."
MSG = "Use `%<prefer>s` instead of `%<current>s`."
def_node_matcher :exists_and_not_empty?, <<~PATTERN
(and
@ -41,7 +41,7 @@ module RuboCop
exists_and_not_empty?(node) do |var1, var2|
return if var1 != var2
message = format(MSG_EXISTS_AND_NOT_EMPTY, prefer: replacement(var1), current: node.source)
message = format(MSG, prefer: replacement(var1), current: node.source)
add_offense(node, message:) do |corrector|
autocorrect(corrector, node)
@ -53,7 +53,7 @@ module RuboCop
exists_and_not_empty?(node) do |var1, var2|
return if var1 != var2
add_offense(node, message: MSG_EXISTS_AND_NOT_EMPTY) do |corrector|
add_offense(node, message: MSG) do |corrector|
autocorrect(corrector, node)
end
end

View File

@ -7,28 +7,26 @@ module RuboCop
# Checks to make sure safe navigation isn't used with `blank?` in
# a conditional.
#
# @note
# While the safe navigation operator is generally a good idea, when
# checking `foo&.blank?` in a conditional, `foo` being `nil` will actually
# do the opposite of what the author intends.
# NOTE: While the safe navigation operator is generally a good idea, when
# checking `foo&.blank?` in a conditional, `foo` being `nil` will actually
# do the opposite of what the author intends:
#
# For example:
# ```ruby
# foo&.blank? #=> nil
# foo.blank? #=> true
# ```
#
# [source,ruby]
# ----
# foo&.blank? #=> nil
# foo.blank? #=> true
# ----
# ### Example
#
# @example
# # bad
# do_something if foo&.blank?
# do_something unless foo&.blank?
#
# # good
# do_something if foo.blank?
# do_something unless foo.blank?
# ```ruby
# # bad
# do_something if foo&.blank?
# do_something unless foo&.blank?
#
# # good
# do_something if foo.blank?
# do_something unless foo.blank?
# ```
class SafeNavigationWithBlank < Base
extend AutoCorrector

View File

@ -30,7 +30,7 @@ module RuboCop
method_calls.delete(:service)
# NOTE: Solving the first problem here might solve the second one too
# so we don't show both of them at the same time.
# so we don't show both of them at the same time.
if !method_calls.keys.intersect?(REQUIRED_METHOD_CALLS)
offending_node(service_node)
problem "Service blocks require `run` or `name` to be defined."

View File

@ -174,10 +174,19 @@ module RuboCop
# Matches a method with a receiver. Yields to a block with matching method node.
#
# @example to match `Formula.factory(name)`
# find_instance_method_call(node, "Formula", :factory)
# @example to match `build.head?`
# find_instance_method_call(node, :build, :head?)
# ### Examples
#
# Match `Formula.factory(name)`.
#
# ```ruby
# find_instance_method_call(node, "Formula", :factory)
# ```
#
# Match `build.head?`.
#
# ```ruby
# find_instance_method_call(node, :build, :head?)
# ```
def find_instance_method_call(node, instance, method_name)
methods = find_every_method_call_by_name(node, method_name)
methods.each do |method|
@ -194,8 +203,13 @@ module RuboCop
# Matches receiver part of method. Yields to a block with parent node of receiver.
#
# @example to match `ARGV.<whatever>()`
# find_instance_call(node, "ARGV")
# ### Example
#
# Match `ARGV.<whatever>()`.
#
# ```ruby
# find_instance_call(node, "ARGV")
# ```
def find_instance_call(node, name)
node.each_descendant(:send) do |method_node|
next if method_node.receiver.nil?

View File

@ -40,9 +40,11 @@ module RuboCop
until
while
].freeze
private_constant :SHELL_BUILTINS
# https://github.com/ruby/ruby/blob/v2_6_3/process.c#L2495
SHELL_METACHARACTERS = %W[* ? { } [ ] < > ( ) ~ & | \\ $ ; ' ` " \n #].freeze
private_constant :SHELL_METACHARACTERS
# This cop makes sure that shell command arguments are separated.
class ShellCommands < Base
@ -60,6 +62,8 @@ module RuboCop
[:Utils, :popen_write],
[:Utils, :safe_popen_write],
].freeze
private_constant :TARGET_METHODS
RESTRICT_ON_SEND = TARGET_METHODS.map(&:second).uniq.freeze
def on_send(node)

View File

@ -207,7 +207,7 @@ module RuboCop
problem "Use versioned rather than branch tarballs for stable checksums."
end
# Use new-style archive downloads
# Use new-style archive downloads.
archive_gh_pattern = %r{https://.*github.*/(?:tar|zip)ball/}
audit_urls(urls, archive_gh_pattern) do |_, url|
next if url.end_with?(".git")

View File

@ -138,7 +138,7 @@ class SoftwareSpec
end
def go_resource(name, &block)
# odeprecated "SoftwareSpec#go_resource", "Go modules"
# odeprecated "`SoftwareSpec#go_resource`", "Go modules"
resource name, Resource::Go, &block
end
@ -207,18 +207,6 @@ class SoftwareSpec
depends_on UsesFromMacOSDependency.new(dep, tags, bounds:)
end
# @deprecated
def uses_from_macos_elements
# TODO: Remember to remove the delegate from `Formula`.
odisabled "#uses_from_macos_elements", "#declared_deps"
end
# @deprecated
def uses_from_macos_names
# TODO: Remember to remove the delegate from `Formula`.
odisabled "#uses_from_macos_names", "#declared_deps"
end
def deps
dependency_collector.deps.dup_without_system_deps
end
@ -318,7 +306,6 @@ class Bottle
"#{name}--#{version}#{extname}"
end
# @!visibility private
sig { returns(String) }
def to_s = to_str

View File

@ -80,21 +80,12 @@ class Formula
sig { params(args: T.untyped, options: T.untyped, block: T.untyped).returns(T::Boolean) }
def loaded_from_api?(*args, **options, &block); end
sig { params(args: T.untyped, options: T.untyped, block: T.untyped).returns(T.untyped) }
def resource(*args, **options, &block); end
sig { params(args: T.untyped, options: T.untyped, block: T.untyped).returns(T.untyped) }
def deps(*args, **options, &block); end
sig { params(args: T.untyped, options: T.untyped, block: T.untyped).returns(T.untyped) }
def declared_deps(*args, **options, &block); end
sig { params(args: T.untyped, options: T.untyped, block: T.untyped).returns(T.untyped) }
def uses_from_macos_elements(*args, **options, &block); end
sig { params(args: T.untyped, options: T.untyped, block: T.untyped).returns(T.untyped) }
def uses_from_macos_names(*args, **options, &block); end
sig { params(args: T.untyped, options: T.untyped, block: T.untyped).returns(T.untyped) }
def requirements(*args, **options, &block); end
@ -161,9 +152,6 @@ class Formula
sig { params(args: T.untyped, options: T.untyped, block: T.untyped).returns(T.untyped) }
def env(*args, **options, &block); end
sig { params(args: T.untyped, options: T.untyped, block: T.untyped).returns(T.untyped) }
def conflicts(*args, **options, &block); end
sig { returns(T::Boolean) }
def self.loaded_from_api?; end

View File

@ -16,7 +16,6 @@ module Homebrew
@column = column
end
# @!visibility private
sig { returns(String) }
def to_s
"#{line}#{column&.to_s&.prepend(":")}"

View File

@ -179,7 +179,7 @@ module Homebrew
# -f (--force) : we know what we are doing, force apply patches
# -d / (--directory=/) : change to root directory, since we use absolute file paths
# -p0 (--strip=0) : do not strip path prefixes, since we are at root directory
# NOTE: we use short flags where for compatibility
# NOTE: We use short flags for compatibility.
patch_command = %w[patch -g 0 -f -d / -p0]
patches = system_command(shellcheck, args: ["--format=diff", *args]).stdout
Utils.safe_popen_write(*patch_command) { |p| p.write(patches) } if patches.present?

View File

@ -13,12 +13,18 @@ class Tab
FILENAME = "INSTALL_RECEIPT.json"
# Check whether the formula was installed as a dependency.
#
# @api internal
attr_accessor :installed_as_dependency
# Check whether the formula was installed on request.
#
# @api internal
attr_accessor :installed_on_request
# Check whether the formula was poured from a bottle.
#
# @api internal
attr_accessor :poured_from_bottle
@ -27,6 +33,8 @@ class Tab
:built_on
attr_writer :used_options, :unused_options, :compiler, :source_modified_time
# Returns the formula's runtime dependencies.
#
# @api internal
attr_writer :runtime_dependencies
@ -72,7 +80,8 @@ class Tab
end
# Returns the {Tab} for an install receipt at `path`.
# Results are cached.
#
# NOTE: Results are cached.
def self.from_file(path)
cache.fetch(path) do |p|
content = File.read(p)
@ -282,7 +291,10 @@ class Tab
spec == :stable
end
# The options used to install the formula.
#
# @api internal
sig { returns(Options) }
def used_options
Options.create(@used_options)
end
@ -321,6 +333,7 @@ class Tab
built_as_bottle
end
sig { returns(T.nilable(Tap)) }
def tap
tap_name = source["tap"]
Tap.fetch(tap_name) if tap_name
@ -407,7 +420,6 @@ class Tab
tabfile.atomic_write(to_json)
end
# @!visibility private
sig { returns(String) }
def to_s
s = []

View File

@ -154,7 +154,6 @@ class Tap
attr_reader :name
# @api public
# @!visibility private
sig { returns(String) }
def to_s = name

View File

@ -73,8 +73,8 @@ RSpec.describe "ENV" do
expect(subject["foo"]).to eq("1")
end
# NOTE: this may be a wrong behavior; we should probably reject objects that
# do not respond to #to_str. For now this documents existing behavior.
# NOTE: This may be a wrong behavior; we should probably reject objects that
# do not respond to `#to_str`. For now this documents existing behavior.
it "coerces a value to a string" do
subject.append "foo", 42
expect(subject["foo"]).to eq("42")

View File

@ -332,8 +332,8 @@ RSpec.describe Cask::Cask, :cask do
expect(JSON.pretty_generate(h["variations"])).to eq expected_sha256_variations.strip
end
# @note The calls to `Cask.generating_hash!` and `Cask.generated_hash!`
# are not idempotent so they can only be used in one test.
# NOTE: The calls to `Cask.generating_hash!` and `Cask.generated_hash!`
# are not idempotent so they can only be used in one test.
it "returns the correct hash placeholders" do
described_class.generating_hash!
expect(described_class).to be_generating_hash

View File

@ -87,7 +87,7 @@ RSpec.describe Caveats do
expect(caveats).to include("tmux")
end
# @todo This should get deprecated and the service block `plist_name` method should get used instead.
# TODO: This should get deprecated and the service block `plist_name` method should get used instead.
it "prints info when there are custom service files" do
f = formula do
url "foo-1.0"

View File

@ -79,9 +79,9 @@ RSpec.describe Homebrew::Livecheck::Strategy::ElectronBuilder do
end).to eq(find_versions_cached_return_hash.merge({ regex: }))
# NOTE: A regex should be provided using the `#regex` method in a
# `livecheck` block but we're using a regex literal in the `strategy`
# block here simply to ensure this method works as expected when a
# regex isn't provided.
# `livecheck` block but we're using a regex literal in the `strategy`
# block here simply to ensure this method works as expected when a
# regex isn't provided.
expect(electron_builder.find_versions(url: http_url, provided_content: content) do |yaml|
regex = /^v?(\d+(?:\.\d+)+)$/i
yaml["version"][regex, 1]

View File

@ -75,7 +75,7 @@ RSpec.describe Homebrew::Livecheck::Strategy::HeaderMatch do
end
it "returns an array of version strings when given headers and a block" do
# Returning a string from block, no regex
# Returning a string from block, no regex.
expect(
header_match.versions_from_headers(headers[:location]) do |headers|
v = Version.parse(headers["location"], detected_from_url: true)
@ -83,16 +83,17 @@ RSpec.describe Homebrew::Livecheck::Strategy::HeaderMatch do
end,
).to eq(versions[:location])
# Returning a string from block, explicit regex
# Returning a string from block, explicit regex.
expect(
header_match.versions_from_headers(headers[:location], regexes[:latest]) do |headers, regex|
headers["location"] ? headers["location"][regex, 1] : nil
end,
).to eq(versions[:location])
# Returning an array of strings from block
# NOTE: Strategies runs `#compact` on an array from a block, so nil
# values are filtered out without needing to use `#compact` in the block.
# Returning an array of strings from block.
#
# NOTE: Strategies runs `#compact` on an array from a block, so nil values
# are filtered out without needing to use `#compact` in the block.
expect(
header_match.versions_from_headers(
headers[:content_disposition_and_location],

View File

@ -126,9 +126,9 @@ RSpec.describe Homebrew::Livecheck::Strategy::Json do
end).to eq(find_versions_cached_return_hash)
# NOTE: A regex should be provided using the `#regex` method in a
# `livecheck` block but we're using a regex literal in the `strategy`
# block here simply to ensure this method works as expected when a
# regex isn't provided.
# `livecheck` block but we're using a regex literal in the `strategy`
# block here simply to ensure this method works as expected when a
# regex isn't provided.
expect(json.find_versions(url: http_url, provided_content: content) do |json|
regex = /^v?(\d+(?:\.\d+)+)$/i
json["versions"].select { |item| item["version"]&.match?(regex) }

View File

@ -109,11 +109,11 @@ RSpec.describe Homebrew::Livecheck::Strategy::PageMatch do
.to eq(find_versions_cached_return_hash)
# NOTE: Ideally, a regex should always be provided to `#find_versions`
# for `PageMatch` but there are currently some `livecheck` blocks in
# casks where `#regex` isn't used and the regex only exists within a
# `strategy` block. This isn't ideal but, for the moment, we allow a
# `strategy` block to act as a substitution for a regex and we need to
# test this scenario to ensure it works.
# for `PageMatch` but there are currently some `livecheck` blocks in
# casks where `#regex` isn't used and the regex only exists within a
# `strategy` block. This isn't ideal but, for the moment, we allow a
# `strategy` block to act as a substitution for a regex and we need to
# test this scenario to ensure it works.
#
# Under normal circumstances, a regex should be established in a
# `livecheck` block using `#regex` and passed into the `strategy` block

View File

@ -218,9 +218,9 @@ RSpec.describe Homebrew::Livecheck::Strategy::Xml do
end).to eq(find_versions_cached_return_hash)
# NOTE: A regex should be provided using the `#regex` method in a
# `livecheck` block but we're using a regex literal in the `strategy`
# block here simply to ensure this method works as expected when a
# regex isn't provided.
# `livecheck` block but we're using a regex literal in the `strategy`
# block here simply to ensure this method works as expected when a
# regex isn't provided.
expect(xml.find_versions(url: http_url, provided_content: content_version_text) do |xml|
regex = /^v?(\d+(?:\.\d+)+)$/i
xml.get_elements("/versions/version").map { |item| item.text[regex, 1] }

View File

@ -127,9 +127,9 @@ RSpec.describe Homebrew::Livecheck::Strategy::Yaml do
end).to eq(find_versions_cached_return_hash)
# NOTE: A regex should be provided using the `#regex` method in a
# `livecheck` block but we're using a regex literal in the `strategy`
# block here simply to ensure this method works as expected when a
# regex isn't provided.
# `livecheck` block but we're using a regex literal in the `strategy`
# block here simply to ensure this method works as expected when a
# regex isn't provided.
expect(yaml.find_versions(url: http_url, provided_content: content) do |yaml|
regex = /^v?(\d+(?:\.\d+)+)$/i
yaml["versions"].select { |item| item["version"]&.match?(regex) }

View File

@ -1058,8 +1058,8 @@ RSpec.describe Homebrew::Service do
}
end
# @note The calls to `Formula.generating_hash!` and `Formula.generated_hash!`
# are not idempotent so they can only be used in one test.
# NOTE: The calls to `Formula.generating_hash!` and `Formula.generated_hash!`
# are not idempotent so they can only be used in one test.
it "replaces local paths with placeholders" do
f = stub_formula do
service do

View File

@ -10,7 +10,7 @@ RSpec.describe Tapioca::Compilers::Args do
Homebrew::Cmd::List.parser
end
# good testing candidate, bc it has multiple for each of switch, flag, and comma_array args:
# Good testing candidate because it has multiple for each of `switch`, `flag` and `comma_array` args:
let(:update_python_resources_parser) do
require "dev-cmd/update-python-resources"
Homebrew::DevCmd::UpdatePythonResources.parser

View File

@ -35,8 +35,8 @@ module Cachable
# A list of all classes that have been loaded into memory that mixin or
# inherit `Cachable` at the class or module level.
#
# Note: Classes that inherit from `Formula` are excluded since it's not
# necessary to track and clear individual formula caches.
# NOTE: Classes that inherit from `Formula` are excluded since it's not
# necessary to track and clear individual formula caches.
def self.class_list
@class_list ||= []
end
@ -44,8 +44,8 @@ module Cachable
# Clear the cache of every class or module that mixes in or inherits
# `Cachable` at the class or module level.
#
# Note: Classes that inherit from `Formula` are excluded since it's not
# necessary to track and clear individual formula caches.
# NOTE: Classes that inherit from `Formula` are excluded since it's not
# necessary to track and clear individual formula caches.
def self.clear_all_caches
class_list.each(&:clear_cache)
end

View File

@ -6,11 +6,11 @@ RSpec.describe UnpackStrategy::Zstd do
let(:path) { TEST_FIXTURE_DIR/"cask/container.tar.zst" }
it "is correctly detected" do
# UnpackStrategy.detect(path) for a .tar.XXX file returns either UnpackStrategy::Tar if
# the host's tar is able to extract that compressed file or UnpackStrategy::XXX otherwise,
# such as UnpackStrategy::Zstd. On macOS UnpackStrategy.detect("container.tar.zst")
# returns UnpackStrategy::Zstd, and on ubuntu-22.04 it returns UnpackStrategy::Tar,
# because the host's version of tar is recent enough and zstd is installed.
# `UnpackStrategy.detect(path)` for a `.tar.XXX` file returns either `UnpackStrategy::Tar` if
# the host's `tar` is able to extract that compressed file or `UnpackStrategy::XXX` otherwise,
# such as `UnpackStrategy::Zstd`. On macOS `UnpackStrategy.detect("container.tar.zst")`
# returns `UnpackStrategy::Zstd` and on Ubuntu 22.04 it returns `UnpackStrategy::Tar`,
# because the host's version of `tar` is recent enough and `zstd` is installed.
expect(UnpackStrategy.detect(path)).to(be_a(described_class).or(be_a(UnpackStrategy::Tar)))
end
end

View File

@ -72,7 +72,7 @@ RSpec.describe Utils::Shell do
specify "::csh_quote" do
expect(described_class.send(:csh_quote, "")).to eq("''")
expect(described_class.send(:csh_quote, "\\")).to eq("\\\\")
# NOTE: this test is different than for sh
# NOTE: This test is different than for `sh`.
expect(described_class.send(:csh_quote, "\n")).to eq("'\\\n'")
expect(described_class.send(:csh_quote, "$")).to eq("\\$")
expect(described_class.send(:csh_quote, "word")).to eq("word")

View File

@ -15,7 +15,6 @@ class URL
@specs.freeze
end
# @!visibility private
sig { returns(String) }
def to_s
@url

View File

@ -202,7 +202,6 @@ module Utils
end
end
# @!visibility private
sig { returns(String) }
def to_s
to_sym.to_s

Some files were not shown because too many files have changed in this diff Show More