reek 4.7.1 → 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 (260) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +17 -12
  3. data/.rubocop.yml +36 -7
  4. data/.simplecov +1 -0
  5. data/.travis.yml +3 -9
  6. data/.yardopts +1 -1
  7. data/CHANGELOG.md +59 -0
  8. data/CONTRIBUTING.md +1 -1
  9. data/Gemfile +5 -5
  10. data/README.md +134 -104
  11. data/Rakefile +16 -3
  12. data/bin/reek +1 -3
  13. data/docs/API.md +2 -9
  14. data/docs/Basic-Smell-Options.md +51 -11
  15. data/docs/Code-Smells.md +1 -1
  16. data/docs/Command-Line-Options.md +14 -4
  17. data/docs/Duplicate-Method-Call.md +49 -1
  18. data/docs/Feature-Envy.md +44 -0
  19. data/docs/How-To-Write-New-Detectors.md +7 -7
  20. data/docs/Instance-Variable-Assumption.md +1 -1
  21. data/docs/{Prima-Donna-Method.md → Missing-Safe-Method.md} +11 -9
  22. data/docs/Rake-Task.md +1 -1
  23. data/docs/Reek-4-to-Reek-5-migration.md +193 -0
  24. data/docs/Reek-Driven-Development.md +1 -1
  25. data/docs/Uncommunicative-Method-Name.md +45 -6
  26. data/docs/Uncommunicative-Module-Name.md +49 -7
  27. data/docs/Uncommunicative-Parameter-Name.md +43 -5
  28. data/docs/Uncommunicative-Variable-Name.md +73 -2
  29. data/docs/Unused-Private-Method.md +3 -3
  30. data/docs/defaults.reek.yml +129 -0
  31. data/docs/yard_plugin.rb +1 -0
  32. data/features/command_line_interface/basic_usage.feature +2 -2
  33. data/features/command_line_interface/options.feature +46 -4
  34. data/features/command_line_interface/show_progress.feature +4 -4
  35. data/features/command_line_interface/smell_selection.feature +1 -1
  36. data/features/command_line_interface/smells_count.feature +6 -6
  37. data/features/command_line_interface/stdin.feature +30 -8
  38. data/features/configuration_files/accept_setting.feature +45 -28
  39. data/features/configuration_files/directory_specific_directives.feature +80 -75
  40. data/features/configuration_files/exclude_directives.feature +11 -10
  41. data/features/configuration_files/exclude_paths_directives.feature +4 -4
  42. data/features/configuration_files/masking_smells.feature +38 -9
  43. data/features/configuration_files/mix_accept_reject_setting.feature +31 -28
  44. data/features/configuration_files/reject_setting.feature +52 -41
  45. data/features/configuration_files/schema_validation.feature +59 -0
  46. data/features/configuration_files/unused_private_method.feature +18 -16
  47. data/features/configuration_loading.feature +53 -10
  48. data/features/configuration_via_source_comments/erroneous_source_comments.feature +3 -3
  49. data/features/configuration_via_source_comments/well_formed_source_comments.feature +2 -2
  50. data/features/locales.feature +32 -0
  51. data/features/rake_task/rake_task.feature +58 -18
  52. data/features/reports/json.feature +3 -3
  53. data/features/reports/reports.feature +34 -34
  54. data/features/reports/yaml.feature +3 -3
  55. data/features/rspec_matcher.feature +40 -0
  56. data/features/samples.feature +287 -287
  57. data/features/step_definitions/reek_steps.rb +2 -2
  58. data/features/step_definitions/sample_file_steps.rb +9 -4
  59. data/features/support/env.rb +2 -11
  60. data/features/todo_list.feature +26 -23
  61. data/lib/reek/ast/builder.rb +1 -1
  62. data/lib/reek/ast/node.rb +40 -58
  63. data/lib/reek/ast/object_refs.rb +1 -1
  64. data/lib/reek/ast/reference_collector.rb +2 -4
  65. data/lib/reek/ast/sexp_extensions/case.rb +1 -1
  66. data/lib/reek/ast/sexp_extensions/if.rb +8 -1
  67. data/lib/reek/ast/sexp_extensions/logical_operators.rb +1 -1
  68. data/lib/reek/ast/sexp_extensions/methods.rb +4 -6
  69. data/lib/reek/ast/sexp_extensions/send.rb +0 -4
  70. data/lib/reek/cli/application.rb +4 -3
  71. data/lib/reek/cli/command/report_command.rb +1 -2
  72. data/lib/reek/cli/command/todo_list_command.rb +8 -8
  73. data/lib/reek/cli/options.rb +31 -16
  74. data/lib/reek/cli/silencer.rb +14 -3
  75. data/lib/reek/code_comment.rb +14 -16
  76. data/lib/reek/configuration/app_configuration.rb +32 -27
  77. data/lib/reek/configuration/configuration_converter.rb +110 -0
  78. data/lib/reek/configuration/configuration_file_finder.rb +16 -41
  79. data/lib/reek/configuration/configuration_validator.rb +12 -23
  80. data/lib/reek/configuration/default_directive.rb +17 -3
  81. data/lib/reek/configuration/directory_directives.rb +17 -11
  82. data/lib/reek/configuration/excluded_paths.rb +1 -1
  83. data/lib/reek/configuration/rake_task_converter.rb +29 -0
  84. data/lib/reek/configuration/schema.yml +210 -0
  85. data/lib/reek/configuration/schema_validator.rb +38 -0
  86. data/lib/reek/context/attribute_context.rb +3 -3
  87. data/lib/reek/context/code_context.rb +47 -43
  88. data/lib/reek/context/ghost_context.rb +0 -2
  89. data/lib/reek/context/method_context.rb +22 -15
  90. data/lib/reek/context/module_context.rb +5 -9
  91. data/lib/reek/context/root_context.rb +0 -4
  92. data/lib/reek/context/send_context.rb +2 -2
  93. data/lib/reek/context_builder.rb +43 -44
  94. data/lib/reek/detector_repository.rb +11 -11
  95. data/lib/reek/documentation_link.rb +28 -0
  96. data/lib/reek/errors/bad_detector_configuration_key_in_comment_error.rb +14 -13
  97. data/lib/reek/errors/bad_detector_in_comment_error.rb +12 -11
  98. data/lib/reek/errors/base_error.rb +3 -0
  99. data/lib/reek/errors/config_file_error.rb +11 -0
  100. data/lib/reek/errors/encoding_error.rb +43 -0
  101. data/lib/reek/errors/garbage_detector_configuration_in_comment_error.rb +13 -12
  102. data/lib/reek/errors/incomprehensible_source_error.rb +22 -24
  103. data/lib/reek/errors/syntax_error.rb +41 -0
  104. data/lib/reek/examiner.rb +24 -26
  105. data/lib/reek/logging_error_handler.rb +7 -5
  106. data/lib/reek/rake/task.rb +8 -4
  107. data/lib/reek/report/base_report.rb +8 -12
  108. data/lib/reek/report/code_climate/code_climate_configuration.rb +1 -1
  109. data/lib/reek/report/code_climate/code_climate_configuration.yml +6 -10
  110. data/lib/reek/report/documentation_link_warning_formatter.rb +17 -0
  111. data/lib/reek/report/heading_formatter.rb +54 -0
  112. data/lib/reek/report/json_report.rb +1 -1
  113. data/lib/reek/report/location_formatter.rb +40 -0
  114. data/lib/reek/report/progress_formatter.rb +79 -0
  115. data/lib/reek/report/simple_warning_formatter.rb +34 -0
  116. data/lib/reek/report/text_report.rb +1 -2
  117. data/lib/reek/report/xml_report.rb +3 -3
  118. data/lib/reek/report/yaml_report.rb +1 -1
  119. data/lib/reek/report.rb +15 -10
  120. data/lib/reek/smell_configuration.rb +2 -2
  121. data/lib/reek/smell_detectors/attribute.rb +5 -11
  122. data/lib/reek/smell_detectors/base_detector.rb +30 -26
  123. data/lib/reek/smell_detectors/boolean_parameter.rb +3 -5
  124. data/lib/reek/smell_detectors/class_variable.rb +5 -14
  125. data/lib/reek/smell_detectors/control_parameter.rb +18 -33
  126. data/lib/reek/smell_detectors/data_clump.rb +15 -9
  127. data/lib/reek/smell_detectors/duplicate_method_call.rb +23 -17
  128. data/lib/reek/smell_detectors/feature_envy.rb +9 -7
  129. data/lib/reek/smell_detectors/instance_variable_assumption.rb +14 -23
  130. data/lib/reek/smell_detectors/irresponsible_module.rb +5 -12
  131. data/lib/reek/smell_detectors/long_parameter_list.rb +10 -7
  132. data/lib/reek/smell_detectors/long_yield_list.rb +10 -7
  133. data/lib/reek/smell_detectors/manual_dispatch.rb +4 -5
  134. data/lib/reek/smell_detectors/{prima_donna_method.rb → missing_safe_method.rb} +20 -20
  135. data/lib/reek/smell_detectors/module_initialize.rb +3 -5
  136. data/lib/reek/smell_detectors/nested_iterators.rb +16 -24
  137. data/lib/reek/smell_detectors/nil_check.rb +8 -15
  138. data/lib/reek/smell_detectors/repeated_conditional.rb +13 -11
  139. data/lib/reek/smell_detectors/subclassed_from_core_class.rb +7 -8
  140. data/lib/reek/smell_detectors/too_many_constants.rb +10 -9
  141. data/lib/reek/smell_detectors/too_many_instance_variables.rb +10 -6
  142. data/lib/reek/smell_detectors/too_many_methods.rb +11 -7
  143. data/lib/reek/smell_detectors/too_many_statements.rb +10 -6
  144. data/lib/reek/smell_detectors/uncommunicative_method_name.rb +10 -11
  145. data/lib/reek/smell_detectors/uncommunicative_module_name.rb +14 -18
  146. data/lib/reek/smell_detectors/uncommunicative_parameter_name.rb +17 -22
  147. data/lib/reek/smell_detectors/uncommunicative_variable_name.rb +26 -27
  148. data/lib/reek/smell_detectors/unused_parameters.rb +4 -6
  149. data/lib/reek/smell_detectors/unused_private_method.rb +11 -18
  150. data/lib/reek/smell_detectors/utility_function.rb +12 -16
  151. data/lib/reek/smell_detectors.rb +1 -2
  152. data/lib/reek/smell_warning.rb +15 -8
  153. data/lib/reek/source/source_code.rb +57 -58
  154. data/lib/reek/source/source_locator.rb +8 -8
  155. data/lib/reek/spec/should_reek.rb +2 -2
  156. data/lib/reek/spec/should_reek_of.rb +9 -16
  157. data/lib/reek/spec/should_reek_only_of.rb +4 -4
  158. data/lib/reek/spec.rb +6 -6
  159. data/lib/reek/tree_dresser.rb +8 -8
  160. data/lib/reek/version.rb +1 -1
  161. data/reek.gemspec +5 -7
  162. data/samples/checkstyle.xml +1 -1
  163. data/samples/configuration/accepts_rejects_and_excludes_for_detectors.reek.yml +29 -0
  164. data/samples/configuration/accepts_rejects_and_excludes_for_directory_directives.reek.yml +30 -0
  165. data/samples/configuration/full_configuration.reek +8 -4
  166. data/samples/configuration/full_mask.reek +5 -4
  167. data/samples/configuration/partial_mask.reek +3 -2
  168. data/samples/configuration/regular_configuration/.reek.yml +4 -0
  169. data/samples/paths.rb +5 -4
  170. data/samples/source_with_hidden_directories/.hidden/hidden.rb +1 -0
  171. data/samples/source_with_hidden_directories/not_hidden.rb +1 -0
  172. data/spec/factories/factories.rb +2 -13
  173. data/spec/reek/ast/node_spec.rb +103 -10
  174. data/spec/reek/ast/reference_collector_spec.rb +1 -1
  175. data/spec/reek/ast/sexp_extensions_spec.rb +14 -34
  176. data/spec/reek/cli/application_spec.rb +52 -42
  177. data/spec/reek/cli/command/todo_list_command_spec.rb +6 -4
  178. data/spec/reek/cli/silencer_spec.rb +28 -0
  179. data/spec/reek/code_comment_spec.rb +31 -38
  180. data/spec/reek/configuration/app_configuration_spec.rb +46 -33
  181. data/spec/reek/configuration/configuration_file_finder_spec.rb +133 -51
  182. data/spec/reek/configuration/default_directive_spec.rb +1 -1
  183. data/spec/reek/configuration/directory_directives_spec.rb +6 -7
  184. data/spec/reek/configuration/excluded_paths_spec.rb +6 -6
  185. data/spec/reek/configuration/rake_task_converter_spec.rb +33 -0
  186. data/spec/reek/configuration/schema_validator_spec.rb +165 -0
  187. data/spec/reek/context/code_context_spec.rb +70 -106
  188. data/spec/reek/context/ghost_context_spec.rb +9 -9
  189. data/spec/reek/context/method_context_spec.rb +2 -2
  190. data/spec/reek/context/module_context_spec.rb +3 -3
  191. data/spec/reek/context/root_context_spec.rb +1 -1
  192. data/spec/reek/context_builder_spec.rb +20 -0
  193. data/spec/reek/documentation_link_spec.rb +20 -0
  194. data/spec/reek/errors/base_error_spec.rb +13 -0
  195. data/spec/reek/examiner_spec.rb +136 -29
  196. data/spec/reek/rake/task_spec.rb +25 -2
  197. data/spec/reek/report/code_climate/code_climate_fingerprint_spec.rb +82 -80
  198. data/spec/reek/report/code_climate/code_climate_formatter_spec.rb +6 -6
  199. data/spec/reek/report/json_report_spec.rb +13 -46
  200. data/spec/reek/report/{formatter/location_formatter_spec.rb → location_formatter_spec.rb} +5 -5
  201. data/spec/reek/report/{formatter/progress_formatter_spec.rb → progress_formatter_spec.rb} +4 -4
  202. data/spec/reek/report/text_report_spec.rb +4 -4
  203. data/spec/reek/report/xml_report_spec.rb +3 -3
  204. data/spec/reek/report/yaml_report_spec.rb +9 -38
  205. data/spec/reek/report_spec.rb +3 -3
  206. data/spec/reek/smell_detectors/base_detector_spec.rb +4 -5
  207. data/spec/reek/smell_detectors/boolean_parameter_spec.rb +2 -2
  208. data/spec/reek/smell_detectors/class_variable_spec.rb +26 -32
  209. data/spec/reek/smell_detectors/control_parameter_spec.rb +34 -4
  210. data/spec/reek/smell_detectors/duplicate_method_call_spec.rb +3 -3
  211. data/spec/reek/smell_detectors/feature_envy_spec.rb +47 -2
  212. data/spec/reek/smell_detectors/irresponsible_module_spec.rb +0 -11
  213. data/spec/reek/smell_detectors/{prima_donna_method_spec.rb → missing_safe_method_spec.rb} +10 -10
  214. data/spec/reek/smell_detectors/module_initialize_spec.rb +14 -0
  215. data/spec/reek/smell_detectors/nested_iterators_spec.rb +1 -1
  216. data/spec/reek/smell_detectors/too_many_constants_spec.rb +3 -3
  217. data/spec/reek/smell_detectors/too_many_instance_variables_spec.rb +1 -1
  218. data/spec/reek/smell_detectors/uncommunicative_method_name_spec.rb +6 -6
  219. data/spec/reek/smell_detectors/uncommunicative_module_name_spec.rb +6 -4
  220. data/spec/reek/smell_detectors/uncommunicative_parameter_name_spec.rb +36 -15
  221. data/spec/reek/smell_detectors/uncommunicative_variable_name_spec.rb +9 -9
  222. data/spec/reek/smell_detectors/unused_parameters_spec.rb +3 -3
  223. data/spec/reek/smell_detectors/unused_private_method_spec.rb +21 -10
  224. data/spec/reek/smell_detectors/utility_function_spec.rb +57 -5
  225. data/spec/reek/smell_warning_spec.rb +12 -8
  226. data/spec/reek/source/source_code_spec.rb +22 -21
  227. data/spec/reek/source/source_locator_spec.rb +17 -17
  228. data/spec/reek/spec/should_reek_of_spec.rb +25 -29
  229. data/spec/reek/spec/should_reek_only_of_spec.rb +2 -2
  230. data/spec/reek/spec/should_reek_spec.rb +8 -8
  231. data/spec/reek/spec/smell_matcher_spec.rb +23 -23
  232. data/spec/reek/tree_dresser_spec.rb +12 -17
  233. data/spec/spec_helper.rb +6 -17
  234. data/tasks/configuration.rake +8 -5
  235. metadata +74 -41
  236. data/defaults.reek +0 -131
  237. data/features/configuration_files/warn_about_multiple_configuration_files.feature +0 -44
  238. data/lib/reek/report/formatter/heading_formatter.rb +0 -52
  239. data/lib/reek/report/formatter/location_formatter.rb +0 -42
  240. data/lib/reek/report/formatter/progress_formatter.rb +0 -81
  241. data/lib/reek/report/formatter/simple_warning_formatter.rb +0 -35
  242. data/lib/reek/report/formatter/wiki_link_warning_formatter.rb +0 -36
  243. data/lib/reek/report/formatter.rb +0 -33
  244. data/lib/reek/smell_detectors/syntax.rb +0 -37
  245. data/samples/configuration/non_public_modifiers_mask.reek +0 -3
  246. data/samples/smelly_with_inline_mask.rb +0 -8
  247. data/samples/smelly_with_modifiers.rb +0 -12
  248. data/samples/source_with_hidden_directories/.hidden/uncommunicative_method_name.rb +0 -5
  249. data/samples/source_with_non_ruby_files/uncommunicative_parameter_name.rb +0 -6
  250. data/spec/reek/smell_detectors/syntax_spec.rb +0 -17
  251. /data/{samples/configuration/more_than_one_configuration_file/regular.reek → .reek.yml} +0 -0
  252. /data/samples/{clean.rb → clean_source/clean.rb} +0 -0
  253. /data/samples/{exceptions.reek → configuration/home/home.reek.yml} +0 -0
  254. /data/samples/configuration/{more_than_one_configuration_file/todo.reek → regular_configuration/empty_sub_directory/.gitignore} +0 -0
  255. /data/samples/{configuration/single_configuration_file/.reek → no_config_file/.keep} +0 -0
  256. /data/samples/{inline.rb → smelly_source/inline.rb} +0 -0
  257. /data/samples/{optparse.rb → smelly_source/optparse.rb} +0 -0
  258. /data/samples/{redcloth.rb → smelly_source/redcloth.rb} +0 -0
  259. /data/samples/{smelly.rb → smelly_source/smelly.rb} +0 -0
  260. /data/samples/{source_with_hidden_directories/uncommunicative_parameter_name.rb → source_with_non_ruby_files/ruby.rb} +0 -0
