reek 4.4.0 → 4.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -0
  3. data/CONTRIBUTING.md +41 -4
  4. data/README.md +15 -3
  5. data/defaults.reek +1 -1
  6. data/docs/Basic-Smell-Options.md +2 -2
  7. data/docs/Code-Smells.md +4 -0
  8. data/docs/How-To-Write-New-Detectors.md +116 -0
  9. data/docs/How-reek-works-internally.md +3 -4
  10. data/docs/Instance-Variable-Assumption.md +134 -0
  11. data/docs/Simulated-Polymorphism.md +1 -1
  12. data/docs/Smell-Suppression.md +6 -6
  13. data/docs/{style-guide.md → Style-Guide.md} +0 -0
  14. data/docs/Unused-Private-Method.md +1 -1
  15. data/docs/YAML-Reports.md +0 -18
  16. data/features/configuration_files/directory_specific_directives.feature +4 -4
  17. data/features/configuration_files/unused_private_method.feature +2 -2
  18. data/features/samples.feature +122 -117
  19. data/features/smells/subclassed_from_core_class.feature +1 -1
  20. data/lib/reek/code_comment.rb +13 -4
  21. data/lib/reek/context/code_context.rb +1 -0
  22. data/lib/reek/examiner.rb +24 -27
  23. data/lib/reek/smells/class_variable.rb +1 -1
  24. data/lib/reek/smells/control_parameter.rb +1 -1
  25. data/lib/reek/smells/data_clump.rb +1 -1
  26. data/lib/reek/smells/duplicate_method_call.rb +1 -1
  27. data/lib/reek/smells/feature_envy.rb +1 -1
  28. data/lib/reek/smells/instance_variable_assumption.rb +1 -1
  29. data/lib/reek/smells/prima_donna_method.rb +1 -1
  30. data/lib/reek/smells/repeated_conditional.rb +1 -1
  31. data/lib/reek/smells/smell_detector.rb +5 -14
  32. data/lib/reek/smells/smell_repository.rb +1 -5
  33. data/lib/reek/smells/smell_warning.rb +6 -8
  34. data/lib/reek/smells/subclassed_from_core_class.rb +1 -1
  35. data/lib/reek/smells/uncommunicative_variable_name.rb +22 -12
  36. data/lib/reek/smells/unused_private_method.rb +1 -1
  37. data/lib/reek/spec.rb +2 -2
  38. data/lib/reek/spec/should_reek_of.rb +12 -8
  39. data/lib/reek/version.rb +1 -1
  40. data/spec/reek/code_comment_spec.rb +13 -5
  41. data/spec/reek/examiner_spec.rb +2 -2
  42. data/spec/reek/smells/attribute_spec.rb +91 -78
  43. data/spec/reek/smells/boolean_parameter_spec.rb +72 -64
  44. data/spec/reek/smells/class_variable_spec.rb +81 -68
  45. data/spec/reek/smells/control_parameter_spec.rb +101 -141
  46. data/spec/reek/smells/data_clump_spec.rb +94 -149
  47. data/spec/reek/smells/duplicate_method_call_spec.rb +98 -85
  48. data/spec/reek/smells/feature_envy_spec.rb +164 -183
  49. data/spec/reek/smells/instance_variable_assumption_spec.rb +51 -147
  50. data/spec/reek/smells/irresponsible_module_spec.rb +153 -170
  51. data/spec/reek/smells/long_parameter_list_spec.rb +44 -88
  52. data/spec/reek/smells/long_yield_list_spec.rb +41 -41
  53. data/spec/reek/smells/manual_dispatch_spec.rb +36 -18
  54. data/spec/reek/smells/module_initialize_spec.rb +31 -33
  55. data/spec/reek/smells/nested_iterators_spec.rb +189 -183
  56. data/spec/reek/smells/nil_check_spec.rb +48 -37
  57. data/spec/reek/smells/prima_donna_method_spec.rb +41 -26
  58. data/spec/reek/smells/repeated_conditional_spec.rb +75 -87
  59. data/spec/reek/smells/smell_warning_spec.rb +7 -0
  60. data/spec/reek/smells/subclassed_from_core_class_spec.rb +37 -112
  61. data/spec/reek/smells/too_many_constants_spec.rb +109 -199
  62. data/spec/reek/smells/too_many_instance_variables_spec.rb +105 -128
  63. data/spec/reek/smells/too_many_methods_spec.rb +38 -62
  64. data/spec/reek/smells/too_many_statements_spec.rb +69 -45
  65. data/spec/reek/smells/uncommunicative_method_name_spec.rb +16 -29
  66. data/spec/reek/smells/uncommunicative_module_name_spec.rb +24 -37
  67. data/spec/reek/smells/uncommunicative_parameter_name_spec.rb +55 -60
  68. data/spec/reek/smells/uncommunicative_variable_name_spec.rb +108 -95
  69. data/spec/reek/smells/unused_parameters_spec.rb +73 -49
  70. data/spec/reek/smells/unused_private_method_spec.rb +97 -50
  71. data/spec/reek/smells/utility_function_spec.rb +130 -188
  72. data/spec/reek/spec/should_reek_of_spec.rb +2 -2
  73. metadata +6 -7
  74. data/lib/reek/cli/warning_collector.rb +0 -27
  75. data/spec/reek/cli/warning_collector_spec.rb +0 -25
  76. data/spec/reek/smells/smell_detector_shared.rb +0 -29
