Merge pull request #15223 from issyl0/rubocop-cask-stanza-order-in-on-blocks
rubocops/cask: Check for correct stanza order within `on_*` blocks
This commit is contained in:
		
						commit
						c0f8068573
					
				@ -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
 | 
			
		||||
 | 
			
		||||
@ -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|
 | 
			
		||||
 | 
			
		||||
@ -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?
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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 = "`%<stanza>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
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user