reek 3.0.4 → 3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +6 -0
  3. data/README.md +41 -20
  4. data/features/configuration_files/directory_specific_directives.feature +280 -0
  5. data/features/configuration_files/masking_smells.feature +0 -14
  6. data/features/step_definitions/sample_file_steps.rb +2 -2
  7. data/lib/reek/ast/sexp_extensions.rb +72 -60
  8. data/lib/reek/cli/application.rb +2 -5
  9. data/lib/reek/cli/reek_command.rb +1 -1
  10. data/lib/reek/configuration/app_configuration.rb +115 -61
  11. data/lib/reek/configuration/configuration_file_finder.rb +19 -0
  12. data/lib/reek/context/code_context.rb +102 -29
  13. data/lib/reek/context/method_context.rb +11 -48
  14. data/lib/reek/context/module_context.rb +2 -6
  15. data/lib/reek/context/root_context.rb +7 -14
  16. data/lib/reek/examiner.rb +15 -10
  17. data/lib/reek/report/report.rb +5 -4
  18. data/lib/reek/smells/boolean_parameter.rb +1 -1
  19. data/lib/reek/smells/duplicate_method_call.rb +1 -5
  20. data/lib/reek/smells/irresponsible_module.rb +2 -2
  21. data/lib/reek/smells/smell_repository.rb +30 -16
  22. data/lib/reek/smells/utility_function.rb +1 -1
  23. data/lib/reek/source/source_code.rb +10 -6
  24. data/lib/reek/source/source_locator.rb +27 -26
  25. data/lib/reek/spec.rb +8 -6
  26. data/lib/reek/spec/should_reek.rb +6 -2
  27. data/lib/reek/spec/should_reek_of.rb +5 -2
  28. data/lib/reek/spec/should_reek_only_of.rb +1 -1
  29. data/lib/reek/tree_walker.rb +49 -21
  30. data/lib/reek/version.rb +1 -1
  31. data/spec/reek/ast/sexp_extensions_spec.rb +4 -12
  32. data/spec/reek/configuration/app_configuration_spec.rb +80 -52
  33. data/spec/reek/configuration/configuration_file_finder_spec.rb +27 -15
  34. data/spec/reek/context/code_context_spec.rb +66 -17
  35. data/spec/reek/context/method_context_spec.rb +66 -64
  36. data/spec/reek/context/root_context_spec.rb +3 -1
  37. data/spec/reek/context/singleton_method_context_spec.rb +1 -2
  38. data/spec/reek/examiner_spec.rb +5 -8
  39. data/spec/reek/report/xml_report_spec.rb +3 -2
  40. data/spec/reek/smells/attribute_spec.rb +12 -17
  41. data/spec/reek/smells/duplicate_method_call_spec.rb +17 -25
  42. data/spec/reek/smells/feature_envy_spec.rb +4 -5
  43. data/spec/reek/smells/irresponsible_module_spec.rb +43 -0
  44. data/spec/reek/smells/nested_iterators_spec.rb +12 -26
  45. data/spec/reek/smells/too_many_statements_spec.rb +2 -210
  46. data/spec/reek/smells/utility_function_spec.rb +49 -5
  47. data/spec/reek/source/source_locator_spec.rb +39 -43
  48. data/spec/reek/spec/should_reek_of_spec.rb +3 -2
  49. data/spec/reek/spec/should_reek_spec.rb +25 -17
  50. data/spec/reek/tree_walker_spec.rb +214 -23
  51. data/spec/samples/configuration/full_configuration.reek +9 -0
  52. data/spec/spec_helper.rb +10 -10
  53. metadata +4 -3
  54. data/features/configuration_files/overrides_defaults.feature +0 -15
@@ -1,7 +1,6 @@
1
1
  require_relative '../../spec_helper'
2
2
  require_relative '../../../lib/reek/context/method_context'
3
3
  require_relative '../../../lib/reek/context/module_context'
4
- require_relative '../../../lib/reek/context/root_context'
5
4
 
6
5
  RSpec.describe Reek::Context::CodeContext do
