lib/rubocop/cop/sorbet/signatures/allow_incompatible_override.rb
# frozen_string_literal: true require "rubocop" module RuboCop module Cop module Sorbet # Disallows using `.override(allow_incompatible: true)`. # Using `allow_incompatible` suggests a violation of the Liskov # Substitution Principle, meaning that a subclass is not a valid # subtype of its superclass. This Cop prevents these design smells # from occurring. # # @example # # # bad # sig.override(allow_incompatible: true) # # # good # sig.override class AllowIncompatibleOverride < RuboCop::Cop::Base MSG = "Usage of `allow_incompatible` suggests a violation of the Liskov Substitution Principle. " \ "Instead, strive to write interfaces which respect subtyping principles and remove `allow_incompatible`" RESTRICT_ON_SEND = [:override].freeze # @!method sig_dot_override?(node) def_node_matcher(:sig_dot_override?, <<~PATTERN) (send [!nil? #sig?] :override (hash <$(pair (sym :allow_incompatible) true) ...>) ) PATTERN # @!method sig?(node) def_node_search(:sig?, <<~PATTERN) (send _ :sig ...) PATTERN # @!method override?(node) def_node_matcher(:override?, <<~PATTERN) (send _ :override (hash <$(pair (sym :allow_incompatible) true) ...>) ) PATTERN def on_send(node) sig_dot_override?(node) do |allow_incompatible_pair| add_offense(allow_incompatible_pair) end end def on_block(node) return unless sig?(node.send_node) block = node.children.last return unless block&.send_type? receiver = block.receiver while receiver allow_incompatible_pair = override?(receiver) if allow_incompatible_pair add_offense(allow_incompatible_pair) break end receiver = receiver.receiver end end alias_method :on_numblock, :on_block end end end end