reek 1.3 → 1.3.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +6 -0
- data/config/defaults.reek +3 -0
- data/features/command_line_interface/options.feature +16 -0
- data/features/samples.feature +5 -2
- data/features/step_definitions/reek_steps.rb +4 -0
- data/lib/reek/cli/command_line.rb +4 -0
- data/lib/reek/cli/report.rb +8 -0
- data/lib/reek/core/smell_repository.rb +1 -0
- data/lib/reek/smells.rb +1 -0
- data/lib/reek/smells/nil_check.rb +86 -0
- data/lib/reek/version.rb +1 -1
- data/lib/xp.reek +3 -0
- data/reek.gemspec +2 -2
- data/spec/reek/smells/nil_check_spec.rb +60 -0
- metadata +13 -10
data/CHANGELOG
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
== 1.3.1
|
2
|
+
|
3
|
+
* (EmilRehnberg) Added smell for nil checks
|
4
|
+
* (geoffharcourt) Updated dependencies to use ruby2ruby 2.0.2 and ruby_parser 3.1.1
|
5
|
+
* (EmilRehnberg) Added command-line option for printing line numbers with the smell lines
|
6
|
+
|
1
7
|
== 1.3
|
2
8
|
|
3
9
|
* (mvz) Use new ruby_parser 3 and friends
|
data/config/defaults.reek
CHANGED
@@ -39,6 +39,22 @@ Feature: Reek can be controlled using command-line options
|
|
39
39
|
|
40
40
|
Report formatting:
|
41
41
|
-q, --[no-]quiet Suppress headings for smell-free source files
|
42
|
+
-n, --line-number Prefix the output with the line number(s).
|
42
43
|
-y, --yaml Report smells in YAML format
|
43
44
|
|
44
45
|
"""
|
46
|
+
|
47
|
+
Scenario: output line numbers if -n flag is passed
|
48
|
+
When I run reek -n spec/samples/not_quite_masked/dirty.rb
|
49
|
+
Then the exit status indicates smells
|
50
|
+
And it should indicate the line numbers of those smells
|
51
|
+
And it reports:
|
52
|
+
"""
|
53
|
+
spec/samples/not_quite_masked/dirty.rb -- 5 warnings:
|
54
|
+
[7]:Dirty has the variable name '@s' (UncommunicativeVariableName)
|
55
|
+
[6, 8]:Dirty#a calls @s.title twice (DuplicateMethodCall)
|
56
|
+
[6, 8]:Dirty#a calls puts(@s.title) twice (DuplicateMethodCall)
|
57
|
+
[7]:Dirty#a contains iterators nested 2 deep (NestedIterators)
|
58
|
+
[5]:Dirty#a has the name 'a' (UncommunicativeMethodName)
|
59
|
+
|
60
|
+
"""
|
data/features/samples.feature
CHANGED
@@ -10,12 +10,13 @@ Feature: Basic smell detection
|
|
10
10
|
Then the exit status indicates smells
|
11
11
|
And it reports:
|
12
12
|
"""
|
13
|
-
spec/samples/inline.rb --
|
13
|
+
spec/samples/inline.rb -- 42 warnings:
|
14
14
|
File has no descriptive comment (IrresponsibleModule)
|
15
15
|
Inline declares the class variable @@directory (ClassVariable)
|
16
16
|
Inline declares the class variable @@rootdir (ClassVariable)
|
17
17
|
Inline#self.rootdir calls env.nil? twice (DuplicateMethodCall)
|
18
18
|
Inline#self.rootdir has approx 8 statements (TooManyStatements)
|
19
|
+
Inline#self.rootdir performs a nil-check. (NilCheck)
|
19
20
|
Inline::C declares the class variable @@type_map (ClassVariable)
|
20
21
|
Inline::C has at least 13 instance variables (TooManyInstanceVariables)
|
21
22
|
Inline::C takes parameters [options, src] to 5 methods (DataClump)
|
@@ -60,7 +61,7 @@ Feature: Basic smell detection
|
|
60
61
|
Then the exit status indicates smells
|
61
62
|
And it reports:
|
62
63
|
"""
|
63
|
-
spec/samples/optparse.rb --
|
64
|
+
spec/samples/optparse.rb -- 115 warnings:
|
64
65
|
OptionParser has at least 42 methods (TooManyMethods)
|
65
66
|
OptionParser has the variable name 'f' (UncommunicativeVariableName)
|
66
67
|
OptionParser has the variable name 'k' (UncommunicativeVariableName)
|
@@ -99,6 +100,7 @@ Feature: Basic smell detection
|
|
99
100
|
OptionParser#make_switch has the variable name 'q' (UncommunicativeVariableName)
|
100
101
|
OptionParser#make_switch has the variable name 's' (UncommunicativeVariableName)
|
101
102
|
OptionParser#make_switch has the variable name 'v' (UncommunicativeVariableName)
|
103
|
+
OptionParser#make_switch performs a nil-check. (NilCheck)
|
102
104
|
OptionParser#order calls argv[0] twice (DuplicateMethodCall)
|
103
105
|
OptionParser#order refers to argv more than self (FeatureEnvy)
|
104
106
|
OptionParser#parse calls argv[0] twice (DuplicateMethodCall)
|
@@ -117,6 +119,7 @@ Feature: Basic smell detection
|
|
117
119
|
OptionParser#permute calls argv[0] twice (DuplicateMethodCall)
|
118
120
|
OptionParser#permute refers to argv more than self (FeatureEnvy)
|
119
121
|
OptionParser#search has the variable name 'k' (UncommunicativeVariableName)
|
122
|
+
OptionParser#self.inc performs a nil-check. (NilCheck)
|
120
123
|
OptionParser#summarize has 4 parameters (LongParameterList)
|
121
124
|
OptionParser#summarize has the variable name 'l' (UncommunicativeVariableName)
|
122
125
|
OptionParser#ver has the variable name 'v' (UncommunicativeVariableName)
|
@@ -56,6 +56,10 @@ Then /^it reports a parsing error$/ do
|
|
56
56
|
@last_stderr.chomp.should match(/Racc::ParseError/)
|
57
57
|
end
|
58
58
|
|
59
|
+
Then /^it should indicate the line numbers of those smells$/ do
|
60
|
+
@last_stdout.chomp.should match(/\[.*\]:/)
|
61
|
+
end
|
62
|
+
|
59
63
|
Then /^it reports the current version$/ do
|
60
64
|
@last_stdout.should == "reek #{Reek::VERSION}\n"
|
61
65
|
end
|
@@ -37,6 +37,7 @@ module Reek
|
|
37
37
|
#
|
38
38
|
# reek [options] files List the smells in the given files
|
39
39
|
# -c|--config file Specify file(s) with config options
|
40
|
+
# -n|--line-number Prefix smelly lines with line numbers
|
40
41
|
# -q|-[no-]quiet Only list files that have smells
|
41
42
|
# files Names of files or dirs to be checked
|
42
43
|
#
|
@@ -73,6 +74,9 @@ EOB
|
|
73
74
|
@parser.on("-q", "--[no-]quiet", "Suppress headings for smell-free source files") do |opt|
|
74
75
|
@report_class = opt ? QuietReport : VerboseReport
|
75
76
|
end
|
77
|
+
@parser.on("-n", "--line-number", "Prefix the output with the line number(s).") do
|
78
|
+
@report_class = ShowLineReport
|
79
|
+
end
|
76
80
|
@parser.on("-y", "--yaml", "Report smells in YAML format") do
|
77
81
|
@command_class = YamlCommand
|
78
82
|
# SMELL: the args passed to the command should be tested, because it may
|
data/lib/reek/cli/report.rb
CHANGED
@@ -54,5 +54,13 @@ module Reek
|
|
54
54
|
@smell_count > 0 ? "#{header(@desc, @smell_count)}:\n#{format_list(@warnings)}\n" : ''
|
55
55
|
end
|
56
56
|
end
|
57
|
+
|
58
|
+
class ShowLineReport < VerboseReport
|
59
|
+
def format_list(warnings)
|
60
|
+
warnings.map do |warning|
|
61
|
+
" #{warning.lines.inspect}:#{warning.context} #{warning.message} (#{warning.subclass})"
|
62
|
+
end.join("\n")
|
63
|
+
end
|
64
|
+
end
|
57
65
|
end
|
58
66
|
end
|
data/lib/reek/smells.rb
CHANGED
@@ -11,6 +11,7 @@ require File.join( File.dirname( File.expand_path(__FILE__)), 'smells', 'long_me
|
|
11
11
|
require File.join( File.dirname( File.expand_path(__FILE__)), 'smells', 'long_parameter_list')
|
12
12
|
require File.join( File.dirname( File.expand_path(__FILE__)), 'smells', 'long_yield_list')
|
13
13
|
require File.join( File.dirname( File.expand_path(__FILE__)), 'smells', 'nested_iterators')
|
14
|
+
require File.join( File.dirname( File.expand_path(__FILE__)), 'smells', 'nil_check')
|
14
15
|
require File.join( File.dirname( File.expand_path(__FILE__)), 'smells', 'simulated_polymorphism')
|
15
16
|
require File.join( File.dirname( File.expand_path(__FILE__)), 'smells', 'uncommunicative_method_name')
|
16
17
|
require File.join( File.dirname( File.expand_path(__FILE__)), 'smells', 'uncommunicative_module_name')
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require File.join( File.dirname( File.expand_path(__FILE__)), 'smell_detector')
|
2
|
+
require File.join(File.dirname(File.dirname(File.expand_path(__FILE__))), 'smell_warning')
|
3
|
+
|
4
|
+
module Reek
|
5
|
+
module Smells
|
6
|
+
|
7
|
+
class NilCheck < SmellDetector
|
8
|
+
|
9
|
+
SMELL_CLASS = 'NilCheck'
|
10
|
+
SMELL_SUBCLASS = SMELL_CLASS
|
11
|
+
|
12
|
+
def initialize(source, config = NilCheck.default_config)
|
13
|
+
super(source, config)
|
14
|
+
end
|
15
|
+
|
16
|
+
def examine_context(ctx)
|
17
|
+
|
18
|
+
call_nodes = CallNodeFinder.new(ctx)
|
19
|
+
case_nodes = CaseNodeFinder.new(ctx)
|
20
|
+
smelly_calls = call_nodes.smelly
|
21
|
+
smelly_cases = case_nodes.smelly
|
22
|
+
|
23
|
+
smelly_nodes = smelly_calls + smelly_cases
|
24
|
+
|
25
|
+
smelly_nodes.map do |node|
|
26
|
+
SmellWarning.new(SMELL_CLASS, ctx.full_name, node.line,
|
27
|
+
"performs a nil-check.",
|
28
|
+
@source, SMELL_SUBCLASS )
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class NodeFinder
|
33
|
+
SEXP_NIL = Sexp.new(:nil)
|
34
|
+
def initialize(ctx, type)
|
35
|
+
@nodes = Array(ctx.local_nodes(type))
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class CallNodeFinder < NodeFinder
|
40
|
+
def initialize(ctx)
|
41
|
+
super(ctx, :call)
|
42
|
+
end
|
43
|
+
|
44
|
+
def smelly
|
45
|
+
@nodes.select{ |call|
|
46
|
+
nil_chk?(call)
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
def nil_chk?(call)
|
51
|
+
nilQ_use?(call) || eq_nil_use?(call)
|
52
|
+
end
|
53
|
+
|
54
|
+
def nilQ_use?(call)
|
55
|
+
call.last == :nil?
|
56
|
+
end
|
57
|
+
|
58
|
+
def eq_nil_use?(call)
|
59
|
+
include_eq?(call) && call.include?(SEXP_NIL)
|
60
|
+
end
|
61
|
+
|
62
|
+
def include_eq?(call)
|
63
|
+
[:==, :===].any? { |operator| call.include?(operator) }
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class CaseNodeFinder < NodeFinder
|
68
|
+
CASE_NIL_NODE = Sexp.new(:array, SEXP_NIL)
|
69
|
+
def initialize(ctx)
|
70
|
+
super(ctx, :when)
|
71
|
+
end
|
72
|
+
|
73
|
+
def smelly
|
74
|
+
@nodes.select{ |when_node|
|
75
|
+
nil_chk?(when_node)
|
76
|
+
}
|
77
|
+
end
|
78
|
+
|
79
|
+
def nil_chk?(when_node)
|
80
|
+
when_node.include?(CASE_NIL_NODE)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
data/lib/reek/version.rb
CHANGED
data/lib/xp.reek
CHANGED
data/reek.gemspec
CHANGED
@@ -25,9 +25,9 @@ and reports any code smells it finds.
|
|
25
25
|
s.rubygems_version = %q{1.3.6}
|
26
26
|
s.summary = %q{Code smell detector for Ruby}
|
27
27
|
|
28
|
-
s.add_runtime_dependency(%q<ruby_parser>, ["~> 3.
|
28
|
+
s.add_runtime_dependency(%q<ruby_parser>, ["~> 3.1.1"])
|
29
29
|
s.add_runtime_dependency(%q<sexp_processor>)
|
30
|
-
s.add_runtime_dependency(%q<ruby2ruby>, ["~> 2.0.
|
30
|
+
s.add_runtime_dependency(%q<ruby2ruby>, ["~> 2.0.2"])
|
31
31
|
|
32
32
|
s.add_development_dependency(%q<bundler>, ["~> 1.1"])
|
33
33
|
s.add_development_dependency(%q<rake>)
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require File.join(File.dirname(File.dirname(File.dirname(File.expand_path(__FILE__)))), 'spec_helper')
|
2
|
+
require File.join(File.dirname(File.dirname(File.dirname(File.dirname(File.expand_path(__FILE__))))), 'lib', 'reek', 'smells', 'nil_check')
|
3
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), 'smell_detector_shared')
|
4
|
+
|
5
|
+
include Reek
|
6
|
+
include Reek::Smells
|
7
|
+
|
8
|
+
describe NilCheck do
|
9
|
+
|
10
|
+
context 'for methods' do
|
11
|
+
|
12
|
+
it 'should report nothing when scope includes no nil checks' do
|
13
|
+
'def no_nils; end'.should_not smell_of(NilCheck)
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'should report when scope uses multiple nil? methods' do
|
17
|
+
src = <<-eos
|
18
|
+
def chk_multi_nil(para)
|
19
|
+
para.nil?
|
20
|
+
puts "Hello"
|
21
|
+
\"\".nil?
|
22
|
+
end
|
23
|
+
eos
|
24
|
+
src.should smell_of(NilCheck,
|
25
|
+
{NilCheck => nil}, {NilCheck => nil})
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should report twice when scope uses == nil and === nil' do
|
29
|
+
src= <<-eos
|
30
|
+
def chk_eq_nil(para)
|
31
|
+
para == nil
|
32
|
+
para === nil
|
33
|
+
end
|
34
|
+
eos
|
35
|
+
src.should smell_of(NilCheck,
|
36
|
+
{NilCheck => nil}, {NilCheck => nil})
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should report when scope uses nil ==' do
|
40
|
+
'def chk_eq_nil_rev(para); nil == para; end'.should smell_of(NilCheck)
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'should report when scope uses multiple case-clauses checking nil' do
|
44
|
+
src = <<-eos
|
45
|
+
def caseNil
|
46
|
+
case @inst_var
|
47
|
+
when nil then puts "Nil"
|
48
|
+
end
|
49
|
+
puts "Hello"
|
50
|
+
case @inst_var2
|
51
|
+
when 1 then puts 1
|
52
|
+
when nil then puts nil.inspect
|
53
|
+
end
|
54
|
+
end
|
55
|
+
eos
|
56
|
+
src.should smell_of(NilCheck,
|
57
|
+
{NilCheck => nil}, {NilCheck => nil})
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
metadata
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: reek
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 25
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 1
|
8
8
|
- 3
|
9
|
-
|
9
|
+
- 1
|
10
|
+
version: 1.3.1
|
10
11
|
platform: ruby
|
11
12
|
authors:
|
12
13
|
- Kevin Rutherford
|
@@ -16,7 +17,7 @@ autorequire:
|
|
16
17
|
bindir: bin
|
17
18
|
cert_chain: []
|
18
19
|
|
19
|
-
date: 2013-
|
20
|
+
date: 2013-02-02 00:00:00 Z
|
20
21
|
dependencies:
|
21
22
|
- !ruby/object:Gem::Dependency
|
22
23
|
prerelease: false
|
@@ -26,12 +27,12 @@ dependencies:
|
|
26
27
|
requirements:
|
27
28
|
- - ~>
|
28
29
|
- !ruby/object:Gem::Version
|
29
|
-
hash:
|
30
|
+
hash: 1
|
30
31
|
segments:
|
31
32
|
- 3
|
32
|
-
-
|
33
|
-
-
|
34
|
-
version: 3.
|
33
|
+
- 1
|
34
|
+
- 1
|
35
|
+
version: 3.1.1
|
35
36
|
requirement: *id001
|
36
37
|
type: :runtime
|
37
38
|
- !ruby/object:Gem::Dependency
|
@@ -56,12 +57,12 @@ dependencies:
|
|
56
57
|
requirements:
|
57
58
|
- - ~>
|
58
59
|
- !ruby/object:Gem::Version
|
59
|
-
hash:
|
60
|
+
hash: 11
|
60
61
|
segments:
|
61
62
|
- 2
|
62
63
|
- 0
|
63
|
-
-
|
64
|
-
version: 2.0.
|
64
|
+
- 2
|
65
|
+
version: 2.0.2
|
65
66
|
requirement: *id003
|
66
67
|
type: :runtime
|
67
68
|
- !ruby/object:Gem::Dependency
|
@@ -194,6 +195,7 @@ files:
|
|
194
195
|
- lib/reek/smells/attribute.rb
|
195
196
|
- lib/reek/smells/simulated_polymorphism.rb
|
196
197
|
- lib/reek/smells/uncommunicative_method_name.rb
|
198
|
+
- lib/reek/smells/nil_check.rb
|
197
199
|
- lib/reek/smells/class_variable.rb
|
198
200
|
- lib/reek/smells/long_parameter_list.rb
|
199
201
|
- lib/reek/smells/feature_envy.rb
|
@@ -261,6 +263,7 @@ files:
|
|
261
263
|
- spec/reek/smells/nested_iterators_spec.rb
|
262
264
|
- spec/reek/smells/behaves_like_variable_detector.rb
|
263
265
|
- spec/reek/smells/large_class_spec.rb
|
266
|
+
- spec/reek/smells/nil_check_spec.rb
|
264
267
|
- spec/reek/smells/irresponsible_module_spec.rb
|
265
268
|
- spec/reek/smells/uncommunicative_variable_name_spec.rb
|
266
269
|
- spec/reek/smell_warning_spec.rb
|