7
6
  context 'name recognition' do
@@ -55,16 +54,6 @@ RSpec.describe Reek::Context::CodeContext do
55
54
  end
56
55
  end
57
56
 
58
- context 'generics' do
59
- it 'should pass unknown method calls down the stack' do
60
- stop = Reek::Context::RootContext.new
61
- def stop.bananas(arg1, arg2) arg1 + arg2 + 43 end
62
- element = Reek::Context::ModuleContext.new(stop, s(:module, :mod, nil))
63
- element = Reek::Context::MethodContext.new(element, s(:def, :bad, s(:args), nil))
64
- expect(element.bananas(17, -5)).to eq(55)
65
- end
66
- end
67
-
68
57
  context 'enumerating syntax elements' do
69
58
  context 'in an empty module' do
70
59
  before :each do
@@ -161,22 +150,31 @@ EOS
161
150
  end
162
151
 
163
152
  describe '#config_for' do
164
- let(:expression) { double('exp') }
153
+ let(:src) do
154
+ <<-EOS
155
+ # :reek:DuplicateMethodCall: { allow_calls: [ puts ] }')
156
+ def repeated_greeting
157
+ puts 'Hello!'
158
+ puts 'Hello!'
159
+ end
160
+ EOS
161
+ end
162
+ let(:expression) { Reek::Source::SourceCode.from(src).syntax_tree }
165
163
  let(:outer) { nil }
166
164
  let(:context) { Reek::Context::CodeContext.new(outer, expression) }
167
165
  let(:sniffer) { double('sniffer') }
168
166
 
169
167
  before :each do
170
168
  allow(sniffer).to receive(:smell_type).and_return('DuplicateMethodCall')
171
- allow(expression).to receive(:full_comment).and_return(
172
- ':reek:DuplicateMethodCall: { allow_calls: [ puts ] }')
173
169
  end
174
170
 
175
- it 'gets its configuration from the expression comments' do
176
- expect(context.config_for(sniffer)).to eq('allow_calls' => ['puts'])
171
+ context 'when there is no outer context' do
172
+ it 'gets its configuration from the expression comments' do
173
+ expect(context.config_for(sniffer)).to eq('allow_calls' => ['puts'])
174
+ end
177
175
  end
178
176
 
179
- context 'when there is an outer' do
177
+ context 'when there is an outer context' do
180
178
  let(:outer) { double('outer') }
181
179
 
182
180
  before :each do
@@ -190,4 +188,55 @@ EOS
190
188
  end
191
189
  end
192
190
  end
191
+
192
+ describe '#append_child_context' do
193
+ let(:context) { Reek::Context::CodeContext.new(nil, double('exp1')) }
194
+ let(:first_child) { Reek::Context::CodeContext.new(context, double('exp2')) }
195
+ let(:second_child) { Reek::Context::CodeContext.new(context, double('exp3')) }
196
+
197
+ it 'appends the child to the list of children' do
198
+ context.append_child_context first_child
199
+ context.append_child_context second_child
200
+ expect(context.children).to eq [first_child, second_child]
201
+ end
202
+ end
203
+
204
+ describe '#track_visibility' do
205
+ let(:context) { Reek::Context::CodeContext.new(nil, double('exp1')) }
206
+ let(:first_child) { Reek::Context::CodeContext.new(context, double('exp2', name: :foo)) }
207
+ let(:second_child) { Reek::Context::CodeContext.new(context, double('exp3')) }
208
+
209
+ it 'sets visibility on subsequent child contexts' do
210
+ context.append_child_context first_child
211
+ context.track_visibility :private
212
+ context.append_child_context second_child
213
+ expect(first_child.visibility).to eq :public
214
+ expect(second_child.visibility).to eq :private
215
+ end
216
+
217
+ it 'sets visibility on specifically mentioned child contexts' do
218
+ context.append_child_context first_child
219
+ context.track_visibility :private, [first_child.name]
220
+ context.append_child_context second_child
221
+ expect(first_child.visibility).to eq :private
222
+ expect(second_child.visibility).to eq :public
223
+ end
224
+ end
225
+
226
+ describe '#each' do
227
+ let(:context) { Reek::Context::CodeContext.new(nil, double('exp1')) }
228
+ let(:first_child) { Reek::Context::CodeContext.new(context, double('exp2')) }
229
+ let(:second_child) { Reek::Context::CodeContext.new(context, double('exp3')) }
230
+
231
+ it 'yields each child' do
232
+ context.append_child_context first_child
233
+ context.append_child_context second_child
234
+ result = []
235
+ context.each do |ctx|
236
+ result << ctx
237
+ end
238
+
239
+ expect(result).to eq [context, first_child, second_child]
240
+ end
241
+ end
193
242
  end
