lib/rubocop/cop/metrics/perceived_complexity.rb



# encoding: utf-8

module RuboCop
  module Cop
    module Metrics
      # This cop tries to produce a complexity score that's a measure of the
      # complexity the reader experiences when looking at a method. For that
      # reason it considers `when` nodes as something that doesn't add as much
      # complexity as an `if` or a `&&`. Except if it's one of those special
      # `case`/`when` constructs where there's no expression after `case`. Then
      # the cop treats it as an `if`/`elsif`/`elsif`... and lets all the `when`
      # nodes count. In contrast to the CyclomaticComplexity cop, this cop
      # considers `else` nodes as adding complexity.
      #
      # @example
      #
      #   def my_method                   # 1
      #     if cond                       # 1
      #       case var                    # 2 (0.8 + 4 * 0.2, rounded)
      #       when 1 then func_one
      #       when 2 then func_two
      #       when 3 then func_three
      #       when 4..10 then func_other
      #       end
      #     else                          # 1
      #       do_something until a && b   # 2
      #     end                           # ===
      #   end                             # 7 complexity points
      class PerceivedComplexity < Cop
        include MethodComplexity
        include IfNode

        MSG = 'Perceived complexity for %s is too high. [%d/%d]'
        COUNTED_NODES = [:if, :case, :while, :until, :for, :rescue, :and, :or]

        private

        def complexity_score_for(node)
          case node.type
          when :case
            expression, *whens, _else = *node
            # If cond is nil, that means each when has an expression that
            # evaluates to true or false. It's just an alternative to
            # if/elsif/elsif... so the when nodes count.
            if expression.nil?
              whens.length
            else
              # Otherwise, the case node gets 0.8 complexity points and each
              # when gets 0.2.
              (0.8 + 0.2 * whens.length).round
            end
          when :if
            if_else?(node) && !elsif?(node) ? 2 : 1
          else
            1
          end
        end
      end
    end
  end
end