rubocop 0.79.0 → 0.80.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/LICENSE.txt +1 -1
- data/README.md +3 -3
- data/config/default.yml +33 -19
- data/lib/rubocop.rb +5 -1
- data/lib/rubocop/ast/node.rb +0 -12
- data/lib/rubocop/ast/node/regexp_node.rb +2 -4
- data/lib/rubocop/ast/traversal.rb +9 -0
- data/lib/rubocop/comment_config.rb +6 -1
- data/lib/rubocop/config_obsoletion.rb +2 -1
- data/lib/rubocop/cop/layout/empty_line_between_defs.rb +2 -1
- data/lib/rubocop/cop/layout/leading_comment_space.rb +33 -2
- data/lib/rubocop/cop/layout/line_length.rb +30 -1
- data/lib/rubocop/cop/layout/multiline_hash_brace_layout.rb +0 -4
- data/lib/rubocop/cop/layout/space_around_operators.rb +18 -0
- data/lib/rubocop/cop/layout/space_before_first_arg.rb +8 -0
- data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +2 -9
- data/lib/rubocop/cop/lint/debugger.rb +1 -1
- data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +12 -7
- data/lib/rubocop/cop/lint/useless_setter_call.rb +4 -0
- data/lib/rubocop/cop/migration/department_name.rb +14 -1
- data/lib/rubocop/cop/mixin/configurable_enforced_style.rb +4 -0
- data/lib/rubocop/cop/mixin/frozen_string_literal.rb +6 -0
- data/lib/rubocop/cop/mixin/hash_transform_method.rb +172 -0
- data/lib/rubocop/cop/mixin/trailing_comma.rb +2 -9
- data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +1 -1
- data/lib/rubocop/cop/style/block_delimiters.rb +60 -1
- data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +89 -11
- data/lib/rubocop/cop/style/hash_each_methods.rb +87 -0
- data/lib/rubocop/cop/style/hash_transform_keys.rb +79 -0
- data/lib/rubocop/cop/style/hash_transform_values.rb +79 -0
- data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +5 -0
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +5 -4
- data/lib/rubocop/cop/style/or_assignment.rb +3 -2
- data/lib/rubocop/cop/style/symbol_array.rb +2 -2
- data/lib/rubocop/cop/style/ternary_parentheses.rb +1 -1
- data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +0 -22
- data/lib/rubocop/cop/variable_force.rb +4 -1
- data/lib/rubocop/formatter/formatter_set.rb +1 -0
- data/lib/rubocop/formatter/junit_formatter.rb +63 -0
- data/lib/rubocop/node_pattern.rb +96 -10
- data/lib/rubocop/version.rb +1 -1
- metadata +21 -3
- data/lib/rubocop/cop/style/braces_around_hash_parameters.rb +0 -209
@@ -11,8 +11,8 @@ module RuboCop
|
|
11
11
|
#
|
12
12
|
# Configuration option: MinSize
|
13
13
|
# If set, arrays with fewer elements than this value will not trigger the
|
14
|
-
# cop. For example, a `MinSize of `3` will not enforce a style on an
|
15
|
-
# of 2 or fewer elements.
|
14
|
+
# cop. For example, a `MinSize` of `3` will not enforce a style on an
|
15
|
+
# array of 2 or fewer elements.
|
16
16
|
#
|
17
17
|
# @example EnforcedStyle: percent (default)
|
18
18
|
# # good
|
@@ -162,7 +162,7 @@ module RuboCop
|
|
162
162
|
# `RedundantParentheses` cop is enabled, it will cause an infinite loop
|
163
163
|
# as they compete to add and remove the parentheses respectively.
|
164
164
|
def infinite_loop?
|
165
|
-
require_parentheses? &&
|
165
|
+
(require_parentheses? || require_parentheses_when_complex?) &&
|
166
166
|
redundant_parentheses_enabled?
|
167
167
|
end
|
168
168
|
|
@@ -68,28 +68,6 @@ module RuboCop
|
|
68
68
|
def self.autocorrect_incompatible_with
|
69
69
|
[Layout::HeredocArgumentClosingParenthesis]
|
70
70
|
end
|
71
|
-
|
72
|
-
private
|
73
|
-
|
74
|
-
def avoid_autocorrect?(args)
|
75
|
-
args.last.hash_type? && args.last.braces? &&
|
76
|
-
braces_will_be_removed?(args)
|
77
|
-
end
|
78
|
-
|
79
|
-
# Returns true if running with --auto-correct would remove the braces
|
80
|
-
# of the last argument.
|
81
|
-
def braces_will_be_removed?(args)
|
82
|
-
brace_config = config.for_cop('Style/BracesAroundHashParameters')
|
83
|
-
return false unless brace_config.fetch('Enabled')
|
84
|
-
return false if brace_config['AutoCorrect'] == false
|
85
|
-
|
86
|
-
brace_style = brace_config['EnforcedStyle']
|
87
|
-
return true if brace_style == 'no_braces'
|
88
|
-
|
89
|
-
return false unless brace_style == 'context_dependent'
|
90
|
-
|
91
|
-
args.one? || !args[-2].hash_type?
|
92
|
-
end
|
93
71
|
end
|
94
72
|
end
|
95
73
|
end
|
@@ -190,7 +190,10 @@ module RuboCop
|
|
190
190
|
end
|
191
191
|
|
192
192
|
def regexp_captured_names(node)
|
193
|
-
regexp_string = node.children
|
193
|
+
regexp_string = node.children.select(&:str_type?).map do |child|
|
194
|
+
child.children.first
|
195
|
+
end.join || ''
|
196
|
+
|
194
197
|
regexp = Regexp.new(regexp_string)
|
195
198
|
|
196
199
|
regexp.named_captures.keys
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rexml/document'
|
4
|
+
|
5
|
+
#
|
6
|
+
# This code is based on https://github.com/mikian/rubocop-junit-formatter.
|
7
|
+
#
|
8
|
+
# Copyright (c) 2015 Mikko Kokkonen
|
9
|
+
#
|
10
|
+
# MIT License
|
11
|
+
#
|
12
|
+
# https://github.com/mikian/rubocop-junit-formatter/blob/master/LICENSE.txt
|
13
|
+
#
|
14
|
+
module RuboCop
|
15
|
+
module Formatter
|
16
|
+
# This formatter formats the report data in JUnit format.
|
17
|
+
class JUnitFormatter < BaseFormatter
|
18
|
+
def initialize(output, options = {})
|
19
|
+
super
|
20
|
+
|
21
|
+
@document = REXML::Document.new.tap do |document|
|
22
|
+
document << REXML::XMLDecl.new
|
23
|
+
end
|
24
|
+
testsuites = REXML::Element.new('testsuites', @document)
|
25
|
+
testsuite = REXML::Element.new('testsuite', testsuites)
|
26
|
+
@testsuite = testsuite.tap do |element|
|
27
|
+
element.add_attributes('name' => 'rubocop')
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def file_finished(file, offenses)
|
32
|
+
offenses.group_by(&:cop_name).each do |cop_name, grouped_offenses|
|
33
|
+
REXML::Element.new('testcase', @testsuite).tap do |testcase|
|
34
|
+
testcase.attributes['classname'] = file.gsub(
|
35
|
+
/\.rb\Z/, ''
|
36
|
+
).gsub("#{Dir.pwd}/", '').tr('/', '.')
|
37
|
+
testcase.attributes['name'] = cop_name
|
38
|
+
|
39
|
+
add_failure_to(testcase, grouped_offenses, cop_name)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def finished(_inspected_files)
|
45
|
+
@document.write(output, 2)
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def add_failure_to(testcase, offenses, cop_name)
|
51
|
+
# One failure per offense. Zero failures is a passing test case,
|
52
|
+
# for most surefire/nUnit parsers.
|
53
|
+
offenses.each do |offense|
|
54
|
+
REXML::Element.new('failure', testcase).tap do |failure|
|
55
|
+
failure.attributes['type'] = cop_name
|
56
|
+
failure.attributes['message'] = offense.message
|
57
|
+
failure.add_text(offense.location.to_s)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
data/lib/rubocop/node_pattern.rb
CHANGED
@@ -78,6 +78,8 @@ module RuboCop
|
|
78
78
|
# # matching process starts
|
79
79
|
# '^^send' # each ^ ascends one level in the AST
|
80
80
|
# # so this matches against the grandparent node
|
81
|
+
# '`send' # descends any number of level in the AST
|
82
|
+
# # so this matches against any descendant node
|
81
83
|
# '#method' # we call this a 'funcall'; it calls a method in the
|
82
84
|
# # context where a pattern-matching method is defined
|
83
85
|
# # if that returns a truthy value, the match succeeds
|
@@ -112,7 +114,7 @@ module RuboCop
|
|
112
114
|
SYMBOL = %r{:(?:[\w+@*/?!<>=~|%^-]+|\[\]=?)}.freeze
|
113
115
|
IDENTIFIER = /[a-zA-Z_][a-zA-Z0-9_-]*/.freeze
|
114
116
|
META = Regexp.union(
|
115
|
-
%w"( ) { } [ ] $< < > $... $ ! ^ ... + * ?"
|
117
|
+
%w"( ) { } [ ] $< < > $... $ ! ^ ` ... + * ?"
|
116
118
|
).freeze
|
117
119
|
NUMBER = /-?\d+(?:\.\d+)?/.freeze
|
118
120
|
STRING = /".+?"/.freeze
|
@@ -188,7 +190,7 @@ module RuboCop
|
|
188
190
|
|
189
191
|
@temps = 0 # avoid name clashes between temp variables
|
190
192
|
@captures = 0 # number of captures seen
|
191
|
-
@unify = {} # named wildcard -> temp variable
|
193
|
+
@unify = {} # named wildcard -> temp variable
|
192
194
|
@params = 0 # highest % (param) number seen
|
193
195
|
run(node_var)
|
194
196
|
end
|
@@ -223,6 +225,7 @@ module RuboCop
|
|
223
225
|
when '!' then compile_negation
|
224
226
|
when '$' then compile_capture
|
225
227
|
when '^' then compile_ascend
|
228
|
+
when '`' then compile_descend
|
226
229
|
when WILDCARD then compile_wildcard(token[1..-1])
|
227
230
|
when FUNCALL then compile_funcall(token)
|
228
231
|
when LITERAL then compile_literal(token)
|
@@ -466,12 +469,67 @@ module RuboCop
|
|
466
469
|
end
|
467
470
|
end
|
468
471
|
|
472
|
+
def access_unify(name)
|
473
|
+
var = @unify[name]
|
474
|
+
|
475
|
+
if var == :forbidden_unification
|
476
|
+
fail_due_to "Wildcard #{name} was first seen in a subset of a" \
|
477
|
+
" union and can't be used outside that union"
|
478
|
+
end
|
479
|
+
var
|
480
|
+
end
|
481
|
+
|
482
|
+
def forbid_unification(*names)
|
483
|
+
names.each do |name|
|
484
|
+
@unify[name] = :forbidden_unification
|
485
|
+
end
|
486
|
+
end
|
487
|
+
|
488
|
+
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
489
|
+
def unify_in_union(enum)
|
490
|
+
# We need to reset @unify before each branch is processed.
|
491
|
+
# Moreover we need to keep track of newly encountered wildcards.
|
492
|
+
# Var `new_unify_intersection` will hold those that are encountered
|
493
|
+
# in all branches; these are not a problem.
|
494
|
+
# Var `partial_unify` will hold those encountered in only a subset
|
495
|
+
# of the branches; these can't be used outside of the union.
|
496
|
+
|
497
|
+
return to_enum __method__, enum unless block_given?
|
498
|
+
|
499
|
+
new_unify_intersection = nil
|
500
|
+
partial_unify = []
|
501
|
+
unify_before = @unify.dup
|
502
|
+
|
503
|
+
result = enum.each do |e|
|
504
|
+
@unify = unify_before.dup if new_unify_intersection
|
505
|
+
yield e
|
506
|
+
new_unify = @unify.keys - unify_before.keys
|
507
|
+
if new_unify_intersection.nil?
|
508
|
+
# First iteration
|
509
|
+
new_unify_intersection = new_unify
|
510
|
+
else
|
511
|
+
union = new_unify_intersection | new_unify
|
512
|
+
new_unify_intersection &= new_unify
|
513
|
+
partial_unify |= union - new_unify_intersection
|
514
|
+
end
|
515
|
+
end
|
516
|
+
|
517
|
+
# At this point, all members of `new_unify_intersection` can be used
|
518
|
+
# for unification outside of the union, but partial_unify may not
|
519
|
+
|
520
|
+
forbid_unification(*partial_unify)
|
521
|
+
|
522
|
+
result
|
523
|
+
end
|
524
|
+
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize
|
525
|
+
|
469
526
|
def compile_union
|
470
527
|
# we need to ensure that each branch of the {} contains the same
|
471
528
|
# number of captures (since only one branch of the {} can actually
|
472
529
|
# match, the same variables are used to hold the captures for each
|
473
530
|
# branch)
|
474
531
|
enum = tokens_until('}', 'union')
|
532
|
+
enum = unify_in_union(enum)
|
475
533
|
terms = insure_same_captures(enum, 'branch of {}')
|
476
534
|
.map { compile_expr }
|
477
535
|
|
@@ -496,6 +554,19 @@ module RuboCop
|
|
496
554
|
with_context("#{CUR_NODE} && #{compile_expr}", "#{CUR_NODE}.parent")
|
497
555
|
end
|
498
556
|
|
557
|
+
def compile_descend
|
558
|
+
with_temp_variables do |descendant|
|
559
|
+
pattern = with_context(compile_expr, descendant,
|
560
|
+
use_temp_node: false)
|
561
|
+
[
|
562
|
+
"RuboCop::NodePattern.descend(#{CUR_ELEMENT}).",
|
563
|
+
"any? do |#{descendant}|",
|
564
|
+
" #{pattern}",
|
565
|
+
'end'
|
566
|
+
].join("\n")
|
567
|
+
end
|
568
|
+
end
|
569
|
+
|
499
570
|
def compile_wildcard(name)
|
500
571
|
if name.empty?
|
501
572
|
'true'
|
@@ -503,12 +574,12 @@ module RuboCop
|
|
503
574
|
# we have already seen a wildcard with this name before
|
504
575
|
# so the value it matched the first time will already be stored
|
505
576
|
# in a temp. check if this value matches the one stored in the temp
|
506
|
-
"#{CUR_ELEMENT} ==
|
577
|
+
"#{CUR_ELEMENT} == #{access_unify(name)}"
|
507
578
|
else
|
508
|
-
n = @unify[name] =
|
509
|
-
# double assign to
|
510
|
-
"(
|
511
|
-
"
|
579
|
+
n = @unify[name] = "unify_#{name.gsub('-', '__')}"
|
580
|
+
# double assign to avoid "assigned but unused variable"
|
581
|
+
"(#{n} = #{CUR_ELEMENT}; " \
|
582
|
+
"#{n} = #{n}; true)"
|
512
583
|
end
|
513
584
|
end
|
514
585
|
|
@@ -560,9 +631,8 @@ module RuboCop
|
|
560
631
|
def compile_arg(token)
|
561
632
|
case token
|
562
633
|
when WILDCARD then
|
563
|
-
name
|
564
|
-
|
565
|
-
"temp#{number}"
|
634
|
+
name = token[1..-1]
|
635
|
+
access_unify(name) || fail_due_to('invalid in arglist: ' + token)
|
566
636
|
when LITERAL then token
|
567
637
|
when PARAM then get_param(token[1..-1])
|
568
638
|
when CLOSING then fail_due_to("#{token} in invalid position")
|
@@ -796,6 +866,22 @@ module RuboCop
|
|
796
866
|
def to_s
|
797
867
|
"#<#{self.class} #{pattern}>"
|
798
868
|
end
|
869
|
+
|
870
|
+
# Yields its argument and any descendants, depth-first.
|
871
|
+
#
|
872
|
+
def self.descend(element, &block)
|
873
|
+
return to_enum(__method__, element) unless block_given?
|
874
|
+
|
875
|
+
yield element
|
876
|
+
|
877
|
+
if element.is_a?(::RuboCop::AST::Node)
|
878
|
+
element.children.each do |child|
|
879
|
+
descend(child, &block)
|
880
|
+
end
|
881
|
+
end
|
882
|
+
|
883
|
+
nil
|
884
|
+
end
|
799
885
|
end
|
800
886
|
end
|
801
887
|
# rubocop:enable Metrics/ClassLength, Metrics/CyclomaticComplexity
|
data/lib/rubocop/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rubocop
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.80.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bozhidar Batsov
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: exe
|
12
12
|
cert_chain: []
|
13
|
-
date: 2020-
|
13
|
+
date: 2020-02-18 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: jaro_winkler
|
@@ -74,6 +74,20 @@ dependencies:
|
|
74
74
|
- - "<"
|
75
75
|
- !ruby/object:Gem::Version
|
76
76
|
version: '4.0'
|
77
|
+
- !ruby/object:Gem::Dependency
|
78
|
+
name: rexml
|
79
|
+
requirement: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
type: :runtime
|
85
|
+
prerelease: false
|
86
|
+
version_requirements: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
77
91
|
- !ruby/object:Gem::Dependency
|
78
92
|
name: ruby-progressbar
|
79
93
|
requirement: !ruby/object:Gem::Requirement
|
@@ -461,6 +475,7 @@ files:
|
|
461
475
|
- lib/rubocop/cop/mixin/first_element_line_break.rb
|
462
476
|
- lib/rubocop/cop/mixin/frozen_string_literal.rb
|
463
477
|
- lib/rubocop/cop/mixin/hash_alignment_styles.rb
|
478
|
+
- lib/rubocop/cop/mixin/hash_transform_method.rb
|
464
479
|
- lib/rubocop/cop/mixin/heredoc.rb
|
465
480
|
- lib/rubocop/cop/mixin/ignored_methods.rb
|
466
481
|
- lib/rubocop/cop/mixin/ignored_pattern.rb
|
@@ -536,7 +551,6 @@ files:
|
|
536
551
|
- lib/rubocop/cop/style/begin_block.rb
|
537
552
|
- lib/rubocop/cop/style/block_comments.rb
|
538
553
|
- lib/rubocop/cop/style/block_delimiters.rb
|
539
|
-
- lib/rubocop/cop/style/braces_around_hash_parameters.rb
|
540
554
|
- lib/rubocop/cop/style/case_equality.rb
|
541
555
|
- lib/rubocop/cop/style/character_literal.rb
|
542
556
|
- lib/rubocop/cop/style/class_and_module_children.rb
|
@@ -579,7 +593,10 @@ files:
|
|
579
593
|
- lib/rubocop/cop/style/frozen_string_literal_comment.rb
|
580
594
|
- lib/rubocop/cop/style/global_vars.rb
|
581
595
|
- lib/rubocop/cop/style/guard_clause.rb
|
596
|
+
- lib/rubocop/cop/style/hash_each_methods.rb
|
582
597
|
- lib/rubocop/cop/style/hash_syntax.rb
|
598
|
+
- lib/rubocop/cop/style/hash_transform_keys.rb
|
599
|
+
- lib/rubocop/cop/style/hash_transform_values.rb
|
583
600
|
- lib/rubocop/cop/style/identical_conditional_branches.rb
|
584
601
|
- lib/rubocop/cop/style/if_inside_else.rb
|
585
602
|
- lib/rubocop/cop/style/if_unless_modifier.rb
|
@@ -723,6 +740,7 @@ files:
|
|
723
740
|
- lib/rubocop/formatter/fuubar_style_formatter.rb
|
724
741
|
- lib/rubocop/formatter/html_formatter.rb
|
725
742
|
- lib/rubocop/formatter/json_formatter.rb
|
743
|
+
- lib/rubocop/formatter/junit_formatter.rb
|
726
744
|
- lib/rubocop/formatter/offense_count_formatter.rb
|
727
745
|
- lib/rubocop/formatter/pacman_formatter.rb
|
728
746
|
- lib/rubocop/formatter/progress_formatter.rb
|
@@ -1,209 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module RuboCop
|
4
|
-
module Cop
|
5
|
-
module Style
|
6
|
-
# This cop checks for braces around the last parameter in a method call
|
7
|
-
# if the last parameter is a hash.
|
8
|
-
# It supports `braces`, `no_braces` and `context_dependent` styles.
|
9
|
-
#
|
10
|
-
# @example EnforcedStyle: braces
|
11
|
-
# # The `braces` style enforces braces around all method
|
12
|
-
# # parameters that are hashes.
|
13
|
-
#
|
14
|
-
# # bad
|
15
|
-
# some_method(x, y, a: 1, b: 2)
|
16
|
-
#
|
17
|
-
# # good
|
18
|
-
# some_method(x, y, {a: 1, b: 2})
|
19
|
-
#
|
20
|
-
# @example EnforcedStyle: no_braces (default)
|
21
|
-
# # The `no_braces` style checks that the last parameter doesn't
|
22
|
-
# # have braces around it.
|
23
|
-
#
|
24
|
-
# # bad
|
25
|
-
# some_method(x, y, {a: 1, b: 2})
|
26
|
-
#
|
27
|
-
# # good
|
28
|
-
# some_method(x, y, a: 1, b: 2)
|
29
|
-
#
|
30
|
-
# @example EnforcedStyle: context_dependent
|
31
|
-
# # The `context_dependent` style checks that the last parameter
|
32
|
-
# # doesn't have braces around it, but requires braces if the
|
33
|
-
# # second to last parameter is also a hash literal.
|
34
|
-
#
|
35
|
-
# # bad
|
36
|
-
# some_method(x, y, {a: 1, b: 2})
|
37
|
-
# some_method(x, y, {a: 1, b: 2}, a: 1, b: 2)
|
38
|
-
#
|
39
|
-
# # good
|
40
|
-
# some_method(x, y, a: 1, b: 2)
|
41
|
-
# some_method(x, y, {a: 1, b: 2}, {a: 1, b: 2})
|
42
|
-
class BracesAroundHashParameters < Cop
|
43
|
-
include ConfigurableEnforcedStyle
|
44
|
-
include RangeHelp
|
45
|
-
|
46
|
-
MSG = '%<type>s curly braces around a hash parameter.'
|
47
|
-
|
48
|
-
def on_send(node)
|
49
|
-
return if node.assignment_method? || node.operator_method?
|
50
|
-
|
51
|
-
return unless node.arguments? && node.last_argument.hash_type? &&
|
52
|
-
!node.last_argument.empty?
|
53
|
-
|
54
|
-
check(node.last_argument, node.arguments)
|
55
|
-
end
|
56
|
-
alias on_csend on_send
|
57
|
-
|
58
|
-
def autocorrect(send_node)
|
59
|
-
hash_node = send_node.last_argument
|
60
|
-
|
61
|
-
lambda do |corrector|
|
62
|
-
if hash_node.braces?
|
63
|
-
remove_braces_with_whitespace(corrector,
|
64
|
-
hash_node,
|
65
|
-
extra_space(hash_node))
|
66
|
-
else
|
67
|
-
add_braces(corrector, hash_node)
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
private
|
73
|
-
|
74
|
-
def check(arg, args)
|
75
|
-
case style
|
76
|
-
when :braces
|
77
|
-
check_braces(arg)
|
78
|
-
when :no_braces
|
79
|
-
check_no_braces(arg)
|
80
|
-
when :context_dependent
|
81
|
-
check_context_dependent(arg, args)
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
def check_braces(arg)
|
86
|
-
add_arg_offense(arg, :missing) unless arg.braces?
|
87
|
-
end
|
88
|
-
|
89
|
-
def check_no_braces(arg)
|
90
|
-
return unless arg.braces? && !braces_needed_for_semantics?(arg)
|
91
|
-
|
92
|
-
add_arg_offense(arg, :redundant)
|
93
|
-
end
|
94
|
-
|
95
|
-
def check_context_dependent(arg, args)
|
96
|
-
braces_around_second_from_end = args.size > 1 && args[-2].hash_type?
|
97
|
-
|
98
|
-
if arg.braces?
|
99
|
-
unless braces_around_second_from_end ||
|
100
|
-
braces_needed_for_semantics?(arg)
|
101
|
-
add_arg_offense(arg, :redundant)
|
102
|
-
end
|
103
|
-
elsif braces_around_second_from_end
|
104
|
-
add_arg_offense(arg, :missing)
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
# Returns true if there's block inside the braces of the given hash arg
|
109
|
-
# and that block uses do..end. The reason for wanting to check this is
|
110
|
-
# that the do..end could bind to a different method invocation if the
|
111
|
-
# hash braces were removed.
|
112
|
-
def braces_needed_for_semantics?(arg)
|
113
|
-
arg.each_pair do |_key, value|
|
114
|
-
return true if value.block_type? && !value.braces?
|
115
|
-
end
|
116
|
-
false
|
117
|
-
end
|
118
|
-
|
119
|
-
def add_arg_offense(arg, type)
|
120
|
-
add_offense(arg.parent, location: arg.source_range,
|
121
|
-
message: format(MSG,
|
122
|
-
type: type.to_s.capitalize))
|
123
|
-
end
|
124
|
-
|
125
|
-
def extra_space(hash_node)
|
126
|
-
{
|
127
|
-
newlines: extra_left_space?(hash_node) &&
|
128
|
-
extra_right_space?(hash_node),
|
129
|
-
left: extra_left_space?(hash_node),
|
130
|
-
right: extra_right_space?(hash_node)
|
131
|
-
}
|
132
|
-
end
|
133
|
-
|
134
|
-
def extra_left_space?(hash_node)
|
135
|
-
@extra_left_space ||= begin
|
136
|
-
top_line = hash_node.source_range.source_line
|
137
|
-
top_line.delete(' ') == '{'
|
138
|
-
end
|
139
|
-
end
|
140
|
-
|
141
|
-
def extra_right_space?(hash_node)
|
142
|
-
@extra_right_space ||= begin
|
143
|
-
bottom_line_number = hash_node.source_range.last_line
|
144
|
-
bottom_line = processed_source.lines[bottom_line_number - 1]
|
145
|
-
bottom_line.delete(' ') == '}'
|
146
|
-
end
|
147
|
-
end
|
148
|
-
|
149
|
-
def remove_braces_with_whitespace(corrector, node, space)
|
150
|
-
loc = node.loc
|
151
|
-
|
152
|
-
if node.multiline?
|
153
|
-
remove_braces_with_range(corrector,
|
154
|
-
left_whole_line_range(loc.begin),
|
155
|
-
right_whole_line_range(loc.end))
|
156
|
-
else
|
157
|
-
remove_braces_with_range(corrector,
|
158
|
-
left_brace_and_space(loc.begin, space),
|
159
|
-
right_brace_and_space(loc.end, space))
|
160
|
-
end
|
161
|
-
end
|
162
|
-
|
163
|
-
def remove_braces_with_range(corrector, left_range, right_range)
|
164
|
-
corrector.remove(left_range)
|
165
|
-
corrector.remove(right_range)
|
166
|
-
end
|
167
|
-
|
168
|
-
def left_whole_line_range(loc_begin)
|
169
|
-
if range_by_whole_lines(loc_begin).source.strip == '{'
|
170
|
-
range_by_whole_lines(loc_begin, include_final_newline: true)
|
171
|
-
else
|
172
|
-
loc_begin
|
173
|
-
end
|
174
|
-
end
|
175
|
-
|
176
|
-
def right_whole_line_range(loc_end)
|
177
|
-
if range_by_whole_lines(loc_end).source.strip =~ /\A}\s*,?\z/
|
178
|
-
range_by_whole_lines(loc_end, include_final_newline: true)
|
179
|
-
else
|
180
|
-
loc_end
|
181
|
-
end
|
182
|
-
end
|
183
|
-
|
184
|
-
def left_brace_and_space(loc_begin, space)
|
185
|
-
range_with_surrounding_space(range: loc_begin,
|
186
|
-
side: :right,
|
187
|
-
newlines: space[:newlines],
|
188
|
-
whitespace: space[:left])
|
189
|
-
end
|
190
|
-
|
191
|
-
def right_brace_and_space(loc_end, space)
|
192
|
-
brace_and_space =
|
193
|
-
range_with_surrounding_space(
|
194
|
-
range: loc_end,
|
195
|
-
side: :left,
|
196
|
-
newlines: space[:newlines],
|
197
|
-
whitespace: space[:right]
|
198
|
-
)
|
199
|
-
range_with_surrounding_comma(brace_and_space, :left)
|
200
|
-
end
|
201
|
-
|
202
|
-
def add_braces(corrector, node)
|
203
|
-
corrector.insert_before(node.source_range, '{')
|
204
|
-
corrector.insert_after(node.source_range, '}')
|
205
|
-
end
|
206
|
-
end
|
207
|
-
end
|
208
|
-
end
|
209
|
-
end
|