reek 1.3 → 1.3.1
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/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
|