@@ -1,94 +1,96 @@
1
1
  require_relative '../../spec_helper'
2
2
  require_relative '../../../lib/reek/context/method_context'
3
- require_relative '../../../lib/reek/context/root_context'
4
3
 
5
- RSpec.describe Reek::Context::MethodContext, 'matching' do
6
- before :each do
7
- exp = double('exp').as_null_object
8
- expect(exp).to receive(:full_name).at_least(:once).and_return('mod')
9
- @element = Reek::Context::MethodContext.new(Reek::Context::RootContext.new, exp)
10
- end
4
+ RSpec.describe Reek::Context::MethodContext do
5
+ let(:method_context) { Reek::Context::MethodContext.new(nil, exp) }
11
6
 
12
- it 'should recognise itself in a collection of names' do
13
- expect(@element.matches?(['banana', 'mod'])).to eq(true)
14
- expect(@element.matches?(['banana'])).to eq(false)
15
- end
7
+ describe '#matches?' do
8
+ let(:exp) { double('exp').as_null_object }
16
9
 
17
- it 'should recognise itself in a collection of REs' do
18
- expect(@element.matches?([/banana/, /mod/])).to eq(true)
19
- expect(@element.matches?([/banana/])).to eq(false)
20
- end
21
- end
10
+ before :each do
11
+ expect(exp).to receive(:full_name).at_least(:once).and_return('mod')
12
+ end
22
13
 
23
- RSpec.describe Reek::Context::MethodContext do
24
- let(:mc) do
25
- sexp = s(:def, :foo, s(:args, s(:arg, :bar)), nil)
26
- Reek::Context::MethodContext.new(Reek::Context::RootContext.new, sexp)
14
+ it 'should recognise itself in a collection of names' do
15
+ expect(method_context.matches?(['banana', 'mod'])).to eq(true)
16
+ expect(method_context.matches?(['banana'])).to eq(false)
17
+ end
18
+
19
+ it 'should recognise itself in a collection of REs' do
20
+ expect(method_context.matches?([/banana/, /mod/])).to eq(true)
21
+ expect(method_context.matches?([/banana/])).to eq(false)
22
+ end
27
23
  end
28
24
 
29
25
  describe '#envious_receivers' do
26
+ let(:exp) { s(:def, :foo, s(:args, s(:arg, :bar)), nil) }
27
+
30
28
  it 'should ignore ivars as refs to self' do
31
- mc.record_call_to s(:send, s(:ivar, :@cow), :feed_to)
32
- expect(mc.envious_receivers).to be_empty
29
+ method_context.record_call_to s(:send, s(:ivar, :@cow), :feed_to)
30
+ expect(method_context.envious_receivers).to be_empty
33
31
  end
34
32
 
35
33
  it 'should ignore explicit calls to self' do
36
- mc.refs.record_reference_to :other
37
- mc.record_call_to s(:send, s(:self), :thing)
38
- expect(mc.envious_receivers).to be_empty
34
+ method_context.refs.record_reference_to :other
35
+ method_context.record_call_to s(:send, s(:self), :thing)
36
+ expect(method_context.envious_receivers).to be_empty
39
37
  end
40
38
 
41
39
  it 'should ignore implicit calls to self' do
