rubocop 0.31.0 → 0.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +315 -0
- data/README.md +199 -38
- data/config/default.yml +91 -12
- data/config/disabled.yml +45 -4
- data/config/enabled.yml +107 -9
- data/lib/rubocop/ast_node.rb +48 -0
- data/lib/rubocop/cli.rb +11 -1
- data/lib/rubocop/comment_config.rb +4 -1
- data/lib/rubocop/config.rb +26 -17
- data/lib/rubocop/config_loader.rb +61 -14
- data/lib/rubocop/cop/commissioner.rb +7 -12
- data/lib/rubocop/cop/cop.rb +43 -20
- data/lib/rubocop/cop/lint/block_alignment.rb +1 -1
- data/lib/rubocop/cop/lint/circular_argument_reference.rb +69 -0
- data/lib/rubocop/cop/lint/debugger.rb +9 -48
- data/lib/rubocop/cop/lint/def_end_alignment.rb +8 -4
- data/lib/rubocop/cop/lint/deprecated_class_methods.rb +42 -23
- data/lib/rubocop/cop/lint/duplicate_methods.rb +2 -2
- data/lib/rubocop/cop/lint/duplicated_key.rb +37 -0
- data/lib/rubocop/cop/lint/end_alignment.rb +33 -13
- data/lib/rubocop/cop/lint/eval.rb +6 -2
- data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +175 -0
- data/lib/rubocop/cop/lint/literal_in_condition.rb +0 -5
- data/lib/rubocop/cop/lint/literal_in_interpolation.rb +10 -0
- data/lib/rubocop/cop/lint/nested_method_definition.rb +31 -0
- data/lib/rubocop/cop/lint/non_local_exit_from_iterator.rb +19 -1
- data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +1 -1
- data/lib/rubocop/cop/lint/space_before_first_arg.rb +1 -1
- data/lib/rubocop/cop/lint/unneeded_disable.rb +72 -0
- data/lib/rubocop/cop/lint/unused_block_argument.rb +6 -0
- data/lib/rubocop/cop/lint/unused_method_argument.rb +8 -0
- data/lib/rubocop/cop/metrics/abc_size.rb +17 -6
- data/lib/rubocop/cop/metrics/class_length.rb +1 -1
- data/lib/rubocop/cop/metrics/method_length.rb +1 -3
- data/lib/rubocop/cop/metrics/module_length.rb +1 -1
- data/lib/rubocop/cop/metrics/parameter_lists.rb +1 -1
- data/lib/rubocop/cop/mixin/access_modifier_node.rb +1 -1
- data/lib/rubocop/cop/mixin/annotation_comment.rb +1 -2
- data/lib/rubocop/cop/mixin/autocorrect_alignment.rb +28 -4
- data/lib/rubocop/cop/mixin/autocorrect_unless_changing_ast.rb +26 -3
- data/lib/rubocop/cop/mixin/check_assignment.rb +2 -3
- data/lib/rubocop/cop/mixin/configurable_enforced_style.rb +59 -12
- data/lib/rubocop/cop/mixin/configurable_max.rb +1 -1
- data/lib/rubocop/cop/mixin/configurable_naming.rb +14 -3
- data/lib/rubocop/cop/mixin/empty_lines_around_body.rb +1 -3
- data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +10 -1
- data/lib/rubocop/cop/mixin/first_element_line_break.rb +41 -0
- data/lib/rubocop/cop/mixin/if_node.rb +10 -0
- data/lib/rubocop/cop/mixin/method_preference.rb +28 -0
- data/lib/rubocop/cop/mixin/negative_conditional.rb +1 -1
- data/lib/rubocop/cop/mixin/on_method_def.rb +4 -5
- data/lib/rubocop/cop/mixin/safe_assignment.rb +3 -14
- data/lib/rubocop/cop/mixin/space_after_punctuation.rb +8 -1
- data/lib/rubocop/cop/mixin/space_before_punctuation.rb +8 -1
- data/lib/rubocop/cop/mixin/statement_modifier.rb +4 -7
- data/lib/rubocop/cop/mixin/string_help.rb +1 -1
- data/lib/rubocop/cop/mixin/string_literals_help.rb +1 -1
- data/lib/rubocop/cop/mixin/surrounding_space.rb +5 -4
- data/lib/rubocop/cop/offense.rb +16 -3
- data/lib/rubocop/cop/performance/case_when_splat.rb +160 -0
- data/lib/rubocop/cop/performance/count.rb +35 -30
- data/lib/rubocop/cop/performance/detect.rb +16 -3
- data/lib/rubocop/cop/performance/fixed_size.rb +50 -0
- data/lib/rubocop/cop/performance/flat_map.rb +3 -3
- data/lib/rubocop/cop/performance/sample.rb +103 -59
- data/lib/rubocop/cop/performance/size.rb +2 -1
- data/lib/rubocop/cop/performance/string_replacement.rb +187 -0
- data/lib/rubocop/cop/rails/action_filter.rb +31 -5
- data/lib/rubocop/cop/rails/date.rb +15 -14
- data/lib/rubocop/cop/rails/pluralization_grammar.rb +97 -0
- data/lib/rubocop/cop/rails/read_write_attribute.rb +1 -1
- data/lib/rubocop/cop/rails/time_zone.rb +46 -18
- data/lib/rubocop/cop/style/alias.rb +1 -0
- data/lib/rubocop/cop/style/align_hash.rb +8 -15
- data/lib/rubocop/cop/style/align_parameters.rb +19 -7
- data/lib/rubocop/cop/style/and_or.rb +42 -13
- data/lib/rubocop/cop/style/auto_resource_cleanup.rb +2 -1
- data/lib/rubocop/cop/style/block_comments.rb +4 -2
- data/lib/rubocop/cop/style/block_delimiters.rb +69 -24
- data/lib/rubocop/cop/style/braces_around_hash_parameters.rb +40 -12
- data/lib/rubocop/cop/style/case_indentation.rb +18 -4
- data/lib/rubocop/cop/style/collection_methods.rb +2 -20
- data/lib/rubocop/cop/style/command_literal.rb +2 -10
- data/lib/rubocop/cop/style/comment_annotation.rb +29 -8
- data/lib/rubocop/cop/style/copyright.rb +5 -3
- data/lib/rubocop/cop/style/documentation.rb +21 -12
- data/lib/rubocop/cop/style/dot_position.rb +6 -0
- data/lib/rubocop/cop/style/double_negation.rb +4 -15
- data/lib/rubocop/cop/style/each_with_object.rb +17 -4
- data/lib/rubocop/cop/style/else_alignment.rb +2 -1
- data/lib/rubocop/cop/style/empty_else.rb +25 -0
- data/lib/rubocop/cop/style/empty_line_between_defs.rb +39 -14
- data/lib/rubocop/cop/style/encoding.rb +10 -4
- data/lib/rubocop/cop/style/extra_spacing.rb +126 -5
- data/lib/rubocop/cop/style/first_array_element_line_break.rb +41 -0
- data/lib/rubocop/cop/style/first_hash_element_line_break.rb +35 -0
- data/lib/rubocop/cop/style/first_method_argument_line_break.rb +37 -0
- data/lib/rubocop/cop/style/first_method_parameter_line_break.rb +42 -0
- data/lib/rubocop/cop/style/first_parameter_indentation.rb +5 -3
- data/lib/rubocop/cop/style/for.rb +2 -1
- data/lib/rubocop/cop/style/hash_syntax.rb +5 -0
- data/lib/rubocop/cop/style/if_unless_modifier.rb +32 -5
- data/lib/rubocop/cop/style/indent_hash.rb +67 -37
- data/lib/rubocop/cop/style/indentation_width.rb +36 -10
- data/lib/rubocop/cop/style/initial_indentation.rb +37 -0
- data/lib/rubocop/cop/style/leading_comment_space.rb +3 -2
- data/lib/rubocop/cop/style/method_call_parentheses.rb +28 -1
- data/lib/rubocop/cop/style/method_def_parentheses.rb +10 -7
- data/lib/rubocop/cop/style/multiline_operation_indentation.rb +21 -24
- data/lib/rubocop/cop/style/mutable_constant.rb +35 -0
- data/lib/rubocop/cop/style/nested_modifier.rb +97 -0
- data/lib/rubocop/cop/style/next.rb +50 -15
- data/lib/rubocop/cop/style/non_nil_check.rb +12 -8
- data/lib/rubocop/cop/style/one_line_conditional.rb +8 -4
- data/lib/rubocop/cop/style/option_hash.rb +64 -0
- data/lib/rubocop/cop/style/optional_arguments.rb +49 -0
- data/lib/rubocop/cop/style/parallel_assignment.rb +218 -0
- data/lib/rubocop/cop/style/percent_literal_delimiters.rb +3 -66
- data/lib/rubocop/cop/style/predicate_name.rb +7 -2
- data/lib/rubocop/cop/style/redundant_begin.rb +2 -13
- data/lib/rubocop/cop/style/redundant_freeze.rb +37 -0
- data/lib/rubocop/cop/style/redundant_return.rb +32 -3
- data/lib/rubocop/cop/style/regexp_literal.rb +2 -10
- data/lib/rubocop/cop/style/rescue_ensure_alignment.rb +81 -0
- data/lib/rubocop/cop/style/rescue_modifier.rb +30 -22
- data/lib/rubocop/cop/style/send.rb +18 -0
- data/lib/rubocop/cop/style/signal_exception.rb +24 -11
- data/lib/rubocop/cop/style/single_line_methods.rb +8 -9
- data/lib/rubocop/cop/style/single_space_before_first_arg.rb +1 -1
- data/lib/rubocop/cop/style/space_around_operators.rb +2 -0
- data/lib/rubocop/cop/style/space_inside_string_interpolation.rb +61 -0
- data/lib/rubocop/cop/style/special_global_vars.rb +4 -2
- data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +108 -0
- data/lib/rubocop/cop/style/string_methods.rb +32 -0
- data/lib/rubocop/cop/style/struct_inheritance.rb +11 -10
- data/lib/rubocop/cop/style/symbol_literal.rb +1 -1
- data/lib/rubocop/cop/style/symbol_proc.rb +62 -13
- data/lib/rubocop/cop/style/trailing_blank_lines.rb +9 -1
- data/lib/rubocop/cop/style/trailing_comma.rb +17 -7
- data/lib/rubocop/cop/style/trailing_underscore_variable.rb +23 -2
- data/lib/rubocop/cop/style/trivial_accessors.rb +10 -1
- data/lib/rubocop/cop/style/unneeded_percent_q.rb +31 -20
- data/lib/rubocop/cop/style/variable_name.rb +5 -0
- data/lib/rubocop/cop/style/while_until_do.rb +1 -1
- data/lib/rubocop/cop/style/word_array.rb +15 -2
- data/lib/rubocop/cop/team.rb +25 -5
- data/lib/rubocop/cop/util.rb +7 -2
- data/lib/rubocop/cop/variable_force/locatable.rb +6 -6
- data/lib/rubocop/cop/variable_force.rb +10 -10
- data/lib/rubocop/formatter/base_formatter.rb +1 -1
- data/lib/rubocop/formatter/disabled_config_formatter.rb +70 -8
- data/lib/rubocop/formatter/formatter_set.rb +27 -1
- data/lib/rubocop/formatter/progress_formatter.rb +10 -2
- data/lib/rubocop/formatter/simple_text_formatter.rb +1 -1
- data/lib/rubocop/node_pattern.rb +390 -0
- data/lib/rubocop/options.rb +148 -81
- data/lib/rubocop/processed_source.rb +7 -2
- data/lib/rubocop/rake_task.rb +1 -1
- data/lib/rubocop/remote_config.rb +60 -0
- data/lib/rubocop/result_cache.rb +123 -0
- data/lib/rubocop/runner.rb +85 -22
- data/lib/rubocop/target_finder.rb +4 -4
- data/lib/rubocop/token.rb +2 -1
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop/warning.rb +11 -0
- data/lib/rubocop.rb +32 -3
- data/relnotes/v0.32.0.md +139 -0
- data/relnotes/v0.32.1.md +122 -0
- data/relnotes/v0.33.0.md +157 -0
- data/relnotes/v0.34.0.md +182 -0
- data/relnotes/v0.34.1.md +129 -0
- data/relnotes/v0.34.2.md +139 -0
- data/relnotes/v0.35.0.md +210 -0
- data/rubocop.gemspec +4 -4
- metadata +50 -12
- data/lib/rubocop/cop/performance/parallel_assignment.rb +0 -79
|
@@ -6,24 +6,43 @@ module RuboCop
|
|
|
6
6
|
# This cop checks whether method definitions are
|
|
7
7
|
# separated by empty lines.
|
|
8
8
|
class EmptyLineBetweenDefs < Cop
|
|
9
|
-
|
|
9
|
+
include OnMethodDef
|
|
10
|
+
MSG = 'Use empty lines between method definitions.'
|
|
10
11
|
|
|
11
|
-
def
|
|
12
|
-
|
|
13
|
-
unless @prev_was_single_line && singe_line_def?(node) &&
|
|
14
|
-
cop_config['AllowAdjacentOneLineDefs']
|
|
15
|
-
add_offense(node, :keyword)
|
|
16
|
-
end
|
|
17
|
-
end
|
|
12
|
+
def on_method_def(node, _method_name, _args, _body)
|
|
13
|
+
return unless node.parent && node.parent.begin_type?
|
|
18
14
|
|
|
19
|
-
|
|
20
|
-
|
|
15
|
+
nodes = [prev_node(node), node]
|
|
16
|
+
|
|
17
|
+
return unless nodes.all?(&method(:def_node?))
|
|
18
|
+
return if blank_lines_between?(*nodes)
|
|
19
|
+
return if nodes.all?(&:single_line?) &&
|
|
20
|
+
cop_config['AllowAdjacentOneLineDefs']
|
|
21
|
+
|
|
22
|
+
add_offense(node, :keyword)
|
|
21
23
|
end
|
|
22
24
|
|
|
23
25
|
private
|
|
24
26
|
|
|
25
|
-
def
|
|
26
|
-
|
|
27
|
+
def def_node?(node)
|
|
28
|
+
return unless node
|
|
29
|
+
node.def_type? || node.defs_type?
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def blank_lines_between?(first_def_node, second_def_node)
|
|
33
|
+
lines_between_defs(first_def_node, second_def_node).any?(&:blank?)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def prev_node(node)
|
|
37
|
+
return nil unless node.sibling_index > 0
|
|
38
|
+
|
|
39
|
+
node.parent.children[node.sibling_index - 1]
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def lines_between_defs(first_def_node, second_def_node)
|
|
43
|
+
line_range = def_end(first_def_node)..(def_start(second_def_node) - 2)
|
|
44
|
+
|
|
45
|
+
processed_source.lines[line_range]
|
|
27
46
|
end
|
|
28
47
|
|
|
29
48
|
def def_start(node)
|
|
@@ -35,8 +54,14 @@ module RuboCop
|
|
|
35
54
|
end
|
|
36
55
|
|
|
37
56
|
def autocorrect(node)
|
|
38
|
-
|
|
39
|
-
|
|
57
|
+
prev_def = prev_node(node)
|
|
58
|
+
end_pos = prev_def.loc.end.end_pos
|
|
59
|
+
source_buffer = prev_def.loc.end.source_buffer
|
|
60
|
+
newline_pos = source_buffer.source.index("\n", end_pos)
|
|
61
|
+
newline = Parser::Source::Range.new(source_buffer,
|
|
62
|
+
newline_pos,
|
|
63
|
+
newline_pos + 1)
|
|
64
|
+
->(corrector) { corrector.insert_after(newline, "\n") }
|
|
40
65
|
end
|
|
41
66
|
end
|
|
42
67
|
end
|
|
@@ -16,7 +16,9 @@ module RuboCop
|
|
|
16
16
|
|
|
17
17
|
MSG_MISSING = 'Missing utf-8 encoding comment.'
|
|
18
18
|
MSG_UNNECESSARY = 'Unnecessary utf-8 encoding comment.'
|
|
19
|
-
ENCODING_PATTERN = /#.*coding\s?[:=]\s?(?:UTF|utf)-8
|
|
19
|
+
ENCODING_PATTERN = /#.*coding\s?[:=]\s?(?:UTF|utf)-8/.freeze
|
|
20
|
+
AUTO_CORRECT_ENCODING_COMMENT = 'AutoCorrectEncodingComment'.freeze
|
|
21
|
+
SHEBANG = '#!'.freeze
|
|
20
22
|
|
|
21
23
|
def investigate(processed_source)
|
|
22
24
|
return if processed_source.buffer.source.empty?
|
|
@@ -31,10 +33,14 @@ module RuboCop
|
|
|
31
33
|
end
|
|
32
34
|
|
|
33
35
|
def autocorrect(node)
|
|
34
|
-
encoding = cop_config[
|
|
36
|
+
encoding = cop_config[AUTO_CORRECT_ENCODING_COMMENT]
|
|
35
37
|
if encoding && encoding =~ ENCODING_PATTERN
|
|
36
38
|
lambda do |corrector|
|
|
37
|
-
|
|
39
|
+
if encoding_line_number(processed_source) == 0
|
|
40
|
+
corrector.insert_before(node.pos, "#{encoding}\n")
|
|
41
|
+
else
|
|
42
|
+
corrector.insert_after(node.pos, "\n#{encoding}")
|
|
43
|
+
end
|
|
38
44
|
end
|
|
39
45
|
else
|
|
40
46
|
fail "#{encoding} does not match #{ENCODING_PATTERN}"
|
|
@@ -58,7 +64,7 @@ module RuboCop
|
|
|
58
64
|
|
|
59
65
|
def encoding_line_number(processed_source)
|
|
60
66
|
line_number = 0
|
|
61
|
-
line_number += 1 if processed_source[line_number].start_with?(
|
|
67
|
+
line_number += 1 if processed_source[line_number].start_with?(SHEBANG)
|
|
62
68
|
line_number
|
|
63
69
|
end
|
|
64
70
|
end
|
|
@@ -7,19 +7,36 @@ module RuboCop
|
|
|
7
7
|
#
|
|
8
8
|
# @example
|
|
9
9
|
#
|
|
10
|
-
#
|
|
10
|
+
# # good if AllowForAlignment is true
|
|
11
|
+
# name = "RuboCop"
|
|
12
|
+
# # Some comment and an empty line
|
|
13
|
+
#
|
|
14
|
+
# website += "/bbatsov/rubocop" unless cond
|
|
15
|
+
# puts "rubocop" if debug
|
|
16
|
+
#
|
|
17
|
+
# # bad for any configuration
|
|
18
|
+
# set_app("RuboCop")
|
|
11
19
|
# website = "https://github.com/bbatsov/rubocop"
|
|
12
20
|
class ExtraSpacing < Cop
|
|
13
21
|
MSG = 'Unnecessary spacing detected.'
|
|
14
22
|
|
|
15
23
|
def investigate(processed_source)
|
|
24
|
+
ast = processed_source.ast
|
|
25
|
+
|
|
16
26
|
processed_source.tokens.each_cons(2) do |t1, t2|
|
|
17
|
-
next
|
|
18
|
-
next
|
|
19
|
-
|
|
27
|
+
next if t2.type == :tNL
|
|
28
|
+
next if t1.pos.line != t2.pos.line
|
|
29
|
+
next if t2.pos.begin_pos - 1 <= t1.pos.end_pos
|
|
30
|
+
next if allow_for_alignment? && aligned_with_something?(t2)
|
|
20
31
|
start_pos = t1.pos.end_pos
|
|
32
|
+
next if ignored_ranges(ast).find { |r| r.include?(start_pos) }
|
|
33
|
+
|
|
21
34
|
end_pos = t2.pos.begin_pos - 1
|
|
22
|
-
range = Parser::Source::Range.new(buffer,
|
|
35
|
+
range = Parser::Source::Range.new(processed_source.buffer,
|
|
36
|
+
start_pos, end_pos)
|
|
37
|
+
# Unary + doesn't appear as a token and needs special handling.
|
|
38
|
+
next if unary_plus_non_offense?(range)
|
|
39
|
+
|
|
23
40
|
add_offense(range, range, MSG)
|
|
24
41
|
end
|
|
25
42
|
end
|
|
@@ -27,6 +44,110 @@ module RuboCop
|
|
|
27
44
|
def autocorrect(range)
|
|
28
45
|
->(corrector) { corrector.remove(range) }
|
|
29
46
|
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
|
|
50
|
+
def unary_plus_non_offense?(range)
|
|
51
|
+
range.resize(range.size + 1).source =~ /^ ?\+$/
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Returns an array of ranges that should not be reported. It's the
|
|
55
|
+
# extra spaces between the keys and values in a hash, since those are
|
|
56
|
+
# handled by the Style/AlignHash cop.
|
|
57
|
+
def ignored_ranges(ast)
|
|
58
|
+
return [] unless ast
|
|
59
|
+
|
|
60
|
+
@ignored_ranges ||= begin
|
|
61
|
+
ranges = []
|
|
62
|
+
on_node(:pair, ast) do |pair|
|
|
63
|
+
key, value = *pair
|
|
64
|
+
r = key.loc.expression.end_pos...value.loc.expression.begin_pos
|
|
65
|
+
ranges << r
|
|
66
|
+
end
|
|
67
|
+
ranges
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def allow_for_alignment?
|
|
72
|
+
cop_config['AllowForAlignment']
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def aligned_with_something?(token)
|
|
76
|
+
return aligned_comments?(token) if token.type == :tCOMMENT
|
|
77
|
+
|
|
78
|
+
pre = (token.pos.line - 2).downto(0)
|
|
79
|
+
post = token.pos.line.upto(processed_source.lines.size - 1)
|
|
80
|
+
return true if aligned_with?(pre, token) || aligned_with?(post, token)
|
|
81
|
+
|
|
82
|
+
# If no aligned token was found, search for an aligned token on the
|
|
83
|
+
# nearest line with the same indentation as the checked line.
|
|
84
|
+
base_indentation = processed_source.lines[token.pos.line - 1] =~ /\S/
|
|
85
|
+
aligned_with?(pre, token, base_indentation) ||
|
|
86
|
+
aligned_with?(post, token, base_indentation)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def aligned_comments?(token)
|
|
90
|
+
ix = processed_source.comments.index do |c|
|
|
91
|
+
c.loc.expression.begin_pos == token.pos.begin_pos
|
|
92
|
+
end
|
|
93
|
+
aligned_with_previous_comment?(ix) || aligned_with_next_comment?(ix)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def aligned_with_previous_comment?(ix)
|
|
97
|
+
ix > 0 && comment_column(ix - 1) == comment_column(ix)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def aligned_with_next_comment?(ix)
|
|
101
|
+
ix < processed_source.comments.length - 1 &&
|
|
102
|
+
comment_column(ix + 1) == comment_column(ix)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def comment_column(ix)
|
|
106
|
+
processed_source.comments[ix].loc.column
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Returns true if the previous or next line, not counting empty or
|
|
110
|
+
# comment lines, contains a token that's aligned with the given
|
|
111
|
+
# token. If base_indentation is given, lines with different indentation
|
|
112
|
+
# than the base indentation are also skipped.
|
|
113
|
+
def aligned_with?(indices_to_check, token, base_indentation = nil)
|
|
114
|
+
indices_to_check.each do |ix|
|
|
115
|
+
next if comment_lines.include?(ix + 1)
|
|
116
|
+
line = processed_source.lines[ix]
|
|
117
|
+
next if line.strip.empty?
|
|
118
|
+
if base_indentation
|
|
119
|
+
indentation = line =~ /\S/
|
|
120
|
+
next if indentation != base_indentation
|
|
121
|
+
end
|
|
122
|
+
return (aligned_words?(token, line) ||
|
|
123
|
+
aligned_assignments?(token, line) ||
|
|
124
|
+
aligned_same_character?(token, line))
|
|
125
|
+
end
|
|
126
|
+
false # No line to check was found.
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def comment_lines
|
|
130
|
+
@comment_lines ||=
|
|
131
|
+
begin
|
|
132
|
+
whole_line_comments = processed_source.comments.select do |c|
|
|
133
|
+
begins_its_line?(c.loc.expression)
|
|
134
|
+
end
|
|
135
|
+
whole_line_comments.map { |c| c.loc.line }
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def aligned_words?(token, line)
|
|
140
|
+
line[token.pos.column - 1, 2] =~ /\s\S/
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def aligned_assignments?(token, line)
|
|
144
|
+
token.type == :tOP_ASGN &&
|
|
145
|
+
line[token.pos.column + token.text.length] == '='
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def aligned_same_character?(token, line)
|
|
149
|
+
line[token.pos.column] == token.text.to_s[0]
|
|
150
|
+
end
|
|
30
151
|
end
|
|
31
152
|
end
|
|
32
153
|
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Style
|
|
6
|
+
# This cop checks for a line break before the first element in a
|
|
7
|
+
# multi-line array.
|
|
8
|
+
#
|
|
9
|
+
# @example
|
|
10
|
+
#
|
|
11
|
+
# # bad
|
|
12
|
+
# [ :a,
|
|
13
|
+
# :b]
|
|
14
|
+
#
|
|
15
|
+
# # good
|
|
16
|
+
# [
|
|
17
|
+
# :a,
|
|
18
|
+
# :b]
|
|
19
|
+
#
|
|
20
|
+
class FirstArrayElementLineBreak < Cop
|
|
21
|
+
include FirstElementLineBreak
|
|
22
|
+
|
|
23
|
+
MSG = 'Add a line break before the first element of a ' \
|
|
24
|
+
'multi-line array.'
|
|
25
|
+
|
|
26
|
+
def on_array(node)
|
|
27
|
+
return if !node.loc.begin && !assignment_on_same_line?(node)
|
|
28
|
+
|
|
29
|
+
check_children_line_break(node, node.children)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def assignment_on_same_line?(node)
|
|
35
|
+
source = node.loc.expression.source_line[0...node.loc.column]
|
|
36
|
+
source =~ /\s*\=\s*$/
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Style
|
|
6
|
+
# This cop checks for a line break before the first element in a
|
|
7
|
+
# multi-line hash.
|
|
8
|
+
#
|
|
9
|
+
# @example
|
|
10
|
+
#
|
|
11
|
+
# # bad
|
|
12
|
+
# { a: 1,
|
|
13
|
+
# b: 2}
|
|
14
|
+
#
|
|
15
|
+
# # good
|
|
16
|
+
# {
|
|
17
|
+
# a: 1,
|
|
18
|
+
# b: 2 }
|
|
19
|
+
class FirstHashElementLineBreak < Cop
|
|
20
|
+
include FirstElementLineBreak
|
|
21
|
+
|
|
22
|
+
MSG = 'Add a line break before the first element of a ' \
|
|
23
|
+
'multi-line hash.'
|
|
24
|
+
|
|
25
|
+
def on_hash(node)
|
|
26
|
+
if node.loc.begin
|
|
27
|
+
check_children_line_break(node, node.children)
|
|
28
|
+
elsif method_uses_parens?(node.parent, node)
|
|
29
|
+
check_children_line_break(node, node.children, node.parent)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Style
|
|
6
|
+
# This cop checks for a line break before the first argument in a
|
|
7
|
+
# multi-line method call.
|
|
8
|
+
#
|
|
9
|
+
# @example
|
|
10
|
+
#
|
|
11
|
+
# # bad
|
|
12
|
+
# method(foo, bar,
|
|
13
|
+
# baz)
|
|
14
|
+
#
|
|
15
|
+
# # good
|
|
16
|
+
# method(
|
|
17
|
+
# foo, bar,
|
|
18
|
+
# baz)
|
|
19
|
+
#
|
|
20
|
+
# # ignored
|
|
21
|
+
# method foo, bar,
|
|
22
|
+
# baz
|
|
23
|
+
class FirstMethodArgumentLineBreak < Cop
|
|
24
|
+
include FirstElementLineBreak
|
|
25
|
+
|
|
26
|
+
MSG = 'Add a line break before the first argument of a ' \
|
|
27
|
+
'multi-line method argument list.'
|
|
28
|
+
|
|
29
|
+
def on_send(node)
|
|
30
|
+
_receiver, _name, *args = *node
|
|
31
|
+
|
|
32
|
+
check_method_line_break(node, args)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Style
|
|
6
|
+
# This cop checks for a line break before the first parameter in a
|
|
7
|
+
# multi-line method parameter definition.
|
|
8
|
+
#
|
|
9
|
+
# @example
|
|
10
|
+
#
|
|
11
|
+
# # bad
|
|
12
|
+
# def method(foo, bar,
|
|
13
|
+
# baz)
|
|
14
|
+
# do_something
|
|
15
|
+
# end
|
|
16
|
+
#
|
|
17
|
+
# # good
|
|
18
|
+
# def method(
|
|
19
|
+
# foo, bar,
|
|
20
|
+
# baz)
|
|
21
|
+
# do_something
|
|
22
|
+
# end
|
|
23
|
+
#
|
|
24
|
+
# # ignored
|
|
25
|
+
# def method foo,
|
|
26
|
+
# bar
|
|
27
|
+
# do_something
|
|
28
|
+
# end
|
|
29
|
+
class FirstMethodParameterLineBreak < Cop
|
|
30
|
+
include OnMethodDef
|
|
31
|
+
include FirstElementLineBreak
|
|
32
|
+
|
|
33
|
+
MSG = 'Add a line break before the first parameter of a ' \
|
|
34
|
+
'multi-line method parameter list.'
|
|
35
|
+
|
|
36
|
+
def on_method_def(node, _method_name, args, _body)
|
|
37
|
+
check_method_line_break(node, args.to_a)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -39,14 +39,16 @@ module RuboCop
|
|
|
39
39
|
private
|
|
40
40
|
|
|
41
41
|
def message(arg_node)
|
|
42
|
+
return 'Bad indentation of the first parameter.' if arg_node.nil?
|
|
43
|
+
|
|
42
44
|
send_node = arg_node.parent
|
|
43
45
|
text = base_range(send_node, arg_node).source.strip
|
|
44
46
|
base = if text !~ /\n/ && special_inner_call_indentation?(send_node)
|
|
45
47
|
"`#{text}`"
|
|
46
48
|
elsif text.lines.reverse_each.first =~ /^\s*#/
|
|
47
|
-
'the previous line (not counting the comment)'
|
|
49
|
+
'the start of the previous line (not counting the comment)'
|
|
48
50
|
else
|
|
49
|
-
'the previous line'
|
|
51
|
+
'the start of the previous line'
|
|
50
52
|
end
|
|
51
53
|
format('Indent the first parameter one step more than %s.', base)
|
|
52
54
|
end
|
|
@@ -97,7 +99,7 @@ module RuboCop
|
|
|
97
99
|
.map { |c| c.loc.line }
|
|
98
100
|
|
|
99
101
|
line = ''
|
|
100
|
-
while line
|
|
102
|
+
while line.blank? || @comment_lines.include?(line_number)
|
|
101
103
|
line_number -= 1
|
|
102
104
|
line = processed_source.lines[line_number - 1]
|
|
103
105
|
end
|
|
@@ -9,6 +9,7 @@ module RuboCop
|
|
|
9
9
|
# allowed, however.
|
|
10
10
|
class For < Cop
|
|
11
11
|
include ConfigurableEnforcedStyle
|
|
12
|
+
EACH_LENGTH = 'each'.length
|
|
12
13
|
|
|
13
14
|
def on_for(node)
|
|
14
15
|
if style == :each
|
|
@@ -32,7 +33,7 @@ module RuboCop
|
|
|
32
33
|
if style == :for
|
|
33
34
|
end_pos = method.loc.expression.end_pos
|
|
34
35
|
range = Parser::Source::Range.new(processed_source.buffer,
|
|
35
|
-
end_pos -
|
|
36
|
+
end_pos - EACH_LENGTH,
|
|
36
37
|
end_pos)
|
|
37
38
|
add_offense(range, range, 'Prefer `for` over `each`.') do
|
|
38
39
|
opposite_style_detected
|
|
@@ -101,6 +101,11 @@ module RuboCop
|
|
|
101
101
|
|
|
102
102
|
def valid_19_syntax_symbol?(sym_name)
|
|
103
103
|
sym_name.sub!(/\A:/, '')
|
|
104
|
+
|
|
105
|
+
# Most hash keys can be matched against a simple regex.
|
|
106
|
+
return true if sym_name =~ /\A[_a-z]\w*[?!]?\z/i
|
|
107
|
+
|
|
108
|
+
# For more complicated hash keys, let the Parser validate the syntax.
|
|
104
109
|
RuboCop::ProcessedSource.new("{ #{sym_name}: :foo }").valid_syntax?
|
|
105
110
|
end
|
|
106
111
|
|
|
@@ -9,6 +9,8 @@ module RuboCop
|
|
|
9
9
|
class IfUnlessModifier < Cop
|
|
10
10
|
include StatementModifier
|
|
11
11
|
|
|
12
|
+
ASSIGNMENT_TYPES = [:lvasgn, :casgn, :cvasgn, :gvasgn, :ivasgn, :masgn]
|
|
13
|
+
|
|
12
14
|
def message(keyword)
|
|
13
15
|
"Favor modifier `#{keyword}` usage when having a single-line body." \
|
|
14
16
|
' Another good alternative is the usage of control flow `&&`/`||`.'
|
|
@@ -20,17 +22,41 @@ module RuboCop
|
|
|
20
22
|
return if modifier_if?(node)
|
|
21
23
|
return if elsif?(node)
|
|
22
24
|
return if if_else?(node)
|
|
25
|
+
return if chained?(node)
|
|
23
26
|
return unless fit_within_line_as_modifier_form?(node)
|
|
24
27
|
add_offense(node, :keyword, message(node.loc.keyword.source))
|
|
25
28
|
end
|
|
26
29
|
|
|
27
|
-
def
|
|
28
|
-
if
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
def chained?(node)
|
|
31
|
+
# Don't register offense for `if ... end.method`
|
|
32
|
+
return false if node.parent.nil? || !node.parent.send_type?
|
|
33
|
+
receiver = node.parent.children[0]
|
|
34
|
+
node.equal?(receiver)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def parenthesize?(node)
|
|
38
|
+
# Parenthesize corrected expression if changing to modifier-if form
|
|
39
|
+
# would change the meaning of the parent expression
|
|
40
|
+
# (due to the low operator precedence of modifier-if)
|
|
41
|
+
return false if node.parent.nil?
|
|
42
|
+
return true if ASSIGNMENT_TYPES.include?(node.parent.type)
|
|
43
|
+
|
|
44
|
+
if node.parent.send_type?
|
|
45
|
+
_receiver, _name, *args = *node.parent
|
|
46
|
+
return !method_uses_parens?(node.parent, args.first)
|
|
32
47
|
end
|
|
33
48
|
|
|
49
|
+
false
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def method_uses_parens?(node, limit)
|
|
53
|
+
source = node.loc.expression.source_line[0...limit.loc.column]
|
|
54
|
+
source =~ /\s*\(\s*$/
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def autocorrect(node)
|
|
58
|
+
cond, body, _else = if_node_parts(node)
|
|
59
|
+
|
|
34
60
|
oneline =
|
|
35
61
|
"#{body.loc.expression.source} #{node.loc.keyword.source} " +
|
|
36
62
|
cond.loc.expression.source
|
|
@@ -40,6 +66,7 @@ module RuboCop
|
|
|
40
66
|
if first_line_comment
|
|
41
67
|
oneline << ' ' << first_line_comment.loc.expression.source
|
|
42
68
|
end
|
|
69
|
+
oneline = "(#{oneline})" if parenthesize?(node)
|
|
43
70
|
|
|
44
71
|
->(corrector) { corrector.replace(node.loc.expression, oneline) }
|
|
45
72
|
end
|