reek 4.7.3 → 5.0.0

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.
Files changed (248) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +17 -12
  3. data/.rubocop.yml +18 -3
  4. data/.simplecov +1 -0
  5. data/.travis.yml +3 -4
  6. data/.yardopts +1 -1
  7. data/CHANGELOG.md +45 -0
  8. data/Gemfile +4 -4
  9. data/README.md +134 -104
  10. data/Rakefile +16 -3
  11. data/bin/reek +1 -3
  12. data/docs/API.md +2 -9
  13. data/docs/Basic-Smell-Options.md +51 -11
  14. data/docs/Code-Smells.md +1 -1
  15. data/docs/Command-Line-Options.md +14 -4
  16. data/docs/Duplicate-Method-Call.md +49 -1
  17. data/docs/Feature-Envy.md +44 -0
  18. data/docs/How-To-Write-New-Detectors.md +2 -3
  19. data/docs/Instance-Variable-Assumption.md +1 -1
  20. data/docs/{Prima-Donna-Method.md → Missing-Safe-Method.md} +11 -9
  21. data/docs/Rake-Task.md +1 -1
  22. data/docs/Reek-4-to-Reek-5-migration.md +193 -0
  23. data/docs/Reek-Driven-Development.md +1 -1
  24. data/docs/Uncommunicative-Method-Name.md +45 -6
  25. data/docs/Uncommunicative-Module-Name.md +49 -7
  26. data/docs/Uncommunicative-Parameter-Name.md +43 -5
  27. data/docs/Uncommunicative-Variable-Name.md +73 -2
  28. data/docs/Unused-Private-Method.md +2 -2
  29. data/docs/defaults.reek.yml +129 -0
  30. data/docs/yard_plugin.rb +1 -0
  31. data/features/command_line_interface/basic_usage.feature +2 -2
  32. data/features/command_line_interface/options.feature +46 -4
  33. data/features/command_line_interface/show_progress.feature +4 -4
  34. data/features/command_line_interface/smell_selection.feature +1 -1
  35. data/features/command_line_interface/smells_count.feature +6 -6
  36. data/features/command_line_interface/stdin.feature +30 -8
  37. data/features/configuration_files/accept_setting.feature +45 -28
  38. data/features/configuration_files/directory_specific_directives.feature +78 -73
  39. data/features/configuration_files/exclude_directives.feature +11 -10
  40. data/features/configuration_files/exclude_paths_directives.feature +4 -4
  41. data/features/configuration_files/masking_smells.feature +38 -9
  42. data/features/configuration_files/mix_accept_reject_setting.feature +31 -28
  43. data/features/configuration_files/reject_setting.feature +52 -41
  44. data/features/configuration_files/schema_validation.feature +59 -0
  45. data/features/configuration_files/unused_private_method.feature +18 -16
  46. data/features/configuration_loading.feature +53 -10
  47. data/features/configuration_via_source_comments/erroneous_source_comments.feature +2 -2
  48. data/features/configuration_via_source_comments/well_formed_source_comments.feature +2 -2
  49. data/features/locales.feature +2 -2
  50. data/features/rake_task/rake_task.feature +15 -15
  51. data/features/reports/json.feature +3 -3
  52. data/features/reports/reports.feature +34 -34
  53. data/features/reports/yaml.feature +3 -3
  54. data/features/rspec_matcher.feature +9 -1
  55. data/features/samples.feature +287 -287
  56. data/features/step_definitions/reek_steps.rb +4 -0
  57. data/features/step_definitions/sample_file_steps.rb +9 -4
  58. data/features/support/env.rb +2 -2
  59. data/features/todo_list.feature +26 -23
  60. data/lib/reek/ast/node.rb +40 -55
  61. data/lib/reek/ast/object_refs.rb +1 -1
  62. data/lib/reek/ast/reference_collector.rb +2 -4
  63. data/lib/reek/ast/sexp_extensions/case.rb +1 -1
  64. data/lib/reek/ast/sexp_extensions/if.rb +8 -1
  65. data/lib/reek/ast/sexp_extensions/logical_operators.rb +1 -1
  66. data/lib/reek/ast/sexp_extensions/methods.rb +4 -6
  67. data/lib/reek/cli/application.rb +4 -3
  68. data/lib/reek/cli/command/report_command.rb +1 -2
  69. data/lib/reek/cli/command/todo_list_command.rb +8 -8
  70. data/lib/reek/cli/options.rb +29 -14
  71. data/lib/reek/cli/silencer.rb +14 -3
  72. data/lib/reek/code_comment.rb +14 -16
  73. data/lib/reek/configuration/app_configuration.rb +32 -28
  74. data/lib/reek/configuration/configuration_converter.rb +110 -0
  75. data/lib/reek/configuration/configuration_file_finder.rb +15 -40
  76. data/lib/reek/configuration/configuration_validator.rb +12 -23
  77. data/lib/reek/configuration/default_directive.rb +17 -3
  78. data/lib/reek/configuration/directory_directives.rb +17 -11
  79. data/lib/reek/configuration/excluded_paths.rb +1 -1
  80. data/lib/reek/configuration/rake_task_converter.rb +29 -0
  81. data/lib/reek/configuration/schema.yml +210 -0
  82. data/lib/reek/configuration/schema_validator.rb +38 -0
  83. data/lib/reek/context/attribute_context.rb +1 -1
  84. data/lib/reek/context/code_context.rb +8 -11
  85. data/lib/reek/context/method_context.rb +7 -12
  86. data/lib/reek/context/module_context.rb +4 -4
  87. data/lib/reek/context_builder.rb +11 -11
  88. data/lib/reek/detector_repository.rb +6 -0
  89. data/lib/reek/documentation_link.rb +28 -0
  90. data/lib/reek/errors/bad_detector_configuration_key_in_comment_error.rb +13 -12
  91. data/lib/reek/errors/bad_detector_in_comment_error.rb +11 -10
  92. data/lib/reek/errors/base_error.rb +3 -0
  93. data/lib/reek/errors/config_file_error.rb +11 -0
  94. data/lib/reek/errors/encoding_error.rb +16 -11
  95. data/lib/reek/errors/garbage_detector_configuration_in_comment_error.rb +11 -10
  96. data/lib/reek/errors/incomprehensible_source_error.rb +20 -22
  97. data/lib/reek/errors/syntax_error.rb +41 -0
  98. data/lib/reek/examiner.rb +19 -25
  99. data/lib/reek/logging_error_handler.rb +7 -5
  100. data/lib/reek/rake/task.rb +3 -3
  101. data/lib/reek/report/base_report.rb +8 -12
  102. data/lib/reek/report/code_climate/code_climate_configuration.rb +1 -1
  103. data/lib/reek/report/code_climate/code_climate_configuration.yml +6 -10
  104. data/lib/reek/report/documentation_link_warning_formatter.rb +17 -0
  105. data/lib/reek/report/heading_formatter.rb +54 -0
  106. data/lib/reek/report/json_report.rb +1 -1
  107. data/lib/reek/report/location_formatter.rb +40 -0
  108. data/lib/reek/report/progress_formatter.rb +79 -0
  109. data/lib/reek/report/simple_warning_formatter.rb +34 -0
  110. data/lib/reek/report/text_report.rb +1 -2
  111. data/lib/reek/report/xml_report.rb +3 -3
  112. data/lib/reek/report/yaml_report.rb +1 -1
  113. data/lib/reek/report.rb +15 -10
  114. data/lib/reek/smell_configuration.rb +2 -2
  115. data/lib/reek/smell_detectors/attribute.rb +0 -1
  116. data/lib/reek/smell_detectors/base_detector.rb +9 -12
  117. data/lib/reek/smell_detectors/boolean_parameter.rb +0 -1
  118. data/lib/reek/smell_detectors/class_variable.rb +3 -11
  119. data/lib/reek/smell_detectors/control_parameter.rb +17 -32
  120. data/lib/reek/smell_detectors/data_clump.rb +3 -4
  121. data/lib/reek/smell_detectors/duplicate_method_call.rb +6 -7
  122. data/lib/reek/smell_detectors/feature_envy.rb +1 -1
  123. data/lib/reek/smell_detectors/instance_variable_assumption.rb +1 -10
  124. data/lib/reek/smell_detectors/irresponsible_module.rb +0 -1
  125. data/lib/reek/smell_detectors/long_parameter_list.rb +1 -2
  126. data/lib/reek/smell_detectors/long_yield_list.rb +2 -3
  127. data/lib/reek/smell_detectors/manual_dispatch.rb +3 -3
  128. data/lib/reek/smell_detectors/{prima_donna_method.rb → missing_safe_method.rb} +6 -7
  129. data/lib/reek/smell_detectors/module_initialize.rb +1 -2
  130. data/lib/reek/smell_detectors/nested_iterators.rb +6 -6
  131. data/lib/reek/smell_detectors/nil_check.rb +0 -1
  132. data/lib/reek/smell_detectors/repeated_conditional.rb +3 -4
  133. data/lib/reek/smell_detectors/subclassed_from_core_class.rb +0 -1
  134. data/lib/reek/smell_detectors/too_many_constants.rb +2 -3
  135. data/lib/reek/smell_detectors/too_many_instance_variables.rb +1 -2
  136. data/lib/reek/smell_detectors/too_many_methods.rb +1 -2
  137. data/lib/reek/smell_detectors/too_many_statements.rb +1 -2
  138. data/lib/reek/smell_detectors/uncommunicative_method_name.rb +2 -3
  139. data/lib/reek/smell_detectors/uncommunicative_module_name.rb +2 -3
  140. data/lib/reek/smell_detectors/uncommunicative_parameter_name.rb +2 -3
  141. data/lib/reek/smell_detectors/uncommunicative_variable_name.rb +6 -7
  142. data/lib/reek/smell_detectors/unused_parameters.rb +0 -1
  143. data/lib/reek/smell_detectors/unused_private_method.rb +0 -1
  144. data/lib/reek/smell_detectors/utility_function.rb +2 -3
  145. data/lib/reek/smell_detectors.rb +1 -2
  146. data/lib/reek/smell_warning.rb +15 -8
  147. data/lib/reek/source/source_code.rb +50 -72
  148. data/lib/reek/source/source_locator.rb +7 -7
  149. data/lib/reek/spec/should_reek.rb +2 -2
  150. data/lib/reek/spec/should_reek_of.rb +9 -16
  151. data/lib/reek/spec/should_reek_only_of.rb +4 -4
  152. data/lib/reek/spec.rb +6 -6
  153. data/lib/reek/tree_dresser.rb +5 -5
  154. data/lib/reek/version.rb +1 -1
  155. data/reek.gemspec +5 -5
  156. data/samples/checkstyle.xml +1 -1
  157. data/samples/configuration/accepts_rejects_and_excludes_for_detectors.reek.yml +29 -0
  158. data/samples/configuration/accepts_rejects_and_excludes_for_directory_directives.reek.yml +30 -0
  159. data/samples/configuration/full_configuration.reek +8 -4
  160. data/samples/configuration/full_mask.reek +5 -4
  161. data/samples/configuration/partial_mask.reek +3 -2
  162. data/samples/configuration/regular_configuration/.reek.yml +4 -0
  163. data/samples/paths.rb +5 -4
  164. data/samples/source_with_hidden_directories/.hidden/hidden.rb +1 -0
  165. data/samples/source_with_hidden_directories/not_hidden.rb +1 -0
  166. data/spec/factories/factories.rb +2 -13
  167. data/spec/reek/ast/node_spec.rb +103 -10
  168. data/spec/reek/ast/reference_collector_spec.rb +1 -1
  169. data/spec/reek/ast/sexp_extensions_spec.rb +2 -2
  170. data/spec/reek/cli/application_spec.rb +50 -38
  171. data/spec/reek/cli/command/todo_list_command_spec.rb +6 -4
  172. data/spec/reek/cli/silencer_spec.rb +28 -0
  173. data/spec/reek/code_comment_spec.rb +31 -38
  174. data/spec/reek/configuration/app_configuration_spec.rb +46 -33
  175. data/spec/reek/configuration/configuration_file_finder_spec.rb +133 -49
  176. data/spec/reek/configuration/default_directive_spec.rb +1 -1
  177. data/spec/reek/configuration/directory_directives_spec.rb +6 -7
  178. data/spec/reek/configuration/excluded_paths_spec.rb +6 -6
  179. data/spec/reek/configuration/rake_task_converter_spec.rb +33 -0
  180. data/spec/reek/configuration/schema_validator_spec.rb +165 -0
  181. data/spec/reek/context/code_context_spec.rb +60 -96
  182. data/spec/reek/context/ghost_context_spec.rb +1 -1
  183. data/spec/reek/context/root_context_spec.rb +1 -1
  184. data/spec/reek/documentation_link_spec.rb +20 -0
  185. data/spec/reek/errors/base_error_spec.rb +13 -0
  186. data/spec/reek/examiner_spec.rb +100 -30
  187. data/spec/reek/report/code_climate/code_climate_fingerprint_spec.rb +82 -80
  188. data/spec/reek/report/code_climate/code_climate_formatter_spec.rb +6 -6
  189. data/spec/reek/report/json_report_spec.rb +13 -46
  190. data/spec/reek/report/{formatter/location_formatter_spec.rb → location_formatter_spec.rb} +5 -5
  191. data/spec/reek/report/{formatter/progress_formatter_spec.rb → progress_formatter_spec.rb} +4 -4
  192. data/spec/reek/report/text_report_spec.rb +4 -4
  193. data/spec/reek/report/xml_report_spec.rb +3 -3
  194. data/spec/reek/report/yaml_report_spec.rb +9 -38
  195. data/spec/reek/report_spec.rb +3 -3
  196. data/spec/reek/smell_detectors/boolean_parameter_spec.rb +2 -2
  197. data/spec/reek/smell_detectors/class_variable_spec.rb +26 -32
  198. data/spec/reek/smell_detectors/control_parameter_spec.rb +34 -4
  199. data/spec/reek/smell_detectors/duplicate_method_call_spec.rb +3 -3
  200. data/spec/reek/smell_detectors/feature_envy_spec.rb +47 -2
  201. data/spec/reek/smell_detectors/{prima_donna_method_spec.rb → missing_safe_method_spec.rb} +9 -9
  202. data/spec/reek/smell_detectors/module_initialize_spec.rb +14 -0
  203. data/spec/reek/smell_detectors/nested_iterators_spec.rb +1 -1
  204. data/spec/reek/smell_detectors/too_many_constants_spec.rb +3 -3
  205. data/spec/reek/smell_detectors/too_many_instance_variables_spec.rb +1 -1
  206. data/spec/reek/smell_detectors/uncommunicative_method_name_spec.rb +6 -6
  207. data/spec/reek/smell_detectors/uncommunicative_module_name_spec.rb +6 -4
  208. data/spec/reek/smell_detectors/uncommunicative_parameter_name_spec.rb +6 -4
  209. data/spec/reek/smell_detectors/uncommunicative_variable_name_spec.rb +9 -9
  210. data/spec/reek/smell_detectors/unused_parameters_spec.rb +3 -3
  211. data/spec/reek/smell_detectors/unused_private_method_spec.rb +10 -10
  212. data/spec/reek/smell_detectors/utility_function_spec.rb +5 -5
  213. data/spec/reek/smell_warning_spec.rb +12 -8
  214. data/spec/reek/source/source_code_spec.rb +17 -43
  215. data/spec/reek/source/source_locator_spec.rb +17 -17
  216. data/spec/reek/spec/should_reek_of_spec.rb +7 -11
  217. data/spec/reek/spec/should_reek_only_of_spec.rb +2 -2
  218. data/spec/reek/spec/should_reek_spec.rb +3 -3
  219. data/spec/reek/spec/smell_matcher_spec.rb +3 -3
  220. data/spec/reek/tree_dresser_spec.rb +12 -17
  221. data/spec/spec_helper.rb +6 -17
  222. data/tasks/configuration.rake +8 -5
  223. metadata +71 -41
  224. data/defaults.reek +0 -131
  225. data/features/configuration_files/warn_about_multiple_configuration_files.feature +0 -44
  226. data/lib/reek/report/formatter/heading_formatter.rb +0 -52
  227. data/lib/reek/report/formatter/location_formatter.rb +0 -42
  228. data/lib/reek/report/formatter/progress_formatter.rb +0 -81
  229. data/lib/reek/report/formatter/simple_warning_formatter.rb +0 -35
  230. data/lib/reek/report/formatter/wiki_link_warning_formatter.rb +0 -36
  231. data/lib/reek/report/formatter.rb +0 -33
  232. data/lib/reek/smell_detectors/syntax.rb +0 -37
  233. data/samples/configuration/non_public_modifiers_mask.reek +0 -3
  234. data/samples/smelly_with_inline_mask.rb +0 -8
  235. data/samples/smelly_with_modifiers.rb +0 -12
  236. data/samples/source_with_hidden_directories/.hidden/uncommunicative_method_name.rb +0 -5
  237. data/samples/source_with_non_ruby_files/uncommunicative_parameter_name.rb +0 -6
  238. data/spec/reek/smell_detectors/syntax_spec.rb +0 -17
  239. /data/{samples/configuration/more_than_one_configuration_file/regular.reek → .reek.yml} +0 -0
  240. /data/samples/{clean.rb → clean_source/clean.rb} +0 -0
  241. /data/samples/{exceptions.reek → configuration/home/home.reek.yml} +0 -0
  242. /data/samples/configuration/{more_than_one_configuration_file/todo.reek → regular_configuration/empty_sub_directory/.gitignore} +0 -0
  243. /data/samples/{configuration/single_configuration_file/.reek → no_config_file/.keep} +0 -0
  244. /data/samples/{inline.rb → smelly_source/inline.rb} +0 -0
  245. /data/samples/{optparse.rb → smelly_source/optparse.rb} +0 -0
  246. /data/samples/{redcloth.rb → smelly_source/redcloth.rb} +0 -0
  247. /data/samples/{smelly.rb → smelly_source/smelly.rb} +0 -0
  248. /data/samples/{source_with_hidden_directories/uncommunicative_parameter_name.rb → source_with_non_ruby_files/ruby.rb} +0 -0