42
- mc.record_call_to s(:send, s(:lvar, :text), :each, s(:arglist))
43
- mc.record_call_to s(:send, nil, :shelve, s(:arglist))
44
- expect(mc.envious_receivers).to be_empty
40
+ method_context.record_call_to s(:send, s(:lvar, :text), :each, s(:arglist))
41
+ method_context.record_call_to s(:send, nil, :shelve, s(:arglist))
42
+ expect(method_context.envious_receivers).to be_empty
45
43
  end
46
44
 
47
45
  it 'should record envious calls' do
48
- mc.record_call_to s(:send, s(:lvar, :bar), :baz)
49
- expect(mc.envious_receivers).to include(:bar)
46
+ method_context.record_call_to s(:send, s(:lvar, :bar), :baz)
47
+ expect(method_context.envious_receivers).to include(:bar)
50
48
  end
51
49
  end
52
- end
53
50
 
54
- RSpec.describe Reek::Context::MethodParameters, 'default assignments' do
55
- def assignments_from(src)
56
- exp = Reek::Source::SourceCode.from(src).syntax_tree
57
- ctx = Reek::Context::MethodContext.new(Reek::Context::RootContext.new, exp)
58
- ctx.parameters.default_assignments
59
- end
60
-
61
- context 'with no defaults' do
62
- it 'returns an empty hash' do
63
- src = 'def meth(arga, argb, &blk) end'
64
- expect(assignments_from(src)).to be_empty
51
+ describe '#default_assignments' do
52
+ def assignments_from(src)
53
+ exp = Reek::Source::SourceCode.from(src).syntax_tree
54
+ ctx = Reek::Context::MethodContext.new(nil, exp)
55
+ ctx.default_assignments
65
56
  end
66
- end
67
57
 
68
- context 'with 1 default' do
69
- before :each do
70
- src = 'def meth(arga, argb=456, &blk) end'
71
- @defaults = assignments_from(src)
72
- end
73
- it 'returns the param-value pair' do
74
- expect(@defaults[0]).to eq [:argb, s(:int, 456)]
58
+ context 'with no defaults' do
59
+ it 'returns an empty hash' do
60
+ src = 'def meth(arga, argb, &blk) end'
61
+ expect(assignments_from(src)).to be_empty
62
+ end
75
63
  end
76
- it 'returns the nothing else' do
77
- expect(@defaults.length).to eq(1)
78
- end
79
- end
80
64
 
81
- context 'with 2 defaults' do
82
- before :each do
83
- src = 'def meth(arga=123, argb=456, &blk) end'
84
- @defaults = assignments_from(src)
85
- end
86
- it 'returns both param-value pairs' do
87
- expect(@defaults[0]).to eq [:arga, s(:int, 123)]
88
- expect(@defaults[1]).to eq [:argb, s(:int, 456)]
65
+ context 'with 1 default' do
66
+ before :each do
67
+ src = 'def meth(arga, argb=456, &blk) end'
68
+ @defaults = assignments_from(src)
69
+ end
70
+
71
+ it 'returns the param-value pair' do
72
+ expect(@defaults[0]).to eq [:argb, s(:int, 456)]
73
+ end
74
+
75
+ it 'returns the nothing else' do
76
+ expect(@defaults.length).to eq(1)
77
+ end
89
78
  end
90
- it 'returns nothing else' do
91
- expect(@defaults.length).to eq(2)
79
+
80
+ context 'with 2 defaults' do
81
+ before :each do
82
+ src = 'def meth(arga=123, argb=456, &blk) end'
83
+ @defaults = assignments_from(src)
84
+ end
85
+
86
+ it 'returns both param-value pairs' do
87
+ expect(@defaults[0]).to eq [:arga, s(:int, 123)]
88
+ expect(@defaults[1]).to eq [:argb, s(:int, 456)]
89
+ end
90
+
91
+ it 'returns nothing else' do
92
+ expect(@defaults.length).to eq(2)
93
+ end
92
94
  end
93
95
  end
94
96
  end
@@ -3,7 +3,9 @@ require_relative '../../../lib/reek/context/root_context'
3
3
 
4
4
  RSpec.describe Reek::Context::RootContext do
5
5
  before :each do
