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
@@ -1,34 +1,35 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'base_error'
4
+ require_relative '../documentation_link'
4
5
 
5
6
  module Reek
6
7
  module Errors
7
8
  # Gets raised when trying to use a configuration for a detector
8
9
  # that can't be parsed into a hash.
9
10
  class GarbageDetectorConfigurationInCommentError < BaseError
10
- BAD_DETECTOR_CONFIGURATION_MESSAGE = <<-MESSAGE.freeze
11
+ BAD_DETECTOR_CONFIGURATION_MESSAGE = <<-MESSAGE
11
12
 
12
- Error: You are trying to configure the smell detector '%s'.
13
+ Error: You are trying to configure the smell detector '%<detector>s'.
13
14
  Unfortunately we cannot parse the configuration you have given.
14
- The source is '%s' and the comment belongs to the expression starting in line %d.
15
+ The source is '%<source>s' and the comment belongs to the expression starting in line %<line>d.
15
16
  Here's the original comment:
16
17
 
17
- %s
18
+ %<comment>s
18
19
 
19
20
  Please see the Reek docs for:
20
- * how to configure Reek via source code comments: https://github.com/troessner/reek/blob/master/docs/Smell-Suppression.md
21
- * what smell detectors are available: https://github.com/troessner/reek/blob/master/docs/Code-Smells.md
21
+ * how to configure Reek via source code comments: #{DocumentationLink.build('Smell Suppression')}
22
+ * what smell detectors are available: #{DocumentationLink.build('Code Smells')}
22
23
  Update the offensive comment (or remove it if no longer applicable) and re-run Reek.
23
24
 
24
25
  MESSAGE
25
26
 
26
27
  def initialize(detector_name:, source:, line:, original_comment:)
27
28
  message = format(BAD_DETECTOR_CONFIGURATION_MESSAGE,
28
- detector_name,
29
- source,
30
- line,
31
- original_comment)
29
+ detector: detector_name,
30
+ source: source,
31
+ line: line,
32
+ comment: original_comment)
32
33
  super message
33
34
  end
34
35
  end
@@ -6,42 +6,40 @@ module Reek
6
6
  module Errors
7
7
  # Gets raised when Reek is unable to process the source
8
8
  class IncomprehensibleSourceError < BaseError
9
- INCOMPREHENSIBLE_SOURCE_TEMPLATE = <<-MESSAGE.freeze
10
- !!!
11
- Source %s cannot be processed by Reek.
9
+ TEMPLATE = 'Source %<source>s cannot be processed by Reek.'
12
10
 
13
- This is most likely either a problem in your Reek configuration (config file or
14
- source code comments) or a Reek bug.
11
+ LONG_TEMPLATE = <<-MESSAGE
12
+ !!!
13
+ %<message>s
15
14
 
16
- Please double check your Reek configuration taking the original exception
17
- below into account - you might have misspelled a smell detector for instance.
18
- (In the future Reek will handle configuration errors more gracefully, something
19
- we are working on already).
15
+ This is most likely a Reek bug.
20
16
 
21
- If you feel that this is not a problem with your Reek configuration but with
22
- Reek itself it would be great if you could report this back to the Reek
23
- team by opening up a corresponding issue at https://github.com/troessner/reek/issues.
17
+ It would be great if you could report this back to the Reek team by opening a
18
+ corresponding issue at https://github.com/troessner/reek/issues.
24
19
 
25
- Please make sure to include the source in question, the Reek version
26
- and the original exception below.
20
+ Please make sure to include the source in question, the Reek version and the
21
+ original exception below.
27
22
 
28
23
  Exception message:
29
24
 
30
- %s
25
+ %<exception>s
31
26
 
32
27
  Original exception:
33
28
 
34
- %s
29
+ %<original>s
35
30
 
36
31
  !!!
37
32
  MESSAGE
38
33
 