@@ -1,140 +1,148 @@
1
1
  require_relative '../../spec_helper'
2
2
  require_lib 'reek/smells/uncommunicative_variable_name'
3
- require_relative 'smell_detector_shared'
4
3
 
5
4
  RSpec.describe Reek::Smells::UncommunicativeVariableName do
6
- let(:detector) do
7
- build(:smell_detector, smell_type: :UncommunicativeVariableName)
5
+ it 'reports the right values' do
6
+ src = <<-EOS
7
+ def alfa
8
+ x = 5
9
+ end
10
+ EOS
11
+
12
+ expect(src).to reek_of(:UncommunicativeVariableName,
13
+ lines: [2],
14
+ context: 'alfa',
15
+ message: "has the variable name 'x'",
16
+ source: 'string',
17
+ name: 'x')
8
18
  end
9
19
 
10
- it_should_behave_like 'SmellDetector'
20
+ it 'does count all occurences' do
21
+ src = <<-EOS
22
+ def alfa
23
+ x = 3
24
+ y = 7
25
+ end
26
+ EOS
27
+
28
+ expect(src).to reek_of(:UncommunicativeVariableName,
29
+ lines: [2],
30
+ name: 'x')
31
+ expect(src).to reek_of(:UncommunicativeVariableName,
32
+ lines: [3],
33
+ name: 'y')
34
+ end
11
35
 
12
- context 'field name' do
13
- it 'does not report use of one-letter fieldname' do
14
- src = 'class Thing; def simple(fred) @x end end'
36
+ context 'instance variables' do
37
+ it 'does not report use of one-letter names' do
38
+ src = 'class Alfa; def bravo; @x; end; end'
15
39
  expect(src).not_to reek_of(:UncommunicativeVariableName)
16
40
  end
17
- it 'reports one-letter fieldname in assignment' do
18
- src = 'class Thing; def simple(fred) @x = fred end end'
41
+
42
+ it 'reports one-letter names in assignment' do
43
+ src = 'class Alfa; def bravo(charlie) @x = charlie; end; end'
19
44
  expect(src).to reek_of(:UncommunicativeVariableName, name: '@x')
20
45
  end
21
46
  end
22
47
 
23
- context 'local variable name' do
24
- it 'does not report one-word variable name' do
25
- expect('def help(fred) simple = jim(45) end').
26
- not_to reek_of(:UncommunicativeVariableName)
27
- end
28
-
48
+ context 'local variables' do
29
49
  it 'does not report single underscore as a variable name' do
30
- expect('def help(fred) _ = jim(45) end').not_to reek_of(:UncommunicativeVariableName)
50
+ src = 'def alfa; _ = bravo(); end'
51
+ expect(src).not_to reek_of(:UncommunicativeVariableName)
31
52
  end
32
53
 
33
- it 'reports one-letter variable name' do
34
- src = 'def simple(fred) x = jim(45) end'
54
+ it 'reports one-letter names' do
55
+ src = 'def alfa; x = bravo(); end'
35
56
  expect(src).to reek_of(:UncommunicativeVariableName, name: 'x')