6
- @root = Reek::Context::RootContext.new
6
+ src = 'foo = 1'
7
+ ast = Reek::Source::SourceCode.from(src).syntax_tree
8
+ @root = Reek::Context::RootContext.new(ast)
7
9
  end
8
10
 
9
11
  context 'full_name' do
@@ -1,11 +1,10 @@
1
1
  require_relative '../../spec_helper'
2
2
  require_relative '../../../lib/reek/context/singleton_method_context'
3
- require_relative '../../../lib/reek/context/root_context'
4
3
 
5
4
  RSpec.describe Reek::Context::SingletonMethodContext do
6
5
  let(:smc) do
7
6
  sexp = s(:def, :foo, s(:args, s(:arg, :bar)), nil)
8
- Reek::Context::SingletonMethodContext.new(Reek::Context::RootContext.new, sexp)
7
+ Reek::Context::SingletonMethodContext.new(nil, sexp)
9
8
  end
10
9
 
11
10
  describe '#envious_receivers' do
@@ -45,15 +45,12 @@ RSpec.describe Reek::Examiner do
45
45
  end
46
46
 
47
47
  context 'with a partially masked smelly File' do
48
- around(:each) do |example|
49
- with_test_config('spec/samples/all_but_one_masked/masked.reek') do
50
- example.run
51
- end
52
- end
48
+ let(:path) { SAMPLES_PATH.join('all_but_one_masked/masked.reek') }
49
+ let(:configuration) { test_configuration_for(path) }
53
50
 
54
51
  before :each do
55
- smelly_file = File.new(Dir['spec/samples/all_but_one_masked/d*.rb'][0])
56
- @examiner = described_class.new(smelly_file)
52
+ smelly_file = Pathname.glob(SAMPLES_PATH.join('all_but_one_masked/d*.rb')).first
53
+ @examiner = described_class.new(smelly_file, [], configuration: configuration)
57
54
  end
58
55
 
59
56
  it_should_behave_like 'one smell found'
@@ -61,7 +58,7 @@ RSpec.describe Reek::Examiner do
61
58
 
62
59
  context 'with a fragrant File' do
63
60
  before :each do
64
- clean_file = File.new(Dir['spec/samples/three_clean_files/*.rb'][0])
61
+ clean_file = Pathname.glob(SAMPLES_PATH.join('three_clean_files/*.rb')).first
65
62
  @examiner = described_class.new(clean_file)
66
63
  end
67
64
 
@@ -1,3 +1,4 @@
1
+ require 'pathname'
1
2
  require_relative '../../spec_helper'
2
3
  require_relative '../../../lib/reek/examiner'
3
4
  require_relative '../../../lib/reek/report/report'
@@ -27,8 +28,8 @@ RSpec.describe Reek::Report::XMLReport do
27
28
  end
28
29
 
29
30
  it 'prints non-empty checkstyle xml' do
30
- sample_path = File.expand_path 'checkstyle.xml', 'spec/samples'
31
- expect { instance.show }.to output(File.read(sample_path)).to_stdout
31
+ sample_path = SAMPLES_PATH.join('checkstyle.xml')
32
+ expect { instance.show }.to output(sample_path.read).to_stdout
32
33
  end
33
34
  end
34
35
  end
@@ -6,21 +6,16 @@ require_relative 'smell_detector_shared'
6
6
  RSpec.describe Reek::Smells::Attribute do
7
7
  let(:config) do
8
8
  {
9
- Attribute: { Reek::Smells::SmellConfiguration::ENABLED_KEY => true }
9
+ Reek::Smells::Attribute => { Reek::Smells::SmellConfiguration::ENABLED_KEY => true }
10
10
  }
11
11
  end
12
+ let(:configuration) { test_configuration_for(config) }
12
13
 
13
14
  before(:each) do
14
15
  @source_name = 'dummy_source'
15
16
  @detector = build(:smell_detector, smell_type: :Attribute, source: @source_name)
16
17
  end
17
18
 
18
- around(:each) do |example|
19
- with_test_config(config) do
20
- example.run
21
- end
22
- end
23
-
24
19
  it_should_behave_like 'SmellDetector'