39
- def initialize(origin:, original_exception:)
40
- message = format(INCOMPREHENSIBLE_SOURCE_TEMPLATE,
41
- origin,
42
- original_exception.message,
43
- original_exception.backtrace.join("\n\t"))
44
- super message
34
+ def initialize(origin:)
35
+ super format(TEMPLATE, source: origin)
36
+ end
37
+
38
+ def long_message
39
+ format(LONG_TEMPLATE,
40
+ message: message,
41
+ exception: cause.inspect,
42
+ original: cause.backtrace.join("\n\t"))
45
43
  end
46
44
  end
47
45
  end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base_error'
4
+
5
+ module Reek
6
+ module Errors
7
+ # Gets raised when Reek is unable to process the source due to a SyntaxError
8
+ class SyntaxError < BaseError
9
+ TEMPLATE = "Source '%<source>s' cannot be processed by Reek due to a syntax error in the source file."
10
+
11
+ LONG_TEMPLATE = <<-MESSAGE
12
+ !!!
13
+ %<message>s
14
+
15
+ This is a problem that is outside of Reek's scope and should be fixed by you, the
16
+ user, in order for Reek being able to continue.
17
+
18
+ Exception message:
19
+
20
+ %<exception>s
21
+
22
+ Original backtrace:
23
+
24
+ %<original>s
25
+
26
+ !!!
27
+ MESSAGE
28
+
29
+ def initialize(origin:)
30
+ super format(TEMPLATE, source: origin)
31
+ end
32
+
33
+ def long_message
34
+ format(LONG_TEMPLATE,
35
+ message: message,
36
+ exception: cause.inspect,
37
+ original: cause.backtrace.join("\n\t"))
38
+ end
39
+ end
40
+ end
41
+ end
data/lib/reek/examiner.rb CHANGED
@@ -3,6 +3,8 @@
3
3
  require_relative 'context_builder'
4
4
  require_relative 'detector_repository'
5
5
  require_relative 'errors/incomprehensible_source_error'
6
+ require_relative 'errors/encoding_error'
7
+ require_relative 'errors/syntax_error'
6
8
  require_relative 'source/source_code'
7
9
 
8
10
  module Reek
@@ -10,6 +12,7 @@ module Reek
10
12
  # Applies all available smell detectors to a source.
11
13
  #
12
14
  # @public
15
+ # @quality :reek:TooManyInstanceVariables { max_instance_variables: 5 }
13
16
  class Examiner
14
17
  # Handles no errors
15
18
  class NullHandler
@@ -38,26 +41,17 @@ module Reek
38
41
  detector_repository_class: DetectorRepository,
39
42
  error_handler: NullHandler.new)
40
43
  @source = Source::SourceCode.from(source)
44
+ @origin = @source.origin
41
45
  @smell_types = detector_repository_class.eligible_smell_types(filter_by_smells)
42
46
  @detector_repository = detector_repository_class.new(smell_types: @smell_types,
43
- configuration: configuration.directive_for(description))
47
+ configuration: configuration.directive_for(@origin))
44
48
  @error_handler = error_handler
45
49
  end
46
50
 
47
51
  # @return [String] origin of the source being analysed
48
52
  #
49
53
  # @public
50
- def origin
51
- @origin ||= source.origin
52
- end
53
-
54
- # @return [String] description of the source being analysed
55
- #
56
- # @public
57
- # @deprecated Use origin
58
- def description
59
- origin
60
- end
54
+ attr_reader :origin
61
55
 
62
56
  #
63
57
  # @return [Array<SmellWarning>] the smells found in the source
@@ -95,26 +89,26 @@ module Reek
95
89
  #
96
90
  # @return [Array<SmellWarning>] the smells found in the source
97
91
  #
98
- # :reek:TooManyStatements { max_statements: 6 }
99
92
  def run
100
- if source.valid_syntax? && syntax_tree
93
+ wrap_exceptions do
101
94
  examine_tree
102
- else
103
- SmellDetectors::Syntax.smells_from_source(source)
104
95
  end
105
96
  rescue StandardError => exception
