rubocop-elegant 0.2.0 → 0.3.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/README.md +13 -0
- data/config/default.yml +4 -0
- data/lib/rubocop/cop/elegant/class_in_module.rb +1 -2
- data/lib/rubocop/cop/elegant/indentation_ladder.rb +1 -2
- data/lib/rubocop/cop/elegant/no_comments.rb +3 -6
- data/lib/rubocop/cop/elegant/no_empty_lines_in_blocks.rb +1 -2
- data/lib/rubocop/cop/elegant/no_empty_lines_in_methods.rb +1 -2
- data/lib/rubocop/cop/elegant/no_redundant_variable.rb +108 -0
- data/lib/rubocop/cop/elegant/paired_brackets.rb +2 -4
- data/lib/rubocop/cop/elegant_cops.rb +1 -0
- data/rubocop-elegant.gemspec +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5a4408943315c5a59e194dcb473bc2b48e7d1b2c07182a155a27ce613bc7bef5
|
|
4
|
+
data.tar.gz: a4f9028d7cd0b076315f65b9e00a0ee0c204f395a821957a036795bcf14234ff
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1489006295818667d80b86717b323f179e739cc7a5cf5ff4716d8c07cac67aa6f1ab3d8236a35636f290ea147315f1e19461a1cb17f50461a48d5e48e6b1e098
|
|
7
|
+
data.tar.gz: 0c42fda07e77a2ac808325c596e57a207d1cdc19ac05d7807d2318d69d1ca91822144c75c97c14d4fcbb6656bb92fe36c3d7ea0616480c5daf388393a254b8f7
|
data/README.md
CHANGED
|
@@ -18,6 +18,19 @@ Default RuboCop configuration is too permissive, allowing coding practices
|
|
|
18
18
|
cryptic variable names, and so on.
|
|
19
19
|
This plugin configures existing RuboCop cops with stricter settings
|
|
20
20
|
and adds custom cops for rules that RuboCop doesn't provide out of the box.
|
|
21
|
+
The custom cops add the following restrictions:
|
|
22
|
+
|
|
23
|
+
* Classes must live inside a module; top-level classes are forbidden.
|
|
24
|
+
* A class nested in a module must use compact namespace syntax.
|
|
25
|
+
* Method names must be single lowercase verbs.
|
|
26
|
+
* Variable names must be single lowercase nouns.
|
|
27
|
+
* Each indentation step must add exactly two spaces.
|
|
28
|
+
* Comments are forbidden, except SPDX, magic, RuboCop directives, and class docblocks.
|
|
29
|
+
* Empty lines inside block bodies are forbidden.
|
|
30
|
+
* Empty lines inside method bodies are forbidden.
|
|
31
|
+
* A method cannot return `nil` explicitly.
|
|
32
|
+
* Local variables that are assigned once and read once must be inlined.
|
|
33
|
+
* Brackets must be paired on the same line, or start/end their own line.
|
|
21
34
|
|
|
22
35
|
First, install it:
|
|
23
36
|
|
data/config/default.yml
CHANGED
|
@@ -21,6 +21,10 @@ Elegant/NoNilReturn:
|
|
|
21
21
|
Description: 'Forbids a method or function from returning nil on any code path'
|
|
22
22
|
Enabled: true
|
|
23
23
|
VersionAdded: '0.1.0'
|
|
24
|
+
Elegant/NoRedundantVariable:
|
|
25
|
+
Description: 'Forbids local variables that are assigned once and read once; such variables must be inlined'
|
|
26
|
+
Enabled: true
|
|
27
|
+
VersionAdded: '0.3.0'
|
|
24
28
|
Elegant/PairedBrackets:
|
|
25
29
|
Description: 'Enforces the paired brackets notation: a bracket must pair on the same line, or start/end its line'
|
|
26
30
|
Enabled: true
|
|
@@ -44,7 +44,6 @@ class RuboCop::Cop::Elegant::IndentationLadder < RuboCop::Cop::Base
|
|
|
44
44
|
end
|
|
45
45
|
|
|
46
46
|
def register(num, step)
|
|
47
|
-
|
|
48
|
-
add_offense(target, message: format(MSG, step: step))
|
|
47
|
+
add_offense(processed_source.buffer.line_range(num), message: format(MSG, step: step))
|
|
49
48
|
end
|
|
50
49
|
end
|
|
@@ -48,8 +48,7 @@ class RuboCop::Cop::Elegant::NoComments < RuboCop::Cop::Base
|
|
|
48
48
|
end
|
|
49
49
|
|
|
50
50
|
def docblock?(comment)
|
|
51
|
-
|
|
52
|
-
successor = codeline(line)
|
|
51
|
+
successor = codeline(comment.location.line)
|
|
53
52
|
successor.positive? && definition?(successor)
|
|
54
53
|
end
|
|
55
54
|
|
|
@@ -88,15 +87,13 @@ class RuboCop::Cop::Elegant::NoComments < RuboCop::Cop::Base
|
|
|
88
87
|
end
|
|
89
88
|
|
|
90
89
|
def fullrange(target)
|
|
91
|
-
start = target.begin_pos - target.column
|
|
92
90
|
ending = target.end_pos
|
|
93
91
|
ending += 1 if newline?(ending)
|
|
94
|
-
target.with(begin_pos:
|
|
92
|
+
target.with(begin_pos: target.begin_pos - target.column, end_pos: ending)
|
|
95
93
|
end
|
|
96
94
|
|
|
97
95
|
def prefixed(target, prefix)
|
|
98
|
-
|
|
99
|
-
target.with(begin_pos: target.begin_pos - spaces.length, end_pos: target.end_pos)
|
|
96
|
+
target.with(begin_pos: target.begin_pos - prefix.match(/\s*$/)[0].length, end_pos: target.end_pos)
|
|
100
97
|
end
|
|
101
98
|
|
|
102
99
|
def newline?(pos)
|
|
@@ -70,8 +70,7 @@ class RuboCop::Cop::Elegant::NoEmptyLinesInBlocks < RuboCop::Cop::Base
|
|
|
70
70
|
result = []
|
|
71
71
|
lines.each do |num|
|
|
72
72
|
next if @gaps.include?(num)
|
|
73
|
-
|
|
74
|
-
result << num if line.strip.empty?
|
|
73
|
+
result << num if processed_source.lines[num - 1].strip.empty?
|
|
75
74
|
end
|
|
76
75
|
result
|
|
77
76
|
end
|
|
@@ -36,8 +36,7 @@ class RuboCop::Cop::Elegant::NoEmptyLinesInMethods < RuboCop::Cop::Base
|
|
|
36
36
|
def empty(lines)
|
|
37
37
|
result = []
|
|
38
38
|
lines.each do |num|
|
|
39
|
-
|
|
40
|
-
result << num if line.strip.empty?
|
|
39
|
+
result << num if processed_source.lines[num - 1].strip.empty?
|
|
41
40
|
end
|
|
42
41
|
result
|
|
43
42
|
end
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-FileCopyrightText: Copyright (c) 2019-2026 Yegor Bugayenko
|
|
4
|
+
# SPDX-License-Identifier: MIT
|
|
5
|
+
|
|
6
|
+
# Forbids local variables that are assigned exactly once and then
|
|
7
|
+
# referenced exactly once, since such variables can be inlined at
|
|
8
|
+
# the place of their single use without loss of clarity. The cop
|
|
9
|
+
# inspects each method body in isolation, ignores compound
|
|
10
|
+
# assignments (+=, ||=, &&=), multiple assignments, +for+ loops,
|
|
11
|
+
# rescue variables, and assignments embedded into expressions such
|
|
12
|
+
# as +if+ or +while+ conditions; only top-level statements of a
|
|
13
|
+
# sequence are considered. A variable whose single read sits inside
|
|
14
|
+
# a context that does not also enclose the assignment is left alone:
|
|
15
|
+
# loops or blocks (+block+, +numblock+, +while+, +until+, +for+) so
|
|
16
|
+
# that inlining does not move the right-hand side into a hot path,
|
|
17
|
+
# and conditional branches (+if+, +case+, +case_match+, +and+, +or+,
|
|
18
|
+
# +rescue+, +resbody+, +ensure+) so that inlining does not change
|
|
19
|
+
# whether or when the right-hand side is evaluated. A variable that
|
|
20
|
+
# is reassigned, read more than once, or never read is left alone
|
|
21
|
+
# too.
|
|
22
|
+
class RuboCop::Cop::Elegant::NoRedundantVariable < RuboCop::Cop::Base
|
|
23
|
+
MSG = 'Variable "%<name>s" is redundant and must be inlined: it is read only once'
|
|
24
|
+
public_constant :MSG
|
|
25
|
+
|
|
26
|
+
STATEMENT_PARENTS = %i[begin kwbegin def defs block numblock].freeze
|
|
27
|
+
public_constant :STATEMENT_PARENTS
|
|
28
|
+
|
|
29
|
+
LOOP_TYPES = %i[block numblock while until while_post until_post for].freeze
|
|
30
|
+
public_constant :LOOP_TYPES
|
|
31
|
+
|
|
32
|
+
ALWAYS_FIRST_TYPES = %i[if case case_match and or].freeze
|
|
33
|
+
public_constant :ALWAYS_FIRST_TYPES
|
|
34
|
+
|
|
35
|
+
ALWAYS_HOIST_TYPES = %i[rescue resbody ensure].freeze
|
|
36
|
+
public_constant :ALWAYS_HOIST_TYPES
|
|
37
|
+
|
|
38
|
+
def on_def(node)
|
|
39
|
+
check(node.body)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def on_defs(node)
|
|
43
|
+
check(node.body)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
def check(body)
|
|
49
|
+
return if body.nil?
|
|
50
|
+
assigns = Hash.new { |h, k| h[k] = [] }
|
|
51
|
+
reads = Hash.new { |h, k| h[k] = [] }
|
|
52
|
+
tainted = []
|
|
53
|
+
walk(body, assigns, reads, tainted)
|
|
54
|
+
assigns.each do |name, nodes|
|
|
55
|
+
next if tainted.include?(name)
|
|
56
|
+
next unless nodes.size == 1
|
|
57
|
+
next unless reads[name].size == 1
|
|
58
|
+
next if hoisted?(reads[name].first, nodes.first)
|
|
59
|
+
add_offense(nodes.first, message: format(MSG, name: name))
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def walk(node, assigns, reads, tainted)
|
|
64
|
+
return unless node.is_a?(RuboCop::AST::Node)
|
|
65
|
+
return if node.def_type? || node.defs_type?
|
|
66
|
+
record(node, assigns, reads, tainted)
|
|
67
|
+
node.each_child_node { |child| walk(child, assigns, reads, tainted) }
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def record(node, assigns, reads, tainted)
|
|
71
|
+
if node.op_asgn_type? || node.or_asgn_type? || node.and_asgn_type?
|
|
72
|
+
taint(node.children.first, tainted)
|
|
73
|
+
elsif node.lvasgn_type? && statement?(node)
|
|
74
|
+
assigns[node.children.first] << node
|
|
75
|
+
elsif node.lvar_type?
|
|
76
|
+
reads[node.children.first] << node
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def taint(target, tainted)
|
|
81
|
+
return unless target.is_a?(RuboCop::AST::Node)
|
|
82
|
+
return unless target.lvasgn_type?
|
|
83
|
+
tainted << target.children.first
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def statement?(node)
|
|
87
|
+
return false unless node.children.size == 2
|
|
88
|
+
parent = node.parent
|
|
89
|
+
parent = parent.parent while !parent.nil? && parent.type == :begin && parent.children.size == 1
|
|
90
|
+
return false if parent.nil?
|
|
91
|
+
STATEMENT_PARENTS.include?(parent.type)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def hoisted?(read, assign)
|
|
95
|
+
boundary = assign.parent
|
|
96
|
+
return true if boundary.nil?
|
|
97
|
+
child = read
|
|
98
|
+
parent = read.parent
|
|
99
|
+
while !parent.nil? && !parent.equal?(boundary)
|
|
100
|
+
return true if LOOP_TYPES.include?(parent.type)
|
|
101
|
+
return true if ALWAYS_HOIST_TYPES.include?(parent.type)
|
|
102
|
+
return true if ALWAYS_FIRST_TYPES.include?(parent.type) && !parent.children.first.equal?(child)
|
|
103
|
+
child = parent
|
|
104
|
+
parent = parent.parent
|
|
105
|
+
end
|
|
106
|
+
parent.nil?
|
|
107
|
+
end
|
|
108
|
+
end
|
|
@@ -48,13 +48,11 @@ class RuboCop::Cop::Elegant::PairedBrackets < RuboCop::Cop::Base
|
|
|
48
48
|
end
|
|
49
49
|
|
|
50
50
|
def starts?(tok)
|
|
51
|
-
|
|
52
|
-
line[0...tok.column].strip.empty?
|
|
51
|
+
processed_source.lines[tok.line - 1][0...tok.column].strip.empty?
|
|
53
52
|
end
|
|
54
53
|
|
|
55
54
|
def ends?(tok)
|
|
56
|
-
|
|
57
|
-
after = line[(tok.column + tok.text.length)..-1].to_s.strip
|
|
55
|
+
after = processed_source.lines[tok.line - 1][(tok.column + tok.text.length)..-1].to_s.strip
|
|
58
56
|
after.empty? || after.start_with?('#')
|
|
59
57
|
end
|
|
60
58
|
|
|
@@ -14,4 +14,5 @@ require_relative 'elegant/no_comments'
|
|
|
14
14
|
require_relative 'elegant/no_empty_lines_in_blocks'
|
|
15
15
|
require_relative 'elegant/no_empty_lines_in_methods'
|
|
16
16
|
require_relative 'elegant/no_nil_return'
|
|
17
|
+
require_relative 'elegant/no_redundant_variable'
|
|
17
18
|
require_relative 'elegant/paired_brackets'
|
data/rubocop-elegant.gemspec
CHANGED
|
@@ -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.
|
|
12
|
+
s.version = '0.3.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.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Yegor Bugayenko
|
|
@@ -62,6 +62,7 @@ files:
|
|
|
62
62
|
- lib/rubocop/cop/elegant/no_empty_lines_in_blocks.rb
|
|
63
63
|
- lib/rubocop/cop/elegant/no_empty_lines_in_methods.rb
|
|
64
64
|
- lib/rubocop/cop/elegant/no_nil_return.rb
|
|
65
|
+
- lib/rubocop/cop/elegant/no_redundant_variable.rb
|
|
65
66
|
- lib/rubocop/cop/elegant/paired_brackets.rb
|
|
66
67
|
- lib/rubocop/cop/elegant_cops.rb
|
|
67
68
|
- lib/rubocop/elegant/plugin.rb
|