reek 4.6.0 → 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 (294) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +17 -12
  3. data/.rubocop.yml +79 -26
  4. data/.simplecov +1 -0
  5. data/.travis.yml +3 -9
  6. data/.yardopts +1 -1
  7. data/CHANGELOG.md +76 -0
  8. data/CONTRIBUTING.md +1 -1
  9. data/Gemfile +5 -5
  10. data/README.md +138 -107
  11. data/Rakefile +16 -3
  12. data/bin/code_climate_reek +1 -0
  13. data/bin/reek +2 -3
  14. data/docs/API.md +2 -9
  15. data/docs/Basic-Smell-Options.md +51 -11
  16. data/docs/Code-Smells.md +1 -1
  17. data/docs/Command-Line-Options.md +14 -4
  18. data/docs/Duplicate-Method-Call.md +49 -1
  19. data/docs/Feature-Envy.md +44 -0
  20. data/docs/How-To-Write-New-Detectors.md +7 -7
  21. data/docs/Instance-Variable-Assumption.md +1 -1
  22. data/docs/{Prima-Donna-Method.md → Missing-Safe-Method.md} +11 -9
  23. data/docs/Rake-Task.md +1 -1
  24. data/docs/Reek-4-to-Reek-5-migration.md +193 -0
  25. data/docs/Reek-Driven-Development.md +1 -1
  26. data/docs/Uncommunicative-Method-Name.md +45 -6
  27. data/docs/Uncommunicative-Module-Name.md +49 -7
  28. data/docs/Uncommunicative-Parameter-Name.md +43 -5
  29. data/docs/Uncommunicative-Variable-Name.md +73 -2
  30. data/docs/Unused-Private-Method.md +3 -3
  31. data/docs/defaults.reek.yml +129 -0
  32. data/docs/yard_plugin.rb +1 -0
  33. data/features/command_line_interface/basic_usage.feature +2 -2
  34. data/features/command_line_interface/options.feature +46 -4
  35. data/features/command_line_interface/show_progress.feature +4 -4
  36. data/features/command_line_interface/smell_selection.feature +1 -1
  37. data/features/command_line_interface/smells_count.feature +6 -6
  38. data/features/command_line_interface/stdin.feature +31 -5
  39. data/features/configuration_files/accept_setting.feature +45 -28
  40. data/features/configuration_files/directory_specific_directives.feature +80 -75
  41. data/features/configuration_files/exclude_directives.feature +11 -10
  42. data/features/configuration_files/exclude_paths_directives.feature +4 -4
  43. data/features/configuration_files/masking_smells.feature +38 -9
  44. data/features/configuration_files/mix_accept_reject_setting.feature +31 -28
  45. data/features/configuration_files/reject_setting.feature +52 -41
  46. data/features/configuration_files/schema_validation.feature +59 -0
  47. data/features/configuration_files/unused_private_method.feature +18 -16
  48. data/features/configuration_loading.feature +53 -10
  49. data/features/configuration_via_source_comments/erroneous_source_comments.feature +3 -3
  50. data/features/configuration_via_source_comments/well_formed_source_comments.feature +2 -2
  51. data/features/locales.feature +32 -0
  52. data/features/rake_task/rake_task.feature +58 -18
  53. data/features/reports/codeclimate.feature +59 -0
  54. data/features/reports/json.feature +3 -3
  55. data/features/reports/reports.feature +34 -34
  56. data/features/reports/yaml.feature +3 -3
  57. data/features/rspec_matcher.feature +40 -0
  58. data/features/samples.feature +287 -287
  59. data/features/step_definitions/reek_steps.rb +14 -2
  60. data/features/step_definitions/sample_file_steps.rb +9 -4
  61. data/features/support/env.rb +2 -11
  62. data/features/todo_list.feature +26 -23
  63. data/lib/reek/ast/ast_node_class_map.rb +1 -0
  64. data/lib/reek/ast/builder.rb +16 -0
  65. data/lib/reek/ast/node.rb +50 -58
  66. data/lib/reek/ast/object_refs.rb +2 -1
  67. data/lib/reek/ast/reference_collector.rb +3 -4
  68. data/lib/reek/ast/sexp_extensions/arguments.rb +1 -0
  69. data/lib/reek/ast/sexp_extensions/attribute_assignments.rb +1 -0
  70. data/lib/reek/ast/sexp_extensions/begin.rb +17 -0
  71. data/lib/reek/ast/sexp_extensions/block.rb +1 -0
  72. data/lib/reek/ast/sexp_extensions/case.rb +2 -1
  73. data/lib/reek/ast/sexp_extensions/constant.rb +1 -0
  74. data/lib/reek/ast/sexp_extensions/if.rb +9 -1
  75. data/lib/reek/ast/sexp_extensions/lambda.rb +1 -0
  76. data/lib/reek/ast/sexp_extensions/literal.rb +1 -0
  77. data/lib/reek/ast/sexp_extensions/logical_operators.rb +2 -1
  78. data/lib/reek/ast/sexp_extensions/methods.rb +5 -6
  79. data/lib/reek/ast/sexp_extensions/module.rb +55 -8
  80. data/lib/reek/ast/sexp_extensions/nested_assignables.rb +1 -0
  81. data/lib/reek/ast/sexp_extensions/self.rb +1 -0
  82. data/lib/reek/ast/sexp_extensions/send.rb +1 -4
  83. data/lib/reek/ast/sexp_extensions/super.rb +1 -0
  84. data/lib/reek/ast/sexp_extensions/symbols.rb +1 -0
  85. data/lib/reek/ast/sexp_extensions/variables.rb +1 -0
  86. data/lib/reek/ast/sexp_extensions/when.rb +1 -0
  87. data/lib/reek/ast/sexp_extensions/yield.rb +1 -0
  88. data/lib/reek/ast/sexp_extensions.rb +2 -0
  89. data/lib/reek/cli/application.rb +5 -3
  90. data/lib/reek/cli/command/base_command.rb +1 -0
  91. data/lib/reek/cli/command/report_command.rb +2 -2
  92. data/lib/reek/cli/command/todo_list_command.rb +9 -8
  93. data/lib/reek/cli/options.rb +32 -16
  94. data/lib/reek/cli/silencer.rb +15 -3
  95. data/lib/reek/cli/status.rb +1 -0
  96. data/lib/reek/code_comment.rb +14 -16
  97. data/lib/reek/configuration/app_configuration.rb +34 -28
  98. data/lib/reek/configuration/configuration_converter.rb +110 -0
  99. data/lib/reek/configuration/configuration_file_finder.rb +17 -41
  100. data/lib/reek/configuration/configuration_validator.rb +13 -23
  101. data/lib/reek/configuration/default_directive.rb +18 -3
  102. data/lib/reek/configuration/directory_directives.rb +18 -11
  103. data/lib/reek/configuration/excluded_paths.rb +2 -1
  104. data/lib/reek/configuration/rake_task_converter.rb +29 -0
  105. data/lib/reek/configuration/schema.yml +210 -0
  106. data/lib/reek/configuration/schema_validator.rb +38 -0
  107. data/lib/reek/context/attribute_context.rb +4 -3
  108. data/lib/reek/context/class_context.rb +1 -0
  109. data/lib/reek/context/code_context.rb +49 -44
  110. data/lib/reek/context/ghost_context.rb +1 -2
  111. data/lib/reek/context/method_context.rb +26 -18
  112. data/lib/reek/context/module_context.rb +11 -11
  113. data/lib/reek/context/root_context.rb +1 -4
  114. data/lib/reek/context/send_context.rb +3 -2
  115. data/lib/reek/context/singleton_attribute_context.rb +1 -0
  116. data/lib/reek/context/singleton_method_context.rb +1 -0
  117. data/lib/reek/context/statement_counter.rb +1 -0
  118. data/lib/reek/context/visibility_tracker.rb +1 -0
  119. data/lib/reek/context_builder.rb +44 -44
  120. data/lib/reek/detector_repository.rb +12 -11
  121. data/lib/reek/documentation_link.rb +28 -0
  122. data/lib/reek/errors/bad_detector_configuration_key_in_comment_error.rb +15 -13
  123. data/lib/reek/errors/bad_detector_in_comment_error.rb +13 -11
  124. data/lib/reek/errors/base_error.rb +3 -0
  125. data/lib/reek/errors/config_file_error.rb +11 -0
  126. data/lib/reek/errors/encoding_error.rb +43 -0
  127. data/lib/reek/errors/garbage_detector_configuration_in_comment_error.rb +14 -12
  128. data/lib/reek/errors/incomprehensible_source_error.rb +23 -24
  129. data/lib/reek/errors/syntax_error.rb +41 -0
  130. data/lib/reek/examiner.rb +24 -22
  131. data/lib/reek/logging_error_handler.rb +8 -5
  132. data/lib/reek/rake/task.rb +8 -5
  133. data/lib/reek/report/base_report.rb +9 -12
  134. data/lib/reek/report/code_climate/code_climate_configuration.rb +2 -1
  135. data/lib/reek/report/code_climate/code_climate_configuration.yml +6 -6
  136. data/lib/reek/report/code_climate/code_climate_fingerprint.rb +1 -0
  137. data/lib/reek/report/code_climate/code_climate_formatter.rb +1 -0
  138. data/lib/reek/report/code_climate/code_climate_report.rb +1 -0
  139. data/lib/reek/report/code_climate.rb +1 -0
  140. data/lib/reek/report/documentation_link_warning_formatter.rb +17 -0
  141. data/lib/reek/report/heading_formatter.rb +54 -0
  142. data/lib/reek/report/html_report.rb +1 -0
  143. data/lib/reek/report/json_report.rb +2 -1
  144. data/lib/reek/report/location_formatter.rb +40 -0
  145. data/lib/reek/report/progress_formatter.rb +79 -0
  146. data/lib/reek/report/simple_warning_formatter.rb +34 -0
  147. data/lib/reek/report/text_report.rb +2 -2
  148. data/lib/reek/report/xml_report.rb +4 -3
  149. data/lib/reek/report/yaml_report.rb +2 -1
  150. data/lib/reek/report.rb +16 -10
  151. data/lib/reek/smell_configuration.rb +3 -2
  152. data/lib/reek/smell_detectors/attribute.rb +6 -11
  153. data/lib/reek/smell_detectors/base_detector.rb +31 -26
  154. data/lib/reek/smell_detectors/boolean_parameter.rb +4 -5
  155. data/lib/reek/smell_detectors/class_variable.rb +6 -14
  156. data/lib/reek/smell_detectors/control_parameter.rb +19 -33
  157. data/lib/reek/smell_detectors/data_clump.rb +16 -9
  158. data/lib/reek/smell_detectors/duplicate_method_call.rb +24 -17
  159. data/lib/reek/smell_detectors/feature_envy.rb +10 -7
  160. data/lib/reek/smell_detectors/instance_variable_assumption.rb +15 -23
  161. data/lib/reek/smell_detectors/irresponsible_module.rb +6 -12
  162. data/lib/reek/smell_detectors/long_parameter_list.rb +11 -7
  163. data/lib/reek/smell_detectors/long_yield_list.rb +11 -7
  164. data/lib/reek/smell_detectors/manual_dispatch.rb +5 -5
  165. data/lib/reek/smell_detectors/{prima_donna_method.rb → missing_safe_method.rb} +21 -20
  166. data/lib/reek/smell_detectors/module_initialize.rb +4 -5
  167. data/lib/reek/smell_detectors/nested_iterators.rb +17 -24
  168. data/lib/reek/smell_detectors/nil_check.rb +9 -15
  169. data/lib/reek/smell_detectors/repeated_conditional.rb +14 -11
  170. data/lib/reek/smell_detectors/subclassed_from_core_class.rb +8 -8
  171. data/lib/reek/smell_detectors/too_many_constants.rb +11 -9
  172. data/lib/reek/smell_detectors/too_many_instance_variables.rb +11 -6
  173. data/lib/reek/smell_detectors/too_many_methods.rb +12 -7
  174. data/lib/reek/smell_detectors/too_many_statements.rb +11 -6
  175. data/lib/reek/smell_detectors/uncommunicative_method_name.rb +11 -11
  176. data/lib/reek/smell_detectors/uncommunicative_module_name.rb +15 -18
  177. data/lib/reek/smell_detectors/uncommunicative_parameter_name.rb +18 -22
  178. data/lib/reek/smell_detectors/uncommunicative_variable_name.rb +27 -27
  179. data/lib/reek/smell_detectors/unused_parameters.rb +5 -6
  180. data/lib/reek/smell_detectors/unused_private_method.rb +14 -20
  181. data/lib/reek/smell_detectors/utility_function.rb +13 -16
  182. data/lib/reek/smell_detectors.rb +2 -1
  183. data/lib/reek/smell_warning.rb +16 -8
  184. data/lib/reek/source/source_code.rb +65 -46
  185. data/lib/reek/source/source_locator.rb +9 -8
  186. data/lib/reek/spec/should_reek.rb +3 -2
  187. data/lib/reek/spec/should_reek_of.rb +13 -26
  188. data/lib/reek/spec/should_reek_only_of.rb +5 -4
  189. data/lib/reek/spec/smell_matcher.rb +2 -1
  190. data/lib/reek/spec.rb +7 -6
  191. data/lib/reek/tree_dresser.rb +9 -8
  192. data/lib/reek/version.rb +2 -1
  193. data/lib/reek.rb +1 -0
  194. data/reek.gemspec +5 -6
  195. data/samples/checkstyle.xml +1 -1
  196. data/samples/configuration/accepts_rejects_and_excludes_for_detectors.reek.yml +29 -0
  197. data/samples/configuration/accepts_rejects_and_excludes_for_directory_directives.reek.yml +30 -0
  198. data/samples/configuration/full_configuration.reek +8 -4
  199. data/samples/configuration/full_mask.reek +5 -4
  200. data/samples/configuration/partial_mask.reek +3 -2
  201. data/samples/configuration/regular_configuration/.reek.yml +4 -0
  202. data/samples/paths.rb +5 -4
  203. data/samples/source_with_hidden_directories/.hidden/hidden.rb +1 -0
  204. data/samples/source_with_hidden_directories/not_hidden.rb +1 -0
  205. data/spec/factories/factories.rb +2 -13
  206. data/spec/reek/ast/node_spec.rb +103 -10
  207. data/spec/reek/ast/reference_collector_spec.rb +1 -1
  208. data/spec/reek/ast/sexp_extensions_spec.rb +15 -34
  209. data/spec/reek/cli/application_spec.rb +52 -42
  210. data/spec/reek/cli/command/todo_list_command_spec.rb +6 -4
  211. data/spec/reek/cli/silencer_spec.rb +28 -0
  212. data/spec/reek/code_comment_spec.rb +31 -38
  213. data/spec/reek/configuration/app_configuration_spec.rb +46 -33
  214. data/spec/reek/configuration/configuration_file_finder_spec.rb +133 -51
  215. data/spec/reek/configuration/default_directive_spec.rb +1 -1
  216. data/spec/reek/configuration/directory_directives_spec.rb +6 -7
  217. data/spec/reek/configuration/excluded_paths_spec.rb +6 -6
  218. data/spec/reek/configuration/rake_task_converter_spec.rb +33 -0
  219. data/spec/reek/configuration/schema_validator_spec.rb +165 -0
  220. data/spec/reek/context/code_context_spec.rb +70 -106
  221. data/spec/reek/context/ghost_context_spec.rb +9 -9
  222. data/spec/reek/context/method_context_spec.rb +2 -2
  223. data/spec/reek/context/module_context_spec.rb +3 -3
  224. data/spec/reek/context/root_context_spec.rb +1 -1
  225. data/spec/reek/context/statement_counter_spec.rb +1 -0
  226. data/spec/reek/context_builder_spec.rb +20 -0
  227. data/spec/reek/documentation_link_spec.rb +20 -0
  228. data/spec/reek/errors/base_error_spec.rb +13 -0
  229. data/spec/reek/examiner_spec.rb +137 -29
  230. data/spec/reek/rake/task_spec.rb +25 -2
  231. data/spec/reek/report/code_climate/code_climate_fingerprint_spec.rb +82 -78
  232. data/spec/reek/report/code_climate/code_climate_formatter_spec.rb +6 -6
  233. data/spec/reek/report/code_climate/code_climate_report_spec.rb +22 -22
  234. data/spec/reek/report/json_report_spec.rb +13 -46
  235. data/spec/reek/report/{formatter/location_formatter_spec.rb → location_formatter_spec.rb} +5 -5
  236. data/spec/reek/report/{formatter/progress_formatter_spec.rb → progress_formatter_spec.rb} +4 -4
  237. data/spec/reek/report/text_report_spec.rb +4 -4
  238. data/spec/reek/report/xml_report_spec.rb +3 -3
  239. data/spec/reek/report/yaml_report_spec.rb +17 -46
  240. data/spec/reek/report_spec.rb +3 -3
  241. data/spec/reek/smell_detectors/base_detector_spec.rb +4 -5
  242. data/spec/reek/smell_detectors/boolean_parameter_spec.rb +2 -2
  243. data/spec/reek/smell_detectors/class_variable_spec.rb +26 -32
  244. data/spec/reek/smell_detectors/control_parameter_spec.rb +34 -4
  245. data/spec/reek/smell_detectors/duplicate_method_call_spec.rb +3 -3
  246. data/spec/reek/smell_detectors/feature_envy_spec.rb +47 -2
  247. data/spec/reek/smell_detectors/irresponsible_module_spec.rb +59 -21
  248. data/spec/reek/smell_detectors/{prima_donna_method_spec.rb → missing_safe_method_spec.rb} +10 -10
  249. data/spec/reek/smell_detectors/module_initialize_spec.rb +14 -0
  250. data/spec/reek/smell_detectors/nested_iterators_spec.rb +1 -1
  251. data/spec/reek/smell_detectors/too_many_constants_spec.rb +3 -3
  252. data/spec/reek/smell_detectors/too_many_instance_variables_spec.rb +1 -1
  253. data/spec/reek/smell_detectors/uncommunicative_method_name_spec.rb +6 -6
  254. data/spec/reek/smell_detectors/uncommunicative_module_name_spec.rb +6 -4
  255. data/spec/reek/smell_detectors/uncommunicative_parameter_name_spec.rb +36 -15
  256. data/spec/reek/smell_detectors/uncommunicative_variable_name_spec.rb +9 -9
  257. data/spec/reek/smell_detectors/unused_parameters_spec.rb +3 -3
  258. data/spec/reek/smell_detectors/unused_private_method_spec.rb +21 -10
  259. data/spec/reek/smell_detectors/utility_function_spec.rb +57 -5
  260. data/spec/reek/smell_warning_spec.rb +12 -8
  261. data/spec/reek/source/source_code_spec.rb +27 -38
  262. data/spec/reek/source/source_locator_spec.rb +42 -12
  263. data/spec/reek/spec/should_reek_of_spec.rb +25 -30
  264. data/spec/reek/spec/should_reek_only_of_spec.rb +2 -2
  265. data/spec/reek/spec/should_reek_spec.rb +8 -8
  266. data/spec/reek/spec/smell_matcher_spec.rb +23 -23
  267. data/spec/reek/tree_dresser_spec.rb +12 -17
  268. data/spec/spec_helper.rb +7 -17
  269. data/tasks/configuration.rake +8 -5
  270. metadata +77 -40
  271. data/defaults.reek +0 -128
  272. data/features/configuration_files/warn_about_multiple_configuration_files.feature +0 -44
  273. data/lib/reek/errors/parse_error.rb +0 -19
  274. data/lib/reek/report/formatter/heading_formatter.rb +0 -51
  275. data/lib/reek/report/formatter/location_formatter.rb +0 -41
  276. data/lib/reek/report/formatter/progress_formatter.rb +0 -80
  277. data/lib/reek/report/formatter/simple_warning_formatter.rb +0 -35
  278. data/lib/reek/report/formatter/wiki_link_warning_formatter.rb +0 -35
  279. data/lib/reek/report/formatter.rb +0 -32
  280. data/samples/configuration/non_public_modifiers_mask.reek +0 -3
  281. data/samples/smelly_with_inline_mask.rb +0 -8
  282. data/samples/smelly_with_modifiers.rb +0 -12
  283. data/samples/source_with_hidden_directories/.hidden/uncommunicative_method_name.rb +0 -5
  284. data/samples/source_with_non_ruby_files/uncommunicative_parameter_name.rb +0 -6
  285. /data/{samples/configuration/more_than_one_configuration_file/regular.reek → .reek.yml} +0 -0
  286. /data/samples/{clean.rb → clean_source/clean.rb} +0 -0
  287. /data/samples/{exceptions.reek → configuration/home/home.reek.yml} +0 -0
  288. /data/samples/configuration/{more_than_one_configuration_file/todo.reek → regular_configuration/empty_sub_directory/.gitignore} +0 -0
  289. /data/samples/{configuration/single_configuration_file/.reek → no_config_file/.keep} +0 -0
  290. /data/samples/{inline.rb → smelly_source/inline.rb} +0 -0
  291. /data/samples/{optparse.rb → smelly_source/optparse.rb} +0 -0
  292. /data/samples/{redcloth.rb → smelly_source/redcloth.rb} +0 -0
  293. /data/samples/{smelly.rb → smelly_source/smelly.rb} +0 -0
  294. /data/samples/{source_with_hidden_directories/uncommunicative_parameter_name.rb → source_with_non_ruby_files/ruby.rb} +0 -0
