simplabs-excellent 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|