36
57
  end
37
58
 
38
- it 'reports name of the form "x2"' do
39
- src = 'def simple(fred) x2 = jim(45) end'
40
- expect(src).to reek_of(:UncommunicativeVariableName, name: 'x2')
59
+ it 'reports names ending with a digit' do
60
+ src = 'def alfa; var123 = bravo(); end'
61
+ expect(src).to reek_of(:UncommunicativeVariableName, name: 'var123')
41
62
  end
42
63
 
43
- it 'reports long name ending in a number' do
44
- bad_var = 'var123'
45
- src = "def simple(fred) #{bad_var} = jim(45) end"
46
- expect(src).to reek_of(:UncommunicativeVariableName,
47
- name: bad_var)
48
- end
49
-
50
- it 'reports variable name only once' do
51
- src = 'def simple(fred) x = jim(45); x = y end'
52
- syntax_tree = Reek::Source::SourceCode.from(src).syntax_tree
53
- ctx = Reek::Context::CodeContext.new(nil, syntax_tree)
54
- smells = detector.sniff(ctx)
55
- expect(smells.length).to eq(1)
56
- expect(smells[0].smell_type).to eq(described_class.smell_type)
57
- expect(smells[0].parameters[:name]).to eq('x')
58
- expect(smells[0].lines).to eq([1, 1])
64
+ it 'reports camelcased names' do
65
+ src = 'def alfa; bravoCharlie = delta(); end'
66
+ expect(src).to reek_of(:UncommunicativeVariableName, name: 'bravoCharlie')
59
67
  end
60
68
 
61
69
  it 'reports a bad name inside a block' do
62
- src = 'def clean(text) text.each { q2 = 3 } end'
63
- expect(src).to reek_of(:UncommunicativeVariableName, name: 'q2')
70
+ src = 'def alfa; bravo.each { x = 42 }; end'
71
+ expect(src).to reek_of(:UncommunicativeVariableName, name: 'x')
64
72
  end
65
73
 
66
74
  it 'reports variable name outside any method' do
67
- expect('class Simple; x = jim(45); end').to reek_of(:UncommunicativeVariableName,
68
- name: 'x')
75
+ src = 'class Alfa; x = bravo(); end'
76
+ expect(src).to reek_of(:UncommunicativeVariableName, name: 'x')
69
77
  end
70
- end
71
78
 
72
- context 'block parameter name' do
73
- it 'reports deep block parameter' do
74
- src = <<-EOS
75
- def bad
76
- unless @mod then
77
- @sig.each { |x| x.to_s }
78
- end
79
- end
80
- EOS
81
- expect(src).to reek_of(:UncommunicativeVariableName, name: 'x')
79
+ it "reports bad names starting with '_'" do
80
+ src = 'def alfa; _bravoCharlie_42 = delta(); end'
81
+ expect(src).to reek_of(:UncommunicativeVariableName, name: '_bravoCharlie_42')
82
82
  end
83
+ end
83
84
 
85
+ context 'block parameters' do
84
86
  it 'reports all relevant block parameters' do
85
87
  src = <<-EOS
86
- def bad
87
- @foo.map { |x, y| x + y }
88
+ def alfa
89
+ @bravo.map { |x, y| x + y }
88
90
  end
89
91
  EOS
92
+
90
93
  expect(src).to reek_of(:UncommunicativeVariableName, name: 'x')
91
94
  expect(src).to reek_of(:UncommunicativeVariableName, name: 'y')
92
95
  end
93
96
 
94
97
  it 'reports block parameters used outside of methods' do
95
98
  src = <<-EOS
96
- class Foo
97
- @foo.map { |x| x * 2 }
98
- end
99
+ class Alfa
100
+ @bravo.map { |x| x * 2 }
101
+ end
99
102
  EOS
103
+
100
104
  expect(src).to reek_of(:UncommunicativeVariableName, name: 'x')
101
105
  end
102
106
 
103
107
  it 'reports splatted block parameters correctly' do
104
108
  src = <<-EOS
105
- def bad
106
- @foo.map { |*y| y << 1 }
109
+ def alfa
110
+ @bravo.map { |*y| y << 1 }
107
111
  end
108
112
  EOS