106
- wrapper = wrap_exception exception
107
- raise wrapper unless @error_handler.handle wrapper
97
+ raise unless @error_handler.handle exception
108
98
  []
109
99
  end
110
100
 
111
- def wrap_exception(exception)
112
- case exception
113
- when Errors::BaseError
114
- exception
115
- else
116
- Errors::IncomprehensibleSourceError.new(origin: origin, original_exception: exception)
117
- end
101
+ # @quality :reek:TooManyStatements { max_statements: 6 }
102
+ def wrap_exceptions
103
+ yield
104
+ rescue Errors::BaseError
105
+ raise
106
+ rescue EncodingError
107
+ raise Errors::EncodingError, origin: origin
108
+ rescue Parser::SyntaxError
109
+ raise Errors::SyntaxError, origin: origin
110
+ rescue StandardError
111
+ raise Errors::IncomprehensibleSourceError, origin: origin
118
112
  end
119
113
 
120
114
  def syntax_tree
@@ -1,15 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'errors/bad_detector_configuration_key_in_comment_error'
4
- require_relative 'errors/bad_detector_in_comment_error'
5
- require_relative 'errors/garbage_detector_configuration_in_comment_error'
6
- require_relative 'errors/incomprehensible_source_error'
3
+ require_relative 'errors/base_error'
7
4
 
8
5
  module Reek
9
6
  # Handles errors by logging to stderr
10
7
  class LoggingErrorHandler
11
8
  def handle(exception)
12
- warn exception
9
+ case exception
10
+ when Errors::BaseError
11
+ warn exception.long_message
12
+ else
13
+ warn exception
14
+ end
13
15
  true
14
16
  end
15
17
  end
@@ -33,8 +33,8 @@ module Reek
33
33
  #
34
34
  # @public
35
35
  #
36
- # :reek:TooManyInstanceVariables: { max_instance_variables: 6 }
37
- # :reek:Attribute
36
+ # @quality :reek:TooManyInstanceVariables { max_instance_variables: 6 }
37
+ # @quality :reek:Attribute
38
38
  class Task < ::Rake::TaskLib
39
39
  # Name of Reek task. Defaults to :reek.
40
40
  # @public
@@ -114,7 +114,7 @@ module Reek
114
114
  reject(&:empty?)
115
115
  end
116
116
 
117
- # :reek:UtilityFunction
117
+ # @quality :reek:UtilityFunction
118
118
  def sys_call_failed?
119
119
  !$CHILD_STATUS.success?
120
120
  end
@@ -4,8 +4,6 @@ require 'json'
4
4
  require 'pathname'
5
5
  require 'rainbow'
6
6
 
7
- require_relative 'formatter'
8
-
9
7
  module Reek
10
8
  # @public
11
9
  module Report
@@ -16,22 +14,20 @@ module Reek
16
14
  #
17
15
  # @public
18
16
  #
19
- # :reek:TooManyInstanceVariables: { max_instance_variables: 7 }
17
+ # @quality :reek:TooManyInstanceVariables { max_instance_variables: 7 }
20
18
  class BaseReport
21
19
  NO_WARNINGS_COLOR = :green
22
20
  WARNINGS_COLOR = :red
23
21
 
24
22
  # @public
25
23
  #
26
- # :reek:BooleanParameter
27
- def initialize(heading_formatter: Formatter::QuietHeadingFormatter,
28
- report_formatter: Formatter,
24
+ # @quality :reek:BooleanParameter
25
+ def initialize(heading_formatter: QuietHeadingFormatter,
29
26
  sort_by_issue_count: false,
30
- warning_formatter: Formatter::SimpleWarningFormatter.new,
31
- progress_formatter: Formatter::ProgressFormatter::Quiet.new(0))
27
+ warning_formatter: SimpleWarningFormatter.new,
28
+ progress_formatter: ProgressFormatter::Quiet.new(0))
32
29
  @examiners = []