@@ -6,8 +6,8 @@ When /^I pass "([^\"]*)" to reek *(.*)$/ do |stdin, args|
6
6
  reek_with_pipe(stdin, args)
7
7
  end
8
8
 
9
- When /^I run rake (\w*) with:$/ do |name, task_def|
10
- rake(name, task_def)
9
+ When /^I pass a stdin to reek *(.*) with:$/ do |args, stdin|
10
+ reek_with_pipe(stdin, args)
11
11
  end
12
12
 
13
13
  Then /^it reports nothing$/ do
@@ -1,7 +1,7 @@
1
1
  require_relative '../../samples/paths'
2
2
 
3
3
  Given(/^the smelly file '(.+)'$/) do |filename|
4
- write_file(filename, SAMPLES_PATH.join(filename).read)
4
+ write_file(filename, SAMPLES_DIR.join('smelly_source').join(filename).read)
5
5
  end
6
6
 
7
7
  Given(/^the clean file 'clean.rb'$/) do
@@ -28,13 +28,18 @@ Given(/^a directory called 'smelly' containing two smelly files$/) do
28
28
  end
29
29
 
30
30
  Given(/^the smelly file '(.+)' in a subdirectory$/) do |filename|
31
- contents = SAMPLES_PATH.join(filename).read
31
+ contents = SAMPLES_DIR.join('smelly_source').join(filename).read
32
32
 