113
+
109
114
  expect(src).to reek_of(:UncommunicativeVariableName, name: 'y')
110
115
  end
111
116
 
112
117
  it 'reports nested block parameters' do
113
118
  src = <<-EOS
114
- def bad
115
- @foo.map { |(x, y)| x + y }
119
+ def alfa
120
+ @bravo.map { |(x, y)| x + y }
116
121
  end
117
122
  EOS
123
+
118
124
  expect(src).to reek_of(:UncommunicativeVariableName, name: 'x')
119
125
  expect(src).to reek_of(:UncommunicativeVariableName, name: 'y')
120
126
  end
121
127
 
122
128
  it 'reports splatted nested block parameters' do
123
129
  src = <<-EOS
124
- def bad
125
- @foo.map { |(x, *y)| x + y }
130
+ def def alfa
131
+ @bravo.map { |(x, *y)| x + y }
126
132
  end
127
133
  EOS
134
+
128
135
  expect(src).to reek_of(:UncommunicativeVariableName, name: 'x')
129
136
  expect(src).to reek_of(:UncommunicativeVariableName, name: 'y')
130
137
  end
131
138
 
132
139
  it 'reports deeply nested block parameters' do
133
140
  src = <<-EOS
134
- def bad
135
- @foo.map { |(x, (y, z))| x + y + z }
141
+ def alfa
142
+ @bravo.map { |(x, (y, z))| x + y + z }
136
143
  end
137
144
  EOS
145
+
138
146
  expect(src).to reek_of(:UncommunicativeVariableName, name: 'x')
139
147
  expect(src).to reek_of(:UncommunicativeVariableName, name: 'y')
140
148
  expect(src).to reek_of(:UncommunicativeVariableName, name: 'z')
@@ -142,51 +150,56 @@ RSpec.describe Reek::Smells::UncommunicativeVariableName do
142
150
 
143
151
  it 'reports shadowed block parameters' do
144
152
  src = <<-EOS
145
- def bad
146
- @foo.map { |x; y| y = x * 2 }
153
+ def alfa
154
+ @bravo.map { |x; y| y = x * 2 }
147
155
  end
148
156
  EOS
157
+
149
158
  expect(src).to reek_of(:UncommunicativeVariableName, name: 'x')
150
159
  expect(src).to reek_of(:UncommunicativeVariableName, name: 'y')
151
160
  end
152
161
  end
153
162
 
154
- context 'when a smell is reported' do
155
- let(:warning) do
156
- src = <<-EOS
157
- def bad
158
- unless @mod then
159
- x2 = xy.to_s
160
- x2
161
- x2 = 56
162
- end
163
- end
164
- EOS
165
- syntax_tree = Reek::Source::SourceCode.from(src).syntax_tree
166
- ctx = Reek::Context::CodeContext.new(nil, syntax_tree)
167
- detector.sniff(ctx).first
168
- end
163
+ describe '`accept` patterns' do
164
+ let(:src) { 'def alfa; bravo2 = 42; end' }
169
165
 
170
- it_should_behave_like 'common fields set correctly'
166
+ it 'make smelly names pass via regex / strings given by list / literal' do
167
+ expect(src).to reek_of(:UncommunicativeVariableName)
171
168
 
172
- it 'reports the correct values' do
173
- expect(warning.parameters[:name]).to eq('x2')
174
- expect(warning.lines).to eq([3, 5])
169
+ [[/bravo2/], /bravo2/, ['bravo2'], 'bravo2'].each do |pattern|
170
+ expect(src).to_not reek_of(:UncommunicativeVariableName).with_config('accept' => pattern)
171
+ end
175
172
  end
176
173
  end
177
174
 
178
- context 'when a smell is reported in a singleton method' do
179
- let(:warning) do
180
- src = 'def self.bad() x2 = 4; end'
181
- syntax_tree = Reek::Source::SourceCode.from(src).syntax_tree
182
- ctx = Reek::Context::CodeContext.new(nil, syntax_tree)
183
- detector.sniff(ctx).first
175
+ describe '`reject` patterns' do
176
+ let(:src) { 'def alfa; foobar = 42; end' }
177
+
178
+ it 'reject smelly names via regex / strings given by list / literal' do
179
+ expect(src).not_to reek_of(:UncommunicativeVariableName)
180
+
181
+ [[/foobar/], /foobar/, ['foobar'], 'foobar'].each do |pattern|
182
+ expect(src).to reek_of(:UncommunicativeVariableName).with_config('reject' => pattern)
183
+ end
184
184
  end
