diff --git a/Library/Homebrew/rubocops/cask/ast/cask_block.rb b/Library/Homebrew/rubocops/cask/ast/cask_block.rb index 1b4090ab49..f04aa1033b 100644 --- a/Library/Homebrew/rubocops/cask/ast/cask_block.rb +++ b/Library/Homebrew/rubocops/cask/ast/cask_block.rb @@ -6,27 +6,57 @@ require "forwardable" module RuboCop module Cask module AST - # This class wraps the AST block node that represents the entire cask - # definition. It includes various helper methods to aid cops in their - # analysis. - class CaskBlock - extend Forwardable + class StanzaBlock + extend T::Helpers + sig { returns(RuboCop::AST::BlockNode) } + attr_reader :block_node + + sig { returns(T::Array[Parser::Source::Comment]) } + attr_reader :comments + + sig { params(block_node: RuboCop::AST::BlockNode, comments: T::Array[Parser::Source::Comment]).void } def initialize(block_node, comments) @block_node = block_node @comments = comments end - attr_reader :block_node, :comments + sig { returns(T::Array[Stanza]) } + def stanzas + return [] unless (block_body = block_node.block_body) - alias cask_node block_node + # If a block only contains one stanza, it is that stanza's direct parent, otherwise + # stanzas are grouped in a nested block and the block is that nested block's parent. + is_stanza = if block_body.begin_block? + ->(node) { node.parent.parent == block_node } + else + ->(node) { node.parent == block_node } + end + + @stanzas ||= block_body.each_node + .select(&:stanza?) + .select(&is_stanza) + .map { |node| Stanza.new(node, comments) } + end + end + + # This class wraps the AST block node that represents the entire cask + # definition. It includes various helper methods to aid cops in their + # analysis. + class CaskBlock < StanzaBlock + extend Forwardable + + def cask_node + block_node + end def_delegator :cask_node, :block_body, :cask_body def header - @header ||= CaskHeader.new(cask_node.method_node) + @header ||= CaskHeader.new(block_node.method_node) end + # TODO: Use `StanzaBlock#stanzas` for all cops, where possible. def stanzas return [] unless cask_body @@ -46,28 +76,6 @@ module RuboCop @toplevel_stanzas ||= stanzas.select(&is_toplevel_stanza) end - - def sorted_toplevel_stanzas - @sorted_toplevel_stanzas ||= sort_stanzas(toplevel_stanzas) - end - - private - - def sort_stanzas(stanzas) - stanzas.sort do |s1, s2| - i1 = stanza_order_index(s1) - i2 = stanza_order_index(s2) - if i1 == i2 || i1.blank? || i2.blank? - i1 = stanzas.index(s1) - i2 = stanzas.index(s2) - end - i1 - i2 - end - end - - def stanza_order_index(stanza) - Constants::STANZA_ORDER.index(stanza.stanza_name) - end end end end diff --git a/Library/Homebrew/rubocops/cask/ast/stanza.rb b/Library/Homebrew/rubocops/cask/ast/stanza.rb index 68b6de8218..e9cc111b3a 100644 --- a/Library/Homebrew/rubocops/cask/ast/stanza.rb +++ b/Library/Homebrew/rubocops/cask/ast/stanza.rb @@ -23,6 +23,7 @@ module RuboCop def_delegator :stanza_node, :parent, :parent_node def_delegator :stanza_node, :arch_variable? + def_delegator :stanza_node, :on_system_block? def source_range stanza_node.location_expression @@ -48,6 +49,10 @@ module RuboCop Constants::STANZA_GROUP_HASH[stanza_name] end + def stanza_index + Constants::STANZA_ORDER.index(stanza_name) + end + def same_group?(other) stanza_group == other.stanza_group end @@ -65,7 +70,6 @@ module RuboCop def ==(other) self.class == other.class && stanza_node == other.stanza_node end - alias eql? == Constants::STANZA_ORDER.each do |stanza_name| diff --git a/Library/Homebrew/rubocops/cask/extend/node.rb b/Library/Homebrew/rubocops/cask/extend/node.rb index ec79aa4f3f..54b8654df2 100644 --- a/Library/Homebrew/rubocops/cask/extend/node.rb +++ b/Library/Homebrew/rubocops/cask/extend/node.rb @@ -14,11 +14,18 @@ module RuboCop def_node_matcher :key_node, "{(pair $_ _) (hash (pair $_ _) ...)}" def_node_matcher :val_node, "{(pair _ $_) (hash (pair _ $_) ...)}" - def_node_matcher :cask_block?, "(block (send nil? :cask _) args ...)" + def_node_matcher :cask_block?, "(block (send nil? :cask ...) args ...)" + def_node_matcher :on_system_block?, + "(block (send nil? {#{ON_SYSTEM_METHODS.map(&:inspect).join(" ")}} ...) args ...)" def_node_matcher :arch_variable?, "(lvasgn _ (send nil? :on_arch_conditional ...))" def_node_matcher :begin_block?, "(begin ...)" + sig { returns(T::Boolean) } + def cask_on_system_block? + (on_system_block? && each_ancestor.any?(&:cask_block?)) || false + end + def stanza? return true if arch_variable? diff --git a/Library/Homebrew/rubocops/cask/mixin/cask_help.rb b/Library/Homebrew/rubocops/cask/mixin/cask_help.rb index c4e76607cb..093b93ec61 100644 --- a/Library/Homebrew/rubocops/cask/mixin/cask_help.rb +++ b/Library/Homebrew/rubocops/cask/mixin/cask_help.rb @@ -6,22 +6,46 @@ module RuboCop module Cask # Common functionality for cops checking casks. module CaskHelp - extend T::Helpers + prepend CommentsHelp - abstract! - - sig { abstract.params(cask_block: RuboCop::Cask::AST::CaskBlock).void } + sig { overridable.params(cask_block: RuboCop::Cask::AST::CaskBlock).void } def on_cask(cask_block); end + sig { overridable.params(cask_stanza_block: RuboCop::Cask::AST::StanzaBlock).void } + def on_cask_stanza_block(cask_stanza_block); end + + # FIXME: Workaround until https://github.com/rubocop/rubocop/pull/11858 is released. + def find_end_line(node) + return node.loc.end.line if node.block_type? || node.numblock_type? + + super + end + + sig { params(block_node: RuboCop::AST::BlockNode).void } def on_block(block_node) super if defined? super - return unless respond_to?(:on_cask) + + return if !block_node.cask_block? && !block_node.cask_on_system_block? + + comments = comments_in_range(block_node).to_a + stanza_block = RuboCop::Cask::AST::StanzaBlock.new(block_node, comments) + on_cask_stanza_block(stanza_block) + return unless block_node.cask_block? - comments = processed_source.comments cask_block = RuboCop::Cask::AST::CaskBlock.new(block_node, comments) on_cask(cask_block) end + + def on_system_methods(cask_stanzas) + cask_stanzas.select(&:on_system_block?) + end + + def inner_stanzas(block_node, comments) + block_contents = block_node.child_nodes.select(&:begin_type?) + inner_nodes = block_contents.map(&:child_nodes).flatten.select(&:send_type?) + inner_nodes.map { |n| RuboCop::Cask::AST::Stanza.new(n, comments) } + end end end end diff --git a/Library/Homebrew/rubocops/cask/no_overrides.rb b/Library/Homebrew/rubocops/cask/no_overrides.rb index 484d8696d0..aa2721d1f3 100644 --- a/Library/Homebrew/rubocops/cask/no_overrides.rb +++ b/Library/Homebrew/rubocops/cask/no_overrides.rb @@ -7,7 +7,6 @@ module RuboCop class NoOverrides < Base include CaskHelp - ON_SYSTEM_METHODS = RuboCop::Cask::Constants::ON_SYSTEM_METHODS # These stanzas can be overridden by `on_*` blocks, so take them into account. # TODO: Update this list if new stanzas are added to `Cask::DSL` that call `set_unique_stanza`. OVERRIDEABLE_METHODS = [ @@ -22,8 +21,7 @@ module RuboCop def on_cask(cask_block) cask_stanzas = cask_block.toplevel_stanzas - # Skip if there are no `on_*` blocks. - return if (on_blocks = cask_stanzas.select { |s| ON_SYSTEM_METHODS.include?(s.stanza_name) }).none? + return if (on_blocks = on_system_methods(cask_stanzas)).none? stanzas_in_blocks = on_system_stanzas(on_blocks) @@ -40,9 +38,7 @@ module RuboCop def on_system_stanzas(on_system) names = Set.new method_nodes = on_system.map(&:method_node) - method_nodes.each do |node| - next unless node.block_type? - + method_nodes.select(&:block_type?).each do |node| node.child_nodes.each do |child| child.each_node(:send) do |send_node| # Skip (nested) livecheck blocks as its `url` is different to a download `url`. @@ -51,7 +47,7 @@ module RuboCop if send_node.ancestors.drop_while { |a| !a.begin_type? }.any? { |a| a.dstr_type? || a.regexp_type? } next end - next if ON_SYSTEM_METHODS.include?(send_node.method_name) + next if RuboCop::Cask::Constants::ON_SYSTEM_METHODS.include?(send_node.method_name) names.add(send_node.method_name) end diff --git a/Library/Homebrew/rubocops/cask/stanza_grouping.rb b/Library/Homebrew/rubocops/cask/stanza_grouping.rb index 9fa5a5b35c..93fcaf39b8 100644 --- a/Library/Homebrew/rubocops/cask/stanza_grouping.rb +++ b/Library/Homebrew/rubocops/cask/stanza_grouping.rb @@ -6,7 +6,7 @@ require "forwardable" module RuboCop module Cop module Cask - # This cop checks that a cask's stanzas are grouped correctly. + # This cop checks that a cask's stanzas are grouped correctly, including nested within `on_*` blocks. # @see https://docs.brew.sh/Cask-Cookbook#stanza-order class StanzaGrouping < Base extend Forwardable @@ -14,7 +14,6 @@ module RuboCop include CaskHelp include RangeHelp - ON_SYSTEM_METHODS = RuboCop::Cask::Constants::ON_SYSTEM_METHODS MISSING_LINE_MSG = "stanza groups should be separated by a single empty line" EXTRA_LINE_MSG = "stanzas within the same group should have no lines between them" @@ -24,17 +23,11 @@ module RuboCop cask_stanzas = cask_block.toplevel_stanzas add_offenses(cask_stanzas) - # If present, check grouping of stanzas within `on_*` blocks. - return if (on_blocks = cask_stanzas.select { |s| ON_SYSTEM_METHODS.include?(s.stanza_name) }).none? + return if (on_blocks = on_system_methods(cask_stanzas)).none? - on_blocks.map(&:method_node).each do |on_block| - next unless on_block.block_type? - - block_contents = on_block.child_nodes.select(&:begin_type?) - inner_nodes = block_contents.map(&:child_nodes).flatten.select(&:send_type?) - inner_stanzas = inner_nodes.map { |node| RuboCop::Cask::AST::Stanza.new(node, processed_source.comments) } - - add_offenses(inner_stanzas) + on_blocks.map(&:method_node).select(&:block_type?).each do |on_block| + stanzas = inner_stanzas(on_block, processed_source.comments) + add_offenses(stanzas) end end diff --git a/Library/Homebrew/rubocops/cask/stanza_order.rb b/Library/Homebrew/rubocops/cask/stanza_order.rb index 83264020bb..dfbb6e9432 100644 --- a/Library/Homebrew/rubocops/cask/stanza_order.rb +++ b/Library/Homebrew/rubocops/cask/stanza_order.rb @@ -6,46 +6,68 @@ require "forwardable" module RuboCop module Cop module Cask - # This cop checks that a cask's stanzas are ordered correctly. + # This cop checks that a cask's stanzas are ordered correctly, including nested within `on_*` blocks. # @see https://docs.brew.sh/Cask-Cookbook#stanza-order class StanzaOrder < Base + include IgnoredNode extend Forwardable extend AutoCorrector include CaskHelp MESSAGE = "`%s` stanza out of order" - def on_cask(cask_block) - @cask_block = cask_block - add_offenses - end + def on_cask_stanza_block(stanza_block) + stanzas = stanza_block.stanzas + ordered_stanzas = sort_stanzas(stanzas) - private + return if stanzas == ordered_stanzas - attr_reader :cask_block + stanzas.zip(ordered_stanzas).each do |stanza_before, stanza_after| + next if stanza_before == stanza_after - def_delegators :cask_block, :cask_node, :toplevel_stanzas, - :sorted_toplevel_stanzas + add_offense( + stanza_before.method_node, + message: format(MESSAGE, stanza: stanza_before.stanza_name), + ) do |corrector| + next if part_of_ignored_node?(stanza_before.method_node) - def add_offenses - offending_stanzas.each do |stanza| - message = format(MESSAGE, stanza: stanza.stanza_name) - add_offense(stanza.source_range_with_comments, message: message) do |corrector| - correct_stanza_index = toplevel_stanzas.index(stanza) - correct_stanza = sorted_toplevel_stanzas[correct_stanza_index] - corrector.replace(stanza.source_range_with_comments, - correct_stanza.source_with_comments) + corrector.replace( + stanza_before.source_range_with_comments, + stanza_after.source_with_comments, + ) + + # Ignore node so that nested content is not auto-corrected and clobbered. + ignore_node(stanza_before.method_node) end end end - def offending_stanzas - stanza_pairs = toplevel_stanzas.zip(sorted_toplevel_stanzas) - stanza_pairs.each_with_object([]) do |stanza_pair, offending_stanzas| - stanza, sorted_stanza = *stanza_pair - offending_stanzas << stanza if stanza != sorted_stanza + def on_new_investigation + super + + ignored_nodes.clear + end + + private + + def sort_stanzas(stanzas) + stanzas.sort do |stanza1, stanza2| + i1 = stanza1.stanza_index + i2 = stanza2.stanza_index + + if i1 == i2 + i1 = stanzas.index(stanza1) + i2 = stanzas.index(stanza2) + end + + i1 - i2 end end + + def stanza_order_index(stanza) + stanza_name = stanza.respond_to?(:method_name) ? stanza.method_name : stanza.stanza_name + RuboCop::Cask::Constants::STANZA_ORDER.index(stanza_name) + end end end end diff --git a/Library/Homebrew/sorbet/rbi/hidden-definitions/hidden.rbi b/Library/Homebrew/sorbet/rbi/hidden-definitions/hidden.rbi index 5eed0ed4c0..95271192b2 100644 --- a/Library/Homebrew/sorbet/rbi/hidden-definitions/hidden.rbi +++ b/Library/Homebrew/sorbet/rbi/hidden-definitions/hidden.rbi @@ -6373,6 +6373,8 @@ class RuboCop::AST::Node def method_node(param0=T.unsafe(nil)); end + def on_system_block?(param0=T.unsafe(nil)); end + def val_node(param0=T.unsafe(nil)); end end @@ -6504,12 +6506,61 @@ class RuboCop::Cask::AST::Stanza def zap?(); end end +module RuboCop::Cop::Cask::CaskHelp + include ::RuboCop::Cop::CommentsHelp +end + module RuboCop::Cop::Cask::CaskHelp extend ::T::Private::Abstract::Hooks extend ::T::InterfaceWrapper::Helpers end +class RuboCop::Cop::Cask::Desc + include ::RuboCop::Cop::CommentsHelp +end + +class RuboCop::Cop::Cask::HomepageUrlTrailingSlash + include ::RuboCop::Cop::CommentsHelp +end + +class RuboCop::Cop::Cask::NoDslVersion + include ::RuboCop::Cop::CommentsHelp +end + +class RuboCop::Cop::Cask::NoOverrides + include ::RuboCop::Cop::CommentsHelp +end + +module RuboCop::Cop::Cask::OnDescStanza + include ::RuboCop::Cop::CommentsHelp +end + +module RuboCop::Cop::Cask::OnHomepageStanza + include ::RuboCop::Cop::CommentsHelp +end + +class RuboCop::Cop::Cask::OnSystemConditionals + include ::RuboCop::Cop::CommentsHelp +end + +module RuboCop::Cop::Cask::OnUrlStanza + include ::RuboCop::Cop::CommentsHelp +end + +class RuboCop::Cop::Cask::StanzaGrouping + include ::RuboCop::Cop::CommentsHelp +end + +class RuboCop::Cop::Cask::StanzaOrder + include ::RuboCop::Cop::CommentsHelp +end + +class RuboCop::Cop::Cask::Url + include ::RuboCop::Cop::CommentsHelp +end + class RuboCop::Cop::Cask::Variables + include ::RuboCop::Cop::CommentsHelp def variable_assignment(param0); end end diff --git a/Library/Homebrew/test/rubocops/cask/shared_examples/cask_cop.rb b/Library/Homebrew/test/rubocops/cask/shared_examples/cask_cop.rb index 6436b09c95..c5cdcde9e8 100644 --- a/Library/Homebrew/test/rubocops/cask/shared_examples/cask_cop.rb +++ b/Library/Homebrew/test/rubocops/cask/shared_examples/cask_cop.rb @@ -27,11 +27,11 @@ module CaskCop offenses = inspect_source(source) expect(offenses.size).to eq(expected_offenses.size) expected_offenses.zip(offenses).each do |expected, actual| - expect_offense(expected, actual) + expect_offense2(expected, actual) end end - def expect_offense(expected, actual) + def expect_offense2(expected, actual) expect(actual.message).to eq(expected[:message]) expect(actual.severity).to eq(expected[:severity]) expect(actual.line).to eq(expected[:line]) @@ -39,8 +39,24 @@ module CaskCop expect(actual.location.source).to eq(expected[:source]) end + # TODO: Replace with `expect_correction` from `rubocop-rspec`. def expect_autocorrected_source(source, correct_source) - new_source = autocorrect_source(source) - expect(new_source).to eq(Array(correct_source).join("\n")) + correct_source = Array(correct_source).join("\n") + + current_source = source + + # RuboCop runs auto-correction in a loop to handle nested offenses. + loop do + current_source = autocorrect_source(current_source) + + if (ignored_nodes = cop.instance_variable_get(:@ignored_nodes)) && ignored_nodes.any? + ignored_nodes.clear + next + end + + break + end + + expect(current_source).to eq correct_source end end diff --git a/Library/Homebrew/test/rubocops/cask/stanza_order_spec.rb b/Library/Homebrew/test/rubocops/cask/stanza_order_spec.rb index b7357cd744..9075f1f6a2 100644 --- a/Library/Homebrew/test/rubocops/cask/stanza_order_spec.rb +++ b/Library/Homebrew/test/rubocops/cask/stanza_order_spec.rb @@ -3,11 +3,9 @@ require "rubocops/rubocop-cask" require "test/rubocops/cask/shared_examples/cask_cop" -describe RuboCop::Cop::Cask::StanzaOrder do +describe RuboCop::Cop::Cask::StanzaOrder, :config do include CaskCop - subject(:cop) { described_class.new } - context "when there is only one stanza" do let(:source) do <<~CASK @@ -55,13 +53,13 @@ describe RuboCop::Cop::Cask::StanzaOrder do end let(:expected_offenses) do [{ - message: "Cask/StanzaOrder: `sha256` stanza out of order", + message: "`sha256` stanza out of order", severity: :convention, line: 2, column: 2, source: "sha256 :no_check", }, { - message: "Cask/StanzaOrder: `version` stanza out of order", + message: "`version` stanza out of order", severity: :convention, line: 3, column: 2, @@ -95,19 +93,19 @@ describe RuboCop::Cop::Cask::StanzaOrder do end let(:expected_offenses) do [{ - message: "Cask/StanzaOrder: `version` stanza out of order", + message: "`version` stanza out of order", severity: :convention, line: 2, column: 2, source: "version :latest", }, { - message: "Cask/StanzaOrder: `sha256` stanza out of order", + message: "`sha256` stanza out of order", severity: :convention, line: 3, column: 2, source: "sha256 :no_check", }, { - message: "Cask/StanzaOrder: `arch` stanza out of order", + message: "`arch` stanza out of order", severity: :convention, line: 4, column: 2, @@ -143,13 +141,13 @@ describe RuboCop::Cop::Cask::StanzaOrder do end let(:expected_offenses) do [{ - message: "Cask/StanzaOrder: `sha256` stanza out of order", + message: "`sha256` stanza out of order", severity: :convention, line: 3, column: 2, source: "sha256 :no_check", }, { - message: "Cask/StanzaOrder: `on_arch_conditional` stanza out of order", + message: "`on_arch_conditional` stanza out of order", severity: :convention, line: 5, column: 2, @@ -185,13 +183,13 @@ describe RuboCop::Cop::Cask::StanzaOrder do end let(:expected_offenses) do [{ - message: "Cask/StanzaOrder: `on_arch_conditional` stanza out of order", + message: "`on_arch_conditional` stanza out of order", severity: :convention, line: 2, column: 2, source: 'folder = on_arch_conditional arm: "darwin-arm64", intel: "darwin"', }, { - message: "Cask/StanzaOrder: `arch` stanza out of order", + message: "`arch` stanza out of order", severity: :convention, line: 3, column: 2, @@ -231,26 +229,26 @@ describe RuboCop::Cop::Cask::StanzaOrder do end let(:expected_offenses) do [{ - message: "Cask/StanzaOrder: `url` stanza out of order", + message: "`url` stanza out of order", severity: :convention, line: 2, column: 2, source: "url 'https://foo.brew.sh/foo.zip'", }, { - message: "Cask/StanzaOrder: `uninstall` stanza out of order", + message: "`uninstall` stanza out of order", severity: :convention, line: 3, column: 2, source: "uninstall :quit => 'com.example.foo'," \ "\n :kext => 'com.example.foo.kext'", }, { - message: "Cask/StanzaOrder: `version` stanza out of order", + message: "`version` stanza out of order", severity: :convention, line: 5, column: 2, source: "version :latest", }, { - message: "Cask/StanzaOrder: `sha256` stanza out of order", + message: "`sha256` stanza out of order", severity: :convention, line: 7, column: 2, @@ -458,197 +456,108 @@ describe RuboCop::Cop::Cask::StanzaOrder do include_examples "autocorrects source" end - # TODO: detect out-of-order stanzas in nested expressions - context "when stanzas are nested in a conditional expression" do - let(:source) do - <<~CASK - cask 'foo' do - if true - sha256 :no_check - version :latest - end - end - CASK - end - - include_examples "does not report any offenses" - end - - context "when `on_arch` blocks are out of order" do + context "when `on_arch` blocks and their contents are out of order" do let(:source) do <<~CASK cask 'foo' do on_intel do url "https://foo.brew.sh/foo-intel.zip" - sha256 :no_check - version :latest - end + version :latest + sha256 :no_check + end on_arm do - url "https://foo.brew.sh/foo-arm.zip" - sha256 :no_check version :latest - end + sha256 :no_check - name "Foo" + url "https://foo.brew.sh/foo-arm.zip" + end end CASK end - let(:expected_offenses) do - [{ - message: "Cask/StanzaOrder: `on_intel` stanza out of order", - severity: :convention, - line: 2, - column: 2, - source: "on_intel do\n url \"https://foo.brew.sh/foo-intel.zip\"\n sha256 :no_check\n version :latest\n end", # rubocop:disable Layout/LineLength - }, { - message: "Cask/StanzaOrder: `on_arm` stanza out of order", - severity: :convention, - line: 8, - column: 2, - source: "on_arm do\n url \"https://foo.brew.sh/foo-arm.zip\"\n sha256 :no_check\n version :latest\n end", # rubocop:disable Layout/LineLength - }] - end - let(:correct_source) do <<~CASK cask 'foo' do on_arm do + version :latest + sha256 :no_check + url "https://foo.brew.sh/foo-arm.zip" - sha256 :no_check - version :latest end - on_intel do + version :latest + + sha256 :no_check url "https://foo.brew.sh/foo-intel.zip" - sha256 :no_check - version :latest end - - name "Foo" end CASK end - include_examples "reports offenses" include_examples "autocorrects source" end - # TODO: detect out-of-order stanzas in nested expressions - context "when the on_arch and on_os stanzas are nested" do - let(:source) do - <<~CASK - cask 'foo' do - on_arm do - url "https://foo.brew.sh/foo-arm-all.zip" - sha256 :no_check - version :latest - end - - on_intel do - on_ventura do - url "https://foo.brew.sh/foo-intel-ventura.zip" - sha256 :no_check - end - on_mojave do - url "https://foo.brew.sh/foo-intel-mojave.zip" - sha256 :no_check - end - on_catalina do - url "https://foo.brew.sh/foo-intel-catalina.zip" - sha256 :no_check - end - on_big_sur do - url "https://foo.brew.sh/foo-intel-big-sur.zip" - sha256 :no_check - end - - version :latest - end - - name "Foo" + it "registers an offense when `on_os` stanzas and their contents are out of order" do + expect_offense <<~CASK + cask "foo" do + on_ventura do + ^^^^^^^^^^^^^ `on_ventura` stanza out of order + sha256 "abc123" + ^^^^^^^^^^^^^^^ `sha256` stanza out of order + version :latest + ^^^^^^^^^^^^^^^ `version` stanza out of order + url "https://foo.brew.sh/foo-ventura.zip" end - CASK - end - - include_examples "does not report any offenses" - end - - context "when the on_os stanzas are out of order" do - let(:source) do - <<~CASK - cask "foo" do - on_ventura do - url "https://foo.brew.sh/foo-ventura.zip" - sha256 :no_check - end - on_catalina do - url "https://foo.brew.sh/foo-catalina.zip" - sha256 :no_check - end - on_mojave do - url "https://foo.brew.sh/foo-mojave.zip" - sha256 :no_check - end - on_big_sur do - url "https://foo.brew.sh/foo-big-sur.zip" - sha256 :no_check - end - - name "Foo" + on_catalina do + sha256 "def456" + ^^^^^^^^^^^^^^^ `sha256` stanza out of order + version "0.7" + ^^^^^^^^^^^^^ `version` stanza out of order + url "https://foo.brew.sh/foo-catalina.zip" end - CASK - end - - let(:expected_offenses) do - [{ - message: "Cask/StanzaOrder: `on_ventura` stanza out of order", - severity: :convention, - line: 2, - column: 2, - source: "on_ventura do\n url \"https://foo.brew.sh/foo-ventura.zip\"\n sha256 :no_check\n end", - }, { - message: "Cask/StanzaOrder: `on_mojave` stanza out of order", - severity: :convention, - line: 10, - column: 2, - source: "on_mojave do\n url \"https://foo.brew.sh/foo-mojave.zip\"\n sha256 :no_check\n end", - }, { - message: "Cask/StanzaOrder: `on_big_sur` stanza out of order", - severity: :convention, - line: 14, - column: 2, - source: "on_big_sur do\n url \"https://foo.brew.sh/foo-big-sur.zip\"\n sha256 :no_check\n end", - }] - end - - let(:correct_source) do - <<~CASK - cask "foo" do - on_mojave do - url "https://foo.brew.sh/foo-mojave.zip" - sha256 :no_check - end - on_catalina do - url "https://foo.brew.sh/foo-catalina.zip" - sha256 :no_check - end - on_big_sur do - url "https://foo.brew.sh/foo-big-sur.zip" - sha256 :no_check - end - on_ventura do - url "https://foo.brew.sh/foo-ventura.zip" - sha256 :no_check - end - - name "Foo" + on_mojave do + ^^^^^^^^^^^^ `on_mojave` stanza out of order + version :latest + sha256 "ghi789" + url "https://foo.brew.sh/foo-mojave.zip" end - CASK - end + on_big_sur do + ^^^^^^^^^^^^^ `on_big_sur` stanza out of order + sha256 "jkl012" + ^^^^^^^^^^^^^^^ `sha256` stanza out of order + version :latest + ^^^^^^^^^^^^^^^ `version` stanza out of order - include_examples "reports offenses" - include_examples "autocorrects source" + url "https://foo.brew.sh/foo-big-sur.zip" + end + end + CASK + + expect_correction <<~CASK + cask "foo" do + on_mojave do + version :latest + sha256 "ghi789" + url "https://foo.brew.sh/foo-mojave.zip" + end + on_catalina do + version "0.7" + sha256 "def456" + url "https://foo.brew.sh/foo-catalina.zip" + end + on_big_sur do + version :latest + sha256 "jkl012" + + url "https://foo.brew.sh/foo-big-sur.zip" + end + on_ventura do + version :latest + sha256 "abc123" + url "https://foo.brew.sh/foo-ventura.zip" + end + end + CASK end end