33
33
  write_file("subdir/#{filename}", contents)
34
34
  end
35
35
 
36
36
  Given(/^a configuration file '(.+)'$/) do |filename|
37
- write_file(filename, CONFIG_PATH.join(filename).read)
37
+ write_file(filename, CONFIGURATION_DIR.join(filename).read)
38
+ end
39
+
40
+ Given(/^our default configuration file$/) do
41
+ default_configuration = File.read SAMPLES_DIR.join('..').join('docs').join('defaults.reek.yml')
42
+ write_file('defaults.reek', default_configuration)
38
43
  end
39
44
 
40
45
  When(/^I run "reek (.*?)" in a subdirectory$/) do |args|
@@ -44,7 +49,7 @@ When(/^I run "reek (.*?)" in a subdirectory$/) do |args|
44
49
  end
45
50
 
46
51
  Given(/^a configuration file '(.+)' in a subdirectory$/) do |filename|
47
- contents = CONFIG_PATH.join(filename).read
52
+ contents = CONFIGURATION_DIR.join(filename).read
48
53
 
49
54
  write_file("subdir/#{filename}", contents)
50
55
  end
@@ -13,23 +13,14 @@ end
13
13
  #
14
14
  class ReekWorld
15
15
  def reek(args)
16
- run_simple("reek --no-color #{args}", false)
16
+ run_simple("reek --no-color --no-documentation #{args}", false)
17
17
  end
