rubocop-elegant 0.3.0 → 0.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5a4408943315c5a59e194dcb473bc2b48e7d1b2c07182a155a27ce613bc7bef5
4
- data.tar.gz: a4f9028d7cd0b076315f65b9e00a0ee0c204f395a821957a036795bcf14234ff
3
+ metadata.gz: 0441b4d9f29a02a65680ffc46fb56985251723336a076999f7867ad7eb92185d
4
+ data.tar.gz: 92771eff5ed80253a14a85fd96ecaace40e7ce0476f2e26c6cb42372a26b9706
5
5
  SHA512:
6
- metadata.gz: 1489006295818667d80b86717b323f179e739cc7a5cf5ff4716d8c07cac67aa6f1ab3d8236a35636f290ea147315f1e19461a1cb17f50461a48d5e48e6b1e098
7
- data.tar.gz: 0c42fda07e77a2ac808325c596e57a207d1cdc19ac05d7807d2318d69d1ca91822144c75c97c14d4fcbb6656bb92fe36c3d7ea0616480c5daf388393a254b8f7
6
+ metadata.gz: b1fd06eba3ce66013593a26400d81698fa3eeb01725890aa3ae5e0f78d086729b6b633f9d6a0551e61e2c1118ad844a12daba2ab97da5306ff13b8feb48a3d2b
7
+ data.tar.gz: 7595e8287a2f7f3904d643c98c1bafc412c2bfebcf4eb2de364ae13af4badf491985eba022f86cf93362efb997d0df57a171068a3ecc1ffb0b27d0c0cbf6e028
@@ -19,7 +19,19 @@
19
19
  # whether or when the right-hand side is evaluated. A variable that
20
20
  # is reassigned, read more than once, or never read is left alone
21
21
  # too.
22
+ #
23
+ # Auto-correct inlines the redundant assignment: it replaces the
24
+ # single +lvar+ read with the source of the assignment's right-hand
25
+ # side, then removes the whole assignment line including its leading
26
+ # indent and trailing newline. The right-hand side is wrapped in
27
+ # parentheses unless it is already a primary expression (literal,
28
+ # variable, parenthesized expression, or method call with parentheses
29
+ # or no arguments), so that operator precedence at the read site is
30
+ # preserved.
22
31
  class RuboCop::Cop::Elegant::NoRedundantVariable < RuboCop::Cop::Base
32
+ extend RuboCop::Cop::AutoCorrector
33
+ include RuboCop::Cop::RangeHelp
34
+
23
35
  MSG = 'Variable "%<name>s" is redundant and must be inlined: it is read only once'
24
36
  public_constant :MSG
25
37
 
@@ -35,6 +47,12 @@ class RuboCop::Cop::Elegant::NoRedundantVariable < RuboCop::Cop::Base
35
47
  ALWAYS_HOIST_TYPES = %i[rescue resbody ensure].freeze
36
48
  public_constant :ALWAYS_HOIST_TYPES
37
49
 
50
+ PRIMARY_TYPES = %i[
51
+ int float str sym dstr dsym xstr true false nil array hash regexp
52
+ lvar ivar cvar gvar const self nth_ref back_ref
53
+ ].freeze
54
+ public_constant :PRIMARY_TYPES
55
+
38
56
  def on_def(node)
39
57
  check(node.body)
40
58
  end
@@ -56,10 +74,52 @@ class RuboCop::Cop::Elegant::NoRedundantVariable < RuboCop::Cop::Base
56
74
  next unless nodes.size == 1
57
75
  next unless reads[name].size == 1
58
76
  next if hoisted?(reads[name].first, nodes.first)
59
- add_offense(nodes.first, message: format(MSG, name: name))
77
+ register(nodes.first, reads[name].first, name)
78
+ end
79
+ end
80
+
81
+ def register(assign, read, name)
82
+ return add_offense(assign, message: format(MSG, name: name)) unless solo?(assign)
83
+ add_offense(assign, message: format(MSG, name: name)) do |corrector|
84
+ corrector.replace(read.source_range, inlined(assign.children.last, read))
85
+ corrector.remove(range_by_whole_lines(assign.source_range, include_final_newline: true))
60
86
  end
61
87
  end
62
88
 