@@ -150,7 +150,7 @@ RSpec.describe Reek::SmellDetectors::DuplicateMethodCall do
150
150
  end
151
151
  end
152
152
 
153
- context 'non-repeated method calls' do
153
+ context 'with non-repeated method calls' do
154
154
  it 'does not report similar calls' do
155
155
  src = 'def alfa(bravo) bravo.charlie == self.charlie end'
156
156
  expect(src).not_to reek_of(:DuplicateMethodCall)
@@ -162,7 +162,7 @@ RSpec.describe Reek::SmellDetectors::DuplicateMethodCall do
162
162
  end
163
163
  end
164
164
 
165
- context 'allowing up to 3 calls' do
165
+ context 'when allowing up to 3 calls' do
166
166
  let(:config) do
167
167
  { Reek::SmellDetectors::DuplicateMethodCall::MAX_ALLOWED_CALLS_KEY => 3 }
168
168
  end
@@ -189,7 +189,7 @@ RSpec.describe Reek::SmellDetectors::DuplicateMethodCall do
189
189
  end
190
190
  end
191
191
 
192
- context 'allowing calls to some methods' do
192
+ context 'when allowing calls to some methods' do
193
193
  it 'does not report calls to some methods' do
194
194
  config = { Reek::SmellDetectors::DuplicateMethodCall::ALLOW_CALLS_KEY => ['@bravo.charlie'] }