18
18
 
19
19
  def reek_with_pipe(stdin, args)
20
- run "reek --no-color #{args}"
20
+ run "reek --no-color --no-documentation #{args}"
21
21
  type(stdin)
22
22
  close_input
23
23
  end
24
-
25
- def rake(name, task_def)
26
- header = <<-EOS.strip_heredoc
27
- require 'reek/rake/task'
28
-
29
- EOS
30
- write_file 'Rakefile', header + task_def
31
- run_simple("rake #{name}", false)
32
- end
33
24
  end
34
25
 
35
26
  World do
@@ -15,8 +15,8 @@ Feature: Auto-generate a todo file
15
15
  And it reports:
16
16
  """
17
17
  smelly.rb -- 2 warnings:
18
- [4]:UncommunicativeMethodName: Smelly#x has the name 'x' [https://github.com/troessner/reek/blob/master/docs/Uncommunicative-Method-Name.md]
19
- [5]:UncommunicativeVariableName: Smelly#x has the variable name 'y' [https://github.com/troessner/reek/blob/master/docs/Uncommunicative-Variable-Name.md]
18
+ [4]:UncommunicativeMethodName: Smelly#x has the name 'x'
19
+ [5]:UncommunicativeVariableName: Smelly#x has the variable name 'y'
20
20
  """
