From 9f13ddce9b149add94916d4785b88f1d668ae880 Mon Sep 17 00:00:00 2001 From: Douglas Eichelberger Date: Sat, 5 Oct 2024 18:56:38 -0700 Subject: [PATCH] Add a Delegator DSL compiler --- Library/Homebrew/cask/url.rb | 64 +++++++++++-------- Library/Homebrew/cask/url.rbi | 24 ------- Library/Homebrew/sorbet/rbi/dsl/cask/url.rbi | 64 +++++++++++++++++++ .../sorbet/tapioca/compilers/delegators.rb | 37 +++++++++++ 4 files changed, 138 insertions(+), 51 deletions(-) delete mode 100644 Library/Homebrew/cask/url.rbi create mode 100644 Library/Homebrew/sorbet/rbi/dsl/cask/url.rbi create mode 100644 Library/Homebrew/sorbet/tapioca/compilers/delegators.rb diff --git a/Library/Homebrew/cask/url.rb b/Library/Homebrew/cask/url.rb index 627163ee65fc85..6cbfdd13fae854 100644 --- a/Library/Homebrew/cask/url.rb +++ b/Library/Homebrew/cask/url.rb @@ -8,11 +8,23 @@ module Cask # Class corresponding to the `url` stanza. class URL < Delegator class DSL - attr_reader :uri, :specs, - :verified, :using, - :tag, :branch, :revisions, :revision, - :trust_cert, :cookies, :referer, :header, :user_agent, - :data, :only_path + attr_reader :uri, :tag, :branch, :revisions, :revision, + :trust_cert, :cookies, :header, :data, :only_path + + sig { returns(T.nilable(T.any(URI::Generic, String))) } + attr_reader :referer + + sig { returns(T::Hash[Symbol, T.untyped]) } + attr_reader :specs + + sig { returns(T.nilable(T.any(Symbol, String))) } + attr_reader :user_agent + + sig { returns(T.any(T::Class[T.anything], Symbol, NilClass)) } + attr_reader :using + + sig { returns(T.nilable(String)) } + attr_reader :verified extend Forwardable def_delegators :uri, :path, :scheme, :to_s @@ -137,6 +149,8 @@ def call end end + private + # Allows calling a nested `url` stanza in a `url do` block. # # @api public @@ -150,7 +164,6 @@ def call def url(uri, &block) self.class.new(uri, dsl: @dsl, &block).call end - private :url # This allows calling DSL methods from inside a `url` block. # @@ -162,12 +175,10 @@ def method_missing(method, *args, &block) 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 { @@ -244,30 +255,11 @@ def initialize( @caller_location = caller_location end - def __getobj__ - @dsl - end - - def __setobj__(dsl) - @dsl = dsl - end - sig { returns(Homebrew::SourceLocation) } def location Homebrew::SourceLocation.new(@caller_location.lineno, raw_url_line&.index("url")) end - sig { returns(T.nilable(String)) } - def raw_url_line - return @raw_url_line if defined?(@raw_url_line) - - @raw_url_line = Pathname(T.must(@caller_location.path)) - .each_line - .drop(@caller_location.lineno - 1) - .first - end - private :raw_url_line - sig { params(ignore_major_version: T::Boolean).returns(T::Boolean) } def unversioned?(ignore_major_version: false) interpolated_url = raw_url_line&.then { |line| line[/url\s+"([^"]+)"/, 1] } @@ -283,5 +275,23 @@ def unversioned?(ignore_major_version: false) def from_block? @from_block end + + private + + def __getobj__ = @dsl + + def __setobj__(dsl) + @dsl = dsl + end + + sig { returns(T.nilable(String)) } + def raw_url_line + return @raw_url_line if defined?(@raw_url_line) + + @raw_url_line = Pathname(T.must(@caller_location.path)) + .each_line + .drop(@caller_location.lineno - 1) + .first + end end end diff --git a/Library/Homebrew/cask/url.rbi b/Library/Homebrew/cask/url.rbi deleted file mode 100644 index 3c56476e6d3389..00000000000000 --- a/Library/Homebrew/cask/url.rbi +++ /dev/null @@ -1,24 +0,0 @@ -# typed: strict - -module Cask - class URL - include Kernel - - # TODO: Generate this - - sig { returns(T.any(T::Class[T.anything], Symbol, NilClass)) } - def using; end - - sig { returns(T.nilable(T.any(URI::Generic, String))) } - def referer; end - - sig { returns(T::Hash[Symbol, T.untyped]) } - def specs; end - - sig { returns(T.nilable(T.any(Symbol, String))) } - def user_agent; end - - sig { returns(T.nilable(String)) } - def verified; end - end -end diff --git a/Library/Homebrew/sorbet/rbi/dsl/cask/url.rbi b/Library/Homebrew/sorbet/rbi/dsl/cask/url.rbi new file mode 100644 index 00000000000000..2cd61a1d6fc0c3 --- /dev/null +++ b/Library/Homebrew/sorbet/rbi/dsl/cask/url.rbi @@ -0,0 +1,64 @@ +# typed: true + +# DO NOT EDIT MANUALLY +# This is an autogenerated file for dynamic methods in `Cask::URL`. +# Please instead update this file by running `bin/tapioca dsl Cask::URL`. + + +class Cask::URL + include Kernel + + sig { returns(T.untyped) } + def branch; end + + sig { returns(T.untyped) } + def cookies; end + + sig { returns(T.untyped) } + def data; end + + sig { returns(T.untyped) } + def header; end + + sig { returns(T.untyped) } + def only_path; end + + sig { returns(T.untyped) } + def path; end + + sig { returns(T.nilable(T.any(::String, ::URI::Generic))) } + def referer; end + + sig { returns(T.untyped) } + def revision; end + + sig { returns(T.untyped) } + def revisions; end + + sig { returns(T.untyped) } + def scheme; end + + sig { returns(T::Hash[::Symbol, T.untyped]) } + def specs; end + + sig { returns(T.untyped) } + def tag; end + + sig { returns(T.untyped) } + def to_s; end + + sig { returns(T.untyped) } + def trust_cert; end + + sig { returns(T.untyped) } + def uri; end + + sig { returns(T.nilable(T.any(::String, ::Symbol))) } + def user_agent; end + + sig { returns(T.nilable(T.any(::Symbol, T::Class[T.anything]))) } + def using; end + + sig { returns(T.nilable(::String)) } + def verified; end +end diff --git a/Library/Homebrew/sorbet/tapioca/compilers/delegators.rb b/Library/Homebrew/sorbet/tapioca/compilers/delegators.rb new file mode 100644 index 00000000000000..dfe79c5a4c65a0 --- /dev/null +++ b/Library/Homebrew/sorbet/tapioca/compilers/delegators.rb @@ -0,0 +1,37 @@ +# typed: strict +# frozen_string_literal: true + +require_relative "../../../global" +require "cask/url" + +module Tapioca + module Compilers + # A compiler for subclasses of Delegator. + # To add a new delegator: require it above add add it to the DELEGATIONS hash below. + class Delegators < Tapioca::Dsl::Compiler + # Mapping of delegator classes to the classes they delegate to (as defined in `__getobj__`). + DELEGATIONS = T.let({ + Cask::URL => Cask::URL::DSL, + }.freeze, T::Hash[Module, Module]) + ConstantType = type_member { { fixed: Module } } + + sig { override.returns(T::Enumerable[Module]) } + def self.gather_constants = DELEGATIONS.keys + + sig { override.void } + def decorate + root.create_path(constant) do |klass| + klass.create_include("Kernel") + delegated = DELEGATIONS.fetch(constant) + + delegated.instance_methods(false).each do |method| + signature = T::Utils.signature_for_method(delegated.instance_method(method)) + # TODO: handle methods with parameters + return_type = signature&.return_type&.to_s || "T.untyped" + klass.create_method(method.to_s, return_type:) + end + end + end + end + end +end