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 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
@@ -115,3 +115,6 @@ LongYieldList:
115
115
  UnusedParameters:
116
116
  exclude: *id001
117
117
  enabled: true
118
+ NilCheck:
119
+ exclude: *id001
120
+ enabled: true
@@ -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
+ """
@@ -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 -- 41 warnings:
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 -- 113 warnings:
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
@@ -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
@@ -20,6 +20,7 @@ module Reek
20
20
  Smells::LongParameterList,
21
21
  Smells::LongYieldList,
22
22
  Smells::NestedIterators,
23
+ Smells::NilCheck,
23
24
  Smells::SimulatedPolymorphism,
24
25
  Smells::UncommunicativeMethodName,
25
26
  Smells::UncommunicativeModuleName,
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
@@ -1,3 +1,3 @@
1
1
  module Reek
2
- VERSION = '1.3'
2
+ VERSION = '1.3.1'
3
3
  end
data/lib/xp.reek CHANGED
@@ -61,3 +61,6 @@ LongYieldList:
61
61
  overrides:
62
62
  initialize:
63
63
  max_params: 5
64
+ NilCheck:
65
+ exclude: []
66
+ enabled: true
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.0.4"])
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.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: 9
4
+ hash: 25
5
5
  prerelease:
6
6
  segments:
7
7
  - 1
8
8
  - 3
9
- version: "1.3"
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-01-19 00:00:00 Z
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: 15
30
+ hash: 1
30
31
  segments:
31
32
  - 3
32
- - 0
33
- - 4
34
- version: 3.0.4
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: 15
60
+ hash: 11
60
61
  segments:
61
62
  - 2
62
63
  - 0
63
- - 0
64
- version: 2.0.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