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
@@ -6,6 +6,10 @@ When /^I pass "([^\"]*)" to reek *(.*)$/ do |stdin, args|
6
6
  reek_with_pipe(stdin, args)
7
7
  end
8
8
 
9
+ When /^I pass a stdin to reek *(.*) with:$/ do |args, stdin|
10
+ reek_with_pipe(stdin, args)
11
+ end
12
+
9
13
  Then /^it reports nothing$/ do
10
14
  expect(last_command_started).to have_output_on_stdout('')
11
15
  end
@@ -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,11 +13,11 @@ 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
@@ -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
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
@@ -33,59 +30,47 @@ module Reek
33
30
  loc && loc.line
34
31
  end
35
32
 
33
+ def name
34
+ to_s
35
+ end
36
+
36
37
  #
37
- # Carries out a depth-first traversal of this syntax tree, yielding
38
- # every Sexp of type `target_type`. The traversal ignores any node
39
- # whose type is listed in the Array `ignoring`.
40
- # Takes a block as well.
41
- #
42
- # target_type - the type to look for, e.g. :send, :block
43
- # ignoring - types to ignore, e.g. [:casgn, :class, :module]
44
- # 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`.
45
41
  #
46
- # Examples:
47
- # context.each_node(:send, [:mlhs]) do |call_node| .... end
48
- # 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.
49
44
  #
50
- # Returns an array with all matching nodes.
51
- # TODO: without a block, this doesn't do what one might expect
52
- def each_node(target_type, ignoring = [], &blk)
53
- if block_given?
54
- look_for_type(target_type, ignoring, &blk)
55
- else
56
- result = []
57
- look_for_type(target_type, ignoring) { |exp| result << exp }
58
- result
59
- end
60
- end
61
-
45
+ # If the root's type is ignored, traversal does not stop, unless the root
46
+ # is of a target type.
62
47
  #
63
- # Carries out a depth-first traversal of this syntax tree, yielding
64
- # every Sexp of type `target_type`. The traversal ignores any node
65
- # whose type is listed in the Array `ignoring`, including the top node.
66
48
  # Takes a block as well.
67
49
  #
68
- # target_types - the types to look for, e.g. [:send, :block]
69
- # ignoring - types to ignore, e.g. [:casgn, :class, :module]
70
- # 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.
71
55
  #
72
- # Examples:
73
- # 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
74
60
  #
75
61
  # Returns an array with all matching nodes.
76
- def find_nodes(target_types, ignoring = [])
77
- result = []
78
- look_for_types(target_types, ignoring) { |exp| result << exp }
79
- 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)
80
66
  end
81
67
 
82
68
  def contains_nested_node?(target_type)
83
- look_for_type(target_type) { |_elem| return true }
84
- false
69
+ each_node(target_type).any?
85
70
  end
86
71
 
87
- # :reek:DuplicateMethodCall { max_calls: 2 } is ok for lines.first
88
- # :reek:FeatureEnvy
72
+ # @quality :reek:DuplicateMethodCall { max_calls: 2 } is ok for lines.first
73
+ # @quality :reek:FeatureEnvy
89
74
  def format_to_ruby
90
75
  if location
91
76
  lines = location.expression.source.split("\n").map(&:strip)
@@ -120,22 +105,22 @@ module Reek
120
105
  protected
121
106
 
122
107
  # See ".each_node" for documentation.
123
- 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
124
113
  each_sexp do |elem|
125
- elem.look_for_type(target_type, ignoring, &blk) unless ignoring.include?(elem.type)
114
+ elem.look_for_recurse(target_types, ignoring, &blk)
126
115
  end
127
- yield self if type == target_type
128
116
  end
129
117
 
130
- # See ".find_nodes" for documentation.
131
- def look_for_types(target_types, ignoring = [], &blk)
132
- return if ignoring.include?(type)
133
- if target_types.include? type
134
- yield self
135
- else
136
- each_sexp do |elem|
137
- elem.look_for_types(target_types, ignoring, &blk)
138
- 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)
139
124
  end
140
125
  end
141
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
 
@@ -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
@@ -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
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
@@ -8,17 +8,28 @@ module Reek
8
8
  module Silencer
9
9
  module_function
10
10
 
11
- # :reek:TooManyStatements: { max_statements: 7 }
11
+ # @quality :reek:TooManyStatements { max_statements: 9 }
12
12
  def silently
13
13
  old_verbose = $VERBOSE
14
+ old_stderr = $stderr
15
+ old_stdout = $stdout
16
+
14
17
  $VERBOSE = false
15
18
  $stderr = StringIO.new
16
19
  $stdout = StringIO.new
17
20
  yield
18
21
  ensure
19
22
  $VERBOSE = old_verbose
20
- $stderr = STDERR
21
- $stdout = STDOUT
23
+ $stderr = old_stderr
24
+ $stdout = old_stdout
25
+ end
26
+
27
+ def without_warnings
28
+ old_verbose = $VERBOSE
29
+ $VERBOSE = false
30
+ yield
31
+ ensure
32
+ $VERBOSE = old_verbose
22
33
  end
23
34
  end
24
35
  end