From c495aa3e6309fecbac21b90a86a77b1deca02d4c Mon Sep 17 00:00:00 2001 From: Sam Ford <1584702+samford@users.noreply.github.com> Date: Tue, 22 Apr 2025 16:39:55 -0400 Subject: [PATCH 1/2] Cask::DSL::DependsOn: add empty?, present? methods `#present?` is called on a `DependsOn` object in `Cask::DSL` and this is seemingly deferred to the underlying hash object but Sorbet doesn't understand this kind of `SimpleDelegator` magic. This adds `empty?` and `present?` methods that explicitly interact with the hash in a way that Sorbet can understand. --- Library/Homebrew/cask/dsl/depends_on.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Library/Homebrew/cask/dsl/depends_on.rb b/Library/Homebrew/cask/dsl/depends_on.rb index 37cfb302d0..7a5756f9c0 100644 --- a/Library/Homebrew/cask/dsl/depends_on.rb +++ b/Library/Homebrew/cask/dsl/depends_on.rb @@ -82,6 +82,12 @@ module Cask @arch.concat(arches.map { |arch| VALID_ARCHES[arch] }) end + + sig { returns(T::Boolean) } + def empty? = T.let(__getobj__, T::Hash[Symbol, T.untyped]).empty? + + sig { returns(T::Boolean) } + def present? = !empty? end end end From 5b5c460ab9abf3f876cd1308aa59e3c79e78ec68 Mon Sep 17 00:00:00 2001 From: Sam Ford <1584702+samford@users.noreply.github.com> Date: Tue, 22 Apr 2025 17:53:19 -0400 Subject: [PATCH 2/2] Cask::DSL: define instance variables in initialize We're now seeing warnings related to the cask DSL surfaced by Ruby 3.4: ``` /opt/homebrew/Library/Homebrew/cask/dsl.rb:456: warning: The class Cask::DSL reached 8 shape variations, instance variables accesses will be slower and memory usage increased. It is recommended to define instance variables in a consistent order, for instance by eagerly defining them all in the #initialize method. ``` I've been working on upgrading `Cask::DSL` to `typed: strict` and part of that involves defining all of the instance variables in the `initialize` method, so I've extracted this part of that work as a way of helping to resolve the aforementioned warning. This doesn't fully resolve the warning but it addresses what it was originally referencing, at least. For what it's worth, this includes some type fixes but I've only included what's necessary to pass `brew typecheck`. --- Library/Homebrew/cask/dsl.rb | 61 +++++++++++++++++++++--------------- 1 file changed, 36 insertions(+), 25 deletions(-) diff --git a/Library/Homebrew/cask/dsl.rb b/Library/Homebrew/cask/dsl.rb index 8a9bc99bb7..077f522129 100644 --- a/Library/Homebrew/cask/dsl.rb +++ b/Library/Homebrew/cask/dsl.rb @@ -90,14 +90,14 @@ module Cask :deprecated?, :deprecation_date, :deprecation_reason, - :deprecation_replacement_formula, :deprecation_replacement_cask, + :deprecation_replacement_formula, :disable!, :disabled?, :disable_date, :disable_reason, - :disable_replacement_formula, :disable_replacement_cask, + :disable_replacement_formula, :discontinued?, # TODO: remove once discontinued? is removed (4.5.0) :livecheck, :livecheck_defined?, @@ -112,18 +112,39 @@ module Cask include OnSystem::MacOSAndLinux - attr_reader :cask, :token, :deprecation_date, :deprecation_reason, :deprecation_replacement_formula, - :deprecation_replacement_cask, :disable_date, :disable_reason, :disable_replacement_formula, - :disable_replacement_cask, :on_system_block_min_os + attr_reader :cask, :token, :artifacts, :deprecation_date, :deprecation_reason, + :deprecation_replacement_cask, :deprecation_replacement_formula, + :disable_date, :disable_reason, :disable_replacement_cask, + :disable_replacement_formula, :on_system_block_min_os + sig { params(cask: Cask).void } def initialize(cask) - @cask = cask + @artifacts = T.let(ArtifactSet.new, ArtifactSet) + @called_in_on_system_block = T.let(false, T::Boolean) + @cask = T.let(cask, Cask) + @caveats = T.let(DSL::Caveats.new(cask), DSL::Caveats) + @depends_on = T.let(DSL::DependsOn.new, DSL::DependsOn) @depends_on_set_in_block = T.let(false, T::Boolean) @deprecated = T.let(false, T::Boolean) + @deprecation_date = T.let(nil, T.nilable(Date)) + @deprecation_reason = T.let(nil, T.nilable(T.any(String, Symbol))) + @deprecation_replacement_cask = T.let(nil, T.nilable(String)) + @deprecation_replacement_formula = T.let(nil, T.nilable(String)) + @disable_date = T.let(nil, T.nilable(Date)) + @disable_reason = T.let(nil, T.nilable(T.any(String, Symbol))) + @disable_replacement_cask = T.let(nil, T.nilable(String)) + @disable_replacement_formula = T.let(nil, T.nilable(String)) @disabled = T.let(false, T::Boolean) + @language_blocks = T.let({}, T::Hash[T::Array[String], Proc]) + @language_eval = T.let(nil, T.nilable(String)) + @livecheck = T.let(Livecheck.new(cask), Livecheck) @livecheck_defined = T.let(false, T::Boolean) + @name = T.let([], T::Array[String]) @on_system_blocks_exist = T.let(false, T::Boolean) @token = cask.token + @on_system_block_min_os = T.let(nil, T.nilable(MacOSVersion)) + @staged_path = T.let(nil, T.nilable(Pathname)) + @token = T.let(cask.token, String) end sig { returns(T::Boolean) } @@ -153,7 +174,6 @@ module Cask # # @api public def name(*args) - @name ||= [] return @name if args.empty? @name.concat(args.flatten) @@ -210,7 +230,6 @@ module Cask if args.empty? language_eval elsif block - @language_blocks ||= {} @language_blocks[args] = block return unless default @@ -226,11 +245,13 @@ module Cask end def language_eval - return @language_eval if defined?(@language_eval) + return @language_eval unless @language_eval.nil? - return @language_eval = nil if @language_blocks.blank? + return @language_eval = nil if @language_blocks.empty? - raise CaskInvalidError.new(cask, "No default language specified.") if @language_blocks.default.nil? + if (language_blocks_default = @language_blocks.default).nil? + raise CaskInvalidError.new(cask, "No default language specified.") + end locales = cask.config.languages .filter_map do |language| @@ -241,18 +262,15 @@ module Cask locales.each do |locale| key = locale.detect(@language_blocks.keys) + next if key.nil? || (language_block = @language_blocks[key]).nil? - next if key.nil? - - return @language_eval = @language_blocks[key].call + return @language_eval = language_block.call end - @language_eval = @language_blocks.default.call + @language_eval = language_blocks_default.call end def languages - return [] if @language_blocks.nil? - @language_blocks.keys.flatten end @@ -425,7 +443,6 @@ module Cask # # @api public def depends_on(**kwargs) - @depends_on ||= DSL::DependsOn.new @depends_on_set_in_block = true if @called_in_on_system_block return @depends_on if kwargs.empty? @@ -439,7 +456,7 @@ module Cask # @api private def add_implicit_macos_dependency - return if @depends_on.present? && @depends_on.macos.present? + return if (cask_depends_on = @depends_on).present? && cask_depends_on.macos.present? depends_on macos: ">= :#{MacOSVersion::SYMBOLS.key MacOSVersion::SYMBOLS.values.min}" end @@ -452,10 +469,6 @@ module Cask set_unique_stanza(:conflicts_with, kwargs.empty?) { DSL::ConflictsWith.new(**kwargs) } end - def artifacts - @artifacts ||= ArtifactSet.new - end - sig { returns(Pathname) } def caskroom_path cask.caskroom_path @@ -476,7 +489,6 @@ module Cask # # @api public def caveats(*strings, &block) - @caveats ||= DSL::Caveats.new(cask) if block @caveats.eval_caveats(&block) elsif strings.any? @@ -500,7 +512,6 @@ module Cask # # @api public def livecheck(&block) - @livecheck ||= Livecheck.new(cask) return @livecheck unless block if !@cask.allow_reassignment && @livecheck_defined