class RuboCop::Cop::Style::TrailingComma
]
2
1,
a = [
# good if EnforcedStyleForMultiline is no_comma
]
2,
1,
a = [
# good if EnforcedStyleForMultiline is comma
]
3,
1, 2,
b = [
a = [1, 2,]
# always bad
@example
This cop checks for trailing comma in parameter lists and literals.
def autocorrect(range)
def autocorrect(range) @corrections << lambda do |corrector| case range.source when ',' then corrector.remove(range) else corrector.insert_after(range, ',') end end end
def avoid_comma(kind, comma_begin_pos, sb, extra_info)
def avoid_comma(kind, comma_begin_pos, sb, extra_info) range = Parser::Source::Range.new(sb, comma_begin_pos, comma_begin_pos + 1) article = kind =~ /array/ ? 'an' : 'a' add_offense(range, range, format(MSG, 'Avoid', format(kind, article)) + "#{extra_info}.") end
def brackets?(node)
def brackets?(node) node.loc.end end
def check(node, items, kind, begin_pos, end_pos)
def check(node, items, kind, begin_pos, end_pos) sb = items.first.loc.expression.source_buffer after_last_item = Parser::Source::Range.new(sb, begin_pos, end_pos) return if heredoc?(after_last_item.source) comma_offset = after_last_item.source =~ /,/ should_have_comma = style == :comma && multiline?(node) if comma_offset unless should_have_comma extra_info = if style == :comma ', unless each item is on its own line' end avoid_comma(kind, after_last_item.begin_pos + comma_offset, sb, extra_info) end elsif should_have_comma put_comma(items, kind, sb) end end
def check_literal(node, kind)
def check_literal(node, kind) return if node.children.empty? # A braceless hash is the last parameter of a method call and will be # checked as such. return unless brackets?(node) check(node, node.children, kind, node.children.last.loc.expression.end_pos, node.loc.end.begin_pos) end
def heredoc?(source_after_last_item)
def heredoc?(source_after_last_item) source_after_last_item =~ /\w/ end
def multiline?(node)
on different lines, and each item within is on its own line, and the
Returns true if the round/square/curly brackets of the given node are
def multiline?(node) elements = if node.type == :send _receiver, _method_name, *args = *node args else node.children end items = [*elements.map { |e| e.loc.expression }, node.loc.end] items.each_cons(2) { |a, b| return false if on_same_line?(a, b) } true end
def on_array(node)
def on_array(node) check_literal(node, 'item of %s array') if square_brackets?(node) end
def on_hash(node)
def on_hash(node) check_literal(node, 'item of %s hash') end
def on_same_line?(a, b)
def on_same_line?(a, b) a.line + a.source.count("\n") == b.line end
def on_send(node)
def on_send(node) _receiver, _method_name, *args = *node return if args.empty? # It's impossible for a method call without parentheses to have # a trailing comma. return unless brackets?(node) check(node, args, 'parameter of %s method call', args.last.loc.expression.end_pos, node.loc.expression.end_pos) end
def parameter_name
def parameter_name 'EnforcedStyleForMultiline' end
def put_comma(items, kind, sb)
def put_comma(items, kind, sb) last_item = items.last return if last_item.type == :block_pass last_expr = last_item.loc.expression ix = last_expr.source.rindex("\n") || 0 ix += last_expr.source[ix..-1] =~ /\S/ range = Parser::Source::Range.new(sb, last_expr.begin_pos + ix, last_expr.end_pos) add_offense(range, range, format(MSG, 'Put a', format(kind, 'a multiline') + '.')) end