195
195
  src = 'def alfa; @bravo.charlie + @bravo.charlie; end'
@@ -130,7 +130,7 @@ RSpec.describe Reek::SmellDetectors::FeatureEnvy do
130
130
  delta += bravo.foxtrot
131
131
  delta *= 1.15
132
132
  end
133
- EOS
133
+ EOS
134
134
 
135
135
  expect(src).
136
136
  to reek_of(:FeatureEnvy, name: 'delta').
@@ -145,7 +145,7 @@ RSpec.describe Reek::SmellDetectors::FeatureEnvy do
145
145
  delta += bravo.echo
146
146
  delta += bravo.foxtrot
147
147
  end
148
- EOS
148
+ EOS
149
149
 
150
150
  expect(src).
151
151
  to reek_of(:FeatureEnvy, name: 'delta').
@@ -247,4 +247,49 @@ RSpec.describe Reek::SmellDetectors::FeatureEnvy do
247
247
 
248
248
  expect(src).not_to reek_of(:FeatureEnvy)
249
249
  end
250
+
251
+ it 'does not report on class methods defined by opening the metaclass' do
252
+ src = <<-EOS
253
+ class Alfa
254
+ class << self
255
+ def bravo(charlie)
256
+ delta = new(charlie)
257
+ delta.echo
258
+ delta.echo
259
+ end
260
+ end
261
+ end
262
+ EOS
263
+
264
+ expect(src).not_to reek_of(:FeatureEnvy)
265
+ end
266
+
267
+ it 'does not report on class methods defined with an explicit receiver' do
268
+ src = <<-EOS
269
+ class Alfa
270
+ def self.bravo(charlie)
271
+ delta = new(charlie)
272
+ delta.echo
273
+ delta.echo
274
+ end
275
+ end
276
+ EOS
277
+
278
+ expect(src).not_to reek_of(:FeatureEnvy)
279
+ end
280
+
281
+ it 'does not report module functions' do
282
+ src = <<-EOF
283
+ module Alfa
284
+ module_function
285
+ def bravo(charlie)
286
+ echo = delta(charlie)
287
+ echo.foxtrot
288
+ echo.foxtrot
289
+ end
290
+ end
291
+ EOF
292
+
293
+ expect(src).not_to reek_of(:FeatureEnvy)
294
+ end
250
295
  end
