Enforce finalized sorbet methods

This commit is contained in:
Douglas Eichelberger 2024-11-21 18:10:20 -08:00
parent f0c746a0b9
commit 33d9267d7d
6 changed files with 41 additions and 13 deletions

View File

@ -77,6 +77,9 @@ class Formula
extend Cachable
extend Attrable
extend APIHashable
extend T::Helpers
abstract!
SUPPORTED_NETWORK_ACCESS_PHASES = [:build, :test, :postinstall].freeze
private_constant :SUPPORTED_NETWORK_ACCESS_PHASES
@ -1596,7 +1599,11 @@ class Formula
# Yields |self,staging| with current working directory set to the uncompressed tarball
# where staging is a {Mktemp} staging context.
def brew(fetch: true, keep_tmp: false, debug_symbols: false, interactive: false)
sig(:final) {
params(fetch: T::Boolean, keep_tmp: T::Boolean, debug_symbols: T::Boolean, interactive: T::Boolean,
_blk: T.proc.params(arg0: Formula, arg1: Mktemp).void).void
}
def brew(fetch: true, keep_tmp: false, debug_symbols: false, interactive: false, &_blk)
@prefix_returns_versioned_prefix = true
active_spec.fetch if fetch
stage(interactive:, debug_symbols:) do |staging|
@ -3344,12 +3351,7 @@ class Formula
def method_added(method)
super
case method
when :brew
raise "You cannot override Formula#brew in class #{name}"
when :test
define_method(:test_defined?) { true }
end
define_method(:test_defined?) { true } if method == :test
end
def freeze

View File

@ -13,14 +13,15 @@ require "build_environment"
class Requirement
include Dependable
extend Cachable
extend T::Helpers
# This base class enforces no constraints on its own.
# Individual subclasses use the `satisfy` DSL to define those constraints.
abstract!
attr_reader :name, :cask, :download
def initialize(tags = [])
# Only allow instances of subclasses. This base class enforces no constraints on its own.
# Individual subclasses use the `satisfy` DSL to define those constraints.
raise "Do not call `Requirement.new' directly without a subclass." unless self.class < Requirement
@cask = self.class.cask
@download = self.class.download
tags.each do |tag|

View File

@ -8,7 +8,9 @@ require "extend/module"
# In the future we should consider not doing this monkey patch,
# if assured that there is no performance hit from removing this.
# There are mechanisms to achieve a middle ground (`default_checked_level`).
unless ENV["HOMEBREW_SORBET_RUNTIME"]
if ENV["HOMEBREW_SORBET_RUNTIME"]
T::Configuration.enable_final_checks_on_hooks
else
# Redefine `T.let`, etc. to make the `checked` parameter default to `false` rather than `true`.
# @private
module TNoChecks

View File

@ -60,6 +60,11 @@ RSpec.describe Formula do
expect { klass.new }.to raise_error(ArgumentError)
end
specify "formula instantiation without a subclass" do
expect { described_class.new(name, path, spec) }
.to raise_error(RuntimeError, "Do not call `Formula.new' directly without a subclass.")
end
context "when in a Tap" do
let(:tap) { Tap.fetch("foo", "bar") }
let(:path) { (tap.path/"Formula/#{name}.rb") }

View File

@ -24,7 +24,7 @@ RSpec.describe Formula do
formula do
def brew; end
end
end.to raise_error(RuntimeError, /You cannot override Formula#brew/)
end.to raise_error(RuntimeError, /\AThe method `brew` on #{described_class} was declared as final/)
end
it "validates the `name`" do

View File

@ -10,6 +10,13 @@ RSpec.describe Requirement do
let(:klass) { Class.new(described_class) }
describe "base class" do
it "raises an error when instantiated" do
expect { described_class.new }
.to raise_error(RuntimeError, "Requirement is declared as abstract; it cannot be instantiated")
end
end
describe "#tags" do
subject(:req) { klass.new(tags) }
@ -52,6 +59,17 @@ RSpec.describe Requirement do
describe "#fatal is omitted" do
it { is_expected.not_to be_fatal }
end
describe "in subclasses" do
it "raises an error when instantiated" do
expect do
Class.new(described_class) do
def fatal? = false
end
end
.to raise_error(RuntimeError, /\AThe method `fatal\?` on #{described_class.name} was declared as final/)
end
end
end
describe "#satisfied?" do