89
+ def solo?(assign)
90
+ range = assign.source_range
91
+ return false unless range.first_line == range.last_line
92
+ range.source_buffer.source_line(range.first_line).strip == range.source.strip
93
+ end
94
+
95
+ def inlined(rhs, read)
96
+ return "(#{braced(rhs)})" if wrap?(rhs, read)
97
+ braced(rhs)
98
+ end
99
+
100
+ def braced(rhs)
101
+ return "{ #{rhs.source} }" if rhs.hash_type? && rhs.loc.begin.nil?
102
+ rhs.source
103
+ end
104
+
105
+ def wrap?(rhs, read)
106
+ return true unless primary?(rhs)
107
+ rhs.hash_type? && bare?(read)
108
+ end
109
+
110
+ def primary?(node)
111
+ return true if PRIMARY_TYPES.include?(node.type)
112
+ return !node.loc.begin.nil? || node.arguments.empty? if node.send_type? || node.csend_type?
113
+ node.begin_type? && node.children.size == 1
114
+ end
115
+
116
+ def bare?(read)
117
+ parent = read.parent
118
+ return false if parent.nil?
119
+ return false unless parent.send_type? || parent.csend_type?
120
+ parent.loc.begin.nil? && parent.arguments.include?(read)
121
+ end
122
+
63
123
  def walk(node, assigns, reads, tainted)
64
124
  return unless node.is_a?(RuboCop::AST::Node)
65
125
  return if node.def_type? || node.defs_type?
@@ -9,8 +9,17 @@
9
9
  # closing). Brackets stranded in the middle of a multi-line expression
10
10
  # are forbidden because they hide the structure of the code.
11
11
  #
12
+ # Auto-correct relocates the offending bracket onto its own line: an
13
+ # opener that is not at end-of-line gets a newline and the opener-line
14
+ # indent plus two spaces inserted right after it; a closer that is not
15
+ # at start-of-line gets a newline and the opener-line indent inserted
16
+ # right before it. Surrounding indentation may still need a follow-up
17
+ # layout pass, but the brackets themselves end up paired.
18
+ #
12
19
  # See https://www.yegor256.com/2014/10/23/paired-brackets-notation.html
13
20
  class RuboCop::Cop::Elegant::PairedBrackets < RuboCop::Cop::Base
21
+ extend RuboCop::Cop::AutoCorrector
22
+
14
23
  MSG = 'Bracket %<text>s must be paired on the same line, or start/end its line'
15
24
  public_constant :MSG
16
25
 
@@ -43,8 +52,9 @@ class RuboCop::Cop::Elegant::PairedBrackets < RuboCop::Cop::Base
43
52
  def check(duo)
44
53
  opener, closer = duo
45
54
  return if opener.line == closer.line
46
- register(opener) unless ends?(opener)
47
- register(closer) unless starts?(closer)
55
+ indent = leading(opener)
56
+ register(opener) { |corrector| corrector.insert_after(opener.pos, "\n#{indent} ") } unless ends?(opener)
57
+ register(closer) { |corrector| corrector.insert_before(closer.pos, "\n#{indent}") } unless starts?(closer)
48
58
  end
49
59
 
50
60
  def starts?(tok)
@@ -56,7 +66,11 @@ class RuboCop::Cop::Elegant::PairedBrackets < RuboCop::Cop::Base
56
66
  after.empty? || after.start_with?('#')
57
67
  end
58
68
 
59
- def register(tok)
60
- add_offense(tok.pos, message: format(MSG, text: tok.text))
69
+ def leading(tok)
70
+ processed_source.lines[tok.line - 1][/\A[ \t]*/]
71
+ end
72
+
73
+ def register(tok, &block)
74
+ add_offense(tok.pos, message: format(MSG, text: tok.text), &block)
61
75
  end
62
76
  end
@@ -9,7 +9,7 @@ Gem::Specification.new do |s|
9
9
  s.required_rubygems_version = Gem::Requirement.new('>= 0') if s.respond_to?(:required_rubygems_version=)
10
10
  s.required_ruby_version = '>=2.2'
11
11
  s.name = 'rubocop-elegant'
12
- s.version = '0.3.0'
12
+ s.version = '0.4.0'
13
13
  s.license = 'MIT'
14
14
  s.summary = 'Set of custom RuboCop cops for elegant Ruby coding'
15
15
  s.description =
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop-elegant
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yegor Bugayenko