@@ -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
@@ -103,12 +103,23 @@ RSpec.describe Reek::SmellDetectors::UnusedPrivateMethod do
103
103
  to reek_of(:UnusedPrivateMethod, name: 'bravo', lines: [3]).
104
104
  and reek_of(:UnusedPrivateMethod, name: 'charlie', lines: [4])
105
105
  end
106
+
107
+ it 'reports instance methods defined as private with a modifier' do
108
+ source = <<-EOF
109
+ class Alfa
110
+ private def bravo; end
111
+ end
112
+ EOF
113
+
114
+ expect(source).
115
+ to reek_of(:UnusedPrivateMethod, name: 'bravo')
116
+ end
106
117
  end
107
118
 
108
- describe 'configuring the detector via source code comment' do
119
+ context 'when the detector is configured via a source code comment' do
109
120
  it 'does not report methods we excluded' do
110
121
  source = <<-EOF
111
- # :reek:UnusedPrivateMethod: { exclude: [ bravo ] }
122
+ # :reek:UnusedPrivateMethod { exclude: [ bravo ] }
112
123
  class Alfa
113
124
  private
114
125
  def bravo; end
@@ -122,8 +133,8 @@ RSpec.describe Reek::SmellDetectors::UnusedPrivateMethod do
122
133
  end