185
+ end
186
+
187
+ describe '.default_config' do
188
+ it 'should merge in the default accept and reject patterns' do
189
+ expected = {
190
+ 'enabled' => true,
191
+ 'exclude' => [],
192
+ 'reject' => [/^.$/, /[0-9]$/, /[A-Z]/],
193
+ 'accept' => [/^_$/]
194
+ }
185
195
 
186
- it_should_behave_like 'common fields set correctly'
196
+ expect(described_class.default_config).to eq(expected)
197
+ end
198
+ end
187
199
 
188
- it 'reports the fq context' do
189
- expect(warning.context).to eq('self.bad')
200
+ describe '.contexts' do
201
+ it 'are scoped to classes, modules, instance and singleton methods' do
202
+ expect(described_class.contexts).to eq([:module, :class, :def, :defs])
190
203
  end
191
204
  end
192
205
  end
@@ -1,79 +1,103 @@
1
1
  require_relative '../../spec_helper'
2
2
  require_lib 'reek/smells/unused_parameters'
3
- require_relative 'smell_detector_shared'
4
3
 
5
4
  RSpec.describe Reek::Smells::UnusedParameters do
6
- context 'for methods' do
7
- it 'reports nothing for no parameters' do
8
- expect('def simple; true end').not_to reek_of(:UnusedParameters)
9
- end
5
+ it 'reports the right values' do
6
+ src = <<-EOS
7
+ def alfa(bravo)
8
+ end
9
+ EOS
10
+
11
+ expect(src).to reek_of(:UnusedParameters,
12
+ lines: [1],
13
+ context: 'alfa',
14
+ message: "has unused parameter 'bravo'",
15
+ source: 'string',
16
+ name: 'bravo')
17
+ end
10
18
 
11
- it 'reports nothing for used parameter' do
12
- expect('def simple(sum); sum end').not_to reek_of(:UnusedParameters)
13
- end
19
+ it 'does count all occurences' do
20
+ src = <<-EOS
21
+ def alfa(bravo, charlie)
22
+ end
23
+ EOS
24
+
25
+ expect(src).to reek_of(:UnusedParameters,
26
+ lines: [1],
27
+ name: 'bravo')
28
+ expect(src).to reek_of(:UnusedParameters,
29
+ lines: [1],
30
+ name: 'charlie')
31
+ end
14
32
 
15
- it 'reports for 1 used and 2 unused parameter' do
16
- src = 'def simple(num,sum,denum); sum end'
17
- expect(src).to reek_of(:UnusedParameters, name: 'num')
18
- expect(src).to reek_of(:UnusedParameters, name: 'denum')
19
- end
33
+ it 'reports nothing for no parameters' do
34
+ expect('def alfa; end').not_to reek_of(:UnusedParameters)
35
+ end
20
36
 
21
- it 'reports for 3 used and 1 unused parameter' do
22
- src = 'def simple(num,sum,denum,quotient); num + denum + sum end'
23
- expect(src).to reek_of(:UnusedParameters,
24
- name: 'quotient')
25
- end
37
+ it 'reports nothing for used parameter' do
38
+ expect('def alfa(bravo); bravo; end').not_to reek_of(:UnusedParameters)
39
+ end
26
40
 
27
- it 'reports nothing for used splatted parameter' do
28
- expect('def simple(*sum); sum end').not_to reek_of(:UnusedParameters)
29
- end
41
+ it 'reports for 1 used and 2 unused parameter' do
42
+ src = 'def alfa(bravo, charlie, delta); bravo end'
30
43
 
31
- it 'reports nothing for unused anonymous parameter' do
32
- expect('def simple(_); end').not_to reek_of(:UnusedParameters)
33
- end
44
+ expect(src).not_to reek_of(:UnusedParameters, name: 'bravo')
45
+ expect(src).to reek_of(:UnusedParameters, name: 'charlie')
46
+ expect(src).to reek_of(:UnusedParameters, name: 'delta')
47
+ end
34
48
 
