lib/rubocop/cop/rspec/described_class.rb in rubocop-rspec-1.5.3 vs lib/rubocop/cop/rspec/described_class.rb in rubocop-rspec-1.6.0

- old
+ new

@@ -18,44 +18,97 @@ # subject { described_class.do_something } # end class DescribedClass < Cop include RuboCop::RSpec::TopLevelDescribe - MESSAGE = 'Use `described_class` instead of `%s`'.freeze + DESCRIBED_CLASS = 'described_class'.freeze + MSG = "Use `#{DESCRIBED_CLASS}` instead of `%s`".freeze - def on_block(node) - method, _args, body = *node - return unless top_level_describe?(method) + RSPEC_BLOCK_METHODS = ' + :after + :around + :before + :context + :describe + :example + :example_group + :fcontext + :fdescribe + :feature + :fexample + :ffeature + :fit + :focus + :fscenario + :fspecify + :it + :let + :let! + :scenario + :specify + :xcontext + :xdescribe + :xexample + :xfeature + :xit + :xscenario + :xspecify + '.freeze - _receiver, _method_name, object = *method - return unless object && object.type.equal?(:const) + def_node_matcher :described_constant, <<-PATTERN + (block $(send _ :describe $(const ...)) (args) $_) + PATTERN - inspect_children(body, object) + def_node_matcher :common_instance_exec_closure?, <<-PATTERN + (block (send (const nil {:Class :Module}) :new ...) ...) + PATTERN + + def_node_matcher :rspec_block?, <<-PATTERN + (block (send nil {#{RSPEC_BLOCK_METHODS}} ...) ...) + PATTERN + + def_node_matcher :scope_changing_syntax?, '{def class module}' + + def on_block(node) + describe, described_class, body = described_constant(node) + return unless top_level_describe?(describe) + + find_constant_usage(body, described_class) do |match| + add_offense(match, :expression, format(MSG, match.const_name)) + end end def autocorrect(node) lambda do |corrector| - corrector.replace(node.loc.expression, 'described_class') + corrector.replace(node.loc.expression, DESCRIBED_CLASS) end end private - def inspect_children(node, object) + def find_constant_usage(node, described_class, &block) + yield(node) if node.eql?(described_class) + return unless node.instance_of?(Node) - return if scope_change?(node) || node.type.equal?(:const) + return if scope_change?(node) || node.const_type? node.children.each do |child| - if child.eql?(object) - name = object.loc.expression.source - add_offense(child, :expression, format(MESSAGE, name)) - end - inspect_children(child, object) + find_constant_usage(child, described_class, &block) end end def scope_change?(node) - [:def, :class, :module].include?(node.type) + scope_changing_syntax?(node) || + common_instance_exec_closure?(node) || + skippable_block?(node) + end + + def skippable_block?(node) + node.block_type? && !rspec_block?(node) && skip_blocks? + end + + def skip_blocks? + cop_config['SkipBlocks'].equal?(true) end end end end end