123
134
  end
124
135
 
125
- context 'used private methods' do
126
- it 'are not reported' do
136
+ context 'when a class has only used private methods' do
137
+ it 'reports nothing' do
127
138
  source = <<-EOF
128
139
  class Alfa
129
140
  def bravo
@@ -139,8 +150,8 @@ RSpec.describe Reek::SmellDetectors::UnusedPrivateMethod do
139
150
  end
140
151
  end
141
152
 
142
- context 'unused protected methods' do
143
- it 'are not reported' do
153
+ context 'when a class has unused protected methods' do
154
+ it 'reports nothing' do
144
155
  source = <<-EOF
145
156
  class Alfa
146
157
  protected
@@ -152,8 +163,8 @@ RSpec.describe Reek::SmellDetectors::UnusedPrivateMethod do
152
163
  end
153
164
  end
154
165
 
155
- context 'unused public methods' do
156
- it 'are not reported' do
166
+ context 'when a class has unused public methods' do
167
+ it 'reports nothing' do
157
168
  source = <<-EOF
158
169
  class Alfa
159
170
  def bravo; end
@@ -164,7 +175,7 @@ RSpec.describe Reek::SmellDetectors::UnusedPrivateMethod do
164
175
  end
165
176
  end
166
177
 
167
- describe 'prevent methods from being reported' do
178
+ describe 'preventing methods from being reported' do
168
179
  let(:source) do
