rubocop 0.40.0 → 0.41.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of rubocop might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/README.md +7 -1014
- data/config/default.yml +61 -5
- data/config/disabled.yml +6 -0
- data/config/enabled.yml +63 -4
- data/lib/rubocop.rb +17 -1
- data/lib/rubocop/ast_node.rb +56 -42
- data/lib/rubocop/ast_node/traversal.rb +3 -3
- data/lib/rubocop/cli.rb +14 -9
- data/lib/rubocop/comment_config.rb +85 -32
- data/lib/rubocop/config.rb +29 -8
- data/lib/rubocop/config_loader.rb +1 -1
- data/lib/rubocop/cop/cop.rb +1 -1
- data/lib/rubocop/cop/corrector.rb +13 -0
- data/lib/rubocop/cop/lint/block_alignment.rb +25 -11
- data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +5 -2
- data/lib/rubocop/cop/lint/inherit_exception.rb +69 -0
- data/lib/rubocop/cop/lint/percent_string_array.rb +60 -0
- data/lib/rubocop/cop/lint/percent_symbol_array.rb +57 -0
- data/lib/rubocop/cop/lint/shadowed_exception.rb +95 -0
- data/lib/rubocop/cop/lint/useless_access_modifier.rb +28 -13
- data/lib/rubocop/cop/mixin/autocorrect_alignment.rb +25 -19
- data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +16 -8
- data/lib/rubocop/cop/mixin/if_node.rb +1 -2
- data/lib/rubocop/cop/mixin/integer_node.rb +13 -0
- data/lib/rubocop/cop/mixin/match_range.rb +26 -0
- data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +16 -7
- data/lib/rubocop/cop/mixin/multiline_literal_brace_layout.rb +18 -1
- data/lib/rubocop/cop/mixin/negative_conditional.rb +6 -4
- data/lib/rubocop/cop/mixin/percent_literal.rb +10 -0
- data/lib/rubocop/cop/mixin/space_after_punctuation.rb +24 -6
- data/lib/rubocop/cop/mixin/space_before_punctuation.rb +20 -7
- data/lib/rubocop/cop/mixin/string_literals_help.rb +2 -2
- data/lib/rubocop/cop/mixin/trailing_comma.rb +34 -20
- data/lib/rubocop/cop/performance/flat_map.rb +23 -10
- data/lib/rubocop/cop/performance/push_splat.rb +47 -0
- data/lib/rubocop/cop/performance/redundant_block_call.rb +24 -1
- data/lib/rubocop/cop/performance/redundant_merge.rb +3 -5
- data/lib/rubocop/cop/performance/sample.rb +15 -11
- data/lib/rubocop/cop/rails/exit.rb +62 -0
- data/lib/rubocop/cop/rails/output_safety.rb +45 -0
- data/lib/rubocop/cop/rails/pluralization_grammar.rb +12 -4
- data/lib/rubocop/cop/rails/request_referer.rb +40 -0
- data/lib/rubocop/cop/rails/uniq_before_pluck.rb +63 -28
- data/lib/rubocop/cop/rails/validation.rb +37 -23
- data/lib/rubocop/cop/style/alias.rb +10 -6
- data/lib/rubocop/cop/style/bare_percent_literals.rb +18 -7
- data/lib/rubocop/cop/style/block_delimiters.rb +15 -22
- data/lib/rubocop/cop/style/closing_parenthesis_indentation.rb +19 -8
- data/lib/rubocop/cop/style/comment_indentation.rb +13 -5
- data/lib/rubocop/cop/style/conditional_assignment.rb +111 -59
- data/lib/rubocop/cop/style/documentation.rb +7 -1
- data/lib/rubocop/cop/style/each_for_simple_loop.rb +43 -0
- data/lib/rubocop/cop/style/each_with_object.rb +25 -14
- data/lib/rubocop/cop/style/empty_else.rb +6 -10
- data/lib/rubocop/cop/style/extra_spacing.rb +20 -3
- data/lib/rubocop/cop/style/file_name.rb +16 -4
- data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +1 -1
- data/lib/rubocop/cop/style/hash_syntax.rb +9 -2
- data/lib/rubocop/cop/style/if_unless_modifier.rb +20 -13
- data/lib/rubocop/cop/style/implicit_runtime_error.rb +32 -0
- data/lib/rubocop/cop/style/infinite_loop.rb +42 -5
- data/lib/rubocop/cop/style/lambda.rb +22 -0
- data/lib/rubocop/cop/style/method_def_parentheses.rb +12 -4
- data/lib/rubocop/cop/style/module_function.rb +28 -6
- data/lib/rubocop/cop/style/multiline_method_call_indentation.rb +49 -12
- data/lib/rubocop/cop/style/mutable_constant.rb +8 -1
- data/lib/rubocop/cop/style/nested_modifier.rb +1 -1
- data/lib/rubocop/cop/style/next.rb +43 -31
- data/lib/rubocop/cop/style/not.rb +33 -13
- data/lib/rubocop/cop/style/numeric_literal_prefix.rb +92 -0
- data/lib/rubocop/cop/style/numeric_literals.rb +1 -4
- data/lib/rubocop/cop/style/parallel_assignment.rb +26 -8
- data/lib/rubocop/cop/style/{deprecated_hash_methods.rb → preferred_hash_methods.rb} +8 -8
- data/lib/rubocop/cop/style/redundant_parentheses.rb +29 -19
- data/lib/rubocop/cop/style/redundant_self.rb +13 -6
- data/lib/rubocop/cop/style/space_after_not.rb +7 -5
- data/lib/rubocop/cop/style/space_around_keyword.rb +6 -0
- data/lib/rubocop/cop/style/space_around_operators.rb +5 -1
- data/lib/rubocop/cop/style/space_before_first_arg.rb +21 -9
- data/lib/rubocop/cop/style/space_inside_array_percent_literal.rb +53 -0
- data/lib/rubocop/cop/style/space_inside_block_braces.rb +2 -2
- data/lib/rubocop/cop/style/space_inside_hash_literal_braces.rb +26 -6
- data/lib/rubocop/cop/style/space_inside_percent_literal_delimiters.rb +64 -0
- data/lib/rubocop/cop/style/string_literals.rb +37 -8
- data/lib/rubocop/cop/style/symbol_array.rb +21 -12
- data/lib/rubocop/cop/style/symbol_proc.rb +26 -19
- data/lib/rubocop/cop/style/word_array.rb +1 -5
- data/lib/rubocop/cop/style/zero_length_predicate.rb +6 -6
- data/lib/rubocop/cop/team.rb +40 -27
- data/lib/rubocop/cop/util.rb +13 -42
- data/lib/rubocop/formatter/disabled_config_formatter.rb +37 -14
- data/lib/rubocop/formatter/html_formatter.rb +3 -7
- data/lib/rubocop/result_cache.rb +18 -4
- data/{spec/support → lib/rubocop/rspec}/cop_helper.rb +3 -0
- data/lib/rubocop/rspec/host_environment_simulation_helper.rb +33 -0
- data/lib/rubocop/rspec/shared_contexts.rb +75 -0
- data/lib/rubocop/rspec/shared_examples.rb +101 -0
- data/lib/rubocop/rspec/support.rb +9 -0
- data/lib/rubocop/runner.rb +2 -2
- data/lib/rubocop/string_interpreter.rb +58 -0
- data/lib/rubocop/version.rb +1 -1
- metadata +27 -7
@@ -54,18 +54,29 @@ module RuboCop
|
|
54
54
|
return unless right_paren
|
55
55
|
return unless begins_its_line?(right_paren)
|
56
56
|
|
57
|
-
|
58
|
-
|
59
|
-
correct_column = if line_break_after_left_paren?(left_paren, elements)
|
60
|
-
left_paren.source_line =~ /\S/
|
61
|
-
else
|
62
|
-
left_paren.column
|
63
|
-
end
|
57
|
+
correct_column = expected_column(node, elements)
|
64
58
|
@column_delta = correct_column - right_paren.column
|
65
59
|
return if @column_delta == 0
|
66
60
|
|
61
|
+
left_paren = node.loc.begin
|
67
62
|
msg = correct_column == left_paren.column ? MSG_ALIGN : MSG_INDENT
|
68
|
-
add_offense(
|
63
|
+
add_offense(right_paren, right_paren, msg)
|
64
|
+
end
|
65
|
+
|
66
|
+
def expected_column(node, elements)
|
67
|
+
left_paren = node.loc.begin
|
68
|
+
|
69
|
+
if node.send_type? && fixed_parameter_indentation? ||
|
70
|
+
line_break_after_left_paren?(left_paren, elements)
|
71
|
+
left_paren.source_line =~ /\S/
|
72
|
+
else
|
73
|
+
left_paren.column
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def fixed_parameter_indentation?
|
78
|
+
config.for_cop('Style/AlignParameters')['EnforcedStyle'] ==
|
79
|
+
'with_fixed_indentation'
|
69
80
|
end
|
70
81
|
|
71
82
|
def line_break_after_left_paren?(left_paren, elements)
|
@@ -17,11 +17,9 @@ module RuboCop
|
|
17
17
|
private
|
18
18
|
|
19
19
|
def check(comment)
|
20
|
-
|
21
|
-
own_line = lines[comment.loc.line - 1]
|
22
|
-
return unless own_line =~ /\A\s*#/
|
20
|
+
return unless own_line_comment?(comment)
|
23
21
|
|
24
|
-
next_line =
|
22
|
+
next_line = line_after_comment(comment)
|
25
23
|
correct_comment_indentation = correct_indentation(next_line)
|
26
24
|
column = comment.loc.column
|
27
25
|
|
@@ -33,13 +31,23 @@ module RuboCop
|
|
33
31
|
correct_comment_indentation += configured_indentation_width
|
34
32
|
# We keep @column_delta unchanged so that autocorrect changes to
|
35
33
|
# the preferred style of aligning the comment with the keyword.
|
34
|
+
return if column == correct_comment_indentation
|
36
35
|
end
|
37
36
|
|
38
|
-
return if column == correct_comment_indentation
|
39
37
|
add_offense(comment, comment.loc.expression,
|
40
38
|
format(MSG, column, correct_comment_indentation))
|
41
39
|
end
|
42
40
|
|
41
|
+
def own_line_comment?(comment)
|
42
|
+
own_line = processed_source.lines[comment.loc.line - 1]
|
43
|
+
own_line =~ /\A\s*#/
|
44
|
+
end
|
45
|
+
|
46
|
+
def line_after_comment(comment)
|
47
|
+
lines = processed_source.lines
|
48
|
+
lines[comment.loc.line..-1].find { |line| !line.blank? }
|
49
|
+
end
|
50
|
+
|
43
51
|
def correct_indentation(next_line)
|
44
52
|
return 0 unless next_line
|
45
53
|
|
@@ -213,30 +213,30 @@ module RuboCop
|
|
213
213
|
ASSIGNMENT_TYPES.each do |type|
|
214
214
|
define_method "on_#{type}" do |node|
|
215
215
|
return if part_of_ignored_node?(node)
|
216
|
+
return unless style == :assign_inside_condition
|
217
|
+
|
216
218
|
check_assignment_to_condition(node)
|
217
219
|
end
|
218
220
|
end
|
219
221
|
|
220
222
|
def on_send(node)
|
221
223
|
return unless assignment_type?(node)
|
224
|
+
return unless style == :assign_inside_condition
|
225
|
+
|
222
226
|
check_assignment_to_condition(node)
|
223
227
|
end
|
224
228
|
|
225
229
|
def check_assignment_to_condition(node)
|
226
|
-
return unless style == :assign_inside_condition
|
227
230
|
ignore_node(node)
|
228
|
-
*_variable, assignment = *node
|
229
|
-
if assignment.respond_to?(:type)
|
230
|
-
if assignment.begin_type? && assignment.children.size == 1
|
231
|
-
assignment, = *assignment
|
232
|
-
end
|
233
231
|
|
234
|
-
|
235
|
-
|
232
|
+
assignment = assignment_node(node)
|
233
|
+
return unless condition?(assignment)
|
234
|
+
|
236
235
|
_condition, *branches, else_branch = *assignment
|
237
236
|
return unless else_branch # empty else
|
238
237
|
return if single_line_conditions_only? &&
|
239
238
|
[*branches, else_branch].any?(&:begin_type?)
|
239
|
+
|
240
240
|
add_offense(node, :expression, ASSIGN_TO_CONDITION_MSG)
|
241
241
|
end
|
242
242
|
|
@@ -274,6 +274,20 @@ module RuboCop
|
|
274
274
|
|
275
275
|
private
|
276
276
|
|
277
|
+
def assignment_node(node)
|
278
|
+
*_variable, assignment = *node
|
279
|
+
|
280
|
+
if assignment.begin_type? && assignment.children.size == 1
|
281
|
+
assignment, = *assignment
|
282
|
+
end
|
283
|
+
|
284
|
+
assignment
|
285
|
+
end
|
286
|
+
|
287
|
+
def condition?(node)
|
288
|
+
CONDITION_TYPES.include?(node.type)
|
289
|
+
end
|
290
|
+
|
277
291
|
def move_assignment_outside_condition(node)
|
278
292
|
if ternary?(node)
|
279
293
|
TernaryCorrector.correct(node)
|
@@ -381,14 +395,7 @@ module RuboCop
|
|
381
395
|
module ConditionalCorrectorHelper
|
382
396
|
def remove_whitespace_in_branches(corrector, branch, condition, column)
|
383
397
|
branch.each_node do |child|
|
384
|
-
|
385
|
-
white_space =
|
386
|
-
Parser::Source::Range.new(child_expression.source_buffer,
|
387
|
-
child_expression.begin_pos -
|
388
|
-
(child.loc.expression.column -
|
389
|
-
column - 2),
|
390
|
-
child_expression.begin_pos)
|
391
|
-
|
398
|
+
white_space = white_space_range(child, column)
|
392
399
|
corrector.remove(white_space) if white_space.source.strip.empty?
|
393
400
|
end
|
394
401
|
|
@@ -397,6 +404,15 @@ module RuboCop
|
|
397
404
|
end
|
398
405
|
end
|
399
406
|
|
407
|
+
def white_space_range(node, column)
|
408
|
+
expression = node.loc.expression
|
409
|
+
begin_pos = expression.begin_pos - (expression.column - column - 2)
|
410
|
+
|
411
|
+
Parser::Source::Range.new(expression.source_buffer,
|
412
|
+
begin_pos,
|
413
|
+
expression.begin_pos)
|
414
|
+
end
|
415
|
+
|
400
416
|
def assignment(node)
|
401
417
|
*_, condition = *node
|
402
418
|
Parser::Source::Range.new(node.loc.expression.source_buffer,
|
@@ -439,22 +455,36 @@ module RuboCop
|
|
439
455
|
|
440
456
|
def move_assignment_inside_condition(node)
|
441
457
|
*_var, rhs = *node
|
442
|
-
|
458
|
+
if_branch, else_branch = extract_branches(node)
|
443
459
|
assignment = assignment(node)
|
444
|
-
_condition, if_branch, else_branch = *(condition || rhs)
|
445
460
|
|
446
461
|
lambda do |corrector|
|
462
|
+
remove_parentheses(corrector, rhs) if Util.parentheses?(rhs)
|
447
463
|
corrector.remove(assignment)
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
end
|
452
|
-
corrector.insert_before(if_branch.loc.expression,
|
453
|
-
assignment.source)
|
454
|
-
corrector.insert_before(else_branch.loc.expression,
|
455
|
-
assignment.source)
|
464
|
+
|
465
|
+
move_branch_inside_condition(corrector, if_branch, assignment)
|
466
|
+
move_branch_inside_condition(corrector, else_branch, assignment)
|
456
467
|
end
|
457
468
|
end
|
469
|
+
|
470
|
+
private
|
471
|
+
|
472
|
+
def extract_branches(node)
|
473
|
+
*_var, rhs = *node
|
474
|
+
condition, = *rhs if rhs.begin_type? && rhs.children.size == 1
|
475
|
+
_condition, if_branch, else_branch = *(condition || rhs)
|
476
|
+
|
477
|
+
[if_branch, else_branch]
|
478
|
+
end
|
479
|
+
|
480
|
+
def remove_parentheses(corrector, node)
|
481
|
+
corrector.remove(node.loc.begin)
|
482
|
+
corrector.remove(node.loc.end)
|
483
|
+
end
|
484
|
+
|
485
|
+
def move_branch_inside_condition(corrector, branch, assignment)
|
486
|
+
corrector.insert_before(branch.loc.expression, assignment.source)
|
487
|
+
end
|
458
488
|
end
|
459
489
|
end
|
460
490
|
|
@@ -465,7 +495,7 @@ module RuboCop
|
|
465
495
|
include ConditionalCorrectorHelper
|
466
496
|
|
467
497
|
def correct(cop, node)
|
468
|
-
if_branch, elsif_branches, else_branch =
|
498
|
+
if_branch, elsif_branches, else_branch = extract_tail_branches(node)
|
469
499
|
_variable, *_operator, if_assignment = *if_branch
|
470
500
|
_else_variable, *_operator, else_assignment = *else_branch
|
471
501
|
|
@@ -482,36 +512,44 @@ module RuboCop
|
|
482
512
|
def move_assignment_inside_condition(node)
|
483
513
|
column = node.loc.expression.column
|
484
514
|
*_var, condition = *node
|
485
|
-
_condition, if_branch, else_branch = *condition
|
486
|
-
elsif_branches, else_branch = expand_elses(else_branch)
|
487
515
|
assignment = assignment(node)
|
488
516
|
|
489
517
|
lambda do |corrector|
|
490
518
|
corrector.remove(assignment)
|
491
519
|
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
assignment.source)
|
496
|
-
|
497
|
-
remove_whitespace_in_branches(corrector, branch,
|
498
|
-
condition, column)
|
499
|
-
|
500
|
-
corrector
|
501
|
-
.remove_preceding(branch.parent.loc.else,
|
502
|
-
branch.parent.loc.else.column - column)
|
520
|
+
extract_branches(condition).flatten.each do |branch|
|
521
|
+
move_branch_inside_condition(corrector, branch, condition,
|
522
|
+
assignment, column)
|
503
523
|
end
|
504
524
|
end
|
505
525
|
end
|
506
526
|
|
507
527
|
private
|
508
528
|
|
529
|
+
def extract_tail_branches(node)
|
530
|
+
if_branch, elsif_branches, else_branch = extract_branches(node)
|
531
|
+
elsif_branches.map! { |branch| tail(branch) }
|
532
|
+
|
533
|
+
[tail(if_branch), elsif_branches, tail(else_branch)]
|
534
|
+
end
|
535
|
+
|
509
536
|
def extract_branches(node)
|
510
537
|
_condition, if_branch, else_branch = *node
|
511
538
|
elsif_branches, else_branch = expand_elses(else_branch)
|
512
|
-
elsif_branches.map! { |branch| tail(branch) }
|
513
539
|
|
514
|
-
[
|
540
|
+
[if_branch, elsif_branches, else_branch]
|
541
|
+
end
|
542
|
+
|
543
|
+
def move_branch_inside_condition(corrector, branch, condition,
|
544
|
+
assignment, column)
|
545
|
+
branch_assignment = tail(branch)
|
546
|
+
corrector.insert_before(branch_assignment.loc.expression,
|
547
|
+
assignment.source)
|
548
|
+
|
549
|
+
remove_whitespace_in_branches(corrector, branch, condition, column)
|
550
|
+
|
551
|
+
branch_else = branch.parent.loc.else
|
552
|
+
corrector.remove_preceding(branch_else, branch_else.column - column)
|
515
553
|
end
|
516
554
|
end
|
517
555
|
end
|
@@ -523,10 +561,7 @@ module RuboCop
|
|
523
561
|
include ConditionalCorrectorHelper
|
524
562
|
|
525
563
|
def correct(cop, node)
|
526
|
-
|
527
|
-
else_branch = tail(else_branch)
|
528
|
-
when_branches = expand_when_branches(when_branches)
|
529
|
-
when_branches.map! { |when_branch| tail(when_branch) }
|
564
|
+
when_branches, else_branch = extract_tail_branches(node)
|
530
565
|
_variable, *_operator, else_assignment = *else_branch
|
531
566
|
|
532
567
|
lambda do |corrector|
|
@@ -543,27 +578,44 @@ module RuboCop
|
|
543
578
|
def move_assignment_inside_condition(node)
|
544
579
|
column = node.loc.expression.column
|
545
580
|
*_var, condition = *node
|
546
|
-
_condition, *when_branches, else_branch = *condition
|
547
|
-
when_branches = expand_when_branches(when_branches)
|
548
581
|
assignment = assignment(node)
|
549
582
|
|
550
583
|
lambda do |corrector|
|
551
584
|
corrector.remove(assignment)
|
552
585
|
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
assignment.source)
|
557
|
-
|
558
|
-
remove_whitespace_in_branches(corrector, branch,
|
559
|
-
condition, column)
|
560
|
-
|
561
|
-
corrector
|
562
|
-
.remove_preceding(branch.parent.loc.keyword,
|
563
|
-
branch.parent.loc.keyword.column - column)
|
586
|
+
extract_branches(condition).flatten.each do |branch|
|
587
|
+
move_branch_inside_condition(corrector, branch, condition,
|
588
|
+
assignment, column)
|
564
589
|
end
|
565
590
|
end
|
566
591
|
end
|
592
|
+
|
593
|
+
private
|
594
|
+
|
595
|
+
def extract_tail_branches(node)
|
596
|
+
when_branches, else_branch = extract_branches(node)
|
597
|
+
when_branches.map! { |branch| tail(branch) }
|
598
|
+
[when_branches, tail(else_branch)]
|
599
|
+
end
|
600
|
+
|
601
|
+
def extract_branches(node)
|
602
|
+
_condition, *when_branches, else_branch = *node
|
603
|
+
when_branches = expand_when_branches(when_branches)
|
604
|
+
[when_branches, else_branch]
|
605
|
+
end
|
606
|
+
|
607
|
+
def move_branch_inside_condition(corrector, branch, condition,
|
608
|
+
assignment, column)
|
609
|
+
branch_assignment = tail(branch)
|
610
|
+
corrector.insert_before(branch_assignment.loc.expression,
|
611
|
+
assignment.source)
|
612
|
+
|
613
|
+
remove_whitespace_in_branches(corrector, branch, condition, column)
|
614
|
+
|
615
|
+
parent_keyword = branch.parent.loc.keyword
|
616
|
+
corrector.remove_preceding(parent_keyword,
|
617
|
+
parent_keyword.column - column)
|
618
|
+
end
|
567
619
|
end
|
568
620
|
end
|
569
621
|
|
@@ -65,7 +65,9 @@ module RuboCop
|
|
65
65
|
|
66
66
|
# As long as there's at least one comment line that isn't an
|
67
67
|
# annotation, it's OK.
|
68
|
-
preceding_comments.any?
|
68
|
+
preceding_comments.any? do |comment|
|
69
|
+
!annotation?(comment) && !interpreter_directive_comment?(comment)
|
70
|
+
end
|
69
71
|
end
|
70
72
|
|
71
73
|
def preceding_comments(node, ast_with_comments)
|
@@ -96,6 +98,10 @@ module RuboCop
|
|
96
98
|
|
97
99
|
nodoc_comment?(node.ancestors.first, ast_with_comments, true)
|
98
100
|
end
|
101
|
+
|
102
|
+
def interpreter_directive_comment?(comment)
|
103
|
+
comment.text =~ /^#\s*(frozen_string_literal|encoding):/
|
104
|
+
end
|
99
105
|
end
|
100
106
|
end
|
101
107
|
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module RuboCop
|
5
|
+
module Cop
|
6
|
+
module Style
|
7
|
+
# This cop checks for loops which iterate a constant number of times,
|
8
|
+
# using a Range literal and `#each`. This can be done more readably using
|
9
|
+
# `Integer#times`.
|
10
|
+
#
|
11
|
+
# This check only applies if the block takes no parameters.
|
12
|
+
#
|
13
|
+
# @example
|
14
|
+
# @bad
|
15
|
+
# (0..10).each { }
|
16
|
+
#
|
17
|
+
# @good
|
18
|
+
# 10.times { }
|
19
|
+
class EachForSimpleLoop < Cop
|
20
|
+
def on_block(node)
|
21
|
+
if bad_each_range(node)
|
22
|
+
send_node, = *node
|
23
|
+
range = send_node.receiver.source_range.join(send_node.loc.selector)
|
24
|
+
add_offense(node, range, 'Use `Integer#times` for a simple loop ' \
|
25
|
+
'which iterates a fixed number of times.')
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def autocorrect(node)
|
30
|
+
lambda do |corrector|
|
31
|
+
min, max = bad_each_range(node)
|
32
|
+
corrector.replace(node.children.first.source_range,
|
33
|
+
"#{max - min}.times")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def_node_matcher :bad_each_range, <<-PATTERN
|
38
|
+
(block (send (begin ({irange erange} (int $_) (int $_))) :each) (args) ...)
|
39
|
+
PATTERN
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -23,35 +23,46 @@ module RuboCop
|
|
23
23
|
|
24
24
|
def on_block(node)
|
25
25
|
method, args, body = *node
|
26
|
-
|
27
|
-
# filter out super and zsuper nodes
|
28
|
-
return unless method.type == :send
|
26
|
+
return unless reduce_method?(method)
|
29
27
|
|
30
28
|
_, method_name, method_arg = *method
|
31
|
-
|
32
|
-
return unless METHODS.include? method_name
|
33
|
-
return if method_arg && method_arg.basic_literal?
|
29
|
+
return if simple_method_arg?(method_arg)
|
34
30
|
|
35
31
|
return_value = return_value(body)
|
36
32
|
return unless return_value
|
37
|
-
|
38
33
|
return unless first_argument_returned?(args, return_value)
|
34
|
+
return if accumulator_param_assigned_to?(body, args)
|
35
|
+
|
36
|
+
add_offense(method, :selector, format(MSG, method_name))
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
39
40
|
|
40
|
-
|
41
|
-
|
41
|
+
def reduce_method?(method)
|
42
|
+
return false unless method.send_type?
|
43
|
+
|
44
|
+
_, method_name, _method_arg = *method
|
45
|
+
METHODS.include? method_name
|
46
|
+
end
|
47
|
+
|
48
|
+
def simple_method_arg?(method_arg)
|
49
|
+
method_arg && method_arg.basic_literal?
|
50
|
+
end
|
51
|
+
|
52
|
+
# if the accumulator parameter is assigned to in the block,
|
53
|
+
# then we can't convert to each_with_object
|
54
|
+
def accumulator_param_assigned_to?(body, args)
|
42
55
|
first_arg, = *args
|
43
56
|
accumulator_var, = *first_arg
|
44
|
-
|
57
|
+
|
58
|
+
body.each_descendant.any? do |n|
|
45
59
|
next unless n.assignment?
|
60
|
+
|
46
61
|
lhs, _rhs = *n
|
47
62
|
lhs.equal?(accumulator_var)
|
48
63
|
end
|
49
|
-
|
50
|
-
add_offense(method, :selector, format(MSG, method_name))
|
51
64
|
end
|
52
65
|
|
53
|
-
private
|
54
|
-
|
55
66
|
def return_value(body)
|
56
67
|
return unless body
|
57
68
|
|