diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 440d07fb3e..f17db8b4ee 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -120,8 +120,7 @@ jobs: - name: Run brew audit --skip-style on all taps run: brew audit --skip-style - # TODO: remove --quiet when possible. - - run: brew typecheck --quiet + - run: brew typecheck - name: Run vale for docs linting run: | diff --git a/.gitignore b/.gitignore index 49f15cd5b0..8786b176d2 100644 --- a/.gitignore +++ b/.gitignore @@ -35,6 +35,7 @@ !**/vendor/bundle/ruby/*/gems/*/lib !**/vendor/bundle/ruby/*/gems/rubocop-performance-*/config !**/vendor/bundle/ruby/*/gems/rubocop-rspec-*/config +!**/vendor/bundle/ruby/*/gems/rubocop-sorbet-*/config # Ignore partially included gems where we don't need all files **/vendor/bundle/ruby/*/gems/activesupport-*/lib/active_support.rb @@ -134,6 +135,7 @@ **/vendor/bundle/ruby/*/gems/simplecov-html-*/ **/vendor/bundle/ruby/*/gems/sorbet-*/ **/vendor/bundle/ruby/*/gems/sorbet-runtime-*/ +!**/vendor/bundle/ruby/*/gems/sorbet-runtime-stub-*/ **/vendor/bundle/ruby/*/gems/spoom-*/ **/vendor/bundle/ruby/*/gems/stackprof-*/ **/vendor/bundle/ruby/*/gems/strscan-*/ diff --git a/Library/.rubocop.yml b/Library/.rubocop.yml index e87f5d9c58..7653a9495c 100644 --- a/Library/.rubocop.yml +++ b/Library/.rubocop.yml @@ -264,6 +264,14 @@ Layout/LineLength: ' "~/Library/Application Support/', '"~/Library/Caches/', '"~/Application Support', ' was verified as official when first introduced to the cask'] +# Enable once we are using `sorbet-runtime`. +Sorbet/FalseSigil: + Enabled: false + +# Try getting rid of these. +Sorbet/ConstantsFromStrings: + Enabled: false + # Avoid false positives on modifiers used on symbols of methods # See https://github.com/rubocop-hq/rubocop/issues/5953 Style/AccessModifierDeclarations: diff --git a/Library/Homebrew/Gemfile b/Library/Homebrew/Gemfile index 776b7abe6c..57014db73f 100644 --- a/Library/Homebrew/Gemfile +++ b/Library/Homebrew/Gemfile @@ -25,4 +25,6 @@ gem "patchelf" gem "plist" gem "rubocop-performance" gem "rubocop-rspec" +gem "rubocop-sorbet" gem "ruby-macho" +gem "sorbet-runtime-stub" diff --git a/Library/Homebrew/Gemfile.lock b/Library/Homebrew/Gemfile.lock index b4d08c5b50..1279c0becf 100644 --- a/Library/Homebrew/Gemfile.lock +++ b/Library/Homebrew/Gemfile.lock @@ -114,6 +114,8 @@ GEM rubocop-ast (>= 0.4.0) rubocop-rspec (1.43.2) rubocop (~> 0.87) + rubocop-sorbet (0.5.1) + rubocop ruby-macho (2.2.0) ruby-progressbar (1.10.1) simplecov (0.19.0) @@ -123,6 +125,7 @@ GEM sorbet (0.5.5942) sorbet-static (= 0.5.5942) sorbet-runtime (0.5.5942) + sorbet-runtime-stub (0.2.0) sorbet-static (0.5.5942-universal-darwin-14) spoom (1.0.4) colorize @@ -167,10 +170,12 @@ DEPENDENCIES rubocop rubocop-performance rubocop-rspec + rubocop-sorbet ruby-macho simplecov sorbet sorbet-runtime + sorbet-runtime-stub tapioca BUNDLED WITH diff --git a/Library/Homebrew/cask/dsl/conflicts_with.rb b/Library/Homebrew/cask/dsl/conflicts_with.rb index d5de4edeb9..f49f3a0620 100644 --- a/Library/Homebrew/cask/dsl/conflicts_with.rb +++ b/Library/Homebrew/cask/dsl/conflicts_with.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require "delegate" + require "extend/hash_validator" using HashValidator @@ -8,7 +10,7 @@ module Cask # Class corresponding to the `conflicts_with` stanza. # # @api private - class ConflictsWith < DelegateClass(Hash) + class ConflictsWith < SimpleDelegator VALID_KEYS = [ :formula, :cask, @@ -18,12 +20,13 @@ module Cask :java, ].freeze - def initialize(**pairs) - pairs.assert_valid_keys!(*VALID_KEYS) + def initialize(**options) + options.assert_valid_keys!(*VALID_KEYS) - super(pairs.transform_values { |v| Set.new(Array(v)) }) + conflicts = options.transform_values { |v| Set.new(Array(v)) } + conflicts.default = Set.new - self.default = Set.new + super(conflicts) end def to_json(generator) diff --git a/Library/Homebrew/cask/dsl/depends_on.rb b/Library/Homebrew/cask/dsl/depends_on.rb index e8beba968d..a3721b0530 100644 --- a/Library/Homebrew/cask/dsl/depends_on.rb +++ b/Library/Homebrew/cask/dsl/depends_on.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require "delegate" + require "requirements/macos_requirement" module Cask @@ -7,7 +9,7 @@ module Cask # Class corresponding to the `depends_on` stanza. # # @api private - class DependsOn < DelegateClass(Hash) + class DependsOn < SimpleDelegator VALID_KEYS = Set.new([ :formula, :cask, diff --git a/Library/Homebrew/cask/url.rb b/Library/Homebrew/cask/url.rb index 2e59372f9d..723647ecfd 100644 --- a/Library/Homebrew/cask/url.rb +++ b/Library/Homebrew/cask/url.rb @@ -1,32 +1,63 @@ +# typed: strict # frozen_string_literal: true # Class corresponding to the `url` stanza. # # @api private class URL - ATTRIBUTES = [ - :using, - :tag, :branch, :revisions, :revision, - :trust_cert, :cookies, :referer, :user_agent, - :data - ].freeze - private_constant :ATTRIBUTES + extend T::Sig - attr_reader :uri, :specs, *ATTRIBUTES + attr_reader :uri, :specs, + :using, + :tag, :branch, :revisions, :revision, + :trust_cert, :cookies, :referer, :user_agent, + :data extend Forwardable def_delegators :uri, :path, :scheme, :to_s - def initialize(uri, **options) - @uri = URI(uri) - @user_agent = :default + sig do + params( + uri: T.any(URI::Generic, String), + using: T.nilable(Symbol), + tag: T.nilable(String), + branch: T.nilable(String), + revisions: T.nilable(T::Array[String]), + revision: T.nilable(String), + trust_cert: T.nilable(T::Boolean), + cookies: T.nilable(T::Hash[String, String]), + referer: T.nilable(T.any(URI::Generic, String)), + user_agent: T.nilable(T.any(Symbol, String)), + data: T.nilable(T::Hash[String, String]), + ).returns(T.untyped) + end + def initialize( + uri, + using: nil, + tag: nil, + branch: nil, + revisions: nil, + revision: nil, + trust_cert: nil, + cookies: nil, + referer: nil, + user_agent: nil, + data: nil + ) + @uri = URI(uri) - ATTRIBUTES.each do |attribute| - next unless options.key?(attribute) + specs = {} + specs[:using] = @using = using + specs[:tag] = @tag = tag + specs[:branch] = @branch = branch + specs[:revisions] = @revisions = revisions + specs[:revision] = @revision = revision + specs[:trust_cert] = @trust_cert = trust_cert + specs[:cookies] = @cookies = cookies + specs[:referer] = @referer = referer + specs[:user_agent] = @user_agent = user_agent || :default + specs[:data] = @data = data - instance_variable_set("@#{attribute}", options[attribute]) - end - - @specs = options + @specs = specs.compact end end diff --git a/Library/Homebrew/commands.rb b/Library/Homebrew/commands.rb index e453a5086e..4654d4e580 100644 --- a/Library/Homebrew/commands.rb +++ b/Library/Homebrew/commands.rb @@ -27,6 +27,7 @@ module Commands "environment" => "--env", "--config" => "config", "-v" => "--version", + "tc" => "typecheck", }.freeze def valid_internal_cmd?(cmd) diff --git a/Library/Homebrew/debrew/irb.rb b/Library/Homebrew/debrew/irb.rb index e37d35b92c..b88aa44c73 100644 --- a/Library/Homebrew/debrew/irb.rb +++ b/Library/Homebrew/debrew/irb.rb @@ -4,34 +4,30 @@ require "irb" # @private module IRB - @setup_done = false + def self.parse_opts(argv: nil); end - extend Module.new { - def parse_opts; end - - def start_within(binding) - unless @setup_done - setup(nil, argv: []) - @setup_done = true - end - - workspace = WorkSpace.new(binding) - irb = Irb.new(workspace) - - @CONF[:IRB_RC]&.call(irb.context) - @CONF[:MAIN_CONTEXT] = irb.context - - trap("SIGINT") do - irb.signal_handle - end - - begin - catch(:IRB_EXIT) do - irb.eval_input - end - ensure - irb_at_exit - end + def self.start_within(binding) + unless @setup_done + setup(nil, argv: []) + @setup_done = true end - } + + workspace = WorkSpace.new(binding) + irb = Irb.new(workspace) + + @CONF[:IRB_RC]&.call(irb.context) + @CONF[:MAIN_CONTEXT] = irb.context + + trap("SIGINT") do + irb.signal_handle + end + + begin + catch(:IRB_EXIT) do + irb.eval_input + end + ensure + irb_at_exit + end + end end diff --git a/Library/Homebrew/dependencies.rb b/Library/Homebrew/dependencies.rb index 6a39871b13..07d79190d5 100644 --- a/Library/Homebrew/dependencies.rb +++ b/Library/Homebrew/dependencies.rb @@ -6,7 +6,7 @@ require "cask_dependent" # A collection of dependencies. # # @api private -class Dependencies < DelegateClass(Array) +class Dependencies < SimpleDelegator def initialize(*args) super(args) end @@ -41,7 +41,7 @@ end # A collection of requirements. # # @api private -class Requirements < DelegateClass(Set) +class Requirements < SimpleDelegator def initialize(*args) super(Set.new(args)) end diff --git a/Library/Homebrew/dev-cmd/tests.rb b/Library/Homebrew/dev-cmd/tests.rb index db91f4fc8b..7512cabb88 100644 --- a/Library/Homebrew/dev-cmd/tests.rb +++ b/Library/Homebrew/dev-cmd/tests.rb @@ -58,6 +58,7 @@ module Homebrew ENV["HOMEBREW_NO_COMPAT"] = "1" if args.no_compat? ENV["HOMEBREW_TEST_GENERIC_OS"] = "1" if args.generic? ENV["HOMEBREW_TEST_ONLINE"] = "1" if args.online? + ENV["HOMEBREW_SORBET_RUNTIME"] = "1" ENV["USER"] ||= system_command!("id", args: ["-nu"]).stdout.chomp diff --git a/Library/Homebrew/env_config.rb b/Library/Homebrew/env_config.rb index 30d56dff17..747906d200 100644 --- a/Library/Homebrew/env_config.rb +++ b/Library/Homebrew/env_config.rb @@ -251,6 +251,10 @@ module Homebrew "of macOS. This is useful in development on new macOS versions.", boolean: true, }, + HOMEBREW_SORBET_RUNTIME: { + description: "Enable runtime typechecking using Sorbet.", + boolean: true, + }, HOMEBREW_SVN: { description: "Use this as the `svn`(1) binary.", default_text: "A Homebrew-built Subversion (if installed), or the system-provided binary.", diff --git a/Library/Homebrew/extend/os/mac/unpack_strategy/zip.rb b/Library/Homebrew/extend/os/mac/unpack_strategy/zip.rb index b6bf16fa23..fa619816d9 100644 --- a/Library/Homebrew/extend/os/mac/unpack_strategy/zip.rb +++ b/Library/Homebrew/extend/os/mac/unpack_strategy/zip.rb @@ -2,7 +2,7 @@ module UnpackStrategy class Zip - prepend Module.new { + module MacOSZipExtension def extract_to_dir(unpack_dir, basename:, verbose:) if merge_xattrs && contains_extended_attributes?(path) # We use ditto directly, because dot_clean has issues if the __MACOSX @@ -46,6 +46,9 @@ module UnpackStrategy end end end - } + end + private_constant :MacOSZipExtension + + prepend MacOSZipExtension end end diff --git a/Library/Homebrew/global.rb b/Library/Homebrew/global.rb index 227e581f8e..ac124aa09c 100644 --- a/Library/Homebrew/global.rb +++ b/Library/Homebrew/global.rb @@ -31,6 +31,8 @@ ActiveSupport::Inflector.inflections(:en) do |inflect| inflect.irregular "it", "they" end +require "utils/sorbet" + HOMEBREW_BOTTLE_DEFAULT_DOMAIN = ENV["HOMEBREW_BOTTLE_DEFAULT_DOMAIN"] HOMEBREW_BREW_DEFAULT_GIT_REMOTE = ENV["HOMEBREW_BREW_DEFAULT_GIT_REMOTE"] HOMEBREW_CORE_DEFAULT_GIT_REMOTE = ENV["HOMEBREW_CORE_DEFAULT_GIT_REMOTE"] diff --git a/Library/Homebrew/rubocops.rb b/Library/Homebrew/rubocops.rb index 76b8d207b8..b2b748bb03 100644 --- a/Library/Homebrew/rubocops.rb +++ b/Library/Homebrew/rubocops.rb @@ -4,6 +4,8 @@ require_relative "load_path" require "rubocop-performance" require "rubocop-rspec" +require "rubocop-sorbet" + require "rubocops/formula_desc" require "rubocops/components_order" require "rubocops/components_redundancy" diff --git a/Library/Homebrew/sorbet/config b/Library/Homebrew/sorbet/config index d633b4b3ad..02fbf5ab02 100644 --- a/Library/Homebrew/sorbet/config +++ b/Library/Homebrew/sorbet/config @@ -3,3 +3,6 @@ --ignore /vendor + +--ignore +/test/.gem diff --git a/Library/Homebrew/sorbet/rbi/utils/user.rbi b/Library/Homebrew/sorbet/rbi/utils/user.rbi index 49e9f1b3f4..ef1997fafd 100644 --- a/Library/Homebrew/sorbet/rbi/utils/user.rbi +++ b/Library/Homebrew/sorbet/rbi/utils/user.rbi @@ -1,9 +1,11 @@ # typed: strict -class User < String - def gui? - end +class User < SimpleDelegator + include Kernel - def self.current - end + sig { returns(T::Boolean) } + def gui?; end + + sig { returns(T.nilable(T.attached_class)) } + def self.current; end end diff --git a/Library/Homebrew/sorbet/tapioca/require.rb b/Library/Homebrew/sorbet/tapioca/require.rb index ac84d1d3f0..fe1388b13a 100644 --- a/Library/Homebrew/sorbet/tapioca/require.rb +++ b/Library/Homebrew/sorbet/tapioca/require.rb @@ -1,4 +1,4 @@ -# frozen_string_literal: true # typed: false +# frozen_string_literal: true # Add your extra requires here diff --git a/Library/Homebrew/utils/sorbet.rb b/Library/Homebrew/utils/sorbet.rb new file mode 100644 index 0000000000..c4caa0a9a2 --- /dev/null +++ b/Library/Homebrew/utils/sorbet.rb @@ -0,0 +1,10 @@ +# typed: strict +# frozen_string_literal: true + +if ENV["HOMEBREW_SORBET_RUNTIME"] + require "utils/gems" + Homebrew.install_bundler_gems! + require "sorbet-runtime" +else + require "sorbet-runtime-stub" +end diff --git a/Library/Homebrew/utils/user.rb b/Library/Homebrew/utils/user.rb index 0d5b94fb34..d40a7b6072 100644 --- a/Library/Homebrew/utils/user.rb +++ b/Library/Homebrew/utils/user.rb @@ -8,7 +8,7 @@ require "system_command" # A system user. # # @api private -class User < DelegateClass(String) +class User < SimpleDelegator # Return whether the user has an active GUI session. def gui? out, _, status = system_command "who" diff --git a/Library/Homebrew/vendor/bundle/bundler/setup.rb b/Library/Homebrew/vendor/bundle/bundler/setup.rb index ff51c2cae9..89d30e2fef 100644 --- a/Library/Homebrew/vendor/bundle/bundler/setup.rb +++ b/Library/Homebrew/vendor/bundle/bundler/setup.rb @@ -75,9 +75,11 @@ $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/unicode-display_width $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-0.92.0/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-performance-1.8.1/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-rspec-1.43.2/lib" +$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-sorbet-0.5.1/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/ruby-macho-2.2.0/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/sorbet-static-0.5.5942-universal-darwin-19/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/sorbet-0.5.5942/lib" +$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/sorbet-runtime-stub-0.2.0/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/thor-1.0.1/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/spoom-1.0.4/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/tapioca-0.4.7/lib" diff --git a/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/config/default.yml b/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/config/default.yml new file mode 100644 index 0000000000..4e1602c037 --- /dev/null +++ b/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/config/default.yml @@ -0,0 +1,128 @@ +inherit_mode: + merge: + - Exclude + +Sorbet/AllowIncompatibleOverride: + Description: 'Disallows using `.override(allow_incompatible: true)`.' + Enabled: true + VersionAdded: 0.2.0 + +Sorbet/BindingConstantWithoutTypeAlias: + Description: >- + Disallows binding the return value of `T.any`, `T.all`, `T.enum` + to a constant directly. To bind the value, one must use `T.type_alias`. + Enabled: true + VersionAdded: 0.2.0 + +Sorbet/CheckedTrueInSignature: + Description: 'Disallows the usage of `checked(true)` in signatures.' + Enabled: true + VersionAdded: 0.2.0 + +Sorbet/ConstantsFromStrings: + Description: >- + Forbids constant access through meta-programming. + + For example, things like `constantize` or `const_get` + are forbidden. + Enabled: true + VersionAdded: 0.2.0 + +Sorbet/EnforceSigilOrder: + Description: 'Ensures that Sorbet sigil comes first in a file.' + Enabled: true + VersionAdded: 0.3.4 + +Sorbet/EnforceSignatures: + Description: 'Ensures all methods have a valid signature.' + Enabled: false + VersionAdded: 0.3.4 + +Sorbet/FalseSigil: + Description: 'All files must be at least at strictness `false`.' + Enabled: true + VersionAdded: 0.3.3 + SuggestedStrictness: true + Include: + - "**/*.rb" + - "**/*.rbi" + - "**/*.rake" + - "**/*.ru" + Exclude: + - bin/**/* + - db/**/*.rb + - script/**/* + +Sorbet/ForbidIncludeConstLiteral: + Description: 'Forbids include of non-literal constants.' + Enabled: false + VersionAdded: 0.2.0 + VersionChanged: 0.5.0 + +Sorbet/ForbidSuperclassConstLiteral: + Description: 'Forbid superclasses which are non-literal constants.' + Enabled: false + VersionAdded: 0.2.0 + VersionChanged: 0.5.0 + +Sorbet/ForbidUntypedStructProps: + Description: >- + Disallows use of `T.untyped` or `T.nilable(T.untyped)` as a + prop type for `T::Struct` subclasses. + Enabled: true + VersionAdded: 0.4.0 + +Sorbet/HasSigil: + Description: 'Makes the Sorbet typed sigil mandatory in all files.' + Enabled: false + VersionAdded: 0.3.3 + +Sorbet/IgnoreSigil: + Description: 'All files must be at least at strictness `ignore`.' + Enabled: false + VersionAdded: 0.3.3 + +Sorbet/KeywordArgumentOrdering: + Description: >- + Enforces a compatible keyword arguments with Sorbet. + + All keyword arguments must be at the end of the parameters + list, and all keyword arguments with a default value must be + after those without default values. + Enabled: true + VersionAdded: 0.2.0 + +Sorbet/ParametersOrderingInSignature: + Description: 'Enforces same parameter order between a method and its signature.' + Enabled: true + VersionAdded: 0.2.0 + +Sorbet/SignatureBuildOrder: + Description: >- + Enforces the order of parts in a signature. + + The order is first inheritance related builders, + then params, then return and finally the modifier + such as: `abstract.params(...).returns(...).soft`.' + Enabled: true + VersionAdded: 0.3.0 + +Sorbet/StrictSigil: + Description: 'All files must be at least at strictness `strict`.' + Enabled: false + VersionAdded: 0.3.3 + +Sorbet/StrongSigil: + Description: 'All files must be at least at strictness `strong`.' + Enabled: false + VersionAdded: 0.3.3 + +Sorbet/TrueSigil: + Description: 'All files must be at least at strictness `true`.' + Enabled: false + VersionAdded: 0.3.3 + +Sorbet/ValidSigil: + Description: 'All files must have a valid sigil.' + Enabled: true + VersionAdded: 0.3.3 diff --git a/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop-sorbet.rb b/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop-sorbet.rb new file mode 100644 index 0000000000..f3fd8d7c5c --- /dev/null +++ b/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop-sorbet.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +require 'rubocop' + +require_relative 'rubocop/sorbet' +require_relative 'rubocop/sorbet/version' +require_relative 'rubocop/sorbet/inject' + +RuboCop::Sorbet::Inject.defaults! + +require_relative 'rubocop/cop/sorbet_cops' diff --git a/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/binding_constants_without_type_alias.rb b/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/binding_constants_without_type_alias.rb new file mode 100644 index 0000000000..b7885ff89e --- /dev/null +++ b/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/binding_constants_without_type_alias.rb @@ -0,0 +1,121 @@ +# frozen_string_literal: true + +require 'rubocop' + +module RuboCop + module Cop + module Sorbet + # This cop disallows binding the return value of `T.any`, `T.all`, `T.enum` + # to a constant directly. To bind the value, one must use `T.type_alias`. + # + # @example + # + # # bad + # FooOrBar = T.any(Foo, Bar) + # + # # good + # FooOrBar = T.type_alias { T.any(Foo, Bar) } + class BindingConstantWithoutTypeAlias < RuboCop::Cop::Cop + def_node_matcher(:binding_unaliased_type?, <<-PATTERN) + (casgn _ _ [#not_nil? #not_t_let? #not_dynamic_type_creation_with_block? #not_generic_parameter_decl? #method_needing_aliasing_on_t?]) + PATTERN + + def_node_matcher(:using_type_alias?, <<-PATTERN) + (block + (send + (const nil? :T) :type_alias) + _ + _ + ) + PATTERN + + def_node_matcher(:using_deprecated_type_alias_syntax?, <<-PATTERN) + ( + send + (const nil? :T) + :type_alias + _ + ) + PATTERN + + def_node_matcher(:t_let?, <<-PATTERN) + ( + send + (const nil? :T) + :let + _ + _ + ) + PATTERN + + def_node_matcher(:dynamic_type_creation_with_block?, <<-PATTERN) + (block + (send + const :new ...) + _ + _ + ) + PATTERN + + def_node_matcher(:generic_parameter_decl?, <<-PATTERN) + ( + send nil? {:type_template :type_member} ... + ) + PATTERN + + def_node_search(:method_needing_aliasing_on_t?, <<-PATTERN) + ( + send + (const nil? :T) + {:any :all :noreturn :class_of :untyped :nilable :self_type :enum :proc} + ... + ) + PATTERN + + def not_t_let?(node) + !t_let?(node) + end + + def not_dynamic_type_creation_with_block?(node) + !dynamic_type_creation_with_block?(node) + end + + def not_generic_parameter_decl?(node) + !generic_parameter_decl?(node) + end + + def not_nil?(node) + !node.nil? + end + + def on_casgn(node) + return unless binding_unaliased_type?(node) && !using_type_alias?(node.children[2]) + if using_deprecated_type_alias_syntax?(node.children[2]) + add_offense( + node.children[2], + message: "It looks like you're using the old `T.type_alias` syntax. " \ + '`T.type_alias` now expects a block.' \ + 'Run Sorbet with the options "--autocorrect --error-white-list=5043" ' \ + 'to automatically upgrade to the new syntax.' + ) + return + end + add_offense( + node.children[2], + message: "It looks like you're trying to bind a type to a constant. " \ + 'To do this, you must alias the type using `T.type_alias`.' + ) + end + + def autocorrect(node) + lambda do |corrector| + corrector.replace( + node.source_range, + "T.type_alias { #{node.source} }" + ) + end + end + end + end + end +end diff --git a/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/constants_from_strings.rb b/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/constants_from_strings.rb new file mode 100644 index 0000000000..d235527348 --- /dev/null +++ b/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/constants_from_strings.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +require 'rubocop' + +module RuboCop + module Cop + module Sorbet + # This cop disallows the calls that are used to get constants fom Strings + # such as +constantize+, +const_get+, and +constants+. + # + # The goal of this cop is to make the code easier to statically analyze, + # more IDE-friendly, and more predictable. It leads to code that clearly + # expresses which values the constant can have. + # + # @example + # + # # bad + # class_name.constantize + # + # # bad + # constants.detect { |c| c.name == "User" } + # + # # bad + # const_get(class_name) + # + # # good + # case class_name + # when "User" + # User + # else + # raise ArgumentError + # end + # + # # good + # { "User" => User }.fetch(class_name) + class ConstantsFromStrings < ::RuboCop::Cop::Cop + def_node_matcher(:constant_from_string?, <<-PATTERN) + (send _ {:constantize :constants :const_get} ...) + PATTERN + + def on_send(node) + return unless constant_from_string?(node) + add_offense( + node, + location: :selector, + message: "Don't use `#{node.method_name}`, it makes the code harder to understand, less editor-friendly, " \ + "and impossible to analyze. Replace `#{node.method_name}` with a case/when or a hash." + ) + end + end + end + end +end diff --git a/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/forbid_include_const_literal.rb b/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/forbid_include_const_literal.rb new file mode 100644 index 0000000000..30c52390cf --- /dev/null +++ b/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/forbid_include_const_literal.rb @@ -0,0 +1,58 @@ +# encoding: utf-8 +# frozen_string_literal: true + +require 'rubocop' + +# Correct `send` expressions in include statements by constant literals. +# +# Sorbet, the static checker, is not (yet) able to support constructs on the +# following form: +# +# ```ruby +# class MyClass +# include send_expr +# end +# ``` +# +# Multiple occurences of this can be found in Shopify's code base like: +# +# ```ruby +# include Rails.application.routes.url_helpers +# ``` +# or +# ```ruby +# include Polaris::Engine.helpers +# ``` +module RuboCop + module Cop + module Sorbet + class ForbidIncludeConstLiteral < RuboCop::Cop::Cop + MSG = 'Includes must only contain constant literals' + + attr_accessor :used_names + + def_node_matcher :not_lit_const_include?, <<-PATTERN + (send nil? {:include :extend :prepend} + $_ + ) + PATTERN + + def initialize(*) + super + self.used_names = Set.new + end + + def on_send(node) + return unless not_lit_const_include?(node) do |send_argument| + ![:const, :self].include?(send_argument.type) + end + parent = node.parent + return unless parent + parent = parent.parent if [:begin, :block].include?(parent.type) + return unless [:module, :class, :sclass].include?(parent.type) + add_offense(node) + end + end + end + end +end diff --git a/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/forbid_superclass_const_literal.rb b/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/forbid_superclass_const_literal.rb new file mode 100644 index 0000000000..e87e636a16 --- /dev/null +++ b/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/forbid_superclass_const_literal.rb @@ -0,0 +1,45 @@ +# encoding: utf-8 +# frozen_string_literal: true + +require 'rubocop' + +# Correct superclass `send` expressions by constant literals. +# +# Sorbet, the static checker, is not (yet) able to support constructs on the +# following form: +# +# ```ruby +# class Foo < send_expr; end +# ``` +# +# Multiple occurences of this can be found in Shopify's code base like: +# +# ```ruby +# class ShopScope < Component::TrustedIdScope[ShopIdentity::ShopId] +# ``` +# or +# ```ruby +# class ApiClientEligibility < Struct.new(:api_client, :match_results, :shop) +# ``` +module RuboCop + module Cop + module Sorbet + class ForbidSuperclassConstLiteral < RuboCop::Cop::Cop + MSG = 'Superclasses must only contain constant literals' + + def_node_matcher :not_lit_const_superclass?, <<-PATTERN + (class + (const ...) + (send ...) + ... + ) + PATTERN + + def on_class(node) + return unless not_lit_const_superclass?(node) + add_offense(node.child_nodes[1]) + end + end + end + end +end diff --git a/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/forbid_untyped_struct_props.rb b/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/forbid_untyped_struct_props.rb new file mode 100644 index 0000000000..49b8887706 --- /dev/null +++ b/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/forbid_untyped_struct_props.rb @@ -0,0 +1,58 @@ +# encoding: utf-8 +# frozen_string_literal: true + +require 'rubocop' + +module RuboCop + module Cop + module Sorbet + # This cop disallows use of `T.untyped` or `T.nilable(T.untyped)` + # as a prop type for `T::Struct`. + # + # @example + # + # # bad + # class SomeClass + # const :foo, T.untyped + # prop :bar, T.nilable(T.untyped) + # end + # + # # good + # class SomeClass + # const :foo, Integer + # prop :bar, T.nilable(String) + # end + class ForbidUntypedStructProps < RuboCop::Cop::Cop + MSG = 'Struct props cannot be T.untyped' + + def_node_matcher :t_struct, <<~PATTERN + (const (const nil? :T) :Struct) + PATTERN + + def_node_matcher :t_untyped, <<~PATTERN + (send (const nil? :T) :untyped) + PATTERN + + def_node_matcher :t_nilable_untyped, <<~PATTERN + (send (const nil? :T) :nilable {#t_untyped #t_nilable_untyped}) + PATTERN + + def_node_matcher :subclass_of_t_struct?, <<~PATTERN + (class (const ...) #t_struct ...) + PATTERN + + def_node_search :untyped_props, <<~PATTERN + (send nil? {:prop :const} _ {#t_untyped #t_nilable_untyped} ...) + PATTERN + + def on_class(node) + return unless subclass_of_t_struct?(node) + + untyped_props(node).each do |untyped_prop| + add_offense(untyped_prop.child_nodes[1]) + end + end + end + end + end +end diff --git a/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/sigils/enforce_sigil_order.rb b/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/sigils/enforce_sigil_order.rb new file mode 100644 index 0000000000..42feb2b610 --- /dev/null +++ b/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/sigils/enforce_sigil_order.rb @@ -0,0 +1,115 @@ +# frozen_string_literal: true + +require 'rubocop' + +module RuboCop + module Cop + module Sorbet + # This cop checks that the Sorbet sigil comes as the first magic comment in the file. + # + # The expected order for magic comments is: typed, (en)?coding, warn_indent then frozen_string_literal. + # + # For example, the following bad ordering: + # + # ```ruby + # # frozen_string_literal: true + # # typed: true + # class Foo; end + # ``` + # + # Will be corrected as: + # + # ```ruby + # # typed: true + # # frozen_string_literal: true + # class Foo; end + # ``` + # + # Only `typed`, `(en)?coding`, `warn_indent` and `frozen_string_literal` magic comments are considered, + # other comments or magic comments are left in the same place. + class EnforceSigilOrder < ValidSigil + include RangeHelp + + def investigate(processed_source) + return if processed_source.tokens.empty? + + tokens = extract_magic_comments(processed_source) + return if tokens.empty? + + check_magic_comments_order(tokens) + end + + def autocorrect(_node) + lambda do |corrector| + tokens = extract_magic_comments(processed_source) + + # Get the magic comments tokens in their expected order + expected = PREFERRED_ORDER.keys.map do |re| + tokens.select { |token| re.match?(token.text) } + end.flatten + + tokens.each_with_index do |token, index| + corrector.replace(token.pos, expected[index].text) + end + + # Remove blank lines between the magic comments + lines = tokens.map(&:line).to_set + (lines.min...lines.max).each do |line| + next if lines.include?(line) + next unless processed_source[line - 1].empty? + corrector.remove(source_range(processed_source.buffer, line, 0)) + end + end + end + + protected + + CODING_REGEX = /#\s+(en)?coding:(?:\s+([\w]+))?/ + INDENT_REGEX = /#\s+warn_indent:(?:\s+([\w]+))?/ + FROZEN_REGEX = /#\s+frozen_string_literal:(?:\s+([\w]+))?/ + + PREFERRED_ORDER = { + CODING_REGEX => 'encoding', + SIGIL_REGEX => 'typed', + INDENT_REGEX => 'warn_indent', + FROZEN_REGEX => 'frozen_string_literal', + }.freeze + + MAGIC_REGEX = Regexp.union(*PREFERRED_ORDER.keys) + + # extraction + + # Get all the tokens in `processed_source` that match `MAGIC_REGEX` + def extract_magic_comments(processed_source) + processed_source.tokens + .take_while { |token| token.type == :tCOMMENT } + .select { |token| MAGIC_REGEX.match?(token.text) } + end + + # checks + + def check_magic_comments_order(tokens) + # Get the current magic comments order + order = tokens.map do |token| + PREFERRED_ORDER.keys.find { |re| re.match?(token.text) } + end.compact.uniq + + # Get the expected magic comments order based on the one used in the actual source + expected = PREFERRED_ORDER.keys.select do |re| + tokens.any? { |token| re.match?(token.text) } + end.uniq + + if order != expected + tokens.each do |token| + add_offense( + token, + location: token.pos, + message: "Magic comments should be in the following order: #{PREFERRED_ORDER.values.join(', ')}." + ) + end + end + end + end + end + end +end diff --git a/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/sigils/false_sigil.rb b/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/sigils/false_sigil.rb new file mode 100644 index 0000000000..1a3c8f18cb --- /dev/null +++ b/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/sigils/false_sigil.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +require 'rubocop' +require_relative 'has_sigil' + +module RuboCop + module Cop + module Sorbet + # This cop makes the Sorbet `false` sigil mandatory in all files. + class FalseSigil < HasSigil + def minimum_strictness + 'false' + end + end + end + end +end diff --git a/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/sigils/has_sigil.rb b/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/sigils/has_sigil.rb new file mode 100644 index 0000000000..6fd2d6df34 --- /dev/null +++ b/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/sigils/has_sigil.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +require 'rubocop' +require_relative 'valid_sigil' + +module RuboCop + module Cop + module Sorbet + # This cop makes the Sorbet typed sigil mandatory in all files. + # + # Options: + # + # * `SuggestedStrictness`: Sorbet strictness level suggested in offense messages (default: 'false') + # * `MinimumStrictness`: If set, make offense if the strictness level in the file is below this one + # + # If a `MinimumStrictness` level is specified, it will be used in offense messages and autocorrect. + class HasSigil < ValidSigil + @registry = Cop.registry # So we can properly subclass this cop + + def require_sigil_on_all_files? + true + end + end + end + end +end diff --git a/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/sigils/ignore_sigil.rb b/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/sigils/ignore_sigil.rb new file mode 100644 index 0000000000..5f7cf9f75e --- /dev/null +++ b/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/sigils/ignore_sigil.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +require 'rubocop' +require_relative 'has_sigil' + +module RuboCop + module Cop + module Sorbet + # This cop makes the Sorbet `ignore` sigil mandatory in all files. + class IgnoreSigil < HasSigil + def minimum_strictness + 'ignore' + end + end + end + end +end diff --git a/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/sigils/strict_sigil.rb b/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/sigils/strict_sigil.rb new file mode 100644 index 0000000000..efe26d991b --- /dev/null +++ b/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/sigils/strict_sigil.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +require 'rubocop' +require_relative 'has_sigil' + +module RuboCop + module Cop + module Sorbet + # This cop makes the Sorbet `strict` sigil mandatory in all files. + class StrictSigil < HasSigil + def minimum_strictness + 'strict' + end + end + end + end +end diff --git a/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/sigils/strong_sigil.rb b/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/sigils/strong_sigil.rb new file mode 100644 index 0000000000..0b4cf36b68 --- /dev/null +++ b/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/sigils/strong_sigil.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +require 'rubocop' +require_relative 'has_sigil' + +module RuboCop + module Cop + module Sorbet + # This cop makes the Sorbet `strong` sigil mandatory in all files. + class StrongSigil < HasSigil + def minimum_strictness + 'strong' + end + end + end + end +end diff --git a/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/sigils/true_sigil.rb b/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/sigils/true_sigil.rb new file mode 100644 index 0000000000..60b81b037b --- /dev/null +++ b/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/sigils/true_sigil.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +require 'rubocop' +require_relative 'has_sigil' + +module RuboCop + module Cop + module Sorbet + # This cop makes the Sorbet `true` sigil mandatory in all files. + class TrueSigil < HasSigil + def minimum_strictness + 'true' + end + end + end + end +end diff --git a/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/sigils/valid_sigil.rb b/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/sigils/valid_sigil.rb new file mode 100644 index 0000000000..fbcc307ba4 --- /dev/null +++ b/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/sigils/valid_sigil.rb @@ -0,0 +1,160 @@ +# frozen_string_literal: true + +require 'rubocop' + +module RuboCop + module Cop + module Sorbet + # This cop checks that every Ruby file contains a valid Sorbet sigil. + # Adapted from: https://gist.github.com/clarkdave/85aca4e16f33fd52aceb6a0a29936e52 + # + # Options: + # + # * `RequireSigilOnAllFiles`: make offense if the Sorbet typed is not found in the file (default: false) + # * `SuggestedStrictness`: Sorbet strictness level suggested in offense messages (default: 'false') + # * `MinimumStrictness`: If set, make offense if the strictness level in the file is below this one + # + # If a `MinimumStrictness` level is specified, it will be used in offense messages and autocorrect. + class ValidSigil < RuboCop::Cop::Cop + @registry = Cop.registry # So we can properly subclass this cop + + def investigate(processed_source) + return if processed_source.tokens.empty? + + sigil = extract_sigil(processed_source) + return unless check_sigil_present(sigil) + + strictness = extract_strictness(sigil) + return unless check_strictness_not_empty(sigil, strictness) + return unless check_strictness_valid(sigil, strictness) + return unless check_strictness_level(sigil, strictness) + end + + def autocorrect(_node) + lambda do |corrector| + return unless require_sigil_on_all_files? + return unless extract_sigil(processed_source).nil? + + token = processed_source.tokens.first + replace_with = suggested_strictness_level(minimum_strictness, suggested_strictness) + sigil = "# typed: #{replace_with}" + if token.text.start_with?("#!") # shebang line + corrector.insert_after(token.pos, "\n#{sigil}") + else + corrector.insert_before(token.pos, "#{sigil}\n") + end + end + end + + protected + + STRICTNESS_LEVELS = %w(ignore false true strict strong) + SIGIL_REGEX = /#\s+typed:(?:\s+([\w]+))?/ + + # extraction + + def extract_sigil(processed_source) + processed_source.tokens + .take_while { |token| token.type == :tCOMMENT } + .find { |token| SIGIL_REGEX.match?(token.text) } + end + + def extract_strictness(sigil) + sigil.text.match(SIGIL_REGEX)&.captures&.first + end + + # checks + + def check_sigil_present(sigil) + return true unless sigil.nil? + + token = processed_source.tokens.first + if require_sigil_on_all_files? + strictness = suggested_strictness_level(minimum_strictness, suggested_strictness) + add_offense( + token, + location: token.pos, + message: 'No Sorbet sigil found in file. ' \ + "Try a `typed: #{strictness}` to start (you can also use `rubocop -a` to automatically add this)." + ) + end + false + end + + def suggested_strictness_level(minimum_strictness, suggested_strictness) + # if no minimum strictness is set (eg. using Sorbet/HasSigil without config) then + # we always use the suggested strictness which defaults to `false` + return suggested_strictness unless minimum_strictness + + # special case: if you're using Sorbet/IgnoreSigil without config, we should recommend `ignore` + return "ignore" if minimum_strictness == "ignore" && cop_config['SuggestedStrictness'].nil? + + # if a minimum strictness is set (eg. you're using Sorbet/FalseSigil) + # we want to compare the minimum strictness and suggested strictness. this is because + # the suggested strictness might be higher than the minimum (eg. if you want all new files + # at a higher strictness level, without having to migrate existing files at lower levels). + + suggested_level = STRICTNESS_LEVELS.index(suggested_strictness) + minimum_level = STRICTNESS_LEVELS.index(minimum_strictness) + + suggested_level > minimum_level ? suggested_strictness : minimum_strictness + end + + def check_strictness_not_empty(sigil, strictness) + return true if strictness + + add_offense( + sigil, + location: sigil.pos, + message: 'Sorbet sigil should not be empty.' + ) + false + end + + def check_strictness_valid(sigil, strictness) + return true if STRICTNESS_LEVELS.include?(strictness) + + add_offense( + sigil, + location: sigil.pos, + message: "Invalid Sorbet sigil `#{strictness}`." + ) + false + end + + def check_strictness_level(sigil, strictness) + return true unless minimum_strictness + + minimum_level = STRICTNESS_LEVELS.index(minimum_strictness) + current_level = STRICTNESS_LEVELS.index(strictness) + if current_level < minimum_level + add_offense( + sigil, + location: sigil.pos, + message: "Sorbet sigil should be at least `#{minimum_strictness}` got `#{strictness}`." + ) + return false + end + true + end + + # options + + # Default is `false` + def require_sigil_on_all_files? + !!cop_config['RequireSigilOnAllFiles'] + end + + # Default is `'false'` + def suggested_strictness + STRICTNESS_LEVELS.include?(cop_config['SuggestedStrictness']) ? cop_config['SuggestedStrictness'] : 'false' + end + + # Default is `nil` + def minimum_strictness + cop_config['MinimumStrictness'] if STRICTNESS_LEVELS.include?(cop_config['MinimumStrictness']) + end + end + end + end +end diff --git a/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/signatures/allow_incompatible_override.rb b/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/signatures/allow_incompatible_override.rb new file mode 100644 index 0000000000..e4fc7eb273 --- /dev/null +++ b/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/signatures/allow_incompatible_override.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: true + +require 'rubocop' + +module RuboCop + module Cop + module Sorbet + # This cop disallows using `.override(allow_incompatible: true)`. + # Using `allow_incompatible` suggests a violation of the Liskov + # Substitution Principle, meaning that a subclass is not a valid + # subtype of it's superclass. This Cop prevents these design smells + # from occurring. + # + # @example + # + # # bad + # sig.override(allow_incompatible: true) + # + # # good + # sig.override + class AllowIncompatibleOverride < RuboCop::Cop::Cop + def_node_search(:sig?, <<-PATTERN) + ( + send + nil? + :sig + ... + ) + PATTERN + + def not_nil?(node) + !node.nil? + end + + def_node_search(:allow_incompatible?, <<-PATTERN) + (pair (sym :allow_incompatible) (true)) + PATTERN + + def_node_matcher(:allow_incompatible_override?, <<-PATTERN) + ( + send + [#not_nil? #sig?] + :override + [#not_nil? #allow_incompatible?] + ) + PATTERN + + def on_send(node) + return unless allow_incompatible_override?(node) + add_offense( + node.children[2], + message: 'Usage of `allow_incompatible` suggests a violation of the Liskov Substitution Principle. '\ + 'Instead, strive to write interfaces which respect subtyping principles and remove `allow_incompatible`', + ) + end + end + end + end +end diff --git a/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/signatures/checked_true_in_signature.rb b/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/signatures/checked_true_in_signature.rb new file mode 100644 index 0000000000..d4e53a67b6 --- /dev/null +++ b/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/signatures/checked_true_in_signature.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +require 'rubocop' +require_relative 'signature_cop' + +module RuboCop + module Cop + module Sorbet + # This cop disallows the usage of `checked(true)`. This usage could cause + # confusion; it could lead some people to believe that a method would be checked + # even if runtime checks have not been enabled on the class or globally. + # Additionally, in the event where checks are enabled, `checked(true)` would + # be redundant; only `checked(false)` or `soft` would change the behaviour. + # + # @example + # + # # bad + # sig { void.checked(true) } + # + # # good + # sig { void } + class CheckedTrueInSignature < SignatureCop + include(RuboCop::Cop::RangeHelp) + + def_node_search(:offending_node, <<~PATTERN) + (send _ :checked (true)) + PATTERN + + MESSAGE = + 'Using `checked(true)` in a method signature definition is not allowed. ' \ + '`checked(true)` is the default behavior for modules/classes with runtime checks enabled. ' \ + 'To enable typechecking at runtime for this module, regardless of global settings, ' \ + '`include(WaffleCone::RuntimeChecks)` to this module and set other methods to `checked(false)`.' + private_constant(:MESSAGE) + + def on_signature(node) + error = offending_node(node).first + return unless error + + add_offense( + error, + location: source_range( + processed_source.buffer, + error.location.line, + (error.location.selector.begin_pos)..(error.location.end.begin_pos), + ), + message: MESSAGE + ) + end + end + end + end +end diff --git a/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/signatures/enforce_signatures.rb b/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/signatures/enforce_signatures.rb new file mode 100644 index 0000000000..92e1f73642 --- /dev/null +++ b/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/signatures/enforce_signatures.rb @@ -0,0 +1,135 @@ +# frozen_string_literal: true + +require 'rubocop' +require 'stringio' +require_relative 'signature_cop' + +module RuboCop + module Cop + module Sorbet + # This cop checks that every method definition and attribute accessor has a Sorbet signature. + # + # It also suggest an autocorrect with placeholders so the following code: + # + # ``` + # def foo(a, b, c); end + # ``` + # + # Will be corrected as: + # + # ``` + # sig { params(a: T.untyped, b: T.untyped, c: T.untyped).returns(T.untyped) + # def foo(a, b, c); end + # ``` + # + # You can configure the placeholders used by changing the following options: + # + # * `ParameterTypePlaceholder`: placeholders used for parameter types (default: 'T.untyped') + # * `ReturnTypePlaceholder`: placeholders used for return types (default: 'T.untyped') + class EnforceSignatures < SignatureCop + def_node_matcher(:accessor?, <<-PATTERN) + (send nil? {:attr_reader :attr_writer :attr_accessor} ...) + PATTERN + + def on_def(node) + check_node(node) + end + + def on_defs(node) + check_node(node) + end + + def on_send(node) + return unless accessor?(node) + check_node(node) + end + + def autocorrect(node) + lambda do |corrector| + suggest = SigSuggestion.new(node.loc.column, param_type_placeholder, return_type_placeholder) + + if node.is_a?(RuboCop::AST::DefNode) # def something + node.arguments.each do |arg| + suggest.params << arg.children.first + end + elsif accessor?(node) # attr reader, writer, accessor + method = node.children[1] + symbol = node.children[2] + suggest.params << symbol.value if symbol && (method == :attr_writer || method == :attr_accessor) + suggest.returns = 'void' if method == :attr_writer + end + + corrector.insert_before(node.loc.expression, suggest.to_autocorrect) + end + end + + private + + def check_node(node) + prev = previous_node(node) + unless signature?(prev) + add_offense( + node, + message: "Each method is required to have a signature." + ) + end + end + + def previous_node(node) + parent = node.parent + return nil unless parent + parent.children[node.sibling_index - 1] + end + + def param_type_placeholder + cop_config['ParameterTypePlaceholder'] || 'T.untyped' + end + + def return_type_placeholder + cop_config['ReturnTypePlaceholder'] || 'T.untyped' + end + + class SigSuggestion + attr_accessor :params, :returns + + def initialize(indent, param_placeholder, return_placeholder) + @params = [] + @returns = nil + @indent = indent + @param_placeholder = param_placeholder + @return_placeholder = return_placeholder + end + + def to_autocorrect + out = StringIO.new + out << 'sig { ' + out << generate_params + out << generate_return + out << " }\n" + out << ' ' * @indent # preserve indent for the next line + out.string + end + + private + + def generate_params + return if @params.empty? + out = StringIO.new + out << 'params(' + out << @params.map do |param| + "#{param}: #{@param_placeholder}" + end.join(", ") + out << ').' + out.string + end + + def generate_return + return "returns(#{@return_placeholder})" if @returns.nil? + return @returns if @returns == 'void' + "returns(#{@returns})" + end + end + end + end + end +end diff --git a/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/signatures/keyword_argument_ordering.rb b/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/signatures/keyword_argument_ordering.rb new file mode 100644 index 0000000000..800115304e --- /dev/null +++ b/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/signatures/keyword_argument_ordering.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +require 'rubocop' +require_relative 'signature_cop' + +module RuboCop + module Cop + module Sorbet + # This cop checks for the ordering of keyword arguments required by + # sorbet-runtime. The ordering requires that all keyword arguments + # are at the end of the parameters list, and all keyword arguments + # with a default value must be after those without default values. + # + # @example + # + # # bad + # sig { params(a: Integer, b: String).void } + # def foo(a: 1, b:); end + # + # # good + # sig { params(b: String, a: Integer).void } + # def foo(b:, a: 1); end + class KeywordArgumentOrdering < SignatureCop + def on_signature(node) + method_node = node.parent.children[node.sibling_index + 1] + return if method_node.nil? + method_parameters = method_node.arguments + + check_order_for_kwoptargs(method_parameters) + end + + private + + def check_order_for_kwoptargs(parameters) + out_of_kwoptarg = false + + parameters.reverse.each do |param| + out_of_kwoptarg = true unless param.type == :kwoptarg || param.type == :blockarg || param.type == :kwrestarg + + next unless param.type == :kwoptarg && out_of_kwoptarg + + add_offense( + param, + message: 'Optional keyword arguments must be at the end of the parameter list.' + ) + end + end + end + end + end +end diff --git a/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/signatures/parameters_ordering_in_signature.rb b/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/signatures/parameters_ordering_in_signature.rb new file mode 100644 index 0000000000..9c442fb005 --- /dev/null +++ b/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/signatures/parameters_ordering_in_signature.rb @@ -0,0 +1,70 @@ +# frozen_string_literal: true + +require 'rubocop' +require_relative 'signature_cop' + +module RuboCop + module Cop + module Sorbet + # This cop checks for inconsistent ordering of parameters between the + # signature and the method definition. The sorbet-runtime gem raises + # when such inconsistency occurs. + # + # @example + # + # # bad + # sig { params(a: Integer, b: String).void } + # def foo(b:, a:); end + # + # # good + # sig { params(a: Integer, b: String).void } + # def foo(a:, b:); end + class ParametersOrderingInSignature < SignatureCop + def_node_search(:signature_params, <<-PATTERN) + (send _ :params ...) + PATTERN + + def on_signature(node) + sig_params = signature_params(node).first + + sig_params_order = extract_parameters(sig_params) + return if sig_params_order.nil? + method_node = node.parent.children[node.sibling_index + 1] + return if method_node.nil? || method_node.type != :def + method_parameters = method_node.arguments + + check_for_inconsistent_param_ordering(sig_params_order, method_parameters) + end + + private + + def extract_parameters(sig_params) + return [] if sig_params.nil? + + arguments = sig_params.arguments.first + return arguments.keys.map(&:value) if RuboCop::AST::HashNode === arguments + + add_offense( + sig_params, + message: "Invalid signature." + ) + end + + def check_for_inconsistent_param_ordering(sig_params_order, parameters) + parameters.each_with_index do |param, index| + param_name = param.children[0] + sig_param_name = sig_params_order[index] + + next if param_name == sig_param_name + + add_offense( + param, + message: "Inconsistent ordering of arguments at index #{index}. " \ + "Expected `#{sig_param_name}` from sig above." + ) + end + end + end + end + end +end diff --git a/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/signatures/signature_build_order.rb b/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/signatures/signature_build_order.rb new file mode 100644 index 0000000000..245cf8fbf3 --- /dev/null +++ b/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/signatures/signature_build_order.rb @@ -0,0 +1,104 @@ +# frozen_string_literal: true + +require 'rubocop' +require_relative 'signature_cop' + +begin + require 'unparser' +rescue LoadError + nil +end + +module RuboCop + module Cop + module Sorbet + class SignatureBuildOrder < SignatureCop + ORDER = + [ + :abstract, + :override, + :overridable, + :type_parameters, + :params, + :returns, + :void, + :soft, + :checked, + :on_failure, + ].each_with_index.to_h.freeze + + def_node_search(:root_call, <<~PATTERN) + (send nil? {#{ORDER.keys.map(&:inspect).join(' ')}} ...) + PATTERN + + def on_signature(node) + calls = call_chain(node.children[2]).map(&:method_name) + return unless calls.any? + + expected_order = calls.sort_by { |call| ORDER[call] } + return if expected_order == calls + + message = "Sig builders must be invoked in the following order: #{expected_order.join(', ')}." + + unless can_autocorrect? + message += ' For autocorrection, add the `unparser` gem to your project.' + end + + add_offense( + node.children[2], + message: message, + ) + node + end + + def autocorrect(node) + return nil unless can_autocorrect? + + lambda do |corrector| + tree = call_chain(node_with_index_sends(node)) + .sort_by { |call| ORDER[call.method_name] } + .reduce(nil) do |receiver, caller| + caller.updated(nil, [receiver] + caller.children.drop(1)) + end + + corrector.replace( + node.source_range, + Unparser.unparse(tree), + ) + end + end + + private + + def node_with_index_sends(node) + # This is really dirty hack to reparse the current node with index send + # emitting enabled, which is necessary to unparse them back as index accessors. + emit_index_value = RuboCop::AST::Builder.emit_index + RuboCop::AST::Builder.emit_index = true + RuboCop::AST::ProcessedSource.new(node.source, target_ruby_version, processed_source.path).ast + ensure + RuboCop::AST::Builder.emit_index = emit_index_value + end + + def can_autocorrect? + defined?(::Unparser) + end + + def call_chain(sig_child_node) + call_node = root_call(sig_child_node).first + return [] unless call_node + + calls = [] + while call_node != sig_child_node + calls << call_node + call_node = call_node.parent + end + + calls << sig_child_node + + calls + end + end + end + end +end diff --git a/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/signatures/signature_cop.rb b/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/signatures/signature_cop.rb new file mode 100644 index 0000000000..d583799d81 --- /dev/null +++ b/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet/signatures/signature_cop.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +require 'rubocop' + +module RuboCop + module Cop + module Sorbet + # Abstract cop specific to Sorbet signatures + # + # You can subclass it to use the `on_signature` trigger and the `signature?` node matcher. + class SignatureCop < RuboCop::Cop::Cop + @registry = Cop.registry # So we can properly subclass this cop + + def_node_matcher(:signature?, <<~PATTERN) + (block (send nil? :sig) (args) ...) + PATTERN + + def on_block(node) + on_signature(node) if signature?(node) + end + + def on_signature(_) + # To be defined in subclasses + end + end + end + end +end diff --git a/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet_cops.rb b/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet_cops.rb new file mode 100644 index 0000000000..b61037489f --- /dev/null +++ b/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/cop/sorbet_cops.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true +require_relative 'sorbet/binding_constants_without_type_alias' +require_relative 'sorbet/constants_from_strings' +require_relative 'sorbet/forbid_superclass_const_literal' +require_relative 'sorbet/forbid_include_const_literal' +require_relative 'sorbet/forbid_untyped_struct_props' + +require_relative 'sorbet/signatures/allow_incompatible_override' +require_relative 'sorbet/signatures/checked_true_in_signature' +require_relative 'sorbet/signatures/keyword_argument_ordering' +require_relative 'sorbet/signatures/parameters_ordering_in_signature' +require_relative 'sorbet/signatures/signature_build_order' +require_relative 'sorbet/signatures/enforce_signatures' + +require_relative 'sorbet/sigils/valid_sigil' +require_relative 'sorbet/sigils/has_sigil' +require_relative 'sorbet/sigils/ignore_sigil' +require_relative 'sorbet/sigils/false_sigil' +require_relative 'sorbet/sigils/true_sigil' +require_relative 'sorbet/sigils/strict_sigil' +require_relative 'sorbet/sigils/strong_sigil' +require_relative 'sorbet/sigils/enforce_sigil_order' diff --git a/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/sorbet.rb b/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/sorbet.rb new file mode 100644 index 0000000000..ddfa9e7a51 --- /dev/null +++ b/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/sorbet.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true +require "rubocop/sorbet/version" +require "yaml" + +module RuboCop + module Sorbet + class Error < StandardError; end + + PROJECT_ROOT = Pathname.new(__dir__).parent.parent.expand_path.freeze + CONFIG_DEFAULT = PROJECT_ROOT.join('config', 'default.yml').freeze + CONFIG = YAML.safe_load(CONFIG_DEFAULT.read).freeze + + private_constant(:CONFIG_DEFAULT, :PROJECT_ROOT) + end +end diff --git a/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/sorbet/inject.rb b/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/sorbet/inject.rb new file mode 100644 index 0000000000..f0440faac0 --- /dev/null +++ b/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/sorbet/inject.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +# The original code is from https://github.com/rubocop-hq/rubocop-rspec/blob/master/lib/rubocop/rspec/inject.rb +# See https://github.com/rubocop-hq/rubocop-rspec/blob/master/MIT-LICENSE.md +module RuboCop + module Sorbet + # Because RuboCop doesn't yet support plugins, we have to monkey patch in a + # bit of our configuration. + module Inject + def self.defaults! + path = CONFIG_DEFAULT.to_s + hash = ConfigLoader.send(:load_yaml_configuration, path) + config = Config.new(hash, path).tap(&:make_excludes_absolute) + puts "configuration from #{path}" if ConfigLoader.debug? + config = ConfigLoader.merge_with_default(config, path) + ConfigLoader.instance_variable_set(:@default_configuration, config) + end + end + end +end diff --git a/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/sorbet/version.rb b/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/sorbet/version.rb new file mode 100644 index 0000000000..ea49b569f3 --- /dev/null +++ b/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-sorbet-0.5.1/lib/rubocop/sorbet/version.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true +module RuboCop + module Sorbet + VERSION = "0.5.1" + end +end diff --git a/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/sorbet-runtime-stub-0.2.0/lib/sorbet-runtime-stub.rb b/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/sorbet-runtime-stub-0.2.0/lib/sorbet-runtime-stub.rb new file mode 100644 index 0000000000..3d6e9b7a28 --- /dev/null +++ b/Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/sorbet-runtime-stub-0.2.0/lib/sorbet-runtime-stub.rb @@ -0,0 +1,146 @@ +# typed: ignore + +begin + gem "sorbet-runtime" + return +rescue Gem::LoadError +end + +module T + class << self + def absurd(value); end + def all(type_a, type_b, *types); end + def any(type_a, type_b, *types); end + def attached_class; end + def class_of(klass); end + def enum(values); end + def nilable(type); end + def noreturn; end + def self_type; end + def type_alias(type=nil, &_blk); end + def type_parameter(name); end + def untyped; end + + def assert_type!(value, _type, _checked: true) + value + end + + def cast(value, _type, _checked: true) + value + end + + def let(value, _type, _checked: true) + value + end + + def must(arg, _msg = nil) + arg + end + + def proc + T::Proc.new + end + + def reveal_type(value) + value + end + + def unsafe(value) + value + end + end + + module Sig + def sig(arg0=nil, &blk); end + end + + module Helpers + def abstract!; end + def interface!; end + def final!; end + def sealed!; end + def mixes_in_class_methods(mod); end + end + + module Generic + include T::Helpers + + def type_parameters(*params); end + def type_member(variance=:invariant, fixed: nil, lower: nil, upper: BasicObject); end + def type_template(variance=:invariant, fixed: nil, lower: nil, upper: BasicObject); end + + def [](*types) + self + end + end + + module Array + def self.[](type); end + end + + Boolean = Object.new.freeze + + module Configuration + def self.call_validation_error_handler(signature, opts); end + def self.call_validation_error_handler=(value); end + def self.default_checked_level=(default_checked_level); end + def self.enable_checking_for_sigs_marked_checked_tests; end + def self.enable_final_checks_on_hooks; end + def self.enable_legacy_t_enum_migration_mode; end + def self.reset_final_checks_on_hooks; end + def self.hard_assert_handler(str, extra); end + def self.hard_assert_handler=(value); end + def self.inline_type_error_handler(error); end + def self.inline_type_error_handler=(value); end + def self.log_info_handler(str, extra); end + def self.log_info_handler=(value); end + def self.scalar_types; end + def self.scalar_types=(values); end + def self.sealed_violation_whitelist; end + def self.sealed_violation_whitelist=(sealed_violation_whitelist); end + def self.sig_builder_error_handler(error, location); end + def self.sig_builder_error_handler=(value); end + def self.sig_validation_error_handler(error, opts); end + def self.sig_validation_error_handler=(value); end + def self.soft_assert_handler(str, extra); end + def self.soft_assert_handler=(value); end + end + + module Enumerable + def self.[](type); end + end + + module Enumerator + def self.[](type); end + end + + module Hash + def self.[](keys, values); end + end + + class Proc + def bind(*_) + self + end + + def params(*_param) + self + end + + def void + self + end + + def returns(_type) + self + end + end + + module Range + def self.[](type); end + end + + module Set + def self.[](type); end + end +end diff --git a/completions/internal_commands_list.txt b/completions/internal_commands_list.txt index cf63a940c6..4e7367131a 100644 --- a/completions/internal_commands_list.txt +++ b/completions/internal_commands_list.txt @@ -81,6 +81,7 @@ switch tap tap-info tap-new +tc test tests typecheck diff --git a/docs/Manpage.md b/docs/Manpage.md index 7cbce54a99..ad972b3e1e 100644 --- a/docs/Manpage.md +++ b/docs/Manpage.md @@ -1770,6 +1770,9 @@ For example, you might add something like the following to your ~/.profile, ~/.b * `HOMEBREW_SKIP_OR_LATER_BOTTLES`: If set with `HOMEBREW_DEVELOPER`, do not use bottles from older versions of macOS. This is useful in development on new macOS versions. + * `HOMEBREW_SORBET_RUNTIME`: + Enable runtime typechecking using Sorbet. + * `HOMEBREW_SVN`: Use this as the `svn`(1) binary. diff --git a/manpages/brew.1 b/manpages/brew.1 index 851358882f..f8d8da4472 100644 --- a/manpages/brew.1 +++ b/manpages/brew.1 @@ -2446,6 +2446,10 @@ If set, use Pry for the \fBbrew irb\fR command\. If set with \fBHOMEBREW_DEVELOPER\fR, do not use bottles from older versions of macOS\. This is useful in development on new macOS versions\. . .TP +\fBHOMEBREW_SORBET_RUNTIME\fR +Enable runtime typechecking using Sorbet\. +. +.TP \fBHOMEBREW_SVN\fR Use this as the \fBsvn\fR(1) binary\. .