21
21
  When I run reek --todo smelly.rb
22
22
  Then it succeeds
@@ -29,13 +29,14 @@ Feature: Auto-generate a todo file
29
29
  And the file ".todo.reek" should contain:
30
30
  """
31
31
  ---
32
- UncommunicativeMethodName:
33
- exclude:
34
- - Smelly#x
35
- UncommunicativeVariableName:
36
- exclude:
37
- - Smelly#x
38
- """
32
+ detectors:
33
+ UncommunicativeMethodName:
34
+ exclude:
35
+ - Smelly#x
36
+ UncommunicativeVariableName:
37
+ exclude:
38
+ - Smelly#x
39
+ """
39
40
  When I run reek -c .todo.reek smelly.rb
40
41
  Then it succeeds
41
42
 
@@ -54,20 +55,21 @@ Feature: Auto-generate a todo file
54
55
  And a file named ".todo.reek" with:
55
56
  """
56
57
  ---
57
- # smelly.rb reeks of UncommunicativeMethodName and UncommunicativeVariableName
58
- # so the configuration below will partially mask this
59
- UncommunicativeMethodName:
60
- enabled: false
58
+ detectors:
59
+ # smelly.rb reeks of UncommunicativeMethodName and UncommunicativeVariableName
60
+ # so the configuration below will partially mask this
61
+ UncommunicativeMethodName:
62
+ enabled: false
61
63
  """
62
- When I run `reek -c .todo.reek smelly.rb`
64
+ When I run reek -c .todo.reek smelly.rb
63
65
  Then it reports:
64
66
  """
65
67
  smelly.rb -- 1 warning:
66
- [5]:UncommunicativeVariableName: Smelly#x has the variable name 'y' [https://github.com/troessner/reek/blob/master/docs/Uncommunicative-Variable-Name.md]
68
+ [5]:UncommunicativeVariableName: Smelly#x has the variable name 'y'
67
69
  """
68
- When I run `reek --todo smelly.rb`
70
+ When I run reek --todo smelly.rb
69
71
  Then it succeeds
70
- When I run `reek -c .todo.reek smelly.rb`
72
+ When I run reek -c .todo.reek smelly.rb
71
73
  Then it reports nothing
72
74
 
73
75
  Scenario: Ignore existing other configuration files that are passed explicitly
@@ -77,16 +79,17 @@ Feature: Auto-generate a todo file
77
79
  ---
78
80
  # smelly.rb reeks of UncommunicativeMethodName and UncommunicativeVariableName
79
81
  # so the configuration below will partially mask this
80
- UncommunicativeMethodName:
81
- enabled: false
82
+ detectors:
83
+ UncommunicativeMethodName:
84
+ enabled: false
82
85
  """
83
- When I run `reek -c config.reek smelly.rb`
86
+ When I run reek -c config.reek smelly.rb
84
87
  Then it reports:
85
88
  """
86
89
  smelly.rb -- 1 warning:
87
- [5]:UncommunicativeVariableName: Smelly#x has the variable name 'y' [https://github.com/troessner/reek/blob/master/docs/Uncommunicative-Variable-Name.md]
90
+ [5]:UncommunicativeVariableName: Smelly#x has the variable name 'y'
88
91
  """
89
- When I run `reek -c config.reek --todo smelly.rb`
92
+ When I run reek -c config.reek --todo smelly.rb
90
93
  Then it succeeds
91
- When I run `reek -c .todo.reek smelly.rb`
94
+ When I run reek -c .todo.reek smelly.rb
92
95
  Then it reports nothing
@@ -6,7 +6,7 @@ module Reek
6
6
  # An AST Builder for ruby parser.
7
7
  #
8
8
  class Builder < ::Parser::Builders::Default
9
- # This is a work around for parsing ruby code that has a string with invliad sequence as UTF-8.
9
+ # This is a work around for parsing ruby code that has a string with invalid sequence as UTF-8.
10
10
  # See. https://github.com/whitequark/parser/issues/283
11
11
  def string_value(token)
12
12
  value(token)
data/lib/reek/ast/node.rb CHANGED
@@ -1,10 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative '../cli/silencer'
4
-
5
- Reek::CLI::Silencer.silently do
6
- require 'parser'
7
- end
4
+ Reek::CLI::Silencer.without_warnings { require 'parser' }
8
5
 
9
6
  module Reek
10
7
  module AST
@@ -12,11 +9,8 @@ module Reek
12
9
  # methods to ease the transition from Sexp to AST::Node.
13
10
  #
14
11
  class Node < ::Parser::AST::Node
15
- attr_reader :parent
16
-
17
12
  def initialize(type, children = [], options = {})
18
13
  @comments = options.fetch(:comments, [])
19
- @parent = options.fetch(:parent, nil)
20
14
  super
21
15
  end
22
16
 
@@ -36,59 +30,47 @@ module Reek
36
30
  loc && loc.line
37
31
  end
38
32
 
33
+ def name
34
+ to_s
35
+ end
36
+
39
37
  #
40
- # Carries out a depth-first traversal of this syntax tree, yielding
41
- # every Sexp of type `target_type`. The traversal ignores any node
42
- # whose type is listed in the Array `ignoring`.
43
- # Takes a block as well.
44
- #
45
- # target_type - the type to look for, e.g. :send, :block
46
- # ignoring - types to ignore, e.g. [:casgn, :class, :module]
47
- # blk - block to execute for every hit. Gets passed in the matching element itself.
38
+ # Carries out a depth-first traversal of this syntax tree, yielding every
39
+ # Sexp of the searched for type or types. The traversal stops at any node
40
+ # whose type is listed in `ignoring`.
48
41
  #
49
- # Examples:
50
- # context.each_node(:send, [:mlhs]) do |call_node| .... end
51
- # context.each_node(:lvar).any? { |it| it.var_name == 'something' }
42
+ # If a type is searched for *and* listed in ignoring, it will be yielded
43
+ # but traversal will not continue to its children.
52
44
  #