25
20
 
26
21
  context 'with no attributes' do
@@ -28,7 +23,7 @@ RSpec.describe Reek::Smells::Attribute do
28
23
  expect('
29
24
  class Klass
30
25
  end
31
- ').to_not reek_of(:Attribute)
26
+ ').to_not reek_of(:Attribute, {}, configuration)
32
27
  end
33
28
  end
34
29
 
@@ -45,7 +40,7 @@ RSpec.describe Reek::Smells::Attribute do
45
40
  private
46
41
  attr :super_thing2
47
42
  end
48
- ').to_not reek_of(:Attribute)
43
+ ').to_not reek_of(:Attribute, {}, configuration)
49
44
  end
50
45
 
51
46
  it 'records attr attribute in a module' do
@@ -53,7 +48,7 @@ RSpec.describe Reek::Smells::Attribute do
53
48
  module Mod
54
49
  attr :my_attr
55
50
  end
56
- ').to reek_of(:Attribute, name: 'my_attr')
51
+ ').to reek_of(:Attribute, { name: 'my_attr' }, configuration)
57
52
  end
58
53
 
59
54
  it 'records attr attribute' do
@@ -61,7 +56,7 @@ RSpec.describe Reek::Smells::Attribute do
61
56
  class Klass
62
57
  attr :my_attr
63
58
  end
64
- ').to reek_of(:Attribute, name: 'my_attr')
59
+ ').to reek_of(:Attribute, { name: 'my_attr' }, configuration)
65
60
  end
66
61
 
67
62
  it 'records reader attribute' do
@@ -69,7 +64,7 @@ RSpec.describe Reek::Smells::Attribute do
69
64
  class Klass
70
65
  attr_reader :my_attr
71
66
  end
72
- ').to reek_of(:Attribute, name: 'my_attr')
67
+ ').to reek_of(:Attribute, { name: 'my_attr' }, configuration)
73
68
  end
74
69
 
75
70
  it 'records writer attribute' do
@@ -77,7 +72,7 @@ RSpec.describe Reek::Smells::Attribute do
77
72
  class Klass
78
73
  attr_writer :my_attr
79
74
  end
80
- ').to reek_of(:Attribute, name: 'my_attr')
75
+ ').to reek_of(:Attribute, { name: 'my_attr' }, configuration)
81
76
  end
82
77
 
83
78
  it 'records accessor attribute' do
@@ -85,7 +80,7 @@ RSpec.describe Reek::Smells::Attribute do
85
80
  class Klass
86
81
  attr_accessor :my_attr
87
82
  end
88
- ').to reek_of(:Attribute, name: 'my_attr')
83
+ ').to reek_of(:Attribute, { name: 'my_attr' }, configuration)
89
84
  end
90
85
 
91
86
  it 'records attr attribute after switching visbility' do
@@ -97,7 +92,7 @@ RSpec.describe Reek::Smells::Attribute do
97
92
  private :my_attr
98
93
  public :my_attr
99
94
  end
100
- ').to reek_of(:Attribute, name: 'my_attr')
95
+ ').to reek_of(:Attribute, { name: 'my_attr' }, configuration)
101
96
  end
102
97
 
103
98
  it "doesn't record protected attributes" do
@@ -107,7 +102,7 @@ RSpec.describe Reek::Smells::Attribute do
107
102
  attr :iam_protected
108
103
  end
109
104
  '
110
- expect(src).to_not reek_of(:Attribute, name: 'iam_protected')
105
+ expect(src).to_not reek_of(:Attribute, { name: 'iam_protected' }, configuration)
111
106
  end
112
107
 
113
108
  it "doesn't record private attributes" do
@@ -117,7 +112,7 @@ RSpec.describe Reek::Smells::Attribute do
117
112
  attr :iam_private
118
113
  end
119
114
  '
120
- expect(src).to_not reek_of(:Attribute, name: 'iam_private')
115
+ expect(src).to_not reek_of(:Attribute, { name: 'iam_private' }, configuration)
121
116
  end
122
117
  end
123
118
  end