33
- @heading_formatter = heading_formatter.new(report_formatter)
34
- @report_formatter = report_formatter
30
+ @heading_formatter = heading_formatter.new
35
31
  @progress_formatter = progress_formatter
36
32
  @sort_by_issue_count = sort_by_issue_count
37
33
  @total_smell_count = 0
@@ -58,7 +54,7 @@ module Reek
58
54
  end
59
55
 
60
56
  def smells?
61
- total_smell_count > 0
57
+ total_smell_count.positive?
62
58
  end
63
59
 
64
60
  def smells
@@ -71,7 +67,7 @@ module Reek
71
67
 
72
68
  private
73
69
 
74
- attr_reader :examiners, :heading_formatter, :report_formatter,
70
+ attr_reader :examiners, :heading_formatter,
75
71
  :sort_by_issue_count, :warning_formatter, :progress_formatter
76
72
  end
77
73
  end
@@ -5,7 +5,7 @@ module Reek
5
5
  # loads the smell type metadata to present in Code Climate
6
6
  module CodeClimateConfiguration
7
7
  def self.load
8
- config_file = File.expand_path('../code_climate_configuration.yml', __FILE__)
8
+ config_file = File.expand_path('code_climate_configuration.yml', __dir__)
9
9
  YAML.load_file config_file
10
10
  end
11
11
  end
@@ -284,7 +284,7 @@ InstanceVariableAssumption:
284
284
  would report:
285
285
 
286
286
  ```Bash
287
- [1]:InstanceVariableAssumption: Dummy assumes too much for instance variable @ivar [https://github.com/troessner/reek/blob/master/docs/Instance-Variable-Assumption.md]
287
+ [1]:InstanceVariableAssumption: Dummy assumes too much for instance variable @ivar
288
288
  ```
289
289
 
290
290
  Note that this example would trigger this smell warning as well:
@@ -565,10 +565,10 @@ NilCheck:
565
565
  test.rb -- 1 warning:
566
566
  [3]:Klass#nil_checker performs a nil-check. (NilCheck)
567
567
  ```
568
- PrimaDonnaMethod:
568
+ MissingSafeMethod:
569
569
  remediation_points: 250_000
570
570
  content: |
571
- A candidate method for the `Prima Donna Method` smell are methods whose names end with an exclamation mark.
571
+ A candidate method for the `Missing Safe Method` smell are methods whose names end with an exclamation mark.
572
572
 
573
573
  An exclamation mark in method names means (the explanation below is taken from [here](http://dablog.rubypal.com/2007/8/15/bang-methods-or-danger-will-rubyist) ):
574
574
 
@@ -576,7 +576,7 @@ PrimaDonnaMethod:
576
576
  The ! in method names that end with ! means, “This method is dangerous”—or, more precisely, this method is the “dangerous” version of an otherwise equivalent method, with the same name minus the !. “Danger” is relative; the ! doesn’t mean anything at all unless the method name it’s in corresponds to a similar but bang-less method name.
577
577
  So, for example, gsub! is the dangerous version of gsub. exit! is the dangerous version of exit. flatten! is the dangerous version of flatten. And so forth.
578
578
 
579
- Such a method is called `Prima Donna Method` if and only if her non-bang version does not exist and this method is reported as a smell.
579
+ Such a method is called `Missing Safe Method` if and only if her non-bang version does not exist and this method is reported as a smell.
580
580
 
581
581
  ## Example
582
582
 
@@ -590,7 +590,7 @@ PrimaDonnaMethod:
590
590
  end
591
591
  ```
592
592
 
593
- Reek would report `bar!` as `prima donna method` smell but not `foo!`.
593
+ Reek would report `bar!` as `Missing Safe Method` smell but not `foo!`.
594
594
 
595
595
  Reek reports this smell only in a class context, not in a module context in order to allow perfectly legit code like this:
596
596
 
@@ -612,7 +612,7 @@ PrimaDonnaMethod:
612
612
  end