53
- # Returns an array with all matching nodes.
54
- # TODO: without a block, this doesn't do what one might expect
55
- def each_node(target_type, ignoring = [], &blk)
56
- if block_given?
57
- look_for_type(target_type, ignoring, &blk)
58
- else
59
- result = []
60
- look_for_type(target_type, ignoring) { |exp| result << exp }
61
- result
62
- end
63
- end
64
-
45
+ # If the root's type is ignored, traversal does not stop, unless the root
46
+ # is of a target type.
65
47
  #
66
- # Carries out a depth-first traversal of this syntax tree, yielding
67
- # every Sexp of type `target_type`. The traversal ignores any node
68
- # whose type is listed in the Array `ignoring`, including the top node.
69
48
  # Takes a block as well.
70
49
  #
71
- # target_types - the types to look for, e.g. [:send, :block]
72
- # ignoring - types to ignore, e.g. [:casgn, :class, :module]
73
- # blk - block to execute for every hit
50
+ # @param target_types [Symbol, Array<Symbol>] the type or types to look
51
+ # for
52
+ # @param ignoring [Array<Symbol>] types to ignore
53
+ # @param blk block to execute for every hit. Gets passed in the
54
+ # matching element itself.
74
55
  #
75
- # Examples:
76
- # exp.find_nodes([:block]).flat_map do |elem| ... end
56
+ # @example
57
+ # node.each_node(:send, [:mlhs]) do |call_node| .... end
58
+ # node.each_node(:lvar).any? { |it| it.var_name == 'something' }
59
+ # node.each_node([:block]).flat_map do |elem| ... end
77
60
  #
78
61
  # Returns an array with all matching nodes.
79
- def find_nodes(target_types, ignoring = [])
80
- result = []
81
- look_for_types(target_types, ignoring) { |exp| result << exp }
82
- result
62
+ def each_node(target_types, ignoring = [], &blk)
63
+ return enum_for(:each_node, target_types, ignoring) unless block_given?
64
+
65
+ look_for(Array(target_types), ignoring, &blk)
83
66
  end
84
67
 
85
68
  def contains_nested_node?(target_type)
86
- look_for_type(target_type) { |_elem| return true }
87
- false
69
+ each_node(target_type).any?
88
70
  end
89
71
 
90
- # :reek:DuplicateMethodCall { max_calls: 2 } is ok for lines.first
91
- # :reek:FeatureEnvy
72
+ # @quality :reek:DuplicateMethodCall { max_calls: 2 } is ok for lines.first
73
+ # @quality :reek:FeatureEnvy
92
74
  def format_to_ruby
93
75
  if location
94
76
  lines = location.expression.source.split("\n").map(&:strip)
@@ -123,22 +105,22 @@ module Reek
123
105
  protected
124
106
 
125
107
  # See ".each_node" for documentation.
126
- def look_for_type(target_type, ignoring = [], &blk)
108
+ def look_for(target_types, ignoring, &blk)
109
+ if target_types.include? type
110
+ yield self
111
+ return if ignoring.include?(type)
112
+ end
127
113
  each_sexp do |elem|
128
- elem.look_for_type(target_type, ignoring, &blk) unless ignoring.include?(elem.type)
114
+ elem.look_for_recurse(target_types, ignoring, &blk)
129
115
  end
130
- yield self if type == target_type
131
116
  end
132
117
 
133
- # See ".find_nodes" for documentation.
134
- def look_for_types(target_types, ignoring = [], &blk)
135
- return if ignoring.include?(type)
136
- if target_types.include? type
137
- yield self
138
- else
139
- each_sexp do |elem|
140
- elem.look_for_types(target_types, ignoring, &blk)
141
- end
118
+ # See ".each_node" for documentation.
119
+ def look_for_recurse(target_types, ignoring, &blk)
120
+ yield self if target_types.include? type
121
+ return if ignoring.include? type
122
+ each_sexp do |elem|
123
+ elem.look_for_recurse(target_types, ignoring, &blk)
142
124
  end
143
125
  end
144
126
 
@@ -50,7 +50,7 @@ module Reek
50
50
  end
51
51
 
52
52
  def self_is_max?
53
- refs.empty? || most_popular.keys.include?(:self)
53
+ refs.empty? || most_popular.key?(:self)
54
54
  end
55
55
 
56
56
  private
@@ -12,7 +12,7 @@ module Reek
12
12
  end
13
13
 
14
14
  def num_refs_to_self
15
- (explicit_self_calls + implicit_self_calls).size
15
+ (explicit_self_calls.to_a + implicit_self_calls.to_a).size
16
16
  end
17
17
 
18
18
  private
@@ -20,9 +20,7 @@ module Reek
20
20
  attr_reader :ast
21
21
 
22
22
  def explicit_self_calls
23
- [:self, :super, :zsuper, :ivar, :ivasgn].flat_map do |node_type|
24
- ast.each_node(node_type)
25
- end
23
+ ast.each_node([:self, :super, :zsuper, :ivar, :ivasgn])
26
24
  end
27
25
 
28
26
  def implicit_self_calls
@@ -10,7 +10,7 @@ module Reek
10
10
  end
11
11
 
12
12
  def body_nodes(type, ignoring = [])
13
- children[1..-1].compact.flat_map { |child| child.find_nodes(type, ignoring) }
13
+ children[1..-1].compact.flat_map { |child| child.each_node(type, ignoring).to_a }
14
14
  end
15
15
 
16
16
  def else_body
@@ -9,8 +9,15 @@ module Reek
9
9
  children.first
10
10
  end
11
11
 
12
+ # @quality :reek:FeatureEnvy
12
13
  def body_nodes(type, ignoring = [])
