# encoding: utf-8 # rubocop:disable SymbolName module Rubocop module Cop module Style # Common functionality for checking surrounding space. module SurroundingSpace def space_between?(t1, t2) char_preceding_2nd_token = @processed_source[t2.pos.line - 1][t2.pos.column - 1] if char_preceding_2nd_token == '+' && t1.type != :tPLUS # Special case. A unary plus is not present in the tokens. char_preceding_2nd_token = @processed_source[t2.pos.line - 1][t2.pos.column - 2] end t2.pos.line > t1.pos.line || char_preceding_2nd_token == ' ' end def index_of_first_token(node) b = node.loc.expression.begin token_table[[b.line, b.column]] end def index_of_last_token(node) e = node.loc.expression.end (0...e.column).to_a.reverse.find do |c| ix = token_table[[e.line, c]] return ix if ix end end def token_table @token_table ||= begin table = {} @processed_source.tokens.each_with_index do |t, ix| table[[t.pos.line, t.pos.column]] = ix end table end end end # Checks that operators have space around them, except for ** # which should not have surrounding space. class SpaceAroundOperators < Cop include SurroundingSpace MSG_MISSING = "Surrounding space missing for operator '%s'." MSG_DETECTED = 'Space around operator ** detected.' BINARY_OPERATORS = [:tEQL, :tAMPER2, :tPIPE, :tCARET, :tPLUS, :tMINUS, :tSTAR2, :tDIVIDE, :tPERCENT, :tEH, :tCOLON, :tANDOP, :tOROP, :tMATCH, :tNMATCH, :tEQ, :tNEQ, :tGT, :tRSHFT, :tGEQ, :tLT, :tLSHFT, :tLEQ, :tASSOC, :tEQQ, :tCMP, :tOP_ASGN] def investigate(processed_source) return unless processed_source.ast @processed_source = processed_source tokens = processed_source.tokens tokens.each_cons(3) do |token_before, token, token_after| next if token_before.type == :kDEF # TODO: remove? next if token_before.type == :tDOT # Called as method. next if positions_not_to_check.include?(token.pos) case token.type when :tPOW if has_space?(token_before, token, token_after) convention(nil, token.pos, MSG_DETECTED) end when *BINARY_OPERATORS check_missing_space(token_before, token, token_after) end end end # Returns an array of positions marking the tokens that this cop # should not check, either because the token is not an operator # or because another cop does the check. def positions_not_to_check @positions_not_to_check ||= begin positions = [] positions.concat(do_not_check_block_arg_pipes) positions.concat(do_not_check_param_default) positions.concat(do_not_check_class_lshift_self) positions.concat(do_not_check_def_things) positions.concat(do_not_check_singleton_operator_defs) positions end end def do_not_check_block_arg_pipes # each { |a| } # ^ ^ positions = [] on_node(:block, @processed_source.ast) do |b| on_node(:args, b) do |a| positions << a.loc.begin << a.loc.end if a.loc.begin end end positions end def do_not_check_param_default # func(a, b=nil) # ^ positions = [] tokens = @processed_source.tokens on_node(:optarg, @processed_source.ast) do |optarg| _arg, equals, _value = tokens[index_of_first_token(optarg), 3] positions << equals.pos end positions end def do_not_check_class_lshift_self # class <