simplabs-excellent 1.0.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.
- data/History.txt +3 -0
- data/README.markdown +30 -0
- data/VERSION.yml +4 -0
- data/bin/excellent +21 -0
- data/lib/simplabs/excellent.rb +12 -0
- data/lib/simplabs/excellent/checks.rb +16 -0
- data/lib/simplabs/excellent/checks/abc_metric_method_check.rb +80 -0
- data/lib/simplabs/excellent/checks/assignment_in_conditional_check.rb +38 -0
- data/lib/simplabs/excellent/checks/base.rb +42 -0
- data/lib/simplabs/excellent/checks/case_missing_else_check.rb +25 -0
- data/lib/simplabs/excellent/checks/class_line_count_check.rb +34 -0
- data/lib/simplabs/excellent/checks/class_name_check.rb +37 -0
- data/lib/simplabs/excellent/checks/class_variable_check.rb +25 -0
- data/lib/simplabs/excellent/checks/control_coupling_check.rb +32 -0
- data/lib/simplabs/excellent/checks/cyclomatic_complexity_block_check.rb +32 -0
- data/lib/simplabs/excellent/checks/cyclomatic_complexity_check.rb +39 -0
- data/lib/simplabs/excellent/checks/cyclomatic_complexity_method_check.rb +33 -0
- data/lib/simplabs/excellent/checks/empty_rescue_body_check.rb +39 -0
- data/lib/simplabs/excellent/checks/for_loop_check.rb +25 -0
- data/lib/simplabs/excellent/checks/line_count_check.rb +45 -0
- data/lib/simplabs/excellent/checks/method_line_count_check.rb +34 -0
- data/lib/simplabs/excellent/checks/method_name_check.rb +34 -0
- data/lib/simplabs/excellent/checks/module_line_count_check.rb +34 -0
- data/lib/simplabs/excellent/checks/module_name_check.rb +34 -0
- data/lib/simplabs/excellent/checks/name_check.rb +32 -0
- data/lib/simplabs/excellent/checks/parameter_number_check.rb +37 -0
- data/lib/simplabs/excellent/core.rb +2 -0
- data/lib/simplabs/excellent/core/checking_visitor.rb +34 -0
- data/lib/simplabs/excellent/core/error.rb +31 -0
- data/lib/simplabs/excellent/core/extensions/underscore.rb +27 -0
- data/lib/simplabs/excellent/core/iterator_visitor.rb +29 -0
- data/lib/simplabs/excellent/core/parse_tree_runner.rb +88 -0
- data/lib/simplabs/excellent/core/parser.rb +33 -0
- data/lib/simplabs/excellent/core/visitable_sexp.rb +31 -0
- data/spec/checks/abc_metric_method_check_spec.rb +94 -0
- data/spec/checks/assignment_in_conditional_check_spec.rb +73 -0
- data/spec/checks/case_missing_else_check_spec.rb +42 -0
- data/spec/checks/class_line_count_check_spec.rb +49 -0
- data/spec/checks/class_name_check_spec.rb +48 -0
- data/spec/checks/class_variable_check_spec.rb +26 -0
- data/spec/checks/control_coupling_check_spec.rb +32 -0
- data/spec/checks/cyclomatic_complexity_block_check_spec.rb +51 -0
- data/spec/checks/cyclomatic_complexity_method_check_spec.rb +184 -0
- data/spec/checks/empty_rescue_body_check_spec.rb +132 -0
- data/spec/checks/for_loop_check_spec.rb +52 -0
- data/spec/checks/method_line_count_check_spec.rb +50 -0
- data/spec/checks/method_name_check_spec.rb +91 -0
- data/spec/checks/module_line_count_check_spec.rb +49 -0
- data/spec/checks/module_name_check_spec.rb +37 -0
- data/spec/checks/parameter_number_check_spec.rb +61 -0
- data/spec/core/extensions/underscore_spec.rb +13 -0
- data/spec/spec_helper.rb +11 -0
- metadata +115 -0
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'simplabs/excellent/checks/base'
|
2
|
+
|
3
|
+
module Simplabs
|
4
|
+
|
5
|
+
module Excellent
|
6
|
+
|
7
|
+
module Checks
|
8
|
+
|
9
|
+
class CyclomaticComplexityCheck < Base
|
10
|
+
|
11
|
+
COMPLEXITY_NODE_TYPES = [:if, :while, :until, :for, :rescue, :case, :when, :and, :or]
|
12
|
+
|
13
|
+
def initialize(threshold)
|
14
|
+
super()
|
15
|
+
@threshold = threshold
|
16
|
+
end
|
17
|
+
|
18
|
+
protected
|
19
|
+
|
20
|
+
def count_complexity(node)
|
21
|
+
count_branches(node) + 1
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def count_branches(node)
|
27
|
+
count = 0
|
28
|
+
count = count + 1 if COMPLEXITY_NODE_TYPES.include? node.node_type
|
29
|
+
node.children.each { |child| count += count_branches(child) }
|
30
|
+
count
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'simplabs/excellent/checks/cyclomatic_complexity_check'
|
2
|
+
|
3
|
+
module Simplabs
|
4
|
+
|
5
|
+
module Excellent
|
6
|
+
|
7
|
+
module Checks
|
8
|
+
|
9
|
+
class CyclomaticComplexityMethodCheck < CyclomaticComplexityCheck
|
10
|
+
|
11
|
+
DEFAULT_THRESHOLD = 8
|
12
|
+
|
13
|
+
def initialize(options = {})
|
14
|
+
complexity = options[:threshold] || DEFAULT_THRESHOLD
|
15
|
+
super(complexity)
|
16
|
+
end
|
17
|
+
|
18
|
+
def interesting_nodes
|
19
|
+
[:defn]
|
20
|
+
end
|
21
|
+
|
22
|
+
def evaluate(node)
|
23
|
+
score = count_complexity(node)
|
24
|
+
add_error('Method {{method}} has cyclomatic complexity of {{score}}.', { :method => node[1], :score => score }) unless score <= @threshold
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'simplabs/excellent/checks/base'
|
2
|
+
|
3
|
+
module Simplabs
|
4
|
+
|
5
|
+
module Excellent
|
6
|
+
|
7
|
+
module Checks
|
8
|
+
|
9
|
+
class EmptyRescueBodyCheck < Base
|
10
|
+
|
11
|
+
STATEMENT_NODES = [:fcall, :return, :attrasgn, :vcall, :call, :str, :lit]
|
12
|
+
|
13
|
+
def interesting_nodes
|
14
|
+
[:resbody]
|
15
|
+
end
|
16
|
+
|
17
|
+
def evaluate(node)
|
18
|
+
add_error('Rescue block is empty.', {}, -1) unless has_statement?(node)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def has_statement?(node)
|
24
|
+
return true if STATEMENT_NODES.include?(node.node_type)
|
25
|
+
return true if assigning_other_than_exception_to_local_variable?(node)
|
26
|
+
return true if node.children.any? { |child| has_statement?(child) }
|
27
|
+
end
|
28
|
+
|
29
|
+
def assigning_other_than_exception_to_local_variable?(node)
|
30
|
+
node.node_type == :lasgn && node[2].to_a != [:gvar, :$!]
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'simplabs/excellent/checks/base'
|
2
|
+
|
3
|
+
module Simplabs
|
4
|
+
|
5
|
+
module Excellent
|
6
|
+
|
7
|
+
module Checks
|
8
|
+
|
9
|
+
class ForLoopCheck < Base
|
10
|
+
|
11
|
+
def interesting_nodes
|
12
|
+
[:for]
|
13
|
+
end
|
14
|
+
|
15
|
+
def evaluate(node)
|
16
|
+
add_error('For loop used.')
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'simplabs/excellent/checks/base'
|
2
|
+
|
3
|
+
module Simplabs
|
4
|
+
|
5
|
+
module Excellent
|
6
|
+
|
7
|
+
module Checks
|
8
|
+
|
9
|
+
class LineCountCheck < Base
|
10
|
+
|
11
|
+
def initialize(interesting_nodes, threshold)
|
12
|
+
super()
|
13
|
+
@interesting_nodes = interesting_nodes
|
14
|
+
@threshold = threshold
|
15
|
+
end
|
16
|
+
|
17
|
+
def interesting_nodes
|
18
|
+
@interesting_nodes
|
19
|
+
end
|
20
|
+
|
21
|
+
def evaluate(node)
|
22
|
+
line_count = count_lines(node_to_count(node)) - 1
|
23
|
+
add_error(*error_args(node, line_count)) unless line_count <= @threshold
|
24
|
+
end
|
25
|
+
|
26
|
+
protected
|
27
|
+
|
28
|
+
def node_to_count(node)
|
29
|
+
node
|
30
|
+
end
|
31
|
+
|
32
|
+
def count_lines(node, line_numbers = [])
|
33
|
+
count = 0
|
34
|
+
line_numbers << node.line
|
35
|
+
node.children.each { |child| count += count_lines(child, line_numbers) }
|
36
|
+
line_numbers.uniq.length
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'simplabs/excellent/checks/line_count_check'
|
2
|
+
|
3
|
+
module Simplabs
|
4
|
+
|
5
|
+
module Excellent
|
6
|
+
|
7
|
+
module Checks
|
8
|
+
|
9
|
+
class MethodLineCountCheck < LineCountCheck
|
10
|
+
|
11
|
+
DEFAULT_THRESHOLD = 20
|
12
|
+
|
13
|
+
def initialize(options = {})
|
14
|
+
threshold = options[:threshold] || DEFAULT_THRESHOLD
|
15
|
+
super([:defn], threshold)
|
16
|
+
end
|
17
|
+
|
18
|
+
protected
|
19
|
+
|
20
|
+
def node_to_count(node)
|
21
|
+
node[3][1]
|
22
|
+
end
|
23
|
+
|
24
|
+
def error_args(node, line_count)
|
25
|
+
['Method {{method}} has {{count}} lines.', { :method => node[1], :count => line_count }]
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'simplabs/excellent/checks/base'
|
2
|
+
|
3
|
+
module Simplabs
|
4
|
+
|
5
|
+
module Excellent
|
6
|
+
|
7
|
+
module Checks
|
8
|
+
|
9
|
+
class MethodNameCheck < NameCheck
|
10
|
+
|
11
|
+
DEFAULT_PATTERN = /^[_a-z<>=\[|+-\/\*\~\%\&`]+[_a-z0-9_<>=~@\[\]]*[=!\?]?$/
|
12
|
+
|
13
|
+
def initialize(options = {})
|
14
|
+
pattern = options['pattern'] || DEFAULT_PATTERN
|
15
|
+
super([:defn], pattern)
|
16
|
+
end
|
17
|
+
|
18
|
+
def find_name(node)
|
19
|
+
node[1]
|
20
|
+
end
|
21
|
+
|
22
|
+
protected
|
23
|
+
|
24
|
+
def error_args(node)
|
25
|
+
['Bad method name {{method}}.', { :method => node[1] }]
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'simplabs/excellent/checks/line_count_check'
|
2
|
+
|
3
|
+
module Simplabs
|
4
|
+
|
5
|
+
module Excellent
|
6
|
+
|
7
|
+
module Checks
|
8
|
+
|
9
|
+
class ModuleLineCountCheck < LineCountCheck
|
10
|
+
|
11
|
+
DEFAULT_THRESHOLD = 300
|
12
|
+
|
13
|
+
def initialize(options = {})
|
14
|
+
threshold = options[:threshold] || DEFAULT_THRESHOLD
|
15
|
+
super([:module], threshold)
|
16
|
+
end
|
17
|
+
|
18
|
+
protected
|
19
|
+
|
20
|
+
def node_to_count(node)
|
21
|
+
node[2]
|
22
|
+
end
|
23
|
+
|
24
|
+
def error_args(node, line_count)
|
25
|
+
['Module {{module}} has {{count}} lines.', { :module => node[1], :count => line_count }]
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'simplabs/excellent/checks/base'
|
2
|
+
|
3
|
+
module Simplabs
|
4
|
+
|
5
|
+
module Excellent
|
6
|
+
|
7
|
+
module Checks
|
8
|
+
|
9
|
+
class ModuleNameCheck < NameCheck
|
10
|
+
|
11
|
+
DEFAULT_PATTERN = /^[A-Z][a-zA-Z0-9]*$/
|
12
|
+
|
13
|
+
def initialize(options = {})
|
14
|
+
pattern = options['pattern'] || DEFAULT_PATTERN
|
15
|
+
super([:module], pattern)
|
16
|
+
end
|
17
|
+
|
18
|
+
def find_name(node)
|
19
|
+
node[1].class == Symbol ? node[1] : node[1].last
|
20
|
+
end
|
21
|
+
|
22
|
+
protected
|
23
|
+
|
24
|
+
def error_args(node)
|
25
|
+
['Bad module name {{module}}.', { :module => node[1] }]
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'simplabs/excellent/checks/base'
|
2
|
+
|
3
|
+
module Simplabs
|
4
|
+
|
5
|
+
module Excellent
|
6
|
+
|
7
|
+
module Checks
|
8
|
+
|
9
|
+
class NameCheck < Base
|
10
|
+
|
11
|
+
def initialize(interesting_nodes, pattern)
|
12
|
+
super()
|
13
|
+
@interesting_nodes = interesting_nodes
|
14
|
+
@pattern = pattern
|
15
|
+
end
|
16
|
+
|
17
|
+
def interesting_nodes
|
18
|
+
@interesting_nodes
|
19
|
+
end
|
20
|
+
|
21
|
+
def evaluate(node)
|
22
|
+
name = find_name(node)
|
23
|
+
add_error(*error_args(node)) unless name.to_s =~ @pattern
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'simplabs/excellent/checks/base'
|
2
|
+
|
3
|
+
module Simplabs
|
4
|
+
|
5
|
+
module Excellent
|
6
|
+
|
7
|
+
module Checks
|
8
|
+
|
9
|
+
class ParameterNumberCheck < Base
|
10
|
+
|
11
|
+
DEFAULT_THRESHOLD = 3
|
12
|
+
|
13
|
+
def initialize(options = {})
|
14
|
+
super()
|
15
|
+
@threshold = options[:threshold] || DEFAULT_THRESHOLD
|
16
|
+
end
|
17
|
+
|
18
|
+
def interesting_nodes
|
19
|
+
[:defn]
|
20
|
+
end
|
21
|
+
|
22
|
+
def evaluate(node)
|
23
|
+
method_name = node[1]
|
24
|
+
parameters = node[2][1..-1]
|
25
|
+
parameter_count = parameters.inject(0) { |count, each| count = count + (each.class == Symbol ? 1 : 0) }
|
26
|
+
unless parameter_count <= @threshold
|
27
|
+
add_error('Method {{method}} has {{parameter_count}} parameters.', { :method => method_name, :parameter_count => parameter_count })
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Simplabs
|
2
|
+
|
3
|
+
module Excellent
|
4
|
+
|
5
|
+
module Core
|
6
|
+
|
7
|
+
class CheckingVisitor
|
8
|
+
|
9
|
+
def initialize(*checks)
|
10
|
+
@checks ||= {}
|
11
|
+
checks.first.each do |check|
|
12
|
+
nodes = check.interesting_nodes
|
13
|
+
nodes.each do |node|
|
14
|
+
@checks[node] ||= []
|
15
|
+
@checks[node] << check
|
16
|
+
@checks[node].uniq!
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def visit(node)
|
22
|
+
@last_newline = node if node.node_type == :newline
|
23
|
+
checks = @checks[node.node_type]
|
24
|
+
checks.each { |check| check.evaluate_node(node) } unless checks.nil?
|
25
|
+
nil
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Simplabs
|
2
|
+
|
3
|
+
module Excellent
|
4
|
+
|
5
|
+
module Core
|
6
|
+
|
7
|
+
class Error
|
8
|
+
|
9
|
+
attr_reader :check, :info, :filename, :line_number, :message, :message_template
|
10
|
+
|
11
|
+
def initialize(check, message, filename, line_number, info)
|
12
|
+
@check = check.to_s.underscore.to_sym
|
13
|
+
@info = info
|
14
|
+
@filename = filename
|
15
|
+
@line_number = line_number.to_i
|
16
|
+
|
17
|
+
@message = ''
|
18
|
+
if !message.nil?
|
19
|
+
@message_template = message
|
20
|
+
@info.each { |key, value| message.gsub!(/\{\{#{key}\}\}/, value.to_s) }
|
21
|
+
@message = message
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|