13
- children[1..-1].compact.flat_map { |child| child.find_nodes(type, ignoring) }
14
+ children[1..-1].compact.flat_map do |child|
15
+ if ignoring.include? child.type
16
+ []
17
+ else
18
+ child.each_node(type, ignoring).to_a
19
+ end
20
+ end
14
21
  end
15
22
  end
16
23
  end
@@ -10,7 +10,7 @@ module Reek
10
10
  end
11
11
 
12
12
  def body_nodes(type, ignoring = [])
13
- children[1].find_nodes type, ignoring
13
+ children[1].each_node type, ignoring
14
14
  end
15
15
  end
16
16
 
@@ -30,11 +30,9 @@ module Reek
30
30
  end
31
31
 
32
32
  def body_nodes(types, ignoring = [])
33
- if body
34
- body.find_nodes(types, ignoring)
35
- else
36
- []
37
- end
33
+ return [] unless body
34
+ return [] if ignoring.include?(body.type)
35
+ body.each_node(types, ignoring | types)
38
36
  end
39
37
  end
40
38
 
@@ -59,7 +57,7 @@ module Reek
59
57
  end
60
58
 
61
59
  def depends_on_instance?
62
- ReferenceCollector.new(self).num_refs_to_self > 0
60
+ ReferenceCollector.new(self).num_refs_to_self.positive?
63
61
  end
64
62
  end
65
63
 
@@ -23,10 +23,6 @@ module Reek
23
23
  ([receiver] + args).compact
24
24
  end
25
25
 
26
- def arg_names
27
- args.map { |arg| arg.children.first }
28
- end
29
-
30
26
  def module_creation_call?
31
27
  object_creation_call? && module_creation_receiver?
32
28
  end
@@ -6,6 +6,7 @@ require_relative '../configuration/app_configuration'
6
6
  require_relative '../source/source_locator'
7
7
  require_relative 'command/report_command'
8
8
  require_relative 'command/todo_list_command'
9
+ require_relative '../errors/config_file_error'
9
10
 
10
11
  module Reek
11
12
  module CLI
@@ -42,7 +43,7 @@ module Reek
42
43
 
43
44
  def configure_app_configuration(config_file)
44
45
  Configuration::AppConfiguration.from_path(config_file)
45
- rescue Reek::Configuration::ConfigFileException => error
46
+ rescue Errors::ConfigFileError => error
46
47
  warn "Error: #{error}"
47
48
  exit Status::DEFAULT_ERROR_EXIT_CODE
48
49
  end
@@ -68,7 +69,7 @@ module Reek
68
69
  options.argv
69
70
  end
70
71
 
71
- # :reek:UtilityFunction
72
+ # @quality :reek:UtilityFunction
72
73
  def input_was_piped?
73
74
  !$stdin.tty?
74
75
  end
@@ -88,7 +89,7 @@ module Reek
88
89
  end
89
90
 
90
91
  def source_from_pipe
91
- [$stdin]
92
+ [Source::SourceCode.from($stdin, origin: options.stdin_filename)]
92
93
  end
93
94
 
94
95
  def disable_progress_output_unless_verbose
@@ -38,7 +38,6 @@ module Reek
38
38
  @reporter ||=
39
39
  report_class.new(
40
40
  warning_formatter: warning_formatter,
41
- report_formatter: Report::Formatter,
42
41
  sort_by_issue_count: sort_by_issue_count,
43
42
  heading_formatter: heading_formatter,
44
43
  progress_formatter: progress_formatter.new(sources.length))
@@ -53,7 +52,7 @@ module Reek
53
52
  end
54
53
 
55
54
  def warning_formatter_class
56
- Report.warning_formatter_class(options.show_links ? :wiki_links : :simple)
55
+ Report.warning_formatter_class(options.show_links ? :documentation_links : :simple)
57
56
  end
58
57
 
59
58
  def location_formatter
@@ -2,6 +2,7 @@
2
2
 
3
3
  require_relative 'base_command'
4
4
  require_relative '../../examiner'
5
+ require_relative '../../configuration/app_configuration'
5
6
 
6
7
  module Reek
7
8
  module CLI
@@ -11,15 +12,15 @@ module Reek
11
12
  # file that can serve as a todo list.
12
13
  #
13
14
  class TodoListCommand < BaseCommand
14
- FILE_NAME = '.todo.reek'.freeze
15
+ FILE_NAME = '.todo.reek'
15
16
 
16
17
  def execute
17
- smells = scan_for_smells
18
18
  if smells.empty?
19
19
  puts "\n'.todo.reek' not generated because "\
20
20
  'there were no smells found!'
21
21
  else
22
- File.write FILE_NAME, groups_for(smells).to_yaml
22
+ File.write FILE_NAME,
23
+ { Configuration::AppConfiguration::DETECTORS_KEY => groups }.to_yaml
23
24
  puts "\n'.todo.reek' generated! You can now use "\
24
25
  'this as a starting point for your configuration.'
25
26
  end
@@ -28,14 +29,13 @@ module Reek
28
29
 
29
30
  private
30
31
 
31
- def scan_for_smells
32
- sources.map do |source|
33
- Examiner.new(source,
34
- filter_by_smells: smell_names)
32
+ def smells
33
+ @smells ||= sources.map do |source|
34
+ Examiner.new(source, filter_by_smells: smell_names)
35
35
  end.map(&:smells).flatten
36
36
  end
37
37
 
38
- def groups_for(smells)
38
+ def groups
39
39
  @groups ||=
40
40
  begin
41
41
  todos = smells.group_by(&:smell_class).map do |smell_class, smells_for_class|
@@ -4,6 +4,8 @@ require 'optparse'
4
4
  require 'rainbow'
5
5
  require_relative '../version'
6
6
  require_relative 'status'
7
+ require_relative '../detector_repository'
8
+ require_relative '../documentation_link'
7
9
 
8
10
  module Reek
9
11
  module CLI
