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 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