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
@@ -13,7 +13,7 @@ module Reek
13
13
  class TooManyStatements < BaseDetector
14
14
  # The name of the config field that sets the maximum number of
15
15
  # statements permitted in any method.
16
- MAX_ALLOWED_STATEMENTS_KEY = 'max_statements'.freeze
16
+ MAX_ALLOWED_STATEMENTS_KEY = 'max_statements'
17
17
  DEFAULT_MAX_STATEMENTS = 5
18
18
 
19
19
  def self.default_config
@@ -31,7 +31,6 @@ module Reek
31
31
  count = context.number_of_statements
32
32
  return [] if count <= max_allowed_statements
33
33
  [smell_warning(
34
- context: context,
35
34
  lines: [source_line],
36
35
  message: "has approx #{count} statements",
37
36
  parameters: { count: count })]
@@ -20,8 +20,8 @@ module Reek
20
20
  #
21
21
  # See {file:docs/Uncommunicative-Method-Name.md} for details.
22
22
  class UncommunicativeMethodName < BaseDetector
23
- REJECT_KEY = 'reject'.freeze
24
- ACCEPT_KEY = 'accept'.freeze
23
+ REJECT_KEY = 'reject'
24
+ ACCEPT_KEY = 'accept'
25
25
  DEFAULT_REJECT_PATTERNS = [/^[a-z]$/, /[0-9]$/, /[A-Z]/].freeze
26
26
  DEFAULT_ACCEPT_PATTERNS = [].freeze
27
27
 
@@ -41,7 +41,6 @@ module Reek
41
41
  return [] if acceptable_name?(name)
42
42
 
43
43
  [smell_warning(
44
- context: context,
45
44
  lines: [source_line],
46
45
  message: "has the name '#{name}'",
47
46
  parameters: { name: name })]
@@ -21,13 +21,13 @@ module Reek
21
21
  class UncommunicativeModuleName < BaseDetector
22
22
  # The name of the config field that lists the regexps of
23
23
  # smelly names to be reported.
24
- REJECT_KEY = 'reject'.freeze
24
+ REJECT_KEY = 'reject'
25
25
  DEFAULT_REJECT_PATTERNS = [/^.$/, /[0-9]$/].freeze
26
26
 
27
27
  # The name of the config field that lists the specific names that are
28
28
  # to be treated as exceptions; these names will not be reported as
29
29
  # uncommunicative.
30
- ACCEPT_KEY = 'accept'.freeze
30
+ ACCEPT_KEY = 'accept'
31
31
  DEFAULT_ACCEPT_PATTERNS = [].freeze
32
32
 
33
33
  def self.default_config
@@ -53,7 +53,6 @@ module Reek
53
53
  fully_qualified_name: fully_qualified_name)
54
54
 
55
55
  [smell_warning(
56
- context: context,
57
56
  lines: [source_line],
58
57
  message: "has the name '#{module_name}'",
59
58
  parameters: { name: module_name })]
@@ -21,10 +21,10 @@ module Reek
21
21
  #
22
22
  # See {file:docs/Uncommunicative-Parameter-Name.md} for details.
23
23
  class UncommunicativeParameterName < BaseDetector
24
- REJECT_KEY = 'reject'.freeze
24
+ REJECT_KEY = 'reject'
25
25
  DEFAULT_REJECT_PATTERNS = [/^.$/, /[0-9]$/, /[A-Z]/, /^_/].freeze
26
26
 
27
- ACCEPT_KEY = 'accept'.freeze
27
+ ACCEPT_KEY = 'accept'
28
28
  DEFAULT_ACCEPT_PATTERNS = [].freeze
29
29
 
30
30
  def self.default_config
@@ -45,7 +45,6 @@ module Reek
45
45
 
46
46
  params.map(&:name).map do |name|
47
47
  smell_warning(
48
- context: context,
49
48
  lines: [source_line],
50
49
  message: "has the parameter name '#{name}'",
51
50
  parameters: { name: name.to_s })
@@ -24,7 +24,7 @@ module Reek
24
24
  class UncommunicativeVariableName < BaseDetector