169
180
  <<-EOF
170
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
@@ -221,9 +221,21 @@ RSpec.describe Reek::SmellDetectors::UtilityFunction do
221
221
 
222
222
  expect(src).not_to reek_of(:UtilityFunction).with_config(config)
223
223
  end
224
+
225
+ it 'does not report UtilityFunction when private is used as a def modifier' do
226
+ src = <<-EOS
227
+ class Alfa
228
+ private def bravo(charlie)
229
+ charlie.delta.echo
230
+ end
231
+ end
232
+ EOS
233
+
234
+ expect(src).not_to reek_of(:UtilityFunction).with_config(config)
235
+ end
224
236
  end
225
237
 
226
- context 'protected methods' do
238
+ context 'when examining protected methods' do
227
239
  it 'does not report UtilityFunction' do
228
240
  src = <<-EOS
229
241
  class Alfa
@@ -236,6 +248,46 @@ RSpec.describe Reek::SmellDetectors::UtilityFunction do
236
248
 
237
249
  expect(src).not_to reek_of(:UtilityFunction).with_config(config)
238
250
  end
251
+
252
+ it 'does not report UtilityFunction when protected is used as a def modifier' do
253
+ src = <<-EOS
254
+ class Alfa
255
+ protected def bravo(charlie)
256
+ charlie.delta.echo
257
+ end
258
+ end
259
+ EOS
260
+
261
+ expect(src).not_to reek_of(:UtilityFunction).with_config(config)
262
+ end
263
+ end
264
+ end
265
+
266
+ describe 'disabling with a comment' do
267
+ it 'disables the method following the comment' do
268
+ src = <<-EOS
269
+ class Alfa
270
+ # :reek:UtilityFunction
271
+ def bravo(charlie)
272
+ charlie.delta.echo
273
+ end
274
+ end
275
+ EOS
276
+
277
+ expect(src).not_to reek_of(:UtilityFunction)
278
+ end
279
+
280
+ it 'disables a method when it has a visibility modifier' do
281
+ src = <<-EOS
282
+ class Alfa
283
+ # :reek:UtilityFunction
284
+ private def bravo(charlie)
285
+ charlie.delta.echo
286
+ end
287
+ end
288
+ EOS
289
+
290
+ expect(src).not_to reek_of(:UtilityFunction)
239
291
  end
