lib/fasterer/scanners/method_definition_scanner.rb
require 'fasterer/method_definition'
require 'fasterer/method_call'
require 'fasterer/offense'
require 'fasterer/scanners/offensive'
module Fasterer
class MethodDefinitionScanner
include Fasterer::Offensive
attr_reader :element
def initialize(element)
@element = element
check_offense
end
private
def check_offense
if method_definition.has_block?
scan_block_call_offense
else
scan_getter_and_setter_offense
end
end
def scan_block_call_offense
traverse_tree(method_definition.body) do |element|
next unless element.sexp_type == :call
method_call = MethodCall.new(element)
if method_call.receiver.is_a?(Fasterer::VariableReference) &&
method_call.receiver.name == method_definition.block_argument_name &&
method_call.method_name == :call
add_offense(:proc_call_vs_yield) && return
end
end
end
def method_definition
@method_definition ||= MethodDefinition.new(element)
end
def traverse_tree(sexp_tree, &block)
sexp_tree.each do |element|
next unless element.is_a?(Array)
yield element
traverse_tree(element, &block)
end
end
def scan_getter_and_setter_offense
method_definition.setter? ? scan_setter_offense : scan_getter_offense
end
def scan_setter_offense
return if method_definition.arguments.size != 1
return if method_definition.body.size != 1
first_argument = method_definition.arguments.first
return if first_argument.type != :regular_argument
if method_definition.body.first.sexp_type == :iasgn &&
method_definition.body.first[1].to_s == "@#{method_definition.name.to_s[0..-2]}" &&
method_definition.body.first[2][1] == first_argument.name
add_offense(:setter_vs_attr_writer)
end
end
def scan_getter_offense
return if method_definition.arguments.size > 0
return if method_definition.body.size != 1
if method_definition.body.first.sexp_type == :ivar &&
method_definition.body.first[1].to_s == "@#{method_definition.name}"
add_offense(:getter_vs_attr_reader)
end
end
end
end