35
- it 'reports nothing for named parameters prefixed with _' do
36
- expect('def simple(_name); end').not_to reek_of(:UnusedParameters)
37
- end
49
+ it 'reports nothing for named parameters prefixed with _' do
50
+ expect('def alfa(_bravo); end').not_to reek_of(:UnusedParameters)
51
+ end
38
52
 
39
- it 'reports nothing for unused anonymous splatted parameter' do
40
- expect('def simple(*); end').not_to reek_of(:UnusedParameters)
53
+ it 'reports nothing when using a parameter via self assignment' do
54
+ expect('def alfa(bravo); bravo += 1; end').not_to reek_of(:UnusedParameters)
55
+ end
56
+
57
+ it 'reports nothing when using a parameter on a rescue' do
58
+ expect('def alfa(bravo = 3); puts "nothing"; rescue; retry if bravo -= 1 > 0; raise; end').
59
+ not_to reek_of(:UnusedParameters)
60
+ end
61
+
62
+ context 'using super' do
63
+ it 'reports nothing with implicit arguments' do
64
+ expect('def alfa(*bravo); super; end').not_to reek_of(:UnusedParameters)
41
65
  end
42
66
 
43
- it 'reports nothing when using super with implicit arguments' do
44
- expect('def simple(*args); super; end').not_to reek_of(:UnusedParameters)
67
+ it 'reports something when explicitely passing no arguments' do
68
+ expect('def alfa(*bravo); super(); end').to reek_of(:UnusedParameters)
45
69
  end
46
70
 
47
- it 'reports something when using super explicitely passing no arguments' do
48
- expect('def simple(*args); super(); end').to reek_of(:UnusedParameters)
71
+ it 'reports nothing when explicitely passing all arguments' do
72
+ expect('def alfa(*bravo); super(*bravo); end').not_to reek_of(:UnusedParameters)
49
73
  end
50
74
 
51
- it 'reports nothing when using super explicitely passing all arguments' do
52
- expect('def simple(*args); super(*args); end').not_to reek_of(:UnusedParameters)
75
+ it 'reports nothing in a nested context' do
76
+ expect('def alfa(*bravo); charlie(super); end').not_to reek_of(:UnusedParameters)
53
77
  end
78
+ end
54
79
 
55
- it 'reports nothing when using super in a nested context' do
56
- expect('def simple(*args); call_other("something", super); end').
57
- not_to reek_of(:UnusedParameters)
80
+ context 'anonymous parameters' do
81
+ it 'reports nothing for unused anonymous parameter' do
82
+ expect('def alfa(_); end').not_to reek_of(:UnusedParameters)
58
83
  end
59
84
 
60
- it 'reports something when not using a keyword argument with splat' do
61
- expect('def simple(var, kw: :val, **args); @var, @kw = var, kw; end').
62
- to reek_of(:UnusedParameters)
85
+ it 'reports nothing for unused anonymous splatted parameter' do
86
+ expect('def alfa(*); end').not_to reek_of(:UnusedParameters)
63
87
  end
88
+ end
64
89
 
65
- it 'reports nothing when using a keyword argument with splat' do
66
- expect('def simple(var, kw: :val, **args); @var, @kw, @args = var, kw, args; end').
67
- not_to reek_of(:UnusedParameters)
90
+ context 'splatted parameters' do
91
+ it 'reports nothing for used splatted parameter' do
92
+ expect('def alfa(*bravo); bravo; end').not_to reek_of(:UnusedParameters)
68
93
  end
69
94
 
70
- it 'reports nothing when using a parameter via self assignment' do
71
- expect('def simple(counter); counter += 1; end').not_to reek_of(:UnusedParameters)
95
+ it 'reports something when not using a keyword argument with splat' do
96
+ expect('def alfa(**bravo); end').to reek_of(:UnusedParameters)
72
97
  end
73
98
 
74
- it 'reports nothing when using a parameter on a rescue' do
75
- expect('def simple(tries = 3); puts "nothing"; rescue; retry if tries -= 1 > 0; raise; end').
76
- not_to reek_of(:UnusedParameters)
99
+ it 'reports nothing when using a keyword argument with splat' do
100
+ expect('def alfa(**bravo); bravo; end').not_to reek_of(:UnusedParameters)
77
101
  end
78
102
  end
79
103
  end