240
292
  end
241
293
  end
@@ -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,71 +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
- end
27
-
28
- context 'when the parser fails' do
29
- let(:source_name) { 'Test source' }
30
- let(:error_message) { 'Error message' }
31
- let(:parser) { class_double(Parser::Ruby23) }
32
- let(:src) { described_class.new(code: '', origin: source_name, parser: parser) }
33
26
 
34
- shared_examples_for 'handling and recording the error' do
35
- it 'raises an informative error' do
36
- expect { src.syntax_tree }.
37
- to raise_error(/#{source_name}: #{error_class.name}: #{error_message}/)
38
- end
27
+ it 'does not crash with sequences incompatible with UTF-8' do
28
+ source = '"\xFF"'
29
+ source_code = described_class.new(source: source, origin: '(string)')
30
+ result = source_code.syntax_tree
31
+ expect(result.children.first).to eq "\xFF"
39
32
  end
40
33
 
41
- context 'with a Parser::SyntaxError' do
42
- let(:error_class) { Parser::SyntaxError }
43
- let(:diagnostic) { instance_double('Parser::Diagnostic', message: error_message) }
44
-
45
- before do
46
- allow(parser).to receive(:parse_with_comments).
47
- and_raise error_class.new(diagnostic)
48
- end
49
-
50
- it_behaves_like 'handling and recording the error'
34
+ it 'returns a :lambda node for lambda expressions' do
35
+ source = '->() { }'
36
+ source_code = described_class.new(source: source, origin: '(string)')
37
+ result = source_code.syntax_tree
38
+ expect(result.children.first.type).to eq :lambda
51
39
  end
52
40
 
53
- context 'with a Racc::ParseError' do
54
- let(:error_class) { Racc::ParseError }
41
+ context 'when the parser fails with a Parser::SyntaxError' do
42
+ let(:src) { described_class.new(source: code) }
43
+ let(:code) { '== Invalid Syntax ==' }
55
44
 
56
- before do
57
- allow(parser).to receive(:parse_with_comments).
58
- and_raise(error_class.new(error_message))
45
+ it 'raises the error' do
46
+ expect { src.syntax_tree }.to raise_error Parser::SyntaxError
59
47
  end
60
-
61
- it_behaves_like 'handling and recording the error'
62
48
  end
63
49
 
64
- context 'with a generic error' do
50
+ context 'when the parser fails with a generic error' do
51
+ let(:code) { '' }
52
+ let(:parser) { instance_double('Parser::Ruby25') }
53
+ let(:src) { described_class.new(source: code, parser: parser) }
65
54
  let(:error_class) { RuntimeError }
55
+ let(:error_message) { 'An error' }
66
56
 
67
57
  before do
68
- allow(parser).to receive(:parse_with_comments).
69
- and_raise(error_class.new(error_message))
58
+ allow(parser).to receive(:parse_with_comments).and_raise(error_class, error_message)
70
59
  end
71
60
 
72
61
  it 'raises the error' do
73
- expect { src.syntax_tree }.to raise_error error_class
62
+ expect { src.syntax_tree }.to raise_error error_class, error_message
74
63
  end
75
64
  end
76
65
  end
@@ -5,15 +5,15 @@ require_lib 'reek/source/source_locator'
5
5
 
6
6
  RSpec.describe Reek::Source::SourceLocator do
7
7
  describe '#sources' do
8
- context 'applied to hidden directories' do
9
- let(:path) { SAMPLES_PATH.join('source_with_hidden_directories') }
8
+ context 'when applied to hidden directories' do
9
+ let(:path) { SAMPLES_DIR.join('source_with_hidden_directories') }
10
10
 
11
11
  let(:expected_paths) do
12
- [path.join('uncommunicative_parameter_name.rb')]
12
+ [path.join('not_hidden.rb')]
13
13
  end
14
14
 
15
15
  let(:paths_that_are_expected_to_be_ignored) do
16
- [path.join('.hidden/uncommunicative_method_name.rb')]
16
+ [path.join('.hidden/hidden.rb')]
17
17
  end
18
18
 
19
19
  it 'does not scan hidden directories' do
@@ -30,15 +30,45 @@ RSpec.describe Reek::Source::SourceLocator do
30
30
  end
31
31
 
32
32
  # rubocop:disable RSpec/NestedGroups
33
- context 'exclude paths' do
33
+ context 'with excluded paths' do
34
34
  let(:configuration) do
35
- test_configuration_for(CONFIG_PATH.join('with_excluded_paths.reek'))
35
+ test_configuration_for(CONFIGURATION_DIR.join('with_excluded_paths.reek'))
36
36
  end
37
37
 
38
38
  let(:options) { instance_double('Reek::CLI::Options', force_exclusion?: false) }
39
39
 
40
+ context 'when the path is absolute' do
41
+ let(:path) do
42
+ SAMPLES_DIR.join('source_with_exclude_paths',
43
+ 'ignore_me',
44
+ 'uncommunicative_method_name.rb').expand_path
45
+ end
46
+
47
+ context 'when options.force_exclusion? is true' do
48
+ before do
49
+ allow(options).to receive(:force_exclusion?).and_return(true)
50
+ end
51
+
52
+ it 'excludes this file' do
53
+ sources = described_class.new([path], configuration: configuration, options: options).sources
54
+ expect(sources).not_to include(path)
55
+ end
56
+ end
57
+
58
+ context 'when options.force_exclusion? is false' do
59
+ before do
60
+ allow(options).to receive(:force_exclusion?).and_return(false)
61
+ end
62
+
63
+ it 'includes this file' do
64
+ sources = described_class.new([path], configuration: configuration, options: options).sources
65
+ expect(sources).to include(path)
66
+ end
67
+ end
68
+ end
69
+
40
70
  context 'when the path is a file name in an excluded directory' do
41
- let(:path) { SAMPLES_PATH.join('source_with_exclude_paths', 'ignore_me', 'uncommunicative_method_name.rb') }
71
+ let(:path) { SAMPLES_DIR.join('source_with_exclude_paths', 'ignore_me', 'uncommunicative_method_name.rb') }
42
72
 
43
73
  context 'when options.force_exclusion? is true' do
44
74
  before do
@@ -64,7 +94,7 @@ RSpec.describe Reek::Source::SourceLocator do
64
94
  end
65
95
 
66
96
  context 'when path is a directory' do
67
- let(:path) { SAMPLES_PATH.join('source_with_exclude_paths') }
97
+ let(:path) { SAMPLES_DIR.join('source_with_exclude_paths') }
68
98
 
69
99
  let(:expected_paths) do
70
100
  [path.join('nested/uncommunicative_parameter_name.rb')]
@@ -90,10 +120,10 @@ RSpec.describe Reek::Source::SourceLocator do
90
120
  end
91
121
  # rubocop:enable RSpec/NestedGroups
92
122
 
93
- context 'non-Ruby paths' do
94
- let(:path) { SAMPLES_PATH.join('source_with_non_ruby_files') }
123
+ context 'with non-Ruby paths' do
124
+ let(:path) { SAMPLES_DIR.join('source_with_non_ruby_files') }
95
125
  let(:expected_sources) do
96
- [path.join('uncommunicative_parameter_name.rb')]
126
+ [path.join('ruby.rb')]
97
127
  end
98
128
  let(:paths_that_are_expected_to_be_ignored) do
99
129
  [
@@ -115,7 +145,7 @@ RSpec.describe Reek::Source::SourceLocator do
115
145
  end
116
146
  end
117
147
 
118
- context 'passing "." or "./" as argument' do
148
+ context 'when passing "." or "./" as argument' do
119
149
  let(:expected_sources) do
120
150
  [Pathname.new('spec/spec_helper.rb'), Pathname.new('lib/reek.rb')]
121
151
  end