@@ -12,9 +14,9 @@ module Reek
12
14
  #
13
15
  # See {file:docs/Command-Line-Options.md} for details.
14
16
  #
15
- # :reek:TooManyInstanceVariables: { max_instance_variables: 12 }
16
- # :reek:TooManyMethods: { max_methods: 18 }
17
- # :reek:Attribute: { enabled: false }
17
+ # @quality :reek:TooManyInstanceVariables { max_instance_variables: 12 }
18
+ # @quality :reek:TooManyMethods { max_methods: 18 }
19
+ # @quality :reek:Attribute { enabled: false }
18
20
  #
19
21
  class Options
20
22
  attr_reader :argv, :parser, :smells_to_detect
@@ -26,6 +28,7 @@ module Reek
26
28
  :show_empty,
27
29
  :show_links,
28
30
  :sorting,
31
+ :stdin_filename,
29
32
  :success_exit_code,
30
33
  :failure_exit_code,
31
34
  :generate_todo_list,
@@ -64,12 +67,12 @@ module Reek
64
67
  # processing by a machine, but will be viewed by a human. This means
65
68
  # features like coloring can be safely enabled by default.
66
69
  #
67
- # :reek:UtilityFunction
70
+ # @quality :reek:UtilityFunction
68
71
  def tty_output?
69
72
  $stdout.tty?
70
73
  end
71
74
 
72
- # :reek:TooManyStatements: { max_statements: 7 }
75
+ # @quality :reek:TooManyStatements { max_statements: 7 }
73
76
  def set_up_parser
74
77
  set_banner
75
78
  set_configuration_options
@@ -82,7 +85,7 @@ module Reek
82
85
 
83
86
  def set_banner
84
87
  program_name = parser.program_name
85
- parser.banner = <<-EOB.gsub(/^[ ]+/, '')
88
+ parser.banner = <<-BANNER.gsub(/^[ ]+/, '')
86
89
  Usage: #{program_name} [options] [files]
87
90
 
88
91
  Examples:
@@ -91,12 +94,12 @@ module Reek
91
94
  #{program_name} -s lib
92
95
  cat my_class.rb | #{program_name}
93
96
 
94
- See https://wiki.github.com/troessner/reek for detailed help.
97
+ See https://github.com/troessner/reek for detailed help.
95
98
 
96
- EOB
99
+ BANNER
97
100
  end
98
101
 
99
- # :reek:TooManyStatements: { max_statements: 6 }
102
+ # @quality :reek:TooManyStatements { max_statements: 7 }
100
103
  def set_configuration_options
101
104
  parser.separator 'Configuration:'
102
105
  parser.on('-c', '--config FILE', 'Read configuration options from FILE') do |file|
@@ -104,11 +107,16 @@ module Reek
104
107
  end
105
108
  parser.on('--smell SMELL',
106
109
  'Only look for a specific smell.',
107
- 'Call it like this: reek --smell PrimaDonnaMethod source.rb',
108
- 'Check out https://github.com/troessner/reek/blob/master/docs/Code-Smells.md '\
110
+ 'Call it like this: reek --smell MissingSafeMethod source.rb',
111
+ "Check out #{DocumentationLink.build('Code Smells')} "\
109
112
  'for a list of smells') do |smell|
110
113
  smells_to_detect << smell
111
114
  end
115
+ parser.on('--stdin-filename FILE',
116
+ 'When passing code in via pipe, assume this filename when '\
117
+ 'checking file or directory rules in the config.') do |file|
118
+ self.stdin_filename = file
119
+ end
112
120
  end
113
121
 
114
122
  def set_generate_todo_list_options
@@ -128,7 +136,7 @@ module Reek
128
136
  end
129
137
  end
130
138
 
131
- # :reek:TooManyStatements: { max_statements: 7 }
139
+ # @quality :reek:TooManyStatements { max_statements: 7 }
132
140
  def set_report_formatting_options
133
141
  parser.separator "\nText format options:"
134
142
  set_up_color_option
@@ -150,8 +158,8 @@ module Reek
150
158
  'Show headings for smell-free source files (default: false)') do |show_empty|
151
159
  self.show_empty = show_empty
152
160
  end
153
- parser.on('-U', '--[no-]wiki-links',
154
- 'Show link to related wiki page for each smell (default: true)') do |show_links|
161
+ parser.on('-U', '--[no-]documentation',
162
+ 'Show link to related documentation page for each smell (default: true)') do |show_links|
155
163
  self.show_links = show_links
156
164
  end
157
165
  end
@@ -191,7 +199,7 @@ module Reek
191
199
  end
192
200
  end
193
201
 
194
- # :reek:DuplicateMethodCall: { max_calls: 2 }
202
+ # @quality :reek:DuplicateMethodCall { max_calls: 2 }
195
203
  def set_exit_codes
196
204
  parser.separator "\nExit codes:"
197
205
  parser.on('--success-exit-code CODE',
@@ -206,13 +214,20 @@ module Reek
206
214
  end
207
215
  end
208
216
 
209
- # :reek:TooManyStatements: { max_statements: 7 }
217
+ # @quality :reek:TooManyStatements { max_statements: 12 }
210
218
  def set_utility_options
211
219
  parser.separator "\nUtility options:"
212
220
  parser.on_tail('-h', '--help', 'Show this message') do
213
221
  puts parser
214
222
  exit
215
223
  end
224
+ parser.on_tail('-l', '--list', 'List all available smell detectors') do
225
+ puts "All available smell detectors:\n\n"
226
+ puts DetectorRepository.available_detector_names
227
+ puts "\nCheck out #{DocumentationLink.build('Code Smells')} "\
228
+ 'for a details on each detector'
229
+ exit
230
+ end
216
231
  parser.on_tail('-v', '--version', 'Show version') do
217
232
  puts "#{parser.program_name} #{Reek::Version::STRING}\n"
218
233
  exit