@@ -1,7 +1,7 @@
1
1
  require_relative '../../spec_helper'
2
- require_lib 'reek/smell_detectors/prima_donna_method'
2
+ require_lib 'reek/smell_detectors/missing_safe_method'
3
3
 
4
- RSpec.describe Reek::SmellDetectors::PrimaDonnaMethod do
4
+ RSpec.describe Reek::SmellDetectors::MissingSafeMethod do
5
5
  it 'reports the right values' do
6
6
  src = <<-EOS
7
7
  class Alfa
@@ -10,10 +10,10 @@ RSpec.describe Reek::SmellDetectors::PrimaDonnaMethod do
10
10
  end
11
11
  EOS
12
12
 
13
- expect(src).to reek_of(:PrimaDonnaMethod,
13
+ expect(src).to reek_of(:MissingSafeMethod,
14
14
  lines: [2],
15
15
  context: 'Alfa',
16
- message: "has prima donna method 'bravo!'",
16
+ message: "has missing safe method 'bravo!'",
17
17
  source: 'string',
18
18
  name: 'bravo!')
19
19
  end
@@ -30,8 +30,8 @@ RSpec.describe Reek::SmellDetectors::PrimaDonnaMethod do
30
30
  EOS
31
31
 
32
32
  expect(src).
33
- to reek_of(:PrimaDonnaMethod, lines: [2], name: 'bravo!').
34
- and reek_of(:PrimaDonnaMethod, lines: [5], name: 'charlie!')
33
+ to reek_of(:MissingSafeMethod, lines: [2], name: 'bravo!').
34
+ and reek_of(:MissingSafeMethod, lines: [5], name: 'charlie!')
35
35
  end
36
36
 
37
37
  it 'reports nothing when method and bang counterpart exist' do
@@ -45,18 +45,18 @@ RSpec.describe Reek::SmellDetectors::PrimaDonnaMethod do
45
45
  end
46
46
  EOS
47
47
 
48
- expect(src).not_to reek_of(:PrimaDonnaMethod)
48
+ expect(src).not_to reek_of(:MissingSafeMethod)
49
49
  end
50
50
 
51
51
  it 'does not report methods we excluded via comment' do
52
52
  source = <<-EOF
53
- # :reek:PrimaDonnaMethod: { exclude: [ bravo! ] }
53
+ # :reek:MissingSafeMethod: { exclude: [ bravo! ] }
54
54
  class Alfa
55
55
  def bravo!
56
56
  end
57
57
  end
58
58
  EOF
59
59
 
60
- expect(source).not_to reek_of(:PrimaDonnaMethod)
60
+ expect(source).not_to reek_of(:MissingSafeMethod)
61
61
  end
62
62
  end
@@ -50,6 +50,20 @@ RSpec.describe Reek::SmellDetectors::ModuleInitialize do
50
50
  expect(src).not_to reek_of(:ModuleInitialize)
51
51
  end
52
52
 
53
+ it 'reports nothing for a method named initialize in a nested dynamic class' do
54
+ src = <<-EOF
55
+ module Alfa
56
+ def self.bravo
57
+ Class.new do
58
+ def initialize; end
59
+ end
60
+ end
61
+ end
62
+ EOF
63
+
64
+ expect(src).not_to reek_of(:ModuleInitialize)
65
+ end
66
+
53
67
  it 'can be disabled via comment' do
54
68
  src = <<-EOS
55
69
  # :reek:ModuleInitialize
@@ -223,7 +223,7 @@ RSpec.describe Reek::SmellDetectors::NestedIterators do
223
223
  expect(src).not_to reek_of(:NestedIterators)
224
224
  end
225
225
 
226
- context 'setting the allowed nesting depth to 3' do
226
+ context 'when setting the allowed nesting depth to 3' do
227
227
  let(:config) do
228
228
  { Reek::SmellDetectors::NestedIterators::MAX_ALLOWED_NESTING_KEY => 3 }
229
229
  end
@@ -33,7 +33,7 @@ RSpec.describe Reek::SmellDetectors::TooManyConstants do
33
33
 
34
34
  it 'does not report when increasing default' do
35
35
  src = <<-EOS
36
- # :reek:TooManyConstants: { max_constants: 3 }
36
+ # :reek:TooManyConstants { max_constants: 3 }
37
37
  class Alfa
38
38
  Bravo = Charlie = Delta = 1
39
39
  end
@@ -44,7 +44,7 @@ RSpec.describe Reek::SmellDetectors::TooManyConstants do
44
44
 
45
45
  it 'does not report when disabled' do
46
46
  src = <<-EOS
47
- # :reek:TooManyConstants: { enabled: false }
47
+ # :reek:TooManyConstants { enabled: false }
48
48
  class Alfa
49
49
  Bravo = Charlie = Delta = 1
50
50
  end
@@ -96,7 +96,7 @@ RSpec.describe Reek::SmellDetectors::TooManyConstants do
96
96
  it 'does not report outer module when inner module suppressed' do
97
97
  src = <<-EOS
98
98
  module Alfa
99
- # ignore :reek:TooManyConstants:
99
+ # ignore :reek:TooManyConstants
100
100
  module Bravo
101
101
  Charlie = Delta = Echo = 1
102
102
  end
@@ -37,7 +37,7 @@ RSpec.describe Reek::SmellDetectors::TooManyInstanceVariables do
37
37
 
38
38
  it 'has a configurable maximum' do
39
39
  src = <<-EOS
40
- # :reek:TooManyInstanceVariables: { max_instance_variables: 3 }
40
+ # :reek:TooManyInstanceVariables { max_instance_variables: 3 }
41
41
  class Alfa
42
42
  def bravo
43
43
  @charlie = @delta = @echo = 1
@@ -41,18 +41,18 @@ RSpec.describe Reek::SmellDetectors::UncommunicativeMethodName do
41
41
  describe '`accept` patterns' do
42
42
  let(:source) { 'def x; end' }
43
43
 
44
- it 'make smelly names pass via regex / strings given by list / literal' do
45
- [[/x/], /x/, ['x'], 'x'].each do |pattern|
46
- expect(source).not_to reek_of(:UncommunicativeMethodName).with_config('accept' => pattern)
47
- end
44
+ it 'make smelly names pass' do
45
+ pattern = /x/
46
+ expect(source).not_to reek_of(:UncommunicativeMethodName).with_config('accept' => pattern)
48
47
  end
49
48
  end
50
49
 
51
50
  describe '`reject` patterns' do
52
51
  let(:source) { 'def alfa; end' }
53
52
 
54
- it 'reject smelly names via regex / strings given by list / literal' do
55
- [[/alfa/], /alfa/, ['alfa'], 'alfa'].each do |pattern|
53
+ it 'reject smelly names' do
54
+ patterns = [/alf/, /lfa/]
55
+ patterns.each do |pattern|
56
56
  expect(source).to reek_of(:UncommunicativeMethodName).with_config('reject' => pattern)
57
57
  end
58
58
  end
@@ -39,8 +39,9 @@ RSpec.describe Reek::SmellDetectors::UncommunicativeModuleName do
39
39
  describe '`accept` patterns' do
40
40
  let(:source) { 'class Alfa1; end' }
41
41
 
42
- it 'makes smelly names pass via regex / strings given by list / literal' do
43
- [[/lfa/], /lfa/, ['lfa'], 'lfa'].each do |pattern|
42
+ it 'make smelly names pass ' do
43
+ patterns = [/lfa1/, /Alfa1/]
44
+ patterns.each do |pattern|
44
45
  expect(source).not_to reek_of(:UncommunicativeModuleName).with_config('accept' => pattern)
45
46
  end
46
47
  end
@@ -49,8 +50,9 @@ RSpec.describe Reek::SmellDetectors::UncommunicativeModuleName do
49
50
  describe '`reject` patterns' do
50
51
  let(:source) { 'class Alfa; end' }
51
52
 
52
- it 'rejects smelly names via regex / strings given by list / literal' do
53
- [[/Alfa/], /Alfa/, ['Alfa'], 'Alfa'].each do |pattern|
53
+ it 'reject smelly names' do
54
+ patterns = [/lfa/, /Alf/]
55
+ patterns.each do |pattern|
54
56
  expect(source).to reek_of(:UncommunicativeModuleName).with_config('reject' => pattern)
55
57
  end
56
58
  end
@@ -107,8 +107,9 @@ RSpec.describe Reek::SmellDetectors::UncommunicativeParameterName do
107
107
  describe '`accept` patterns' do
108
108
  let(:source) { 'def alfa(bar2); charlie(bar2); end' }
109
109
 
110
- it 'make smelly names pass via regex / strings given by list / literal' do
111
- [[/bar2/], /bar2/, ['bar2'], 'bar2'].each do |pattern|
110
+ it 'make smelly names pass' do
111
+ patterns = [/bar2/, /ar2/]
112
+ patterns.each do |pattern|
112
113
  expect(source).not_to reek_of(:UncommunicativeParameterName).with_config('accept' => pattern)
113
114
  end
114
115
  end
@@ -117,8 +118,9 @@ RSpec.describe Reek::SmellDetectors::UncommunicativeParameterName do
117
118
  describe '`reject` patterns' do
118
119
  let(:source) { 'def alfa(bravo); charlie(bravo); end' }
119
120
 
120
- it 'reject smelly names via regex / strings given by list / literal' do
121
- [[/bravo/], /bravo/, ['bravo'], 'bravo'].each do |pattern|
121
+ it 'reject smelly names' do
122
+ patterns = [/brav/, /ravo/]
123
+ patterns.each do |pattern|
122
124
  expect(source).to reek_of(:UncommunicativeParameterName).with_config('reject' => pattern)
123
125
  end
124
126
  end
@@ -29,7 +29,7 @@ RSpec.describe Reek::SmellDetectors::UncommunicativeVariableName do
29
29
  and reek_of(:UncommunicativeVariableName, lines: [3], name: 'y')
30
30
  end
31
31
 
32
- context 'instance variables' do
32
+ context 'when examining instance variables' do
33
33
  it 'does not report use of one-letter names' do
34
34
  src = 'class Alfa; def bravo; @x; end; end'
35
35
  expect(src).not_to reek_of(:UncommunicativeVariableName)
@@ -41,7 +41,7 @@ RSpec.describe Reek::SmellDetectors::UncommunicativeVariableName do
41
41
  end
42
42
  end
43
43
 
44
- context 'local variables' do
44
+ context 'when examining local variables' do
45
45
  it 'does not report single underscore as a variable name' do
46
46
  src = 'def alfa; _ = bravo(); end'
47
47
  expect(src).not_to reek_of(:UncommunicativeVariableName)
@@ -78,7 +78,7 @@ RSpec.describe Reek::SmellDetectors::UncommunicativeVariableName do
78
78
  end
79
79
  end
80
80
 
81
- context 'block parameters' do
81
+ context 'when examining block parameters' do
82
82
  it 'reports all relevant block parameters' do
83
83
  src = <<-EOS
84
84
  def alfa
@@ -159,9 +159,9 @@ RSpec.describe Reek::SmellDetectors::UncommunicativeVariableName do
159
159
  describe '`accept` patterns' do
160
160
  let(:src) { 'def alfa; bravo2 = 42; end' }
161
161
 
162
- # FIXME: Move the loop out of the it?
163
- it 'make smelly names pass via regex / strings given by list / literal' do
164
- [[/bravo2/], /bravo2/, ['bravo2'], 'bravo2'].each do |pattern|
162
+ it 'make smelly names pass' do
163
+ patterns = [/ravo2/, /bravo2/]
164
+ patterns.each do |pattern|
165
165
  expect(src).to reek_of(:UncommunicativeVariableName).
166
166
  and not_reek_of(:UncommunicativeVariableName).with_config('accept' => pattern)
167
167
  end
@@ -171,9 +171,9 @@ RSpec.describe Reek::SmellDetectors::UncommunicativeVariableName do
171
171
  describe '`reject` patterns' do
172
172
  let(:src) { 'def alfa; foobar = 42; end' }
173
173
 
174
- # FIXME: Move the loop out of the it?
175
- it 'reject smelly names via regex / strings given by list / literal' do
176
- [[/foobar/], /foobar/, ['foobar'], 'foobar'].each do |pattern|
174
+ it 'reject smelly names' do
175
+ patterns = [/fooba/, /oobar/]
176
+ patterns.each do |pattern|
177
177
  expect(src).to not_reek_of(:UncommunicativeVariableName).
178
178
  and reek_of(:UncommunicativeVariableName).with_config('reject' => pattern)
179
179
  end
@@ -61,7 +61,7 @@ RSpec.describe Reek::SmellDetectors::UnusedParameters do
61
61
  expect(src).not_to reek_of(:UnusedParameters)
62
62
  end
63
63
 
64
- context 'using super' do
64
+ context 'when using super' do
65
65
  it 'reports nothing with implicit arguments' do
66
66
  src = 'def alfa(*bravo); super; end'
67
67
  expect(src).not_to reek_of(:UnusedParameters)
@@ -83,7 +83,7 @@ RSpec.describe Reek::SmellDetectors::UnusedParameters do
83
83
  end
84
84
  end
85
85
 
86
- context 'anonymous parameters' do
86
+ context 'with anonymous parameters' do
87
87
  it 'reports nothing for unused anonymous parameter' do
88
88
  src = 'def alfa(_); end'
89
89
  expect(src).not_to reek_of(:UnusedParameters)
@@ -95,7 +95,7 @@ RSpec.describe Reek::SmellDetectors::UnusedParameters do
95
95
  end
96
96
  end
97
97
 
98
- context 'splatted parameters' do
98
+ context 'with splatted parameters' do
99
99
  it 'reports nothing for used splatted parameter' do
100
100
  src = 'def alfa(*bravo); bravo; end'
101
101
  expect(src).not_to reek_of(:UnusedParameters)
@@ -38,7 +38,7 @@ RSpec.describe Reek::SmellDetectors::UnusedPrivateMethod do
38
38
  and reek_of(:UnusedPrivateMethod, lines: [7], name: 'charlie')
39
39
  end
40
40
 
41
- context 'unused private methods' do
41
+ context 'when a class has unused private methods' do
42
42
  it 'reports instance methods' do
43
43
  source = <<-EOF
44
44
  class Alfa
@@ -116,10 +116,10 @@ RSpec.describe Reek::SmellDetectors::UnusedPrivateMethod do
116
116
  end
117
117
  end
118
118
 
119
- describe 'configuring the detector via source code comment' do
119
+ context 'when the detector is configured via a source code comment' do
120
120
  it 'does not report methods we excluded' do
121
121
  source = <<-EOF
122
- # :reek:UnusedPrivateMethod: { exclude: [ bravo ] }
122
+ # :reek:UnusedPrivateMethod { exclude: [ bravo ] }
123
123
  class Alfa
124
124
  private
125
125
  def bravo; end
@@ -133,8 +133,8 @@ RSpec.describe Reek::SmellDetectors::UnusedPrivateMethod do
133
133
  end
134
134
  end
135
135
 
136
- context 'used private methods' do
137
- it 'are not reported' do
136
+ context 'when a class has only used private methods' do
137
+ it 'reports nothing' do
138
138
  source = <<-EOF
139
139
  class Alfa
140
140
  def bravo
@@ -150,8 +150,8 @@ RSpec.describe Reek::SmellDetectors::UnusedPrivateMethod do
150
150
  end
151
151
  end
152
152
 
153
- context 'unused protected methods' do
154
- it 'are not reported' do
153
+ context 'when a class has unused protected methods' do
154
+ it 'reports nothing' do
155
155
  source = <<-EOF
156
156
  class Alfa
157
157
  protected
@@ -163,8 +163,8 @@ RSpec.describe Reek::SmellDetectors::UnusedPrivateMethod do
163
163
  end
164
164
  end
165
165
 
166
- context 'unused public methods' do
167
- it 'are not reported' do
166
+ context 'when a class has unused public methods' do
167
+ it 'reports nothing' do
168
168
  source = <<-EOF
169
169
  class Alfa
170
170
  def bravo; end
@@ -175,7 +175,7 @@ RSpec.describe Reek::SmellDetectors::UnusedPrivateMethod do
175
175
  end
176
176
  end
177
177
 
178
- describe 'prevent methods from being reported' do
178
+ describe 'preventing methods from being reported' do
179
179
  let(:source) do
180
180
  <<-EOF
181
181
  class Alfa
@@ -98,7 +98,7 @@ RSpec.describe Reek::SmellDetectors::UtilityFunction do
98
98
  expect(src).to reek_of(:UtilityFunction, context: 'simple')
99
99
  end
100
100
 
101
- context 'Singleton methods' do
101
+ context 'when examining singleton methods' do
102
102
  ['class', 'module'].each do |scope|
103
103
  it "does not report for #{scope} with `class << self` notation" do
104
104
  src = "#{scope} Alfa; class << self; def bravo(charlie) charlie.to_s; end; end; end"
@@ -111,7 +111,7 @@ RSpec.describe Reek::SmellDetectors::UtilityFunction do
111
111
  end
112
112
  end
113
113
 
114
- context 'by using `module_function`' do
114
+ context 'when defined by using `module_function`' do
115
115
  it 'does not report UtilityFunction also when using multiple arguments' do
116
116
  src = <<-EOS
117
117
  class Alfa
@@ -194,7 +194,7 @@ RSpec.describe Reek::SmellDetectors::UtilityFunction do
194
194
  { Reek::SmellDetectors::UtilityFunction::PUBLIC_METHODS_ONLY_KEY => true }
195
195
  end
196
196
 
197
- context 'public methods' do
197
+ context 'when examining public methods' do
198
198
  it 'still reports UtilityFunction' do
199
199
  src = <<-EOS
200
200
  class Alfa
@@ -208,7 +208,7 @@ RSpec.describe Reek::SmellDetectors::UtilityFunction do
208
208
  end
209
209
  end
210
210
 
211
- context 'private methods' do
211
+ context 'when examining private methods' do
212
212
  it 'does not report UtilityFunction' do
213
213
  src = <<-EOS
214
214
  class Alfa
@@ -235,7 +235,7 @@ RSpec.describe Reek::SmellDetectors::UtilityFunction do
235
235
  end
236
236
  end
237
237
 
238
- context 'protected methods' do
238
+ context 'when examining protected methods' do
239
239
  it 'does not report UtilityFunction' do
240
240
  src = <<-EOS
241
241
  class Alfa
@@ -7,7 +7,7 @@ RSpec.describe Reek::SmellWarning do
7
7
  let(:utility_function_detector) { build(:smell_detector, smell_type: 'UtilityFunction') }
8
8
  let(:uncommunicative_name_detector) { build(:smell_detector, smell_type: 'UncommunicativeVariableName') }
9
9
 
10
- context 'sort order' do
10
+ describe 'sort order' do
11
11
  shared_examples_for 'first sorts ahead of second' do
12
12
  it 'hash differently' do
13
13
  expect(first.hash).not_to eq(second.hash)
@@ -26,21 +26,21 @@ RSpec.describe Reek::SmellWarning do
26
26
  end
27
27
  end
28
28
 
29
- context 'smells differing only by detector' do
29
+ context 'when smells differ only by detector' do
30
30
  let(:first) { build(:smell_warning, smell_detector: duplication_detector) }
31
31
  let(:second) { build(:smell_warning, smell_detector: feature_envy_detector) }
32
32
 
33
33
  it_behaves_like 'first sorts ahead of second'
34
34
  end
35
35
 
36
- context 'smells differing only by lines' do
36
+ context 'when smells differ only by lines' do
37
37
  let(:first) { build(:smell_warning, smell_detector: feature_envy_detector, lines: [2]) }
38
38
  let(:second) { build(:smell_warning, smell_detector: feature_envy_detector, lines: [3]) }
39
39
 
40
40
  it_behaves_like 'first sorts ahead of second'
41
41
  end
42
42
 
43
- context 'smells differing only by context' do
43
+ context 'when smells differ only by context' do
44
44
  let(:first) { build(:smell_warning, smell_detector: duplication_detector, context: 'first') }
45
45
  let(:second) do
46
46
  build(:smell_warning, smell_detector: duplication_detector, context: 'second')
@@ -49,7 +49,7 @@ RSpec.describe Reek::SmellWarning do
49
49
  it_behaves_like 'first sorts ahead of second'
50
50
  end
51
51
 
52
- context 'smells differing only by message' do
52
+ context 'when smells differ only by message' do
53
53
  let(:first) do
54
54
  build(:smell_warning, smell_detector: duplication_detector,
55
55
  context: 'ctx', message: 'first message')
@@ -62,7 +62,7 @@ RSpec.describe Reek::SmellWarning do
62
62
  it_behaves_like 'first sorts ahead of second'
63
63
  end
64
64
 
65
- context 'smell name takes precedence over message' do
65
+ context 'when smells differ by name and message' do
66
66
  let(:first) do
67
67
  build(:smell_warning, smell_detector: feature_envy_detector, message: 'second message')
68
68
  end
@@ -73,7 +73,7 @@ RSpec.describe Reek::SmellWarning do
73
73
  it_behaves_like 'first sorts ahead of second'
74
74
  end
75
75
 
76
- context 'smells differing everywhere' do
76
+ context 'when smells differ everywhere' do
77
77
  let(:first) do
78
78
  build(:smell_warning, smell_detector: duplication_detector,
79
79
  context: 'Dirty#a',
@@ -97,7 +97,7 @@ RSpec.describe Reek::SmellWarning do
97
97
  end
98
98
  end
99
99
 
100
- context '#yaml_hash' do
100
+ describe '#yaml_hash' do
101
101
  let(:context_name) { 'Module::Class#method/block' }
102
102
  let(:lines) { [24, 513] }
103
103
  let(:message) { 'test message' }
@@ -135,6 +135,10 @@ RSpec.describe Reek::SmellWarning do
135
135
  expect(yaml['source']).to eq source
136
136
  end
137
137
 
138
+ it 'includes the documentation link' do
139
+ expect(yaml['documentation_link']).to eq Reek::DocumentationLink.build('FeatureEnvy')
140
+ end
141
+
138
142
  it 'includes the parameters' do
139
143
  parameters.each do |key, value|
140
144
  expect(yaml[key]).to eq value
@@ -6,86 +6,60 @@ RSpec.describe Reek::Source::SourceCode do
6
6
  describe '#syntax_tree' do
7
7
  it 'associates comments with the AST' do
8
8
  source = "# this is\n# a comment\ndef foo; end"
9
- source_code = described_class.new(code: source, origin: '(string)')
9
+ source_code = described_class.new(source: source, origin: '(string)')
10
10
  result = source_code.syntax_tree
11
11
  expect(result.leading_comment).to eq "# this is\n# a comment"
12
12
  end
13
13
 
14
14
  it 'cleanly processes empty source' do
15
- source_code = described_class.new(code: '', origin: '(string)')
15
+ source_code = described_class.new(source: '', origin: '(string)')
16
16
  result = source_code.syntax_tree
17
- expect(result).to be_nil
17
+ expect(result.type).to eq :empty
18
18
  end
19
19
 
20
20
  it 'cleanly processes empty source with comments' do
21
21
  source = "# this is\n# a comment\n"
22
- source_code = described_class.new(code: source, origin: '(string)')
22
+ source_code = described_class.new(source: source, origin: '(string)')
23
23
  result = source_code.syntax_tree
24
- expect(result).to be_nil
24
+ expect(result.type).to eq :empty
25
25
  end
26
26
 
27
27
  it 'does not crash with sequences incompatible with UTF-8' do
28
28
  source = '"\xFF"'
29
- source_code = described_class.new(code: source, origin: '(string)')
29
+ source_code = described_class.new(source: source, origin: '(string)')
30
30
  result = source_code.syntax_tree
31
31
  expect(result.children.first).to eq "\xFF"
32
32
  end
33
33
 
34
34
  it 'returns a :lambda node for lambda expressions' do
35
35
  source = '->() { }'
36
- source_code = described_class.new(code: source, origin: '(string)')
36
+ source_code = described_class.new(source: source, origin: '(string)')
37
37
  result = source_code.syntax_tree
38
38
  expect(result.children.first.type).to eq :lambda
39
39
  end
40
- end
41
-
42
- context 'with a source that triggers an encoding error' do
43
- let(:source_name) { 'Bad source' }
44
- let(:code) do
45
- <<-SRC.strip_heredoc
46
- # encoding: US-ASCII
47
- puts 'こんにちは世界'
48
- SRC
49
- end
50
- let(:src) { described_class.new(code: code, origin: source_name) }
51
40
 
52
- it 'raises an encoding error' do
53
- expect { src.syntax_tree }.to raise_error Reek::Errors::EncodingError
54
- end
55
-
56
- it 'explains the origin of the error' do
57
- message = "Source '#{source_name}' cannot be processed by Reek due to an encoding error in the source file."
58
- expect { src.syntax_tree }.to raise_error.with_message(/#{message}/)
59
- end
60
- end
61
-
62
- context 'when the parser fails' do
63
- let(:source_name) { 'Test source' }
64
- let(:src) { described_class.new(code: code, origin: source_name, **parser) }
65
-
66
- context 'with a Parser::SyntaxError' do
41
+ context 'when the parser fails with a Parser::SyntaxError' do
42
+ let(:src) { described_class.new(source: code) }
67
43
  let(:code) { '== Invalid Syntax ==' }
68
- let(:parser) { {} }
69
44
 
70
- it 'adds a diagnostic' do
71
- expect(src.diagnostics.size).to eq 2
45
+ it 'raises the error' do
46
+ expect { src.syntax_tree }.to raise_error Parser::SyntaxError
72
47
  end
73
48
  end
74
49
 
75
- context 'with a generic error' do
50
+ context 'when the parser fails with a generic error' do
76
51
  let(:code) { '' }
52
+ let(:parser) { instance_double('Parser::Ruby25') }
53
+ let(:src) { described_class.new(source: code, parser: parser) }
77
54
  let(:error_class) { RuntimeError }
78
55
  let(:error_message) { 'An error' }
79
- let(:parser) do
80
- parser = instance_double('Parser::Ruby24')
56
+
57
+ before do
81
58
  allow(parser).to receive(:parse_with_comments).and_raise(error_class, error_message)
82
- {
83
- parser: parser
84
- }
85
59
  end
86
60
 
87
61
  it 'raises the error' do
88
- expect { src.valid_syntax? }.to raise_error error_class, error_message
62
+ expect { src.syntax_tree }.to raise_error error_class, error_message
89
63
  end
90
64
  end
91
65
  end