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
@@ -30,6 +30,14 @@ RSpec.describe Reek::CodeComment do
30
30
 
31
31
  context 'comment config' do
32
32
  it 'parses hashed options' do
33
+ comment = '# :reek:Duplication { enabled: false }'
34
+ config = described_class.new(comment).config
35
+ expect(config).to include('Duplication')
36
+ expect(config['Duplication']).to include('enabled')
37
+ expect(config['Duplication']['enabled']).to be_falsey
38
+ end
39
+
40
+ it "supports hashed options with the legacy separator ':' after the smell detector" do
33
41
  comment = '# :reek:Duplication: { enabled: false }'
34
42
  config = described_class.new(comment).config
35
43
  expect(config).to include('Duplication')
@@ -39,8 +47,8 @@ RSpec.describe Reek::CodeComment do
39
47
 
40
48
  it 'parses multiple hashed options' do
41
49
  config = described_class.new('
42
- # :reek:Duplication: { enabled: false }
43
- # :reek:NestedIterators: { enabled: true }
50
+ # :reek:Duplication { enabled: false }
51
+ # :reek:NestedIterators { enabled: true }
44
52
  ').config
45
53
  expect(config).to include('Duplication', 'NestedIterators')
46
54
  expect(config['Duplication']).to include('enabled')
@@ -51,7 +59,7 @@ RSpec.describe Reek::CodeComment do
51
59
 
52
60
  it 'parses multiple hashed options on the same line' do
53
61
  config = described_class.new('
54
- #:reek:Duplication: { enabled: false } and :reek:NestedIterators: { enabled: true }
62
+ #:reek:Duplication { enabled: false } and :reek:NestedIterators { enabled: true }
55
63
  ').config
56
64
  expect(config).to include('Duplication', 'NestedIterators')
57
65
  expect(config['Duplication']).to include('enabled')
@@ -85,8 +93,8 @@ RSpec.describe Reek::CodeComment do
85
93
  it 'removes the configuration options from the comment' do
86
94
  subject = described_class.new('
87
95
  # Actual
88
- # :reek:Duplication: { enabled: false }
89
- # :reek:NestedIterators: { enabled: true }
96
+ # :reek:Duplication { enabled: false }
97
+ # :reek:NestedIterators { enabled: true }
90
98
  # comment
91
99
  ')
92
100
  expect(subject.send(:sanitized_comment)).to eq('Actual comment')
@@ -67,7 +67,7 @@ RSpec.describe Reek::Examiner do
67
67
  end
68
68
 
69
69
  it 'has been run on the given source' do
70
- expect(examiner.description).to eq('string')
70
+ expect(examiner.origin).to eq('string')
71
71
  end
72
72
 
73
73
  it 'has the right smells' do
@@ -90,7 +90,7 @@ RSpec.describe Reek::Examiner do
90
90
 
91
91
  smell = examiner.smells.first
92
92
  expect(smell).to be_a(Reek::Smells::SmellWarning)
93
- expect(smell.message).to eq('calls bar.call_me() 2 times')
93
+ expect(smell.message).to eq("calls 'bar.call_me()' 2 times")
94
94
  end
95
95
 
96
96
  context 'source is empty' do
@@ -1,187 +1,200 @@
1
1
  require_relative '../../spec_helper'
2
2
  require_lib 'reek/smells/attribute'
3
- require_relative 'smell_detector_shared'
4
3
 
5
4
  RSpec.describe Reek::Smells::Attribute do
6
- let(:detector) { build(:smell_detector, smell_type: :Attribute) }
7
-
8
- it_should_behave_like 'SmellDetector'
9
-
10
5
  it 'reports the right values' do
11
6
  src = <<-EOS
12
- class Klass
13
- attr_writer :my_attr
7
+ class Alfa
8
+ attr_writer :bravo
14
9
  end
15
10
  EOS
11
+
16
12
  expect(src).to reek_of(:Attribute,
17
13
  lines: [2],
14
+ context: 'Alfa#bravo',
18
15
  message: 'is a writable attribute')
19
16
  end
20
17
 
21
- context 'with no attributes' do
22
- it 'records nothing' do
23
- src = <<-EOS
24
- class Klass
25
- end
26
- EOS
27
- expect(src).to_not reek_of(:Attribute)
28
- end
18
+ it 'does count all occurences' do
19
+ src = <<-EOS
20
+ class Alfa
21
+ attr_writer :bravo
22
+ attr_writer :charlie
23
+ end
24
+ EOS
25
+
26
+ expect(src).to reek_of(:Attribute,
27
+ lines: [2],
28
+ context: 'Alfa#bravo')
29
+ expect(src).to reek_of(:Attribute,
30
+ lines: [3],
31
+ context: 'Alfa#charlie')
32
+ end
33
+
34
+ it 'records nothing with no attributes' do
35
+ src = <<-EOS
36
+ class Alfa
37
+ end
38
+ EOS
39
+
40
+ expect(src).to_not reek_of(:Attribute)
29
41
  end
30
42
 
31
43
  context 'with attributes' do
32
44
  it 'records nothing for attribute readers' do
33
45
  src = <<-EOS
34
- class Klass
35
- attr :my_attr
36
- attr_reader :my_attr2
46
+ class Alfa
47
+ attr :bravo
48
+ attr_reader :charlie
37
49
  end
38
50
  EOS
51
+
39
52
  expect(src).to_not reek_of(:Attribute)
40
53
  end
41
54
 
42
55
  it 'records writer attribute' do
43
56
  src = <<-EOS
44
- class Klass
45
- attr_writer :my_attr
57
+ class Alfa
58
+ attr_writer :bravo
46
59
  end
47
60
  EOS
48
- expect(src).to reek_of(:Attribute, context: 'Klass#my_attr')
61
+
62
+ expect(src).to reek_of(:Attribute, context: 'Alfa#bravo')
49
63
  end
50
64
 
51
65
  it 'does not record writer attribute if suppressed with a preceding code comment' do
52
66
  src = <<-EOS
53
- class Klass
67
+ class Alfa
54
68
  # :reek:Attribute
55
- attr_writer :my_attr
69
+ attr_writer :bravo
56
70
  end
57
71
  EOS
72
+
58
73
  expect(src).not_to reek_of(:Attribute)
59
74
  end
60
75
 
61
76
  it 'records attr_writer attribute in a module' do
62
77
  src = <<-EOS
63
78
  module Mod
64
- attr_writer :my_attr
79
+ attr_writer :bravo
65
80
  end
66
81
  EOS
67
- expect(src).to reek_of(:Attribute, context: 'Mod#my_attr')
82
+
83
+ expect(src).to reek_of(:Attribute)
68
84
  end
69
85
 
70
86
  it 'records accessor attribute' do
71
87
  src = <<-EOS
72
- class Klass
73
- attr_accessor :my_attr
88
+ class Alfa
89
+ attr_accessor :bravo
74
90
  end
75
91
  EOS
76
- expect(src).to reek_of(:Attribute, context: 'Klass#my_attr')
92
+
93
+ expect(src).to reek_of(:Attribute)
77
94
  end
78
95
 
79
96
  it 'records attr defining a writer' do
80
97
  src = <<-EOS
81
- class Klass
82
- attr :my_attr, true
98
+ class Alfa
99
+ attr :bravo, true
83
100
  end
84
101
  EOS
85
- expect(src).to reek_of(:Attribute, context: 'Klass#my_attr')
102
+
103
+ expect(src).to reek_of(:Attribute)
86
104
  end
87
105
 
88
106
  it "doesn't record protected attributes" do
89
- src = '
90
- class Klass
107
+ src = <<-EOS
108
+ class Alfa
91
109
  protected
92
- attr_writer :attr1
93
- attr_accessor :attr2
94
- attr :attr3
95
- attr :attr4, true
96
- attr_reader :attr5
110
+ attr_writer :alfa
111
+ attr_accessor :bravo
112
+ attr :charlie
113
+ attr :delta, true
114
+ attr_reader :echo
97
115
  end
98
- '
116
+ EOS
117
+
99
118
  expect(src).to_not reek_of(:Attribute)
100
119
  end
101
120
 
102
121
  it "doesn't record private attributes" do
103
- src = '
104
- class Klass
122
+ src = <<-EOS
123
+ class Alfa
105
124
  private
106
- attr_writer :attr1
107
- attr_accessor :attr2
108
- attr :attr3
109
- attr :attr4, true
110
- attr_reader :attr5
125
+ attr_writer :alfa
126
+ attr_accessor :bravo
127
+ attr :charlie
128
+ attr :delta, true
129
+ attr_reader :echo
111
130
  end
112
- '
131
+ EOS
132
+
113
133
  expect(src).to_not reek_of(:Attribute)
114
134
  end
115
135
 
116
136
  it 'records attr_writer defined in public section' do
117
137
  src = <<-EOS
118
- class Klass
138
+ class Alfa
119
139
  private
120
140
  public
121
- attr_writer :my_attr
141
+ attr_writer :bravo
122
142
  end
123
143
  EOS
124
- expect(src).to reek_of(:Attribute, context: 'Klass#my_attr')
144
+
145
+ expect(src).to reek_of(:Attribute)
125
146
  end
126
147
 
127
148
  it 'records attr_writer after switching visbility to public' do
128
149
  src = <<-EOS
129
- class Klass
150
+ class Alfa
130
151
  private
131
- attr_writer :my_attr
132
- public :my_attr
152
+ attr_writer :bravo
153
+ public :bravo
133
154
  end
134
155
  EOS
135
- expect(src).to reek_of(:Attribute, context: 'Klass#my_attr')
156
+
157
+ expect(src).to reek_of(:Attribute)
136
158
  end
137
159
 
138
160
  it 'resets visibility in new contexts' do
139
- src = '
140
- class Klass
161
+ src = <<-EOS
162
+ class Alfa
141
163
  private
142
- attr_writer :attr1
164
+ attr_writer :bravo
143
165
  end
144
166
 
145
- class OtherKlass
146
- attr_writer :attr1
167
+ class Charlie
168
+ attr_writer :delta
147
169
  end
148
- '
149
- expect(src).to reek_of(:Attribute, context: 'OtherKlass#attr1')
170
+ EOS
171
+
172
+ expect(src).to reek_of(:Attribute, context: 'Charlie#delta')
150
173
  end
151
174
 
152
175
  it 'records attr_writer defining a class attribute' do
153
176
  src = <<-EOS
154
- class Klass
177
+ class Alfa
155
178
  class << self
156
- attr_writer :my_attr
179
+ attr_writer :bravo
157
180
  end
158
181
  end
159
182
  EOS
160
- expect(src).to reek_of(:Attribute, context: 'Klass#my_attr')
183
+
184
+ expect(src).to reek_of(:Attribute)
161
185
  end
162
186
 
163
187
  it 'does not record private class attributes' do
164
188
  src = <<-EOS
165
- class Klass
189
+ class Alfa
166
190
  class << self
167
191
  private
168
- attr_writer :my_attr
192
+ attr_writer :bravo
169
193
  end
170
194
  end
171
195
  EOS
172
- expect(src).not_to reek_of(:Attribute)
173
- end
174
196
 
175
- it 'tracks visibility in metaclasses separately' do
176
- src = <<-EOS
177
- class Klass
178
- private
179
- class << self
180
- attr_writer :my_attr
181
- end
182
- end
183
- EOS
184
- expect(src).to reek_of(:Attribute, context: 'Klass#my_attr')
197
+ expect(src).not_to reek_of(:Attribute)
185
198
  end
186
199
  end
187
200
  end
@@ -1,91 +1,99 @@
1
1
  require_relative '../../spec_helper'
2
2
  require_lib 'reek/smells/boolean_parameter'
3
- require_relative 'smell_detector_shared'
4
3
 
5
4
  RSpec.describe Reek::Smells::BooleanParameter do
6
- context 'parameter defaulted with boolean' do
7
- context 'in a method' do
8
- it 'reports a parameter defaulted to true' do
9
- src = 'def cc(arga = true); arga; end'
10
- expect(src).to reek_of(:BooleanParameter, parameter: 'arga')
5
+ it 'reports the right values' do
6
+ src = <<-EOS
7
+ def alfa(bravo = true)
11
8
  end
9
+ EOS
10
+
11
+ expect(src).to reek_of(:BooleanParameter,
12
+ lines: [1],
13
+ context: 'alfa',
14
+ message: "has boolean parameter 'bravo'",
15
+ source: 'string',
16
+ parameter: 'bravo')
17
+ end
12
18
 
13
- it 'reports a parameter defaulted to false' do
14
- src = 'def cc(arga = false) end'
15
- expect(src).to reek_of(:BooleanParameter, parameter: 'arga')
19
+ it 'does count all occurences' do
20
+ src = <<-EOS
21
+ def alfa(bravo = true, charlie = true)
16
22
  end
23
+ EOS
24
+
25
+ expect(src).to reek_of(:BooleanParameter,
26
+ lines: [1],
27
+ context: 'alfa',
28
+ parameter: 'bravo')
29
+ expect(src).to reek_of(:BooleanParameter,
30
+ lines: [1],
31
+ context: 'alfa',
32
+ parameter: 'charlie')
33
+ end
17
34
 
18
- it 'reports two parameters defaulted to booleans' do
19
- src = 'def cc(nowt, arga = true, argb = false, &blk) end'
20
- expect(src).to reek_of(:BooleanParameter, parameter: 'arga')
21
- expect(src).to reek_of(:BooleanParameter, parameter: 'argb')
22
- end
35
+ context 'in a method' do
36
+ it 'reports a parameter defaulted to false' do
37
+ src = 'def alfa(bravo = false) end'
38
+ expect(src).to reek_of(:BooleanParameter)
39
+ end
23
40
 
24
- it 'reports keyword parameters defaulted to booleans' do
25
- src = 'def cc(arga: true, argb: false) end'
26
- expect(src).to reek_of(:BooleanParameter, parameter: 'arga')
27
- expect(src).to reek_of(:BooleanParameter, parameter: 'argb')
28
- end
41
+ it 'reports two parameters defaulted to booleans in a mixed parameter list' do
42
+ src = 'def alfa(bravo, charlie = true, delta = false, &echo) end'
29
43
 
30
- it 'does not report regular parameters' do
31
- src = 'def cc(a, b) end'
32
- expect(src).not_to reek_of(:BooleanParameter)
33
- end
44
+ expect(src).to reek_of(:BooleanParameter, parameter: 'charlie')
45
+ expect(src).to reek_of(:BooleanParameter, parameter: 'delta')
34
46
 
35
- it 'does not report array decomposition parameters' do
36
- src = 'def cc((a, b)) end'
37
- expect(src).not_to reek_of(:BooleanParameter)
38
- end
47
+ expect(src).not_to reek_of(:BooleanParameter, parameter: 'bravo')
48
+ expect(src).not_to reek_of(:BooleanParameter, parameter: 'echo')
49
+ end
39
50
 
40
- it 'does not report keyword parameters with no default' do
41
- src = 'def cc(a:, b:) end'
42
- expect(src).not_to reek_of(:BooleanParameter)
43
- end
51
+ it 'reports keyword parameters defaulted to booleans' do
52
+ src = 'def alfa(bravo: true, charlie: false) end'
53
+ expect(src).to reek_of(:BooleanParameter, parameter: 'bravo')
54
+ expect(src).to reek_of(:BooleanParameter, parameter: 'charlie')
55
+ end
44
56
 
45
- it 'does not report keyword parameters with non-boolean default' do
46
- src = 'def cc(a: 42, b: "32") end'
47
- expect(src).not_to reek_of(:BooleanParameter)
48
- end
57
+ it 'does not report regular parameters' do
58
+ src = 'def alfa(bravo, charlie) end'
59
+ expect(src).not_to reek_of(:BooleanParameter)
49
60
  end
50
61
 
51
- context 'in a singleton method' do
52
- it 'reports a parameter defaulted to true' do
53
- src = 'def self.cc(arga = true) end'
54
- expect(src).to reek_of(:BooleanParameter, parameter: 'arga')
55
- end
62
+ it 'does not report array decomposition parameters' do
63
+ src = 'def alfa((bravo, charlie)) end'
64
+ expect(src).not_to reek_of(:BooleanParameter)
65
+ end
56
66
 
57
- it 'reports a parameter defaulted to false' do
58
- src = 'def fred.cc(arga = false) end'
59
- expect(src).to reek_of(:BooleanParameter, parameter: 'arga')
60
- end
67
+ it 'does not report keyword parameters with no default' do
68
+ src = 'def alfa(bravo:, charlie:) end'
69
+ expect(src).not_to reek_of(:BooleanParameter)
70
+ end
61
71
 
62
- it 'reports two parameters defaulted to booleans' do
63
- src = 'def Module.cc(nowt, arga = true, argb = false, &blk) end'
64
- expect(src).to reek_of(:BooleanParameter, parameter: 'arga')
65
- expect(src).to reek_of(:BooleanParameter, parameter: 'argb')
66
- end
72
+ it 'does not report keyword parameters with non-boolean default' do
73
+ src = 'def alfa(bravo: 42, charlie: "32") end'
74
+ expect(src).not_to reek_of(:BooleanParameter)
67
75
  end
68
76
  end
69
77
 
70
- context 'when a smell is reported' do
71
- let(:detector) { build(:smell_detector, smell_type: :BooleanParameter) }
78
+ context 'in a singleton method' do
79
+ it 'reports a parameter defaulted to true' do
80
+ src = 'def self.alfa(bravo = true) end'
81
+ expect(src).to reek_of(:BooleanParameter)
82
+ end
72
83
 
73
- it_should_behave_like 'SmellDetector'
84
+ it 'reports a parameter defaulted to false' do
85
+ src = 'def self.alfa(bravo = false) end'
86
+ expect(src).to reek_of(:BooleanParameter)
87
+ end
74
88
 
75
- context 'when a smell is reported' do
76
- let(:warning) do
77
- src = 'def cc(arga = true) end'
78
- ctx = Reek::Context::MethodContext.new(nil, Reek::Source::SourceCode.from(src).syntax_tree)
79
- detector.sniff(ctx).first
80
- end
89
+ it 'reports two parameters defaulted to booleans' do
90
+ src = 'def self.alfa(bravo, charlie = true, delta = false, &echo) end'
81
91
 
82
- it_should_behave_like 'common fields set correctly'
92
+ expect(src).to reek_of(:BooleanParameter, parameter: 'charlie')
93
+ expect(src).to reek_of(:BooleanParameter, parameter: 'delta')
83
94
 
84
- it 'reports the correct values' do
85
- expect(warning.parameters[:parameter]).to eq('arga')
86
- expect(warning.lines).to eq([1])
87
- expect(warning.message).to eq("has boolean parameter 'arga'")
88
- end
95
+ expect(src).not_to reek_of(:BooleanParameter, parameter: 'bravo')
96
+ expect(src).not_to reek_of(:BooleanParameter, parameter: 'echo')
89
97
  end
90
98
  end
91
99
  end