From c354377f3eb2d528e38af15492496a10b7bf72e7 Mon Sep 17 00:00:00 2001 From: Douglas Eichelberger Date: Fri, 20 Sep 2024 10:44:33 -0700 Subject: [PATCH 1/2] Exclude sorbet assignments from Style/MutableConstant cop --- Library/Homebrew/rubocops/all.rb | 1 + .../mutable_constant_exclude_unfreezable.rb | 45 ++ ...table_constant_exclude_unfreezable_spec.rb | 630 ++++++++++++++++++ 3 files changed, 676 insertions(+) create mode 100644 Library/Homebrew/rubocops/extend/mutable_constant_exclude_unfreezable.rb create mode 100644 Library/Homebrew/test/rubocops/extend/mutable_constant_exclude_unfreezable_spec.rb diff --git a/Library/Homebrew/rubocops/all.rb b/Library/Homebrew/rubocops/all.rb index bfcf79a8b9630..deb66f66a5ff4 100644 --- a/Library/Homebrew/rubocops/all.rb +++ b/Library/Homebrew/rubocops/all.rb @@ -5,6 +5,7 @@ require_relative "../extend/blank" require_relative "blank" require_relative "compact_blank" +require_relative "extend/mutable_constant_exclude_unfreezable" require_relative "io_read" require_relative "move_to_extend_os" require_relative "negate_include" diff --git a/Library/Homebrew/rubocops/extend/mutable_constant_exclude_unfreezable.rb b/Library/Homebrew/rubocops/extend/mutable_constant_exclude_unfreezable.rb new file mode 100644 index 0000000000000..5c261a4c36417 --- /dev/null +++ b/Library/Homebrew/rubocops/extend/mutable_constant_exclude_unfreezable.rb @@ -0,0 +1,45 @@ +# typed: strict +# frozen_string_literal: true + +require "rubocop/cop/style/mutable_constant" + +module RuboCop + module Cop + module Sorbet + # TODO: delete this file when https://github.com/Shopify/rubocop-sorbet/pull/256 is available + module MutableConstantExcludeUnfreezable + class << self + sig { params(base: RuboCop::AST::NodePattern::Macros).void } + def prepended(base) + base.def_node_matcher(:t_let, <<~PATTERN) + (send (const nil? :T) :let $_constant _type) + PATTERN + + base.def_node_matcher(:t_type_alias?, <<~PATTERN) + (block (send (const {nil? cbase} :T) :type_alias ...) ...) + PATTERN + + base.def_node_matcher(:type_member?, <<~PATTERN) + (block (send nil? :type_member ...) ...) + PATTERN + end + end + + sig { params(value: RuboCop::AST::Node).void } + def on_assignment(value) + T.unsafe(self).t_let(value) do |constant| + value = T.let(constant, RuboCop::AST::Node) + end + return if T.unsafe(self).t_type_alias?(value) + return if T.unsafe(self).type_member?(value) + + super + end + end + end + end +end + +RuboCop::Cop::Style::MutableConstant.prepend( + RuboCop::Cop::Sorbet::MutableConstantExcludeUnfreezable, +) diff --git a/Library/Homebrew/test/rubocops/extend/mutable_constant_exclude_unfreezable_spec.rb b/Library/Homebrew/test/rubocops/extend/mutable_constant_exclude_unfreezable_spec.rb new file mode 100644 index 0000000000000..b607212b83804 --- /dev/null +++ b/Library/Homebrew/test/rubocops/extend/mutable_constant_exclude_unfreezable_spec.rb @@ -0,0 +1,630 @@ +# frozen_string_literal: true + +require "rubocops/extend/mutable_constant_exclude_unfreezable" + +RSpec.describe(RuboCop::Cop::Style::MutableConstant, :config) do + let(:prefix) { nil } + + shared_examples "mutable objects" do |o| + context "when using T.let" do + context "when assigning with =" do + it "registers an offense for #{o} assigned to a constant " \ + "and corrects by adding .freeze" do + expect_offense([prefix, <<~RUBY].compact.join("\n"), o:) + CONST = T.let(%s, Object) + ^{o} Freeze mutable objects assigned to constants. + RUBY + expect_correction([prefix, <<~RUBY].compact.join("\n")) + CONST = T.let(#{o}.freeze, Object) + RUBY + end + end + + context "when assigning with ||=" do + it "registers an offense for #{o} assigned to a constant " \ + "and corrects by adding .freeze" do + expect_offense([prefix, <<~RUBY].compact.join("\n"), o:) + CONST ||= T.let(%s, Object) + ^{o} Freeze mutable objects assigned to constants. + RUBY + expect_correction([prefix, <<~RUBY].compact.join("\n")) + CONST ||= T.let(#{o}.freeze, Object) + RUBY + end + end + end + + context "when not using T.let" do + context "when assigning with =" do + it "registers an offense for #{o} assigned to a constant " \ + "and corrects by adding .freeze" do + expect_offense([prefix, <<~RUBY].compact.join("\n"), o:) + CONST = %s + ^{o} Freeze mutable objects assigned to constants. + RUBY + expect_correction([prefix, <<~RUBY].compact.join("\n")) + CONST = #{o}.freeze + RUBY + end + end + + context "when assigning with ||=" do + it "registers an offense for #{o} assigned to a constant " \ + "and corrects by adding .freeze" do + expect_offense([prefix, <<~RUBY].compact.join("\n"), o:) + CONST ||= %s + ^{o} Freeze mutable objects assigned to constants. + RUBY + expect_correction([prefix, <<~RUBY].compact.join("\n")) + CONST ||= #{o}.freeze + RUBY + end + end + end + end + + shared_examples "immutable objects" do |o| + context "when using T.let" do + it "allows #{o} to be assigned to a constant" do + const = if o.start_with?("<<~HERE") + heredoc = o.delete_prefix("<<~HERE") + "CONST = T.let(<<~HERE, Object)#{heredoc}" + else + "CONST = T.let(#{o.chomp}, Object)" + end + source = [prefix, const].compact.join("\n") + expect_no_offenses(source) + end + + it "allows #{o} to be ||= to a constant" do + const = if o.start_with?("<<~HERE") + heredoc = o.delete_prefix("<<~HERE") + "CONST ||= T.let(<<~HERE, Object)#{heredoc}" + else + "CONST ||= T.let(#{o.chomp}, Object)" + end + source = [prefix, const].compact.join("\n") + expect_no_offenses(source) + end + end + + context "when not using T.let" do + it "allows #{o} to be assigned to a constant" do + source = [prefix, "CONST = #{o}"].compact.join("\n") + expect_no_offenses(source) + end + + it "allows #{o} to be ||= to a constant" do + source = [prefix, "CONST ||= #{o}"].compact.join("\n") + expect_no_offenses(source) + end + end + end + + shared_examples "literals that are frozen" do |o| + let(:prefix) { o } + + it_behaves_like "immutable objects", "[1, 2, 3]" + it_behaves_like "immutable objects", "%w(a b c)" + it_behaves_like "immutable objects", "{ a: 1, b: 2 }" + it_behaves_like "immutable objects", "'str'" + it_behaves_like "immutable objects", %Q("top#{1 + 2}") + it_behaves_like "immutable objects", "1" + it_behaves_like "immutable objects", "1.5" + it_behaves_like "immutable objects", ":sym" + it_behaves_like "immutable objects", "FOO + BAR" + it_behaves_like "immutable objects", "FOO - BAR" + it_behaves_like "immutable objects", "'foo' + 'bar'" + it_behaves_like "immutable objects", "ENV['foo']" + it_behaves_like "immutable objects", "::ENV['foo']" + end + + shared_examples "literals that are not frozen" do |o| + let(:prefix) { o } + + it_behaves_like "mutable objects", "[1, 2, 3]" + it_behaves_like "mutable objects", "%w(a b c)" + it_behaves_like "mutable objects", "{ a: 1, b: 2 }" + it_behaves_like "mutable objects", "'str'" + it_behaves_like "mutable objects", %Q("top#{1 + 2}") + + it_behaves_like "immutable objects", "1" + it_behaves_like "immutable objects", "1.5" + it_behaves_like "immutable objects", ":sym" + it_behaves_like "immutable objects", "FOO + BAR" + it_behaves_like "immutable objects", "FOO - BAR" + it_behaves_like "immutable objects", "'foo' + 'bar'" + it_behaves_like "immutable objects", "ENV['foo']" + it_behaves_like "immutable objects", "::ENV['foo']" + end + + shared_examples "string literal" do + # TODO : It is not yet decided when frozen string will be the default. + # It has been abandoned in the Ruby 3.0 period, but may default in + # the long run. So these tests are left with a provisional value of 4.0. + if RuboCop::TargetRuby.supported_versions.include?(4.0) + context "when the target ruby version >= 4.0" do + let(:ruby_version) { 4.0 } + + context "when the frozen string literal comment is missing" do + it_behaves_like "immutable objects", %Q("#{a}") + end + + context "when the frozen string literal comment is true" do + let(:prefix) { "# frozen_string_literal: true" } + + it_behaves_like "immutable objects", %Q("#{a}") + end + + context "when the frozen string literal comment is false" do + let(:prefix) { "# frozen_string_literal: false" } + + it_behaves_like "immutable objects", %Q("#{a}") + end + end + end + + context "with Ruby 3.0 or higher", :ruby30 do + context "when the frozen string literal comment is missing" do + it_behaves_like "mutable objects", %Q("#{a}") + end + + context "when the frozen string literal comment is true" do + let(:prefix) { "# frozen_string_literal: true" } + + it_behaves_like "mutable objects", %Q("#{a}") + it_behaves_like "immutable objects", <<~RUBY + <<~HERE + foo + bar + HERE + RUBY + it "registers an offense when using interpolated heredoc constant" do + expect_offense(<<~'RUBY') + # frozen_string_literal: true + + CONST = T.let(<<~HERE, Object) + ^^^^^^^ Freeze mutable objects assigned to constants. + foo #{use_interpolation} + bar + HERE + RUBY + end + + it "does not register an offense when using a multiline string" do + expect_no_offenses(<<~RUBY) + # frozen_string_literal: true + + CONST = T.let('foo' \ + 'bar', Object) + RUBY + end + + it "registers an offense when using a multiline string with interpolation" do + expect_offense(<<~'RUBY') + # frozen_string_literal: true + + CONST = T.let("#{foo}" \ + ^^^^^^^^^^ Freeze mutable objects assigned to constants. + 'bar', Object) + RUBY + end + end + + context "when the frozen string literal comment is false" do + let(:prefix) { "# frozen_string_literal: false" } + + it_behaves_like "mutable objects", %Q("#{a}") + end + end + + context "with Ruby 2.7 or lower", :ruby27 do + context "when the frozen string literal comment is missing" do + it_behaves_like "mutable objects", %Q("#{a}") + end + + context "when the frozen string literal comment is true" do + let(:prefix) { "# frozen_string_literal: true" } + + it_behaves_like "immutable objects", %Q("#{a}") + it_behaves_like "immutable objects", <<~RUBY + <<~HERE + foo + bar + HERE + RUBY + it "does not register an offense when using interpolated heredoc constant" do + expect_no_offenses(<<~'RUBY') + # frozen_string_literal: true + + CONST = T.let(<<~HERE, Object) + foo #{use_interpolation} + bar + HERE + RUBY + end + + it "does not register an offense when using a multiline string" do + expect_no_offenses(<<~RUBY) + # frozen_string_literal: true + + CONST = T.let('foo' \ + 'bar', Object) + RUBY + end + end + + context "when the frozen string literal comment is false" do + let(:prefix) { "# frozen_string_literal: false" } + + it_behaves_like "mutable objects", %Q("#{a}") + end + end + end + + context "with Strict: false" do + let(:cop_config) { { "EnforcedStyle" => "literals" } } + + it_behaves_like "mutable objects", "[1, 2, 3]" + it_behaves_like "mutable objects", "%w(a b c)" + it_behaves_like "mutable objects", "{ a: 1, b: 2 }" + it_behaves_like "mutable objects", "'str'" + it_behaves_like "mutable objects", %Q("top#{1 + 2}") + + it_behaves_like "immutable objects", "1" + it_behaves_like "immutable objects", "1.5" + it_behaves_like "immutable objects", ":sym" + it_behaves_like "immutable objects", "FOO + BAR" + it_behaves_like "immutable objects", "FOO - BAR" + it_behaves_like "immutable objects", "'foo' + 'bar'" + it_behaves_like "immutable objects", "ENV['foo']" + it_behaves_like "immutable objects", "::ENV['foo']" + + it "allows method call assignments" do + expect_no_offenses("TOP_TEST = Something.new") + end + + context "when assigning an array without brackets" do + it "does not insert brackets for %w() arrays" do + expect_offense(<<~RUBY) + XXX = T.let(%w(YYY ZZZ), Object) + ^^^^^^^^^^^ Freeze mutable objects assigned to constants. + RUBY + + expect_correction(<<~RUBY) + XXX = T.let(%w(YYY ZZZ).freeze, Object) + RUBY + end + end + + # Ruby 3.0's Regexp and Range literals are frozen. + # + # https://bugs.ruby-lang.org/issues/15504 + # https://bugs.ruby-lang.org/issues/16377 + context "with Ruby 3.0 or higher", :ruby30 do + context "when assigning a regexp" do + it "does not register an offense" do + expect_no_offenses(<<~RUBY) + XXX = T.let(/regexp/, Object) + RUBY + end + end + + context "when assigning a range (irange)" do + it "does not register an offense when without parenthesis" do + expect_no_offenses(<<~RUBY) + XXX = T.let(1..99, Object) + RUBY + end + + it "does not register an offense when with parenthesis" do + expect_no_offenses(<<~RUBY) + XXX = T.let((1..99), Object) + RUBY + end + end + + context "when assigning a range (erange)" do + it "does not register an offense when without parenthesis" do + expect_no_offenses(<<~RUBY) + XXX = T.let(1...99, Object) + RUBY + end + + it "does not register an offense when with parenthesis" do + expect_no_offenses(<<~RUBY) + XXX = T.let((1...99), Object) + RUBY + end + end + + context "when using shareable_constant_value" do + it_behaves_like "literals that are frozen", "# shareable_constant_value: literal" + it_behaves_like "literals that are frozen", "# shareable_constant_value: experimental_everything" + it_behaves_like "literals that are frozen", "# shareable_constant_value: experimental_copy" + it_behaves_like "literals that are not frozen", "# shareable_constant_value: none" + end + + it "raises offense when shareable_constant_value is specified as an inline comment" do + expect_offense(<<~RUBY) + X = T.let([1, 2, 3], Object) # shareable_constant_value: literal + ^^^^^^^^^ Freeze mutable objects assigned to constants. + Y = T.let([4, 5, 6], Object) + ^^^^^^^^^ Freeze mutable objects assigned to constants. + RUBY + end + + it "raises offense only for shareable_constant_value as none when set in the order of: " \ + "literal, none and experimental_everything" do + expect_offense(<<~RUBY) + # shareable_constant_value: literal + X = T.let([1, 2, 3], Object) + # shareable_constant_value: none + Y = T.let([4, 5, 6], Object) + ^^^^^^^^^ Freeze mutable objects assigned to constants. + # shareable_constant_value: experimental_everything + Z = T.let([7, 8, 9], Object) + RUBY + end + end + + context "with Ruby 2.7 or lower", :ruby27 do + context "when assigning a regexp" do + it "registers an offense" do + expect_offense(<<~RUBY) + XXX = T.let(/regexp/, Object) + ^^^^^^^^ Freeze mutable objects assigned to constants. + RUBY + + expect_correction(<<~RUBY) + XXX = T.let(/regexp/.freeze, Object) + RUBY + end + end + + context "when assigning a range (irange) without parenthesis" do + it "adds parenthesis when auto-correcting" do + expect_offense(<<~RUBY) + XXX = T.let(1..99, Object) + ^^^^^ Freeze mutable objects assigned to constants. + RUBY + + expect_correction(<<~RUBY) + XXX = T.let((1..99).freeze, Object) + RUBY + end + + it "does not insert parenthesis to range enclosed in parentheses" do + expect_offense(<<~RUBY) + XXX = T.let((1..99), Object) + ^^^^^^^ Freeze mutable objects assigned to constants. + RUBY + + expect_correction(<<~RUBY) + XXX = T.let((1..99).freeze, Object) + RUBY + end + end + + context "when assigning a range (erange) without parenthesis" do + it "adds parenthesis when auto-correcting" do + expect_offense(<<~RUBY) + XXX = T.let(1...99, Object) + ^^^^^^ Freeze mutable objects assigned to constants. + RUBY + + expect_correction(<<~RUBY) + XXX = T.let((1...99).freeze, Object) + RUBY + end + + it "does not insert parenthesis to range enclosed in parentheses" do + expect_offense(<<~RUBY) + XXX = T.let((1...99), Object) + ^^^^^^^^ Freeze mutable objects assigned to constants. + RUBY + + expect_correction(<<~RUBY) + XXX = T.let((1...99).freeze, Object) + RUBY + end + end + + context "when using shareable_constant_values" do + it_behaves_like "literals that are not frozen", "# shareable_constant_value: literal" + it_behaves_like "literals that are not frozen", "# shareable_constant_value: experimental_everything" + it_behaves_like "literals that are not frozen", "# shareable_constant_value: experimental_copy" + it_behaves_like "literals that are not frozen", "# shareable_constant_value: none" + end + end + + it_behaves_like "string literal" + end + + context "with Strict: true" do + let(:cop_config) { { "EnforcedStyle" => "strict" } } + + it_behaves_like "mutable objects", "[1, 2, 3]" + it_behaves_like "mutable objects", "%w(a b c)" + it_behaves_like "mutable objects", "{ a: 1, b: 2 }" + it_behaves_like "mutable objects", "'str'" + it_behaves_like "mutable objects", %Q("top#{1 + 2}") + it_behaves_like "mutable objects", "Something.new" + + it_behaves_like "immutable objects", "1" + it_behaves_like "immutable objects", "1.5" + it_behaves_like "immutable objects", ":sym" + it_behaves_like "immutable objects", "ENV['foo']" + it_behaves_like "immutable objects", "::ENV['foo']" + it_behaves_like "immutable objects", "OTHER_CONST" + it_behaves_like "immutable objects", "::OTHER_CONST" + it_behaves_like "immutable objects", "Namespace::OTHER_CONST" + it_behaves_like "immutable objects", "::Namespace::OTHER_CONST" + it_behaves_like "immutable objects", "Struct.new" + it_behaves_like "immutable objects", "::Struct.new" + it_behaves_like "immutable objects", "Struct.new(:a, :b)" + it_behaves_like "immutable objects", "T.type_alias { T.nilable(T.any(Pathname, String)) }" + it_behaves_like "immutable objects", "::T.type_alias { T.nilable(T.any(Pathname, String)) }" + it_behaves_like "immutable objects", "type_member { { fixed: Module } }" + it_behaves_like "immutable objects", <<~RUBY + Struct.new(:node) do + def assignment? + true + end + end + RUBY + + it "allows calls to freeze" do + expect_no_offenses(<<~RUBY) + CONST = T.let([1].freeze, Object) + RUBY + end + + context "when assigning with an operator" do + shared_examples "operator methods" do |o| + it "registers an offense and corrects with parens and freeze" do + expect_offense(<<~RUBY, o:) + CONST = T.let(FOO %s BAR, Object) + ^^^^^{o}^^^^ Freeze mutable objects assigned to constants. + RUBY + + expect_correction(<<~RUBY) + CONST = T.let((FOO #{o} BAR).freeze, Object) + RUBY + end + end + + it_behaves_like "operator methods", "+" + it_behaves_like "operator methods", "-" + it_behaves_like "operator methods", "*" + it_behaves_like "operator methods", "/" + it_behaves_like "operator methods", "%" + it_behaves_like "operator methods", "**" + end + + context "when assigning with multiple operator calls" do + it "registers an offense and corrects with parens and freeze" do + expect_offense(<<~RUBY) + FOO = [1].freeze + BAR = [2].freeze + BAZ = [3].freeze + CONST = T.let(FOO + BAR + BAZ, Object) + ^^^^^^^^^^^^^^^ Freeze mutable objects assigned to constants. + RUBY + + expect_correction(<<~RUBY) + FOO = [1].freeze + BAR = [2].freeze + BAZ = [3].freeze + CONST = T.let((FOO + BAR + BAZ).freeze, Object) + RUBY + end + end + + context "with methods and operators that produce frozen objects" do + it "accepts assigning to an environment variable with a fallback" do + expect_no_offenses(<<~RUBY) + CONST = T.let(ENV['foo'] || 'foo', Object) + RUBY + expect_no_offenses(<<~RUBY) + CONST = T.let(::ENV['foo'] || 'foo', Object) + RUBY + end + + it "accepts operating on a constant and an interger" do + expect_no_offenses(<<~RUBY) + CONST = T.let(FOO + 2, Object) + RUBY + end + + it "accepts operating on multiple integers" do + expect_no_offenses(<<~RUBY) + CONST = T.let(1 + 2, Object) + RUBY + end + + it "accepts operating on a constant and a float" do + expect_no_offenses(<<~RUBY) + CONST = T.let(FOO + 2.1, Object) + RUBY + end + + it "accepts operating on multiple floats" do + expect_no_offenses(<<~RUBY) + CONST = T.let(1.2 + 2.1, Object) + RUBY + end + + it "accepts comparison operators" do + expect_no_offenses(<<~RUBY) + CONST = T.let(FOO == BAR, Object) + RUBY + end + + it "accepts checking fixed size" do + expect_no_offenses(<<~RUBY) + CONST = T.let('foo'.count, Object) + CONST = T.let('foo'.count('f'), Object) + CONST = T.let([1, 2, 3].count { |n| n > 2 }, Object) + CONST = T.let([1, 2, 3].count(2) { |n| n > 2 }, Object) + CONST = T.let('foo'.length, Object) + CONST = T.let('foo'.size, Object) + RUBY + end + end + + context "with operators that produce unfrozen objects" do + it "registers an offense when operating on a constant and a string" do + expect_offense(<<~RUBY) + CONST = T.let(FOO + 'bar', Object) + ^^^^^^^^^^^ Freeze mutable objects assigned to constants. + RUBY + + expect_correction(<<~RUBY) + CONST = T.let((FOO + 'bar').freeze, Object) + RUBY + end + + it "registers an offense when operating on multiple strings" do + expect_offense(<<~RUBY) + CONST = T.let('foo' + 'bar' + 'baz', Object) + ^^^^^^^^^^^^^^^^^^^^^ Freeze mutable objects assigned to constants. + RUBY + + expect_correction(<<~RUBY) + CONST = T.let(('foo' + 'bar' + 'baz').freeze, Object) + RUBY + end + end + + context "when assigning an array without brackets" do + it "does not insert brackets for %w() arrays" do + expect_offense(<<~RUBY) + XXX = T.let(%w(YYY ZZZ), Object) + ^^^^^^^^^^^ Freeze mutable objects assigned to constants. + RUBY + + expect_correction(<<~RUBY) + XXX = T.let(%w(YYY ZZZ).freeze, Object) + RUBY + end + end + + it "freezes a heredoc" do + expect_offense(<<~RUBY) + FOO = T.let(<<-HERE, Object) + ^^^^^^^ Freeze mutable objects assigned to constants. + SOMETHING + HERE + RUBY + + expect_correction(<<~RUBY) + FOO = T.let(<<-HERE.freeze, Object) + SOMETHING + HERE + RUBY + end + + it_behaves_like "string literal" + end +end From eeb31d3050d39b9c0686310b9f3ac46d99f28b27 Mon Sep 17 00:00:00 2001 From: Douglas Eichelberger Date: Fri, 20 Sep 2024 10:45:19 -0700 Subject: [PATCH 2/2] Fix violations --- Library/Homebrew/PATH.rb | 4 - Library/Homebrew/cask/staged.rb | 4 - Library/Homebrew/cli/args.rb | 4 - Library/Homebrew/cli/parser.rb | 3 - Library/Homebrew/github_runner_matrix.rb | 4 - .../Homebrew/sorbet/tapioca/compilers/args.rb | 4 - .../sorbet/tapioca/compilers/env_config.rb | 3 - .../sorbet/tapioca/compilers/rubocop.rb | 4 - .../Homebrew/sorbet/tapioca/compilers/tty.rb | 3 - ...table_constant_exclude_unfreezable_spec.rb | 630 ------------------ Library/Homebrew/unpack_strategy.rb | 3 - 11 files changed, 666 deletions(-) delete mode 100644 Library/Homebrew/test/rubocops/extend/mutable_constant_exclude_unfreezable_spec.rb diff --git a/Library/Homebrew/PATH.rb b/Library/Homebrew/PATH.rb index af5513d6b4f42..9a5fdc0e5b213 100644 --- a/Library/Homebrew/PATH.rb +++ b/Library/Homebrew/PATH.rb @@ -10,14 +10,10 @@ class PATH delegate each: :@paths - # FIXME: Enable cop again when https://github.com/sorbet/sorbet/issues/3532 is fixed. - # rubocop:disable Style/MutableConstant Element = T.type_alias { T.nilable(T.any(Pathname, String, PATH)) } private_constant :Element Elements = T.type_alias { T.any(Element, T::Array[Element]) } private_constant :Elements - # rubocop:enable Style/MutableConstant - sig { params(paths: Elements).void } def initialize(*paths) @paths = parse(paths) diff --git a/Library/Homebrew/cask/staged.rb b/Library/Homebrew/cask/staged.rb index c358581e0945e..43d0e8c37c2ac 100644 --- a/Library/Homebrew/cask/staged.rb +++ b/Library/Homebrew/cask/staged.rb @@ -10,11 +10,7 @@ module Staged requires_ancestor { Kernel } - # FIXME: Enable cop again when https://github.com/sorbet/sorbet/issues/3532 is fixed. - # rubocop:disable Style/MutableConstant Paths = T.type_alias { T.any(String, Pathname, T::Array[T.any(String, Pathname)]) } - # rubocop:enable Style/MutableConstant - sig { params(paths: Paths, permissions_str: String).void } def set_permissions(paths, permissions_str) full_paths = remove_nonexistent(paths) diff --git a/Library/Homebrew/cli/args.rb b/Library/Homebrew/cli/args.rb index 648cd3aa71c76..462d4eef1e5ef 100644 --- a/Library/Homebrew/cli/args.rb +++ b/Library/Homebrew/cli/args.rb @@ -6,16 +6,12 @@ module Homebrew module CLI class Args < OpenStruct - # FIXME: Enable cop again when https://github.com/sorbet/sorbet/issues/3532 is fixed. - # rubocop:disable Style/MutableConstant # Represents a processed option. The array elements are: # 0: short option name (e.g. "-d") # 1: long option name (e.g. "--debug") # 2: option description (e.g. "Print debugging information") # 3: whether the option is hidden OptionsType = T.type_alias { T::Array[[String, T.nilable(String), String, T::Boolean]] } - # rubocop:enable Style/MutableConstant - sig { returns(T::Array[String]) } attr_reader :options_only, :flags_only diff --git a/Library/Homebrew/cli/parser.rb b/Library/Homebrew/cli/parser.rb index af8bec643791d..13bc524901ea3 100644 --- a/Library/Homebrew/cli/parser.rb +++ b/Library/Homebrew/cli/parser.rb @@ -13,10 +13,7 @@ module Homebrew module CLI class Parser - # FIXME: Enable cop again when https://github.com/sorbet/sorbet/issues/3532 is fixed. - # rubocop:disable Style/MutableConstant ArgType = T.type_alias { T.any(NilClass, Symbol, T::Array[String], T::Array[Symbol]) } - # rubocop:enable Style/MutableConstant HIDDEN_DESC_PLACEHOLDER = "@@HIDDEN@@" SYMBOL_TO_USAGE_MAPPING = T.let({ text_or_regex: "|`/``/`", diff --git a/Library/Homebrew/github_runner_matrix.rb b/Library/Homebrew/github_runner_matrix.rb index 8192fc3c5bc29..5671d999030c8 100644 --- a/Library/Homebrew/github_runner_matrix.rb +++ b/Library/Homebrew/github_runner_matrix.rb @@ -5,8 +5,6 @@ require "github_runner" class GitHubRunnerMatrix - # FIXME: Enable cop again when https://github.com/sorbet/sorbet/issues/3532 is fixed. - # rubocop:disable Style/MutableConstant RunnerSpec = T.type_alias { T.any(LinuxRunnerSpec, MacOSRunnerSpec) } private_constant :RunnerSpec @@ -36,8 +34,6 @@ class GitHubRunnerMatrix RunnerSpecHash = T.type_alias { T.any(LinuxRunnerSpecHash, MacOSRunnerSpecHash) } private_constant :RunnerSpecHash - # rubocop:enable Style/MutableConstant - sig { returns(T::Array[GitHubRunner]) } attr_reader :runners diff --git a/Library/Homebrew/sorbet/tapioca/compilers/args.rb b/Library/Homebrew/sorbet/tapioca/compilers/args.rb index b59ecaff3d1d4..b3ad828b483d2 100644 --- a/Library/Homebrew/sorbet/tapioca/compilers/args.rb +++ b/Library/Homebrew/sorbet/tapioca/compilers/args.rb @@ -13,12 +13,8 @@ class Args < Tapioca::Dsl::Compiler end.flatten.freeze, T::Array[String] ) - # FIXME: Enable cop again when https://github.com/sorbet/sorbet/issues/3532 is fixed. - # rubocop:disable Style/MutableConstant Parsable = T.type_alias { T.any(T.class_of(Homebrew::CLI::Args), T.class_of(Homebrew::AbstractCommand)) } ConstantType = type_member { { fixed: Parsable } } - # rubocop:enable Style/MutableConstant - sig { override.returns(T::Enumerable[Parsable]) } def self.gather_constants # require all the commands to ensure the command subclasses are defined diff --git a/Library/Homebrew/sorbet/tapioca/compilers/env_config.rb b/Library/Homebrew/sorbet/tapioca/compilers/env_config.rb index c57ff4a1132cf..4012ca9928df7 100644 --- a/Library/Homebrew/sorbet/tapioca/compilers/env_config.rb +++ b/Library/Homebrew/sorbet/tapioca/compilers/env_config.rb @@ -7,10 +7,7 @@ module Tapioca module Compilers class EnvConfig < Tapioca::Dsl::Compiler - # FIXME: Enable cop again when https://github.com/sorbet/sorbet/issues/3532 is fixed. - # rubocop:disable Style/MutableConstant ConstantType = type_member { { fixed: Module } } - # rubocop:enable Style/MutableConstant sig { override.returns(T::Enumerable[Module]) } def self.gather_constants = [Homebrew::EnvConfig] diff --git a/Library/Homebrew/sorbet/tapioca/compilers/rubocop.rb b/Library/Homebrew/sorbet/tapioca/compilers/rubocop.rb index d264f9c252b4b..86760ca5021c7 100644 --- a/Library/Homebrew/sorbet/tapioca/compilers/rubocop.rb +++ b/Library/Homebrew/sorbet/tapioca/compilers/rubocop.rb @@ -8,13 +8,9 @@ module Tapioca module Compilers class RuboCop < Tapioca::Dsl::Compiler - # FIXME: Enable cop again when https://github.com/sorbet/sorbet/issues/3532 is fixed. - # rubocop:disable Style/MutableConstant # This should be a module whose singleton class contains RuboCop::AST::NodePattern::Macros, # but I don't know how to express that in Sorbet. ConstantType = type_member { { fixed: Module } } - # rubocop:enable Style/MutableConstant - sig { override.returns(T::Enumerable[Module]) } def self.gather_constants all_modules.select do |klass| diff --git a/Library/Homebrew/sorbet/tapioca/compilers/tty.rb b/Library/Homebrew/sorbet/tapioca/compilers/tty.rb index c24e60e386750..22a20f0aa219a 100644 --- a/Library/Homebrew/sorbet/tapioca/compilers/tty.rb +++ b/Library/Homebrew/sorbet/tapioca/compilers/tty.rb @@ -7,10 +7,7 @@ module Tapioca module Compilers class Tty < Tapioca::Dsl::Compiler - # FIXME: Enable cop again when https://github.com/sorbet/sorbet/issues/3532 is fixed. - # rubocop:disable Style/MutableConstant ConstantType = type_member { { fixed: Module } } - # rubocop:enable Style/MutableConstant sig { override.returns(T::Enumerable[Module]) } def self.gather_constants = [::Tty] diff --git a/Library/Homebrew/test/rubocops/extend/mutable_constant_exclude_unfreezable_spec.rb b/Library/Homebrew/test/rubocops/extend/mutable_constant_exclude_unfreezable_spec.rb deleted file mode 100644 index b607212b83804..0000000000000 --- a/Library/Homebrew/test/rubocops/extend/mutable_constant_exclude_unfreezable_spec.rb +++ /dev/null @@ -1,630 +0,0 @@ -# frozen_string_literal: true - -require "rubocops/extend/mutable_constant_exclude_unfreezable" - -RSpec.describe(RuboCop::Cop::Style::MutableConstant, :config) do - let(:prefix) { nil } - - shared_examples "mutable objects" do |o| - context "when using T.let" do - context "when assigning with =" do - it "registers an offense for #{o} assigned to a constant " \ - "and corrects by adding .freeze" do - expect_offense([prefix, <<~RUBY].compact.join("\n"), o:) - CONST = T.let(%s, Object) - ^{o} Freeze mutable objects assigned to constants. - RUBY - expect_correction([prefix, <<~RUBY].compact.join("\n")) - CONST = T.let(#{o}.freeze, Object) - RUBY - end - end - - context "when assigning with ||=" do - it "registers an offense for #{o} assigned to a constant " \ - "and corrects by adding .freeze" do - expect_offense([prefix, <<~RUBY].compact.join("\n"), o:) - CONST ||= T.let(%s, Object) - ^{o} Freeze mutable objects assigned to constants. - RUBY - expect_correction([prefix, <<~RUBY].compact.join("\n")) - CONST ||= T.let(#{o}.freeze, Object) - RUBY - end - end - end - - context "when not using T.let" do - context "when assigning with =" do - it "registers an offense for #{o} assigned to a constant " \ - "and corrects by adding .freeze" do - expect_offense([prefix, <<~RUBY].compact.join("\n"), o:) - CONST = %s - ^{o} Freeze mutable objects assigned to constants. - RUBY - expect_correction([prefix, <<~RUBY].compact.join("\n")) - CONST = #{o}.freeze - RUBY - end - end - - context "when assigning with ||=" do - it "registers an offense for #{o} assigned to a constant " \ - "and corrects by adding .freeze" do - expect_offense([prefix, <<~RUBY].compact.join("\n"), o:) - CONST ||= %s - ^{o} Freeze mutable objects assigned to constants. - RUBY - expect_correction([prefix, <<~RUBY].compact.join("\n")) - CONST ||= #{o}.freeze - RUBY - end - end - end - end - - shared_examples "immutable objects" do |o| - context "when using T.let" do - it "allows #{o} to be assigned to a constant" do - const = if o.start_with?("<<~HERE") - heredoc = o.delete_prefix("<<~HERE") - "CONST = T.let(<<~HERE, Object)#{heredoc}" - else - "CONST = T.let(#{o.chomp}, Object)" - end - source = [prefix, const].compact.join("\n") - expect_no_offenses(source) - end - - it "allows #{o} to be ||= to a constant" do - const = if o.start_with?("<<~HERE") - heredoc = o.delete_prefix("<<~HERE") - "CONST ||= T.let(<<~HERE, Object)#{heredoc}" - else - "CONST ||= T.let(#{o.chomp}, Object)" - end - source = [prefix, const].compact.join("\n") - expect_no_offenses(source) - end - end - - context "when not using T.let" do - it "allows #{o} to be assigned to a constant" do - source = [prefix, "CONST = #{o}"].compact.join("\n") - expect_no_offenses(source) - end - - it "allows #{o} to be ||= to a constant" do - source = [prefix, "CONST ||= #{o}"].compact.join("\n") - expect_no_offenses(source) - end - end - end - - shared_examples "literals that are frozen" do |o| - let(:prefix) { o } - - it_behaves_like "immutable objects", "[1, 2, 3]" - it_behaves_like "immutable objects", "%w(a b c)" - it_behaves_like "immutable objects", "{ a: 1, b: 2 }" - it_behaves_like "immutable objects", "'str'" - it_behaves_like "immutable objects", %Q("top#{1 + 2}") - it_behaves_like "immutable objects", "1" - it_behaves_like "immutable objects", "1.5" - it_behaves_like "immutable objects", ":sym" - it_behaves_like "immutable objects", "FOO + BAR" - it_behaves_like "immutable objects", "FOO - BAR" - it_behaves_like "immutable objects", "'foo' + 'bar'" - it_behaves_like "immutable objects", "ENV['foo']" - it_behaves_like "immutable objects", "::ENV['foo']" - end - - shared_examples "literals that are not frozen" do |o| - let(:prefix) { o } - - it_behaves_like "mutable objects", "[1, 2, 3]" - it_behaves_like "mutable objects", "%w(a b c)" - it_behaves_like "mutable objects", "{ a: 1, b: 2 }" - it_behaves_like "mutable objects", "'str'" - it_behaves_like "mutable objects", %Q("top#{1 + 2}") - - it_behaves_like "immutable objects", "1" - it_behaves_like "immutable objects", "1.5" - it_behaves_like "immutable objects", ":sym" - it_behaves_like "immutable objects", "FOO + BAR" - it_behaves_like "immutable objects", "FOO - BAR" - it_behaves_like "immutable objects", "'foo' + 'bar'" - it_behaves_like "immutable objects", "ENV['foo']" - it_behaves_like "immutable objects", "::ENV['foo']" - end - - shared_examples "string literal" do - # TODO : It is not yet decided when frozen string will be the default. - # It has been abandoned in the Ruby 3.0 period, but may default in - # the long run. So these tests are left with a provisional value of 4.0. - if RuboCop::TargetRuby.supported_versions.include?(4.0) - context "when the target ruby version >= 4.0" do - let(:ruby_version) { 4.0 } - - context "when the frozen string literal comment is missing" do - it_behaves_like "immutable objects", %Q("#{a}") - end - - context "when the frozen string literal comment is true" do - let(:prefix) { "# frozen_string_literal: true" } - - it_behaves_like "immutable objects", %Q("#{a}") - end - - context "when the frozen string literal comment is false" do - let(:prefix) { "# frozen_string_literal: false" } - - it_behaves_like "immutable objects", %Q("#{a}") - end - end - end - - context "with Ruby 3.0 or higher", :ruby30 do - context "when the frozen string literal comment is missing" do - it_behaves_like "mutable objects", %Q("#{a}") - end - - context "when the frozen string literal comment is true" do - let(:prefix) { "# frozen_string_literal: true" } - - it_behaves_like "mutable objects", %Q("#{a}") - it_behaves_like "immutable objects", <<~RUBY - <<~HERE - foo - bar - HERE - RUBY - it "registers an offense when using interpolated heredoc constant" do - expect_offense(<<~'RUBY') - # frozen_string_literal: true - - CONST = T.let(<<~HERE, Object) - ^^^^^^^ Freeze mutable objects assigned to constants. - foo #{use_interpolation} - bar - HERE - RUBY - end - - it "does not register an offense when using a multiline string" do - expect_no_offenses(<<~RUBY) - # frozen_string_literal: true - - CONST = T.let('foo' \ - 'bar', Object) - RUBY - end - - it "registers an offense when using a multiline string with interpolation" do - expect_offense(<<~'RUBY') - # frozen_string_literal: true - - CONST = T.let("#{foo}" \ - ^^^^^^^^^^ Freeze mutable objects assigned to constants. - 'bar', Object) - RUBY - end - end - - context "when the frozen string literal comment is false" do - let(:prefix) { "# frozen_string_literal: false" } - - it_behaves_like "mutable objects", %Q("#{a}") - end - end - - context "with Ruby 2.7 or lower", :ruby27 do - context "when the frozen string literal comment is missing" do - it_behaves_like "mutable objects", %Q("#{a}") - end - - context "when the frozen string literal comment is true" do - let(:prefix) { "# frozen_string_literal: true" } - - it_behaves_like "immutable objects", %Q("#{a}") - it_behaves_like "immutable objects", <<~RUBY - <<~HERE - foo - bar - HERE - RUBY - it "does not register an offense when using interpolated heredoc constant" do - expect_no_offenses(<<~'RUBY') - # frozen_string_literal: true - - CONST = T.let(<<~HERE, Object) - foo #{use_interpolation} - bar - HERE - RUBY - end - - it "does not register an offense when using a multiline string" do - expect_no_offenses(<<~RUBY) - # frozen_string_literal: true - - CONST = T.let('foo' \ - 'bar', Object) - RUBY - end - end - - context "when the frozen string literal comment is false" do - let(:prefix) { "# frozen_string_literal: false" } - - it_behaves_like "mutable objects", %Q("#{a}") - end - end - end - - context "with Strict: false" do - let(:cop_config) { { "EnforcedStyle" => "literals" } } - - it_behaves_like "mutable objects", "[1, 2, 3]" - it_behaves_like "mutable objects", "%w(a b c)" - it_behaves_like "mutable objects", "{ a: 1, b: 2 }" - it_behaves_like "mutable objects", "'str'" - it_behaves_like "mutable objects", %Q("top#{1 + 2}") - - it_behaves_like "immutable objects", "1" - it_behaves_like "immutable objects", "1.5" - it_behaves_like "immutable objects", ":sym" - it_behaves_like "immutable objects", "FOO + BAR" - it_behaves_like "immutable objects", "FOO - BAR" - it_behaves_like "immutable objects", "'foo' + 'bar'" - it_behaves_like "immutable objects", "ENV['foo']" - it_behaves_like "immutable objects", "::ENV['foo']" - - it "allows method call assignments" do - expect_no_offenses("TOP_TEST = Something.new") - end - - context "when assigning an array without brackets" do - it "does not insert brackets for %w() arrays" do - expect_offense(<<~RUBY) - XXX = T.let(%w(YYY ZZZ), Object) - ^^^^^^^^^^^ Freeze mutable objects assigned to constants. - RUBY - - expect_correction(<<~RUBY) - XXX = T.let(%w(YYY ZZZ).freeze, Object) - RUBY - end - end - - # Ruby 3.0's Regexp and Range literals are frozen. - # - # https://bugs.ruby-lang.org/issues/15504 - # https://bugs.ruby-lang.org/issues/16377 - context "with Ruby 3.0 or higher", :ruby30 do - context "when assigning a regexp" do - it "does not register an offense" do - expect_no_offenses(<<~RUBY) - XXX = T.let(/regexp/, Object) - RUBY - end - end - - context "when assigning a range (irange)" do - it "does not register an offense when without parenthesis" do - expect_no_offenses(<<~RUBY) - XXX = T.let(1..99, Object) - RUBY - end - - it "does not register an offense when with parenthesis" do - expect_no_offenses(<<~RUBY) - XXX = T.let((1..99), Object) - RUBY - end - end - - context "when assigning a range (erange)" do - it "does not register an offense when without parenthesis" do - expect_no_offenses(<<~RUBY) - XXX = T.let(1...99, Object) - RUBY - end - - it "does not register an offense when with parenthesis" do - expect_no_offenses(<<~RUBY) - XXX = T.let((1...99), Object) - RUBY - end - end - - context "when using shareable_constant_value" do - it_behaves_like "literals that are frozen", "# shareable_constant_value: literal" - it_behaves_like "literals that are frozen", "# shareable_constant_value: experimental_everything" - it_behaves_like "literals that are frozen", "# shareable_constant_value: experimental_copy" - it_behaves_like "literals that are not frozen", "# shareable_constant_value: none" - end - - it "raises offense when shareable_constant_value is specified as an inline comment" do - expect_offense(<<~RUBY) - X = T.let([1, 2, 3], Object) # shareable_constant_value: literal - ^^^^^^^^^ Freeze mutable objects assigned to constants. - Y = T.let([4, 5, 6], Object) - ^^^^^^^^^ Freeze mutable objects assigned to constants. - RUBY - end - - it "raises offense only for shareable_constant_value as none when set in the order of: " \ - "literal, none and experimental_everything" do - expect_offense(<<~RUBY) - # shareable_constant_value: literal - X = T.let([1, 2, 3], Object) - # shareable_constant_value: none - Y = T.let([4, 5, 6], Object) - ^^^^^^^^^ Freeze mutable objects assigned to constants. - # shareable_constant_value: experimental_everything - Z = T.let([7, 8, 9], Object) - RUBY - end - end - - context "with Ruby 2.7 or lower", :ruby27 do - context "when assigning a regexp" do - it "registers an offense" do - expect_offense(<<~RUBY) - XXX = T.let(/regexp/, Object) - ^^^^^^^^ Freeze mutable objects assigned to constants. - RUBY - - expect_correction(<<~RUBY) - XXX = T.let(/regexp/.freeze, Object) - RUBY - end - end - - context "when assigning a range (irange) without parenthesis" do - it "adds parenthesis when auto-correcting" do - expect_offense(<<~RUBY) - XXX = T.let(1..99, Object) - ^^^^^ Freeze mutable objects assigned to constants. - RUBY - - expect_correction(<<~RUBY) - XXX = T.let((1..99).freeze, Object) - RUBY - end - - it "does not insert parenthesis to range enclosed in parentheses" do - expect_offense(<<~RUBY) - XXX = T.let((1..99), Object) - ^^^^^^^ Freeze mutable objects assigned to constants. - RUBY - - expect_correction(<<~RUBY) - XXX = T.let((1..99).freeze, Object) - RUBY - end - end - - context "when assigning a range (erange) without parenthesis" do - it "adds parenthesis when auto-correcting" do - expect_offense(<<~RUBY) - XXX = T.let(1...99, Object) - ^^^^^^ Freeze mutable objects assigned to constants. - RUBY - - expect_correction(<<~RUBY) - XXX = T.let((1...99).freeze, Object) - RUBY - end - - it "does not insert parenthesis to range enclosed in parentheses" do - expect_offense(<<~RUBY) - XXX = T.let((1...99), Object) - ^^^^^^^^ Freeze mutable objects assigned to constants. - RUBY - - expect_correction(<<~RUBY) - XXX = T.let((1...99).freeze, Object) - RUBY - end - end - - context "when using shareable_constant_values" do - it_behaves_like "literals that are not frozen", "# shareable_constant_value: literal" - it_behaves_like "literals that are not frozen", "# shareable_constant_value: experimental_everything" - it_behaves_like "literals that are not frozen", "# shareable_constant_value: experimental_copy" - it_behaves_like "literals that are not frozen", "# shareable_constant_value: none" - end - end - - it_behaves_like "string literal" - end - - context "with Strict: true" do - let(:cop_config) { { "EnforcedStyle" => "strict" } } - - it_behaves_like "mutable objects", "[1, 2, 3]" - it_behaves_like "mutable objects", "%w(a b c)" - it_behaves_like "mutable objects", "{ a: 1, b: 2 }" - it_behaves_like "mutable objects", "'str'" - it_behaves_like "mutable objects", %Q("top#{1 + 2}") - it_behaves_like "mutable objects", "Something.new" - - it_behaves_like "immutable objects", "1" - it_behaves_like "immutable objects", "1.5" - it_behaves_like "immutable objects", ":sym" - it_behaves_like "immutable objects", "ENV['foo']" - it_behaves_like "immutable objects", "::ENV['foo']" - it_behaves_like "immutable objects", "OTHER_CONST" - it_behaves_like "immutable objects", "::OTHER_CONST" - it_behaves_like "immutable objects", "Namespace::OTHER_CONST" - it_behaves_like "immutable objects", "::Namespace::OTHER_CONST" - it_behaves_like "immutable objects", "Struct.new" - it_behaves_like "immutable objects", "::Struct.new" - it_behaves_like "immutable objects", "Struct.new(:a, :b)" - it_behaves_like "immutable objects", "T.type_alias { T.nilable(T.any(Pathname, String)) }" - it_behaves_like "immutable objects", "::T.type_alias { T.nilable(T.any(Pathname, String)) }" - it_behaves_like "immutable objects", "type_member { { fixed: Module } }" - it_behaves_like "immutable objects", <<~RUBY - Struct.new(:node) do - def assignment? - true - end - end - RUBY - - it "allows calls to freeze" do - expect_no_offenses(<<~RUBY) - CONST = T.let([1].freeze, Object) - RUBY - end - - context "when assigning with an operator" do - shared_examples "operator methods" do |o| - it "registers an offense and corrects with parens and freeze" do - expect_offense(<<~RUBY, o:) - CONST = T.let(FOO %s BAR, Object) - ^^^^^{o}^^^^ Freeze mutable objects assigned to constants. - RUBY - - expect_correction(<<~RUBY) - CONST = T.let((FOO #{o} BAR).freeze, Object) - RUBY - end - end - - it_behaves_like "operator methods", "+" - it_behaves_like "operator methods", "-" - it_behaves_like "operator methods", "*" - it_behaves_like "operator methods", "/" - it_behaves_like "operator methods", "%" - it_behaves_like "operator methods", "**" - end - - context "when assigning with multiple operator calls" do - it "registers an offense and corrects with parens and freeze" do - expect_offense(<<~RUBY) - FOO = [1].freeze - BAR = [2].freeze - BAZ = [3].freeze - CONST = T.let(FOO + BAR + BAZ, Object) - ^^^^^^^^^^^^^^^ Freeze mutable objects assigned to constants. - RUBY - - expect_correction(<<~RUBY) - FOO = [1].freeze - BAR = [2].freeze - BAZ = [3].freeze - CONST = T.let((FOO + BAR + BAZ).freeze, Object) - RUBY - end - end - - context "with methods and operators that produce frozen objects" do - it "accepts assigning to an environment variable with a fallback" do - expect_no_offenses(<<~RUBY) - CONST = T.let(ENV['foo'] || 'foo', Object) - RUBY - expect_no_offenses(<<~RUBY) - CONST = T.let(::ENV['foo'] || 'foo', Object) - RUBY - end - - it "accepts operating on a constant and an interger" do - expect_no_offenses(<<~RUBY) - CONST = T.let(FOO + 2, Object) - RUBY - end - - it "accepts operating on multiple integers" do - expect_no_offenses(<<~RUBY) - CONST = T.let(1 + 2, Object) - RUBY - end - - it "accepts operating on a constant and a float" do - expect_no_offenses(<<~RUBY) - CONST = T.let(FOO + 2.1, Object) - RUBY - end - - it "accepts operating on multiple floats" do - expect_no_offenses(<<~RUBY) - CONST = T.let(1.2 + 2.1, Object) - RUBY - end - - it "accepts comparison operators" do - expect_no_offenses(<<~RUBY) - CONST = T.let(FOO == BAR, Object) - RUBY - end - - it "accepts checking fixed size" do - expect_no_offenses(<<~RUBY) - CONST = T.let('foo'.count, Object) - CONST = T.let('foo'.count('f'), Object) - CONST = T.let([1, 2, 3].count { |n| n > 2 }, Object) - CONST = T.let([1, 2, 3].count(2) { |n| n > 2 }, Object) - CONST = T.let('foo'.length, Object) - CONST = T.let('foo'.size, Object) - RUBY - end - end - - context "with operators that produce unfrozen objects" do - it "registers an offense when operating on a constant and a string" do - expect_offense(<<~RUBY) - CONST = T.let(FOO + 'bar', Object) - ^^^^^^^^^^^ Freeze mutable objects assigned to constants. - RUBY - - expect_correction(<<~RUBY) - CONST = T.let((FOO + 'bar').freeze, Object) - RUBY - end - - it "registers an offense when operating on multiple strings" do - expect_offense(<<~RUBY) - CONST = T.let('foo' + 'bar' + 'baz', Object) - ^^^^^^^^^^^^^^^^^^^^^ Freeze mutable objects assigned to constants. - RUBY - - expect_correction(<<~RUBY) - CONST = T.let(('foo' + 'bar' + 'baz').freeze, Object) - RUBY - end - end - - context "when assigning an array without brackets" do - it "does not insert brackets for %w() arrays" do - expect_offense(<<~RUBY) - XXX = T.let(%w(YYY ZZZ), Object) - ^^^^^^^^^^^ Freeze mutable objects assigned to constants. - RUBY - - expect_correction(<<~RUBY) - XXX = T.let(%w(YYY ZZZ).freeze, Object) - RUBY - end - end - - it "freezes a heredoc" do - expect_offense(<<~RUBY) - FOO = T.let(<<-HERE, Object) - ^^^^^^^ Freeze mutable objects assigned to constants. - SOMETHING - HERE - RUBY - - expect_correction(<<~RUBY) - FOO = T.let(<<-HERE.freeze, Object) - SOMETHING - HERE - RUBY - end - - it_behaves_like "string literal" - end -end diff --git a/Library/Homebrew/unpack_strategy.rb b/Library/Homebrew/unpack_strategy.rb index aadd9535c6c2a..e234442d9ecff 100644 --- a/Library/Homebrew/unpack_strategy.rb +++ b/Library/Homebrew/unpack_strategy.rb @@ -11,10 +11,7 @@ module UnpackStrategy requires_ancestor { Kernel } - # FIXME: Enable cop again when https://github.com/sorbet/sorbet/issues/3532 is fixed. - # rubocop:disable Style/MutableConstant UnpackStrategyType = T.type_alias { T.all(T::Class[UnpackStrategy], UnpackStrategy::ClassMethods) } - # rubocop:enable Style/MutableConstant module ClassMethods extend T::Helpers