613
613
  ```
614
614
 
615
- In this example, Reek would not report the `prima donna method` smell for the method `foo` of the `Dangerous` module.
615
+ In this example, Reek would not report the `Missing Safe Method` smell for the method `foo` of the `Dangerous` module.
616
616
  RepeatedConditional:
617
617
  remediation_points: 400_000
618
618
  content: |
@@ -860,7 +860,3 @@ SubclassedFromCoreClass:
860
860
  you are gonna have a bad time.
861
861
 
862
862
  Source: http://words.steveklabnik.com/beware-subclassing-ruby-core-classes
863
- Syntax:
864
- remediation_points: 250_000
865
- content: |
866
- Check syntax errors.
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'simple_warning_formatter'
4
+
5
+ module Reek
6
+ module Report
7
+ #
8
+ # Formatter that adds a link to the docs to the basic message from
9
+ # SimpleWarningFormatter.
10
+ #
11
+ class DocumentationLinkWarningFormatter < SimpleWarningFormatter
12
+ def format(warning)
13
+ "#{super} [#{warning.explanatory_link}]"
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Reek
4
+ module Report
5
+ #
6
+ # Base class for heading formatters.
7
+ # Is responsible for formatting the heading emitted for each examiner
8
+ #
9
+ # @abstract Override {#show_header?} to implement a heading formatter.
10
+ class HeadingFormatterBase
11
+ # @quality :reek:UtilityFunction
12
+ def show_header?(_examiner)
13
+ raise NotImplementedError
14
+ end
15
+
16
+ def header(examiner)
17
+ if show_header?(examiner)
18
+ formatted_header examiner
19
+ else
20
+ ''
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def formatted_header(examiner)
27
+ count = examiner.smells_count
28
+ result = Rainbow("#{examiner.origin} -- ").cyan +
29
+ Rainbow("#{count} warning").yellow
30
+ result += Rainbow('s').yellow unless count == 1
31
+ result
32
+ end
33
+ end
34
+
35
+ #
36
+ # Lists out each examiner, even if it has no smell
37
+ #
38
+ class VerboseHeadingFormatter < HeadingFormatterBase
39
+ def show_header?(_examiner)
40
+ true
41
+ end
42
+ end
43
+
44
+ #
45
+ # Lists only smelly examiners
46
+ #
47
+ class QuietHeadingFormatter < HeadingFormatterBase
48
+ # @quality :reek:UtilityFunction
49
+ def show_header?(examiner)
50
+ examiner.smelly?
51
+ end
52
+ end
53
+ end
54
+ end
@@ -12,7 +12,7 @@ module Reek
12
12
  #
13
13
  class JSONReport < BaseReport
14
14
  def show(out = $stdout)
15
- out.print ::JSON.generate smells.map { |smell| warning_formatter.format_hash(smell) }
15
+ out.print ::JSON.generate smells.map(&:yaml_hash)
16
16
  end
17
17
  end
18
18
  end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Reek
4
+ module Report
5
+ #
6
+ # Formats the location of a warning as an empty string.
7
+ #
8
+ module BlankLocationFormatter
9
+ module_function
10
+
11
+ def format(_warning)
12
+ ''
13
+ end
14
+ end
15
+
16
+ #
17
+ # Formats the location of a warning as an array of line numbers.
18
+ #
19
+ module DefaultLocationFormatter
20
+ module_function
21
+
22
+ def format(warning)
23
+ "#{warning.lines.sort.inspect}:"
24
+ end
25
+ end
26
+
27
+ #
28
+ # Formats the location of a warning as a combination of source file name
29
+ # and line number. In this format, it is not possible to show more than
30
+ # one line number, so the first number is displayed.
31
+ #
32
+ module SingleLineLocationFormatter
33
+ module_function
34
+
35
+ def format(warning)
36
+ "#{warning.source}:#{warning.lines.min}: "
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Reek
4
+ module Report
5
+ module ProgressFormatter
6
+ #
7
+ # Base class for progress formatters.
8
+ # Is responsible for formatting the progress emitted for each examiner
9
+ #
10
+ # @abstract Override {#header, #progress, #footer} to implement a progress formatter.
11
+ class Base
12
+ attr_reader :sources_count
13
+
14
+ def initialize(sources_count)
15
+ @sources_count = sources_count
16
+ end
17
+
18
+ def header
19
+ raise NotImplementedError
20
+ end
21
+
22
+ def progress(_examiner)
23
+ raise NotImplementedError
24
+ end
25
+
26
+ def footer
27
+ raise NotImplementedError
28
+ end
29
+ end
30
+
31
+ #
32
+ # Shows the status of each source as either a dot (.) or an S
33
+ #
34
+ class Dots < Base
35
+ NO_WARNINGS_COLOR = :green
36
+ WARNINGS_COLOR = :red
37
+
38
+ def header
39
+ "Inspecting #{sources_count} file(s):\n"
40
+ end
41
+
42
+ def progress(examiner)
43
+ examiner.smelly? ? display_smelly : display_clean
44
+ end
45
+
46
+ def footer
47
+ "\n\n"
48
+ end
49
+
50
+ private
51
+
52
+ def display_clean
53
+ Rainbow('.').color(NO_WARNINGS_COLOR)
54
+ end
55
+
56
+ def display_smelly
57
+ Rainbow('S').color(WARNINGS_COLOR)
58
+ end
59
+ end
60
+
61
+ #
62
+ # Does not show progress
63
+ #
64
+ class Quiet < Base
65
+ def header
66
+ ''
67
+ end
68
+
69
+ def progress(_examiner)
70
+ ''
71
+ end
72
+
73
+ def footer
74
+ ''
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'code_climate/code_climate_formatter'
4
+
5
+ module Reek
6
+ module Report
7
+ #
8
+ # Basic formatter that just shows a simple message for each warning,
9
+ # prepended with the result of the passed-in location formatter.
10
+ #
11
+ class SimpleWarningFormatter
12
+ def initialize(location_formatter: BlankLocationFormatter)
13
+ @location_formatter = location_formatter
14
+ end
15
+
16
+ def format(warning)
17
+ "#{location_formatter.format(warning)}#{warning.base_message}"
18
+ end
19
+
20
+ # @quality :reek:UtilityFunction
21
+ def format_code_climate_hash(warning)
22
+ CodeClimateFormatter.new(warning).render
23
+ end
24
+
25
+ def format_list(warnings)
26
+ warnings.map { |warning| " #{format(warning)}" }.join("\n")
27
+ end
28
+
29
+ private
30
+
31
+ attr_reader :location_formatter
32
+ end
33
+ end
34
+ end
@@ -49,8 +49,7 @@ module Reek
49
49
  def summarize_single_examiner(examiner)
50
50
  result = heading_formatter.header(examiner)
51
51
  if examiner.smelly?
52
- formatted_list = report_formatter.format_list(examiner.smells,
53
- formatter: warning_formatter)
52
+ formatted_list = warning_formatter.format_list(examiner.smells)
54
53
  result += ":\n#{formatted_list}"
55
54
  end
56
55
  result
@@ -34,8 +34,8 @@ module Reek
34
34
  end
35
35
  end
36
36
 
37
- # :reek:FeatureEnvy
38
- # :reek:NestedIterators: { max_allowed_nesting: 2 }
37
+ # @quality :reek:FeatureEnvy
38
+ # @quality :reek:NestedIterators { max_allowed_nesting: 2 }
39
39
  def file(name, smells)
40
40
  REXML::Element.new('file').tap do |file|
41
41
  file.add_attribute 'name', File.realpath(name)
@@ -47,7 +47,7 @@ module Reek
47
47
  end
48
48
  end
49
49
 
50
- # :reek:UtilityFunction
50
+ # @quality :reek:UtilityFunction
51
51
  def error(smell, line)
52
52
  REXML::Element.new('error').tap do |error|
53
53
  error.add_attributes 'column' => 0,