25
25
  # The name of the config field that lists the regexps of
26
26
  # smelly names to be reported.
27
- REJECT_KEY = 'reject'.freeze
27
+ REJECT_KEY = 'reject'
28
28
  DEFAULT_REJECT_SET = [
29
29
  /^.$/, # single-character names
30
30
  /[0-9]$/, # any name ending with a number
@@ -34,7 +34,7 @@ module Reek
34
34
  # The name of the config field that lists the specific names that are
35
35
  # to be treated as exceptions; these names will not be reported as
36
36
  # uncommunicative.
37
- ACCEPT_KEY = 'accept'.freeze
37
+ ACCEPT_KEY = 'accept'
38
38
  DEFAULT_ACCEPT_SET = [/^_$/].freeze
39
39
 
40
40
  def self.default_config
@@ -57,7 +57,6 @@ module Reek
57
57
  uncommunicative_variable_name?(name)
58
58
  end.map do |name, lines|
59
59
  smell_warning(
60
- context: context,
61
60
  lines: lines,
62
61
  message: "has the variable name '#{name}'",
63
62
  parameters: { name: name.to_s })
@@ -92,17 +91,17 @@ module Reek
92
91
  end
93
92
 
94
93
  def find_assignment_variable_names(accumulator)
95
- assignment_nodes = expression.each_node(:lvasgn, [:class, :module, :defs, :def])
94
+ assignment_nodes = expression.each_node(:lvasgn, [:class, :module, :defs, :def]).to_a
96
95
 
97
96
  case expression.type
98
97
  when :class, :module
99
- assignment_nodes += expression.each_node(:ivasgn, [:class, :module])
98
+ assignment_nodes += expression.each_node(:ivasgn, [:class, :module]).to_a
100
99
  end
101
100
 
102
101
  assignment_nodes.each { |asgn| accumulator[asgn.children.first].push(asgn.line) }
103
102
  end
104
103
 
105
- # :reek:TooManyStatements: { max_statements: 6 }
104
+ # @quality :reek:TooManyStatements { max_statements: 6 }
106
105
  def find_block_argument_variable_names(accumulator)
107
106
  arg_search_exp = case expression.type
108
107
  when :class, :module
@@ -130,7 +129,7 @@ module Reek
130
129
  end
131
130
  end
132
131
 
133
- # :reek:UtilityFunction
132
+ # @quality :reek:UtilityFunction
134
133
  def record_variable_name(exp, symbol, accumulator)
135
134
  varname = symbol.to_s.sub(/^\*/, '')
136
135
  return if varname == ''
@@ -19,7 +19,6 @@ module Reek
19
19
  context.unused_params.map do |param|
20
20
  name = param.name.to_s
21
21
  smell_warning(
22
- context: context,
23
22
  lines: [source_line],
24
23
  message: "has unused parameter '#{name}'",
25
24
  parameters: { name: name })
@@ -38,7 +38,6 @@ module Reek
38
38
  hits.map do |hit|
39
39
  name = hit.name
40
40
  smell_warning(
41
- context: context,
42
41
  lines: [hit.line],
43
42
  message: "has the unused private instance method '#{name}'",
44
43
  parameters: { name: name.to_s })
@@ -38,7 +38,7 @@ module Reek
38
38
  #
39
39
  # See {file:docs/Utility-Function.md} for details.
40
40
  class UtilityFunction < BaseDetector
41
- PUBLIC_METHODS_ONLY_KEY = 'public_methods_only'.freeze
41
+ PUBLIC_METHODS_ONLY_KEY = 'public_methods_only'
42
42
  PUBLIC_METHODS_ONLY_DEFAULT = false
43
43
 
44
44
  def self.default_config
@@ -63,7 +63,6 @@ module Reek
63
63
  return [] if ignore_method?
64
64
 
65
65
  [smell_warning(
66
- context: context,
67
66
  lines: [source_line],
68
67
  message: "doesn't depend on instance state (maybe move it to another class?)")]
69
68
  end
@@ -71,7 +70,7 @@ module Reek
71
70
  private
72
71
 
73
72
  def num_helper_methods
74
- context.local_nodes(:send).length
73
+ context.local_nodes(:send).to_a.length
75
74
  end
76
75
 
77
76
  def ignore_method?
@@ -15,10 +15,9 @@ require_relative 'smell_detectors/manual_dispatch'
15
15
  require_relative 'smell_detectors/module_initialize'
16
16
  require_relative 'smell_detectors/nested_iterators'
17
17
  require_relative 'smell_detectors/nil_check'
18
- require_relative 'smell_detectors/prima_donna_method'
18
+ require_relative 'smell_detectors/missing_safe_method'
19
19
  require_relative 'smell_detectors/repeated_conditional'
20
20
  require_relative 'smell_detectors/subclassed_from_core_class'
21
- require_relative 'smell_detectors/syntax'
22
21
  require_relative 'smell_detectors/too_many_instance_variables'
23
22
  require_relative 'smell_detectors/too_many_constants'
24
23
  require_relative 'smell_detectors/too_many_methods'
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'forwardable'
4
+ require_relative 'documentation_link'
4
5
 
5
6
  module Reek
6
7
  #
@@ -8,7 +9,7 @@ module Reek
8
9
  #
9
10
  # @public
10
11
  #
11
- # :reek:TooManyInstanceVariables: { max_instance_variables: 6 }
12
+ # @quality :reek:TooManyInstanceVariables { max_instance_variables: 6 }
12
13
  class SmellWarning
13
14
  include Comparable
14
15
  extend Forwardable
@@ -21,7 +22,7 @@ module Reek
21
22
  # objects yourself. This is why the initializer is not part of the
22
23
  # public API.
23
24
  #
24
- # :reek:LongParameterList: { max_params: 6 }
25
+ # @quality :reek:LongParameterList { max_params: 6 }
25
26
  def initialize(smell_detector, context: '', lines:, message:,
26
27
  source:, parameters: {})
27
28
  @smell_detector = smell_detector
@@ -55,7 +56,9 @@ module Reek
55
56
  base_hash.merge(stringified_params)
56
57
  end
57
58
 
58
- alias yaml_hash to_hash
59
+ def yaml_hash
60
+ to_hash.merge('documentation_link' => explanatory_link)
61
+ end
59
62
 
60
63
  def base_message
61
64
  "#{smell_type}: #{context} #{message}"
@@ -65,6 +68,10 @@ module Reek
65
68
  smell_detector.class
66
69
  end
67
70
 
71
+ def explanatory_link
72
+ DocumentationLink.build(smell_type)
73
+ end
74
+
68
75
  protected
69
76
 
70
77
  def identifying_values
@@ -75,11 +82,11 @@ module Reek
75
82
 
76
83
  def base_hash
77
84
  {
78
- 'context' => context,
79
- 'lines' => lines,
80
- 'message' => message,
81
- 'smell_type' => smell_type,
82
- 'source' => source
85
+ 'context' => context,
86
+ 'lines' => lines,
87
+ 'message' => message,
88
+ 'smell_type' => smell_type,
89
+ 'source' => source
83
90
  }
84
91
  end
85
92
  end
@@ -1,13 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative '../cli/silencer'
4
- Reek::CLI::Silencer.silently do
5
- require 'parser/ruby24'
6
- end
4
+ Reek::CLI::Silencer.without_warnings { require 'parser/ruby25' }
7
5
  require_relative '../tree_dresser'
8
6
  require_relative '../ast/node'
9
7
  require_relative '../ast/builder'
10
- require_relative '../errors/encoding_error'
11
8
 
12
9
  # Opt in to new way of representing lambdas
13
10
  Reek::AST::Builder.emit_lambda = true
@@ -15,76 +12,83 @@ Reek::AST::Builder.emit_lambda = true
15
12
  module Reek
16
13
  module Source
17
14
  #
18
- # A +Source+ object represents a chunk of Ruby source code.
15
+ # A +SourceCode+ object represents a chunk of Ruby source code.
19
16
  #
20
17
  class SourceCode
21
- IO_IDENTIFIER = 'STDIN'.freeze
22
- STRING_IDENTIFIER = 'string'.freeze
23
-
24
- attr_reader :origin
18
+ IO_IDENTIFIER = 'STDIN'
19
+ STRING_IDENTIFIER = 'string'
25
20
 
26
21
  # Initializer.
27
22
  #
28
- # code - Ruby code as String
29
- # origin - 'STDIN', 'string' or a filepath as String
30
- # parser - the parser to use for generating AST's out of the given source
31
- def initialize(code:, origin:, parser: default_parser)
23
+ # @param source [File|Pathname|IO|String] Ruby source code
24
+ # @param origin [String] Origin of the source code. Will be determined
25
+ # automatically if left blank.
26
+ # @param parser the parser to use for generating AST's out of the given code
27
+ def initialize(source:, origin: nil, parser: self.class.default_parser)
32
28
  @origin = origin
33
- @diagnostics = []
34
29
  @parser = parser
35
- @code = code
30
+ @source = source
36
31
  end
37
32
 
38
33
  # Initializes an instance of SourceCode given a source.
39
- # This source can come via 4 different ways:
34
+ # This source can come via several different ways:
40
35
  # - from Files or Pathnames a la `reek lib/reek/`
41
36
  # - from IO (STDIN) a la `echo "class Foo; end" | reek`
42
37
  # - from String via our rspec matchers a la `expect("class Foo; end").to reek`
38
+ # - from an existing SourceCode object. This is passed through unchanged
43
39
  #
44
- # @param source [File|IO|String] - the given source
40
+ # @param source [SourceCode|File|Pathname|IO|String] the given source
41
+ # @param origin [String|nil]
45
42
  #
46
43
  # @return an instance of SourceCode
47
- # :reek:DuplicateMethodCall: { max_calls: 2 }
48
- def self.from(source)
44
+ def self.from(source, origin: nil)
49
45
  case source
50
- when File then new(code: source.read, origin: source.path)
51
- when IO then new(code: source.readlines.join, origin: IO_IDENTIFIER)
52
- when Pathname then new(code: source.read, origin: source.to_s)
53
- when String then new(code: source, origin: STRING_IDENTIFIER)
46
+ when self then source
47
+ else new(source: source, origin: origin)
54
48
  end
55
49
  end
56
50
 
57
- # @return [true|false] Returns true if parsed file does not have any syntax errors.
58
- def valid_syntax?
59
- diagnostics.none? { |diagnostic| [:error, :fatal].include?(diagnostic.level) }
51
+ def syntax_tree
52
+ @syntax_tree ||= parse
60
53
  end
61
54
 
62
- def diagnostics
63
- parse_if_needed
64
- @diagnostics
55
+ def self.default_parser
56
+ Parser::Ruby25.new(AST::Builder.new).tap do |parser|
57
+ diagnostics = parser.diagnostics
58
+ diagnostics.all_errors_are_fatal = true
59
+ diagnostics.ignore_warnings = true
60
+ end
65
61
  end
66
62
 
67
- def syntax_tree
68
- parse_if_needed
63
+ def origin
64
+ @origin ||=
65
+ case source
66
+ when File then source.path
67
+ when IO then IO_IDENTIFIER
68
+ when Pathname then source.to_s
69
+ when String then STRING_IDENTIFIER
70
+ end
69
71
  end
70
72
 
71
73
  private
72
74
 
73
- def parse_if_needed
74
- @syntax_tree ||= parse(@parser, @code)
75
+ def code
76
+ @code ||=
77
+ case source
78
+ when File, Pathname then source.read
79
+ when IO then source.readlines.join
80
+ when String then source
81
+ end.force_encoding(Encoding::UTF_8)
75
82
  end
76
83
 
77
- attr_reader :source
84
+ attr_reader :parser, :source
78
85
 
79
- # Parses the given source into an AST and associates the source code comments with it.
86
+ # Parses the given code into an AST and associates the source code comments with it.
80
87
  # This AST is then traversed by a TreeDresser which adorns the nodes in the AST
81
88
  # with our SexpExtensions.
82
89
  # Finally this AST is returned where each node is an anonymous subclass of Reek::AST::Node
83
90
  #
84
- # Important to note is that Reek will not fail on unparseable files but rather register a
85
- # parse error to @diagnostics and then just continue.
86
- #
87
- # Given this @source:
91
+ # Given this @code:
88
92
  #
89
93
  # # comment about C
90
94
  # class C
@@ -105,41 +109,15 @@ module Reek
105
109
  # where each node is possibly adorned with our SexpExtensions (see ast/ast_node_class_map
106
110
  # and ast/sexp_extensions for details).
107
111
  #
108
- # @param parser [Parser::Ruby24]
109
- # @param source [String] - Ruby code
110
- # @return [Anonymous subclass of Reek::AST::Node] the AST presentation
111
- # for the given source
112
- # :reek:TooManyStatements { max_statements: 8 }
113
- def parse(parser, source)
114
- begin
115
- buffer = Parser::Source::Buffer.new(origin, 1)
116
- source.force_encoding(Encoding::UTF_8)
117
- buffer.source = source
118
- rescue EncodingError => exception
119
- raise Errors::EncodingError, origin: origin, original_exception: exception
120
- end
121
- begin
122
- ast, comments = parser.parse_with_comments(buffer)
123
- rescue Parser::SyntaxError # rubocop:disable Lint/HandleExceptions
124
- # All errors are in diagnostics. No need to handle exception.
125
- end
112
+ # @return Reek::AST::Node the AST presentation for the given code
113
+ def parse
114
+ buffer = Parser::Source::Buffer.new(origin, 1)
115
+ buffer.source = code
116
+ ast, comments = parser.parse_with_comments(buffer)
126
117
 
127
118
  # See https://whitequark.github.io/parser/Parser/Source/Comment/Associator.html
128
- comment_map = Parser::Source::Comment.associate(ast, comments) if ast
129
- TreeDresser.new.dress(ast, comment_map)
130
- end
131
-
132
- # :reek:TooManyStatements: { max_statements: 6 }
133
- # :reek:FeatureEnvy
134
- def default_parser
135
- Parser::Ruby24.new(AST::Builder.new).tap do |parser|
136
- diagnostics = parser.diagnostics
137
- diagnostics.all_errors_are_fatal = false
138
- diagnostics.ignore_warnings = false
139
- diagnostics.consumer = lambda do |diagnostic|
140
- @diagnostics << diagnostic
141
- end
142
- end
119
+ comment_map = Parser::Source::Comment.associate(ast, comments)
120
+ TreeDresser.new.dress(ast, comment_map) || AST::Node.new(:empty)
143
121
  end
144
122
  end
145
123
  end
@@ -24,7 +24,7 @@ module Reek
24
24
  # Traverses all paths we initialized the SourceLocator with, finds
25
25
  # all relevant Ruby files and returns them as a list.
26
26
  #
27
- # @return [Array<Pathname>] - Ruby paths found
27
+ # @return [Array<Pathname>] Ruby paths found
28
28
  def sources
29
29
  source_paths
30
30
  end
@@ -33,8 +33,8 @@ module Reek
33
33
 
34
34
  attr_reader :configuration, :paths, :options
35
35
 
36
- # :reek:TooManyStatements: { max_statements: 7 }
37
- # :reek:NestedIterators: { max_allowed_nesting: 2 }
36
+ # @quality :reek:TooManyStatements { max_statements: 7 }
37
+ # @quality :reek:NestedIterators { max_allowed_nesting: 2 }
38
38
  def source_paths
39
39
  paths.each_with_object([]) do |given_path, relevant_paths|
40
40
  unless given_path.exist?
@@ -67,12 +67,12 @@ module Reek
67
67
  configuration.path_excluded?(path)
68
68
  end
69
69
 
70
- # :reek:UtilityFunction
70
+ # @quality :reek:UtilityFunction
71
71
  def print_no_such_file_error(path)
72
72
  warn "Error: No such file - #{path}"
73
73
  end
74
74
 
75
- # :reek:UtilityFunction
75
+ # @quality :reek:UtilityFunction
76
76
  def hidden_directory?(path)
77
77
  path.basename.to_s.start_with? '.'
78
78
  end
@@ -81,12 +81,12 @@ module Reek
81
81
  path_excluded?(path) || hidden_directory?(path)
82
82
  end
83
83
 
84
- # :reek:UtilityFunction
84
+ # @quality :reek:UtilityFunction
85
85
  def ruby_file?(path)
86
86
  path.extname == '.rb'
87
87
  end
88
88
 
89
- # :reek:UtilityFunction
89
+ # @quality :reek:UtilityFunction
90
90
  def current_directory?(path)
91
91
  [Pathname.new('.'), Pathname.new('./')].include?(path)
92
92
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative '../examiner'
4
- require_relative '../report/formatter'
4
+ require_relative '../report/simple_warning_formatter'
5
5
 
6
6
  module Reek
7
7
  module Spec
@@ -23,7 +23,7 @@ module Reek
23
23
  end
24
24
 
25
25
  def failure_message_when_negated
26
- rpt = Report::Formatter.format_list(examiner.smells)
26
+ rpt = Report::SimpleWarningFormatter.new.format_list(examiner.smells)
27
27
  "Expected no smells, but got:\n#{rpt}"
28
28
  end
29
29
 
@@ -2,6 +2,7 @@
2
2
 
3
3
  require_relative '../examiner'
4
4
  require_relative 'smell_matcher'
5
+ require_relative '../configuration/app_configuration'
5
6
 
6
7
  module Reek
7
8
  module Spec
@@ -14,12 +15,13 @@ module Reek
14
15
 
15
16
  attr_reader :failure_message, :failure_message_when_negated
16
17
 
17
- def initialize(smell_type_or_class,
18
+ def initialize(smell_type,
18
19
  smell_details = {},
19
20
  configuration = Configuration::AppConfiguration.default)
20
- @smell_type = normalize smell_type_or_class
21
+ @smell_type = smell_type.to_s
21
22
  @smell_details = smell_details
22
- configuration.load_values(smell_type => { SmellConfiguration::ENABLED_KEY => true })
23
+ configuration.load_values(Configuration::AppConfiguration::DETECTORS_KEY =>
24
+ { smell_type => { SmellConfiguration::ENABLED_KEY => true } })
23
25
  @configuration = configuration
24
26
  end
25
27
 
@@ -34,7 +36,8 @@ module Reek
34
36
 
35
37
  def with_config(config_hash)
36
38
  new_configuration = Configuration::AppConfiguration.default
37
- new_configuration.load_values(smell_type => config_hash)
39
+ new_configuration.load_values(Configuration::AppConfiguration::DETECTORS_KEY =>
40
+ { smell_type => config_hash })
38
41
  self.class.new(smell_type, smell_details, new_configuration)
39
42
  end
40
43
 
@@ -82,7 +85,7 @@ module Reek
82
85
  "#{smell_type} with smell details #{smell_details}, but it did"
83
86
  end
84
87
 
85
- # :reek:FeatureEnvy
88
+ # @quality :reek:FeatureEnvy
86
89
  def all_relevant_smell_details_formatted
87
90
  matching_smell_types.each_with_object([]).with_index do |(smell, accumulator), index|
88
91
  accumulator << "#{index + 1}.)\n"
@@ -93,17 +96,7 @@ module Reek
93
96
  end
94
97
 
95
98
  def origin
96
- examiner.description
97
- end
98
-
99
- # :reek:UtilityFunction
100
- def normalize(smell_type_or_class)
101
- case smell_type_or_class
102
- when Class
103
- smell_type_or_class.smell_type
104
- else
105
- smell_type_or_class.to_s
106
- end
99
+ examiner.origin
107
100
  end
108
101
  end
109
102
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative '../examiner'
4
- require_relative '../report/formatter'
4
+ require_relative '../report/simple_warning_formatter'
5
5
  require_relative 'should_reek_of'
6
6
  require_relative 'smell_matcher'
7
7
 
@@ -24,12 +24,12 @@ module Reek
24
24
  end
25
25
 
26
26
  def failure_message
27
- rpt = Report::Formatter.format_list(warnings)
28
- "Expected #{examiner.description} to reek only of #{smell_type}, but got:\n#{rpt}"
27
+ rpt = Report::SimpleWarningFormatter.new.format_list(warnings)
28
+ "Expected #{examiner.origin} to reek only of #{smell_type}, but got:\n#{rpt}"
29
29
  end
30
30
 
31
31
  def failure_message_when_negated
32
- "Expected #{examiner.description} not to reek only of #{smell_type}, but it did"
32
+ "Expected #{examiner.origin} not to reek only of #{smell_type}, but it did"
33
33
  end
34
34
 
35
35
  private
data/lib/reek/spec.rb CHANGED
@@ -65,7 +65,7 @@ module Reek
65
65
  # raise an ArgumentError to give you a hint that you passed something that doesn't make
66
66
  # much sense.
67
67
  #
68
- # @param smell_type [Symbol, String, Class] The "smell type" to check for.
68
+ # @param smell_type [Symbol, String] The "smell type" to check for.
69
69
  # @param smell_details [Hash] A hash containing "smell warning" parameters
70
70
  #
71
71
  # @example Without smell_details
@@ -84,7 +84,7 @@ module Reek
84
84
  #
85
85
  # @public
86
86
  #
87
- # :reek:UtilityFunction
87
+ # @quality :reek:UtilityFunction
88
88
  def reek_of(smell_type,
89
89
  smell_details = {},
90
90
  configuration = Configuration::AppConfiguration.default)
@@ -99,11 +99,11 @@ module Reek
99
99
  # "reek_only_of" will fail in that case.
100
100
  # 2.) "reek_only_of" doesn't support the additional smell_details hash.
101
101
  #
102
- # @param smell_type [Symbol, String, Class] The "smell type" to check for.
102
+ # @param smell_type [Symbol, String] The "smell type" to check for.
103
103
  #
104
104
  # @public
105
105
  #
106
- # :reek:UtilityFunction
106
+ # @quality :reek:UtilityFunction
107
107
  def reek_only_of(smell_type, configuration = Configuration::AppConfiguration.default)
108
108
  ShouldReekOnlyOf.new(smell_type, configuration)
109
109
  end
@@ -113,8 +113,8 @@ module Reek
113
113
  #
114
114
  # @public
115
115
  #
116
- # :reek:UtilityFunction
117
- def reek(configuration = Configuration::AppConfiguration.from_path)
116
+ # @quality :reek:UtilityFunction
117
+ def reek(configuration = Configuration::AppConfiguration.from_default_path)
118
118
  ShouldReek.new(configuration: configuration)
119
119
  end
120
120
  end
@@ -31,14 +31,14 @@ module Reek
31
31
  # time the nodes will contain type-dependent mixins, e.g. this:
32
32
  # (const nil :Klazz)
33
33
  # will be of type Reek::AST::Node with Reek::AST::SexpExtensions::ConstNode mixed in.
34
- # @param sexp [Parser::AST::Node] - the given sexp
35
- # @param comment_map [Hash] - see the documentation for SourceCode#syntax_tree
36
- # @param parent [Parser::AST::Node] - the parent sexp
34
+ #
35
+ # @param sexp [Parser::AST::Node] the given sexp
36
+ # @param comment_map [Hash] see the documentation for SourceCode#syntax_tree
37
37
  #
38
38
  # @return an instance of Reek::AST::Node with type-dependent sexp extensions mixed in.
39
39
  #
40
- # :reek:FeatureEnvy
41
- # :reek:TooManyStatements: { max_statements: 6 }
40
+ # @quality :reek:FeatureEnvy
41
+ # @quality :reek:TooManyStatements { max_statements: 6 }
42
42
  def dress(sexp, comment_map)
43
43
  return sexp unless sexp.is_a? ::Parser::AST::Node
44
44
  type = sexp.type
data/lib/reek/version.rb CHANGED
@@ -8,6 +8,6 @@ module Reek
8
8
  # @public
9
9
  module Version
10
10
  # @public
11
- STRING = '4.7.3'.freeze
11
+ STRING = '5.0.0'
12
12
  end
13
13
  end