reek 4.6.0 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (294) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +17 -12
  3. data/.rubocop.yml +79 -26
  4. data/.simplecov +1 -0
  5. data/.travis.yml +3 -9
  6. data/.yardopts +1 -1
  7. data/CHANGELOG.md +76 -0
  8. data/CONTRIBUTING.md +1 -1
  9. data/Gemfile +5 -5
  10. data/README.md +138 -107
  11. data/Rakefile +16 -3
  12. data/bin/code_climate_reek +1 -0
  13. data/bin/reek +2 -3
  14. data/docs/API.md +2 -9
  15. data/docs/Basic-Smell-Options.md +51 -11
  16. data/docs/Code-Smells.md +1 -1
  17. data/docs/Command-Line-Options.md +14 -4
  18. data/docs/Duplicate-Method-Call.md +49 -1
  19. data/docs/Feature-Envy.md +44 -0
  20. data/docs/How-To-Write-New-Detectors.md +7 -7
  21. data/docs/Instance-Variable-Assumption.md +1 -1
  22. data/docs/{Prima-Donna-Method.md → Missing-Safe-Method.md} +11 -9
  23. data/docs/Rake-Task.md +1 -1
  24. data/docs/Reek-4-to-Reek-5-migration.md +193 -0
  25. data/docs/Reek-Driven-Development.md +1 -1
  26. data/docs/Uncommunicative-Method-Name.md +45 -6
  27. data/docs/Uncommunicative-Module-Name.md +49 -7
  28. data/docs/Uncommunicative-Parameter-Name.md +43 -5
  29. data/docs/Uncommunicative-Variable-Name.md +73 -2
  30. data/docs/Unused-Private-Method.md +3 -3
  31. data/docs/defaults.reek.yml +129 -0
  32. data/docs/yard_plugin.rb +1 -0
  33. data/features/command_line_interface/basic_usage.feature +2 -2
  34. data/features/command_line_interface/options.feature +46 -4
  35. data/features/command_line_interface/show_progress.feature +4 -4
  36. data/features/command_line_interface/smell_selection.feature +1 -1
  37. data/features/command_line_interface/smells_count.feature +6 -6
  38. data/features/command_line_interface/stdin.feature +31 -5
  39. data/features/configuration_files/accept_setting.feature +45 -28
  40. data/features/configuration_files/directory_specific_directives.feature +80 -75
  41. data/features/configuration_files/exclude_directives.feature +11 -10
  42. data/features/configuration_files/exclude_paths_directives.feature +4 -4
  43. data/features/configuration_files/masking_smells.feature +38 -9
  44. data/features/configuration_files/mix_accept_reject_setting.feature +31 -28
  45. data/features/configuration_files/reject_setting.feature +52 -41
  46. data/features/configuration_files/schema_validation.feature +59 -0
  47. data/features/configuration_files/unused_private_method.feature +18 -16
  48. data/features/configuration_loading.feature +53 -10
  49. data/features/configuration_via_source_comments/erroneous_source_comments.feature +3 -3
  50. data/features/configuration_via_source_comments/well_formed_source_comments.feature +2 -2
  51. data/features/locales.feature +32 -0
  52. data/features/rake_task/rake_task.feature +58 -18
  53. data/features/reports/codeclimate.feature +59 -0
  54. data/features/reports/json.feature +3 -3
  55. data/features/reports/reports.feature +34 -34
  56. data/features/reports/yaml.feature +3 -3
  57. data/features/rspec_matcher.feature +40 -0
  58. data/features/samples.feature +287 -287
  59. data/features/step_definitions/reek_steps.rb +14 -2
  60. data/features/step_definitions/sample_file_steps.rb +9 -4
  61. data/features/support/env.rb +2 -11
  62. data/features/todo_list.feature +26 -23
  63. data/lib/reek/ast/ast_node_class_map.rb +1 -0
  64. data/lib/reek/ast/builder.rb +16 -0
  65. data/lib/reek/ast/node.rb +50 -58
  66. data/lib/reek/ast/object_refs.rb +2 -1
  67. data/lib/reek/ast/reference_collector.rb +3 -4
  68. data/lib/reek/ast/sexp_extensions/arguments.rb +1 -0
  69. data/lib/reek/ast/sexp_extensions/attribute_assignments.rb +1 -0
  70. data/lib/reek/ast/sexp_extensions/begin.rb +17 -0
  71. data/lib/reek/ast/sexp_extensions/block.rb +1 -0
  72. data/lib/reek/ast/sexp_extensions/case.rb +2 -1
  73. data/lib/reek/ast/sexp_extensions/constant.rb +1 -0
  74. data/lib/reek/ast/sexp_extensions/if.rb +9 -1
  75. data/lib/reek/ast/sexp_extensions/lambda.rb +1 -0
  76. data/lib/reek/ast/sexp_extensions/literal.rb +1 -0
  77. data/lib/reek/ast/sexp_extensions/logical_operators.rb +2 -1
  78. data/lib/reek/ast/sexp_extensions/methods.rb +5 -6
  79. data/lib/reek/ast/sexp_extensions/module.rb +55 -8
  80. data/lib/reek/ast/sexp_extensions/nested_assignables.rb +1 -0
  81. data/lib/reek/ast/sexp_extensions/self.rb +1 -0
  82. data/lib/reek/ast/sexp_extensions/send.rb +1 -4
  83. data/lib/reek/ast/sexp_extensions/super.rb +1 -0
  84. data/lib/reek/ast/sexp_extensions/symbols.rb +1 -0
  85. data/lib/reek/ast/sexp_extensions/variables.rb +1 -0
  86. data/lib/reek/ast/sexp_extensions/when.rb +1 -0
  87. data/lib/reek/ast/sexp_extensions/yield.rb +1 -0
  88. data/lib/reek/ast/sexp_extensions.rb +2 -0
  89. data/lib/reek/cli/application.rb +5 -3
  90. data/lib/reek/cli/command/base_command.rb +1 -0
  91. data/lib/reek/cli/command/report_command.rb +2 -2
  92. data/lib/reek/cli/command/todo_list_command.rb +9 -8
  93. data/lib/reek/cli/options.rb +32 -16
  94. data/lib/reek/cli/silencer.rb +15 -3
  95. data/lib/reek/cli/status.rb +1 -0
  96. data/lib/reek/code_comment.rb +14 -16
  97. data/lib/reek/configuration/app_configuration.rb +34 -28
  98. data/lib/reek/configuration/configuration_converter.rb +110 -0
  99. data/lib/reek/configuration/configuration_file_finder.rb +17 -41
  100. data/lib/reek/configuration/configuration_validator.rb +13 -23
  101. data/lib/reek/configuration/default_directive.rb +18 -3
  102. data/lib/reek/configuration/directory_directives.rb +18 -11
  103. data/lib/reek/configuration/excluded_paths.rb +2 -1
  104. data/lib/reek/configuration/rake_task_converter.rb +29 -0
  105. data/lib/reek/configuration/schema.yml +210 -0
  106. data/lib/reek/configuration/schema_validator.rb +38 -0
  107. data/lib/reek/context/attribute_context.rb +4 -3
  108. data/lib/reek/context/class_context.rb +1 -0
  109. data/lib/reek/context/code_context.rb +49 -44
  110. data/lib/reek/context/ghost_context.rb +1 -2
  111. data/lib/reek/context/method_context.rb +26 -18
  112. data/lib/reek/context/module_context.rb +11 -11
  113. data/lib/reek/context/root_context.rb +1 -4
  114. data/lib/reek/context/send_context.rb +3 -2
  115. data/lib/reek/context/singleton_attribute_context.rb +1 -0
  116. data/lib/reek/context/singleton_method_context.rb +1 -0
  117. data/lib/reek/context/statement_counter.rb +1 -0
  118. data/lib/reek/context/visibility_tracker.rb +1 -0
  119. data/lib/reek/context_builder.rb +44 -44
  120. data/lib/reek/detector_repository.rb +12 -11
  121. data/lib/reek/documentation_link.rb +28 -0
  122. data/lib/reek/errors/bad_detector_configuration_key_in_comment_error.rb +15 -13
  123. data/lib/reek/errors/bad_detector_in_comment_error.rb +13 -11
  124. data/lib/reek/errors/base_error.rb +3 -0
  125. data/lib/reek/errors/config_file_error.rb +11 -0
  126. data/lib/reek/errors/encoding_error.rb +43 -0
  127. data/lib/reek/errors/garbage_detector_configuration_in_comment_error.rb +14 -12
  128. data/lib/reek/errors/incomprehensible_source_error.rb +23 -24
  129. data/lib/reek/errors/syntax_error.rb +41 -0
  130. data/lib/reek/examiner.rb +24 -22
  131. data/lib/reek/logging_error_handler.rb +8 -5
  132. data/lib/reek/rake/task.rb +8 -5
  133. data/lib/reek/report/base_report.rb +9 -12
  134. data/lib/reek/report/code_climate/code_climate_configuration.rb +2 -1
  135. data/lib/reek/report/code_climate/code_climate_configuration.yml +6 -6
  136. data/lib/reek/report/code_climate/code_climate_fingerprint.rb +1 -0
  137. data/lib/reek/report/code_climate/code_climate_formatter.rb +1 -0
  138. data/lib/reek/report/code_climate/code_climate_report.rb +1 -0
  139. data/lib/reek/report/code_climate.rb +1 -0
  140. data/lib/reek/report/documentation_link_warning_formatter.rb +17 -0
  141. data/lib/reek/report/heading_formatter.rb +54 -0
  142. data/lib/reek/report/html_report.rb +1 -0
  143. data/lib/reek/report/json_report.rb +2 -1
  144. data/lib/reek/report/location_formatter.rb +40 -0
  145. data/lib/reek/report/progress_formatter.rb +79 -0
  146. data/lib/reek/report/simple_warning_formatter.rb +34 -0
  147. data/lib/reek/report/text_report.rb +2 -2
  148. data/lib/reek/report/xml_report.rb +4 -3
  149. data/lib/reek/report/yaml_report.rb +2 -1
  150. data/lib/reek/report.rb +16 -10
  151. data/lib/reek/smell_configuration.rb +3 -2
  152. data/lib/reek/smell_detectors/attribute.rb +6 -11
  153. data/lib/reek/smell_detectors/base_detector.rb +31 -26
  154. data/lib/reek/smell_detectors/boolean_parameter.rb +4 -5
  155. data/lib/reek/smell_detectors/class_variable.rb +6 -14
  156. data/lib/reek/smell_detectors/control_parameter.rb +19 -33
  157. data/lib/reek/smell_detectors/data_clump.rb +16 -9
  158. data/lib/reek/smell_detectors/duplicate_method_call.rb +24 -17
  159. data/lib/reek/smell_detectors/feature_envy.rb +10 -7
  160. data/lib/reek/smell_detectors/instance_variable_assumption.rb +15 -23
  161. data/lib/reek/smell_detectors/irresponsible_module.rb +6 -12
  162. data/lib/reek/smell_detectors/long_parameter_list.rb +11 -7
  163. data/lib/reek/smell_detectors/long_yield_list.rb +11 -7
  164. data/lib/reek/smell_detectors/manual_dispatch.rb +5 -5
  165. data/lib/reek/smell_detectors/{prima_donna_method.rb → missing_safe_method.rb} +21 -20
  166. data/lib/reek/smell_detectors/module_initialize.rb +4 -5
  167. data/lib/reek/smell_detectors/nested_iterators.rb +17 -24
  168. data/lib/reek/smell_detectors/nil_check.rb +9 -15
  169. data/lib/reek/smell_detectors/repeated_conditional.rb +14 -11
  170. data/lib/reek/smell_detectors/subclassed_from_core_class.rb +8 -8
  171. data/lib/reek/smell_detectors/too_many_constants.rb +11 -9
  172. data/lib/reek/smell_detectors/too_many_instance_variables.rb +11 -6
  173. data/lib/reek/smell_detectors/too_many_methods.rb +12 -7
  174. data/lib/reek/smell_detectors/too_many_statements.rb +11 -6
  175. data/lib/reek/smell_detectors/uncommunicative_method_name.rb +11 -11
  176. data/lib/reek/smell_detectors/uncommunicative_module_name.rb +15 -18
  177. data/lib/reek/smell_detectors/uncommunicative_parameter_name.rb +18 -22
  178. data/lib/reek/smell_detectors/uncommunicative_variable_name.rb +27 -27
  179. data/lib/reek/smell_detectors/unused_parameters.rb +5 -6
  180. data/lib/reek/smell_detectors/unused_private_method.rb +14 -20
  181. data/lib/reek/smell_detectors/utility_function.rb +13 -16
  182. data/lib/reek/smell_detectors.rb +2 -1
  183. data/lib/reek/smell_warning.rb +16 -8
  184. data/lib/reek/source/source_code.rb +65 -46
  185. data/lib/reek/source/source_locator.rb +9 -8
  186. data/lib/reek/spec/should_reek.rb +3 -2
  187. data/lib/reek/spec/should_reek_of.rb +13 -26
  188. data/lib/reek/spec/should_reek_only_of.rb +5 -4
  189. data/lib/reek/spec/smell_matcher.rb +2 -1
  190. data/lib/reek/spec.rb +7 -6
  191. data/lib/reek/tree_dresser.rb +9 -8
  192. data/lib/reek/version.rb +2 -1
  193. data/lib/reek.rb +1 -0
  194. data/reek.gemspec +5 -6
  195. data/samples/checkstyle.xml +1 -1
  196. data/samples/configuration/accepts_rejects_and_excludes_for_detectors.reek.yml +29 -0
  197. data/samples/configuration/accepts_rejects_and_excludes_for_directory_directives.reek.yml +30 -0
  198. data/samples/configuration/full_configuration.reek +8 -4
  199. data/samples/configuration/full_mask.reek +5 -4
  200. data/samples/configuration/partial_mask.reek +3 -2
  201. data/samples/configuration/regular_configuration/.reek.yml +4 -0
  202. data/samples/paths.rb +5 -4
  203. data/samples/source_with_hidden_directories/.hidden/hidden.rb +1 -0
  204. data/samples/source_with_hidden_directories/not_hidden.rb +1 -0
  205. data/spec/factories/factories.rb +2 -13
  206. data/spec/reek/ast/node_spec.rb +103 -10
  207. data/spec/reek/ast/reference_collector_spec.rb +1 -1
  208. data/spec/reek/ast/sexp_extensions_spec.rb +15 -34
  209. data/spec/reek/cli/application_spec.rb +52 -42
  210. data/spec/reek/cli/command/todo_list_command_spec.rb +6 -4
  211. data/spec/reek/cli/silencer_spec.rb +28 -0
  212. data/spec/reek/code_comment_spec.rb +31 -38
  213. data/spec/reek/configuration/app_configuration_spec.rb +46 -33
  214. data/spec/reek/configuration/configuration_file_finder_spec.rb +133 -51
  215. data/spec/reek/configuration/default_directive_spec.rb +1 -1
  216. data/spec/reek/configuration/directory_directives_spec.rb +6 -7
  217. data/spec/reek/configuration/excluded_paths_spec.rb +6 -6
  218. data/spec/reek/configuration/rake_task_converter_spec.rb +33 -0
  219. data/spec/reek/configuration/schema_validator_spec.rb +165 -0
  220. data/spec/reek/context/code_context_spec.rb +70 -106
  221. data/spec/reek/context/ghost_context_spec.rb +9 -9
  222. data/spec/reek/context/method_context_spec.rb +2 -2
  223. data/spec/reek/context/module_context_spec.rb +3 -3
  224. data/spec/reek/context/root_context_spec.rb +1 -1
  225. data/spec/reek/context/statement_counter_spec.rb +1 -0
  226. data/spec/reek/context_builder_spec.rb +20 -0
  227. data/spec/reek/documentation_link_spec.rb +20 -0
  228. data/spec/reek/errors/base_error_spec.rb +13 -0
  229. data/spec/reek/examiner_spec.rb +137 -29
  230. data/spec/reek/rake/task_spec.rb +25 -2
  231. data/spec/reek/report/code_climate/code_climate_fingerprint_spec.rb +82 -78
  232. data/spec/reek/report/code_climate/code_climate_formatter_spec.rb +6 -6
  233. data/spec/reek/report/code_climate/code_climate_report_spec.rb +22 -22
  234. data/spec/reek/report/json_report_spec.rb +13 -46
  235. data/spec/reek/report/{formatter/location_formatter_spec.rb → location_formatter_spec.rb} +5 -5
  236. data/spec/reek/report/{formatter/progress_formatter_spec.rb → progress_formatter_spec.rb} +4 -4
  237. data/spec/reek/report/text_report_spec.rb +4 -4
  238. data/spec/reek/report/xml_report_spec.rb +3 -3
  239. data/spec/reek/report/yaml_report_spec.rb +17 -46
  240. data/spec/reek/report_spec.rb +3 -3
  241. data/spec/reek/smell_detectors/base_detector_spec.rb +4 -5
  242. data/spec/reek/smell_detectors/boolean_parameter_spec.rb +2 -2
  243. data/spec/reek/smell_detectors/class_variable_spec.rb +26 -32
  244. data/spec/reek/smell_detectors/control_parameter_spec.rb +34 -4
  245. data/spec/reek/smell_detectors/duplicate_method_call_spec.rb +3 -3
  246. data/spec/reek/smell_detectors/feature_envy_spec.rb +47 -2
  247. data/spec/reek/smell_detectors/irresponsible_module_spec.rb +59 -21
  248. data/spec/reek/smell_detectors/{prima_donna_method_spec.rb → missing_safe_method_spec.rb} +10 -10
  249. data/spec/reek/smell_detectors/module_initialize_spec.rb +14 -0
  250. data/spec/reek/smell_detectors/nested_iterators_spec.rb +1 -1
  251. data/spec/reek/smell_detectors/too_many_constants_spec.rb +3 -3
  252. data/spec/reek/smell_detectors/too_many_instance_variables_spec.rb +1 -1
  253. data/spec/reek/smell_detectors/uncommunicative_method_name_spec.rb +6 -6
  254. data/spec/reek/smell_detectors/uncommunicative_module_name_spec.rb +6 -4
  255. data/spec/reek/smell_detectors/uncommunicative_parameter_name_spec.rb +36 -15
  256. data/spec/reek/smell_detectors/uncommunicative_variable_name_spec.rb +9 -9
  257. data/spec/reek/smell_detectors/unused_parameters_spec.rb +3 -3
  258. data/spec/reek/smell_detectors/unused_private_method_spec.rb +21 -10
  259. data/spec/reek/smell_detectors/utility_function_spec.rb +57 -5
  260. data/spec/reek/smell_warning_spec.rb +12 -8
  261. data/spec/reek/source/source_code_spec.rb +27 -38
  262. data/spec/reek/source/source_locator_spec.rb +42 -12
  263. data/spec/reek/spec/should_reek_of_spec.rb +25 -30
  264. data/spec/reek/spec/should_reek_only_of_spec.rb +2 -2
  265. data/spec/reek/spec/should_reek_spec.rb +8 -8
  266. data/spec/reek/spec/smell_matcher_spec.rb +23 -23
  267. data/spec/reek/tree_dresser_spec.rb +12 -17
  268. data/spec/spec_helper.rb +7 -17
  269. data/tasks/configuration.rake +8 -5
  270. metadata +77 -40
  271. data/defaults.reek +0 -128
  272. data/features/configuration_files/warn_about_multiple_configuration_files.feature +0 -44
  273. data/lib/reek/errors/parse_error.rb +0 -19
  274. data/lib/reek/report/formatter/heading_formatter.rb +0 -51
  275. data/lib/reek/report/formatter/location_formatter.rb +0 -41
  276. data/lib/reek/report/formatter/progress_formatter.rb +0 -80
  277. data/lib/reek/report/formatter/simple_warning_formatter.rb +0 -35
  278. data/lib/reek/report/formatter/wiki_link_warning_formatter.rb +0 -35
  279. data/lib/reek/report/formatter.rb +0 -32
  280. data/samples/configuration/non_public_modifiers_mask.reek +0 -3
  281. data/samples/smelly_with_inline_mask.rb +0 -8
  282. data/samples/smelly_with_modifiers.rb +0 -12
  283. data/samples/source_with_hidden_directories/.hidden/uncommunicative_method_name.rb +0 -5
  284. data/samples/source_with_non_ruby_files/uncommunicative_parameter_name.rb +0 -6
  285. /data/{samples/configuration/more_than_one_configuration_file/regular.reek → .reek.yml} +0 -0
  286. /data/samples/{clean.rb → clean_source/clean.rb} +0 -0
  287. /data/samples/{exceptions.reek → configuration/home/home.reek.yml} +0 -0
  288. /data/samples/configuration/{more_than_one_configuration_file/todo.reek → regular_configuration/empty_sub_directory/.gitignore} +0 -0
  289. /data/samples/{configuration/single_configuration_file/.reek → no_config_file/.keep} +0 -0
  290. /data/samples/{inline.rb → smelly_source/inline.rb} +0 -0
  291. /data/samples/{optparse.rb → smelly_source/optparse.rb} +0 -0
  292. /data/samples/{redcloth.rb → smelly_source/redcloth.rb} +0 -0
  293. /data/samples/{smelly.rb → smelly_source/smelly.rb} +0 -0
  294. /data/samples/{source_with_hidden_directories/uncommunicative_parameter_name.rb → source_with_non_ruby_files/ruby.rb} +0 -0
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require_relative '../code_comment'
3
4
  require_relative '../ast/object_refs'
4
5
  require_relative 'statement_counter'
@@ -13,51 +14,19 @@ module Reek
13
14
  # code element. CodeContexts form a tree in the same way the code does,
14
15
  # with each context holding a reference to a unique outer context.
15
16
  #
16
- # :reek:TooManyMethods: { max_methods: 19 }
17
- # :reek:TooManyInstanceVariables: { max_instance_variables: 8 }
17
+ # @quality :reek:TooManyMethods { max_methods: 19 }
18
+ # @quality :reek:TooManyInstanceVariables { max_instance_variables: 8 }
18
19
  class CodeContext
19
20
  include Enumerable
20
21
  extend Forwardable
21
- delegate each_node: :exp
22
- delegate %i(name type) => :exp
22
+ delegate [:name, :type] => :exp
23
23
 
24
24
  attr_reader :children, :parent, :exp, :statement_counter
25
25
 
26
26
  # Initializes a new CodeContext.
27
27
  #
28
- # @param _parent [CodeContext, nil] The parent context
29
28
  # @param exp [Reek::AST::Node] The code described by this context
30
- #
31
- # For example, given the following code:
32
- #
33
- # class Omg
34
- # def foo(x)
35
- # puts x
36
- # end
37
- # end
38
- #
39
- # The {ContextBuilder} object first instantiates a {RootContext}, which has no parent.
40
- #
41
- # Next, it instantiates a {ModuleContext}, with +parent+ being the
42
- # {RootContext} just created, and +exp+ looking like this:
43
- #
44
- # (class
45
- # (const nil :Omg) nil
46
- # (def :foo
47
- # (args
48
- # (arg :x))
49
- # (send nil :puts
50
- # (lvar :x))))
51
- #
52
- # Finally, {ContextBuilder} will instantiate a {MethodContext}. This time,
53
- # +parent+ is the {ModuleContext} created above, and +exp+ is:
54
- #
55
- # (def :foo
56
- # (args
57
- # (arg :x))
58
- # (send nil :puts
59
- # (lvar :x)))
60
- def initialize(_parent, exp)
29
+ def initialize(exp)
61
30
  @exp = exp
62
31
  @children = []
63
32
  @statement_counter = StatementCounter.new
@@ -70,12 +39,12 @@ module Reek
70
39
  # @yield block that is executed for every node.
71
40
  #
72
41
  def local_nodes(type, ignored = [], &blk)
73
- ignored += [:casgn, :class, :module]
74
- each_node(type, ignored, &blk)
42
+ ignored |= [:class, :module]
43
+ exp.each_node(type, ignored, &blk)
75
44
  end
76
45
 
77
46
  # Iterate over `self` and child contexts.
78
- # The main difference (among others) to `each_node` is that we are traversing
47
+ # The main difference (among others) to `local_nodes` is that we are traversing
79
48
  # `CodeContexts` here, not AST nodes (see `Reek::AST::Node`).
80
49
  #
81
50
  # @yield block that is executed for every node.
@@ -90,6 +59,44 @@ module Reek
90
59
  end
91
60
  end
92
61
 
62
+ # Link the present context to its parent.
63
+ #
64
+ # @param parent [Reek::AST::Node] The parent context of the code described by this context
65
+ #
66
+ # For example, given the following code:
67
+ #
68
+ # class Omg
69
+ # def foo(x)
70
+ # puts x
71
+ # end
72
+ # end
73
+ #
74
+ # The {ContextBuilder} object first instantiates a {RootContext}, which has no parent.
75
+ #
76
+ # Next, it instantiates a {ModuleContext}, with +exp+ looking like this:
77
+ #
78
+ # (class
79
+ # (const nil :Omg) nil
80
+ # (def :foo
81
+ # (args
82
+ # (arg :x))
83
+ # (send nil :puts
84
+ # (lvar :x))))
85
+ #
86
+ # It will then call #register_with_parent on the {ModuleContext}, passing
87
+ # in the parent {RootContext}.
88
+ #
89
+ # Finally, {ContextBuilder} will instantiate a {MethodContext}. This time,
90
+ # +exp+ is:
91
+ #
92
+ # (def :foo
93
+ # (args
94
+ # (arg :x))
95
+ # (send nil :puts
96
+ # (lvar :x)))
97
+ #
98
+ # Then it will call #register_with_parent on the {MethodContext}, passing
99
+ # in the parent {ModuleContext}.
93
100
  def register_with_parent(parent)
94
101
  @parent = parent.append_child_context(self) if parent
95
102
  end
@@ -103,17 +110,15 @@ module Reek
103
110
  self
104
111
  end
105
112
 
106
- # :reek:TooManyStatements: { max_statements: 6 }
107
- # :reek:FeatureEnvy
113
+ # @quality :reek:TooManyStatements { max_statements: 6 }
114
+ # @quality :reek:FeatureEnvy
108
115
  def record_call_to(exp)
109
116
  receiver = exp.receiver
110
117
  type = receiver ? receiver.type : :self
111
118
  line = exp.line
112
119
  case type
113
120
  when :lvar, :lvasgn
114
- unless exp.object_creation_call?
115
- refs.record_reference(name: receiver.name, line: line)
116
- end
121
+ refs.record_reference(name: receiver.name, line: line) unless exp.object_creation_call?
117
122
  when :self
118
123
  refs.record_reference(name: :self, line: line)
119
124
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require_relative 'code_context'
3
4
  require_relative 'singleton_method_context'
4
5
 
@@ -40,8 +41,6 @@ module Reek
40
41
  names: names
41
42
  end
42
43
 
43
- def track_singleton_visibility(_visibility, _names); end
44
-
45
44
  def record_use_of_self
46
45
  parent.record_use_of_self
47
46
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require_relative 'code_context'
3
4
 
4
5
  module Reek
@@ -6,14 +7,15 @@ module Reek
6
7
  #
7
8
  # A context wrapper for any method definition found in a syntax tree.
8
9
  #
9
- # :reek:Attribute
10
+ # @quality :reek:Attribute
10
11
  class MethodContext < CodeContext
11
12
  attr_accessor :visibility
12
13
  attr_reader :refs
13
14
 
14
- def initialize(context, exp)
15
+ def initialize(exp, parent_exp)
16
+ @parent_exp = parent_exp
15
17
  @visibility = :public
16
- super
18
+ super exp
17
19
  end
18
20
 
19
21
  def references_self?
@@ -21,29 +23,24 @@ module Reek
21
23
  end
22
24
 
23
25
  def uses_param?(param)
24
- # local_nodes(:lvasgn) catches:
26
+ # :lvasgn catches:
27
+ #
25
28
  # def foo(bar); bar += 1; end
26
- # In this example there is no `lvar` node present.
27
29
  #
28
- # local_nodes(:lvar) catches:
30
+ # :lvar catches:
31
+ #
29
32
  # def foo(bar); other(bar); end
30
33
  # def foo(bar); tmp = other(bar); tmp[0]; end
31
34
  #
32
- # Note that in the last example the `lvar` node for `bar` is part of an `lvasgn` node for `tmp`.
33
- # This means that if we would just search for [:lvar, :lvasgn]
34
- # (e.g. via Reek::AST::Node#find_nodes) this would fail for this example since we would
35
- # stop at the `lvasgn` and not detect the contained `lvar`.
36
- # Hence we first get all `lvar` nodes followed by all `lvasgn` nodes.
37
- #
38
- (local_nodes(:lvar) + local_nodes(:lvasgn)).find { |node| node.var_name == param.to_sym }
35
+ local_nodes([:lvar, :lvasgn]).find { |node| node.var_name == param.name }
39
36
  end
40
37
 
41
- # :reek:FeatureEnvy
38
+ # @quality :reek:FeatureEnvy
42
39
  def unused_params
43
- exp.arguments.select do |param|
44
- next if param.anonymous_splat?
45
- next if param.marked_unused?
46
- !uses_param? param.plain_name
40
+ exp.arguments.reject do |param|
41
+ param.anonymous_splat? ||
42
+ param.marked_unused? ||
43
+ uses_param?(param)
47
44
  end
48
45
  end
49
46
 
@@ -79,6 +76,17 @@ module Reek
79
76
  def non_public_visibility?
80
77
  visibility != :public
81
78
  end
79
+
80
+ def full_comment
81
+ own = super
82
+ return own unless own.empty?
83
+ return parent_exp.full_comment if parent_exp
84
+ ''
85
+ end
86
+
87
+ private
88
+
89
+ attr_reader :parent_exp
82
90
  end
83
91
  end
84
92
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require_relative 'code_context'
3
4
  require_relative 'attribute_context'
4
5
  require_relative 'method_context'
@@ -9,13 +10,11 @@ module Reek
9
10
  #
10
11
  # A context wrapper for any module found in a syntax tree.
11
12
  #
12
- # :reek:FeatureEnvy
13
13
  class ModuleContext < CodeContext
14
14
  attr_reader :visibility_tracker
15
15
 
16
- def initialize(context, exp)
16
+ def initialize(exp)
17
17
  super
18
-
19
18
  @visibility_tracker = VisibilityTracker.new
20
19
  end
21
20
 
@@ -44,9 +43,9 @@ module Reek
44
43
  AttributeContext
45
44
  end
46
45
 
47
- def defined_instance_methods(visibility: :public)
46
+ def defined_instance_methods(visibility: :any)
48
47
  instance_method_children.select do |context|
49
- context.visibility == visibility
48
+ visibility == :any || context.visibility == visibility
50
49
  end
51
50
  end
52
51
 
@@ -60,7 +59,7 @@ module Reek
60
59
  # @deprecated use `defined_instance_methods` instead
61
60
  #
62
61
  def node_instance_methods
63
- local_nodes(:def)
62
+ local_nodes(:def).to_a
64
63
  end
65
64
 
66
65
  def descriptively_commented?
@@ -73,24 +72,25 @@ module Reek
73
72
  # However, if the module is empty, it is not considered a namespace module.
74
73
  #
75
74
  # @return true if the module is a namespace module
75
+ #
76
+ # @quality :reek:FeatureEnvy
76
77
  def namespace_module?
77
78
  return false if exp.type == :casgn
78
- contents = exp.children.last
79
- contents && contents.find_nodes([:def, :defs], [:casgn, :class, :module]).empty?
79
+ children = exp.direct_children
80
+ children.any? && children.all? { |child| [:casgn, :class, :module].include? child.type }
80
81
  end
81
82
 
82
83
  def track_visibility(visibility, names)
83
84
  visibility_tracker.track_visibility children: instance_method_children,
84
85
  visibility: visibility,
85
86
  names: names
86
- end
87
-
88
- def track_singleton_visibility(visibility, names)
89
87
  visibility_tracker.track_singleton_visibility children: singleton_method_children,
90
88
  visibility: visibility,
91
89
  names: names
92
90
  end
93
91
 
92
+ private
93
+
94
94
  def instance_method_children
95
95
  children.select(&:instance_method?)
96
96
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require_relative 'code_context'
3
4
  require_relative 'method_context'
4
5
 
@@ -8,10 +9,6 @@ module Reek
8
9
  # A context wrapper representing the root of an abstract syntax tree.
9
10
  #
10
11
  class RootContext < CodeContext
11
- def initialize(exp)
12
- super(nil, exp)
13
- end
14
-
15
12
  def type
16
13
  :root
17
14
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require_relative 'code_context'
3
4
 
4
5
  module Reek
@@ -9,9 +10,9 @@ module Reek
9
10
  class SendContext < CodeContext
10
11
  attr_reader :name
11
12
 
12
- def initialize(context, exp, name)
13
+ def initialize(exp, name)
13
14
  @name = name
14
- super context, exp
15
+ super exp
15
16
  end
16
17
  end
17
18
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require_relative 'attribute_context'
3
4
 
4
5
  module Reek
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require_relative 'method_context'
3
4
 
4
5
  module Reek
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require_relative '../ast/node'
3
4
 
4
5
  module Reek
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Reek
3
4
  module Context
4
5
  # Responsible for tracking visibilities in regards to CodeContexts.
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require_relative 'context/attribute_context'
3
4
  require_relative 'context/class_context'
4
5
  require_relative 'context/ghost_context'
@@ -19,8 +20,9 @@ module Reek
19
20
  # counting. Ideally `ContextBuilder` would only build up the context tree and leave the
20
21
  # statement and reference counting to the contexts.
21
22
  #
22
- # :reek:TooManyMethods: { max_methods: 31 }
23
- # :reek:UnusedPrivateMethod: { exclude: [ !ruby/regexp /process_/ ] }
23
+ # @quality :reek:TooManyMethods { max_methods: 31 }
24
+ # @quality :reek:UnusedPrivateMethod { exclude: [ !ruby/regexp /process_/ ] }
25
+ # @quality :reek:DataClump
24
26
  class ContextBuilder
25
27
  attr_reader :context_tree
26
28
 
@@ -57,10 +59,10 @@ module Reek
57
59
  # RootContext -> children: 1 ModuleContext -> children: 1 MethodContext
58
60
  #
59
61
  # @return [Reek::Context::RootContext] tree of nested contexts
60
- def build(exp)
62
+ def build(exp, parent_exp = nil)
61
63
  context_processor = "process_#{exp.type}"
62
64
  if context_processor_exists?(context_processor)
63
- send(context_processor, exp)
65
+ send(context_processor, exp, parent_exp)
64
66
  else
65
67
  process exp
66
68
  end
@@ -70,12 +72,12 @@ module Reek
70
72
  # Handles every node for which we have no context_processor.
71
73
  #
72
74
  def process(exp)
73
- exp.children.grep(AST::Node).each(&method(:build))
75
+ exp.children.grep(AST::Node).each { |child| build(child, exp) }
74
76
  end
75
77
 
76
78
  # Handles `module` and `class` nodes.
77
79
  #
78
- def process_module(exp)
80
+ def process_module(exp, _parent)
79
81
  inside_new_context(Context::ModuleContext, exp) do
80
82
  process(exp)
81
83
  end
@@ -90,7 +92,7 @@ module Reek
90
92
  # class << self
91
93
  # end
92
94
  #
93
- def process_sclass(exp)
95
+ def process_sclass(exp, _parent)
94
96
  inside_new_context(Context::GhostContext, exp) do
95
97
  process(exp)
96
98
  end
@@ -102,9 +104,9 @@ module Reek
102
104
  #
103
105
  # Foo = Class.new Bar
104
106
  #
105
- def process_casgn(exp)
107
+ def process_casgn(exp, parent)
106
108
  if exp.defines_module?
107
- process_module(exp)
109
+ process_module(exp, parent)
108
110
  else
109
111
  process(exp)
110
112
  end
@@ -118,8 +120,8 @@ module Reek
118
120
  #
119
121
  # Given the above example we would count 2 statements overall.
120
122
  #
121
- def process_def(exp)
122
- inside_new_context(current_context.method_context_class, exp) do
123
+ def process_def(exp, parent)
124
+ inside_new_context(current_context.method_context_class, exp, parent) do
123
125
  increase_statement_count_by(exp.body)
124
126
  process(exp)
125
127
  end
@@ -133,8 +135,8 @@ module Reek
133
135
  #
134
136
  # Given the above example we would count 2 statements overall.
135
137
  #
136
- def process_defs(exp)
137
- inside_new_context(Context::SingletonMethodContext, exp) do
138
+ def process_defs(exp, parent)
139
+ inside_new_context(Context::SingletonMethodContext, exp, parent) do
138
140
  increase_statement_count_by(exp.body)
139
141
  process(exp)
140
142
  end
@@ -150,14 +152,14 @@ module Reek
150
152
  # we also record to what the method call is referring to
151
153
  # which we later use for smell detectors like FeatureEnvy.
152
154
  #
153
- def process_send(exp)
155
+ def process_send(exp, _parent)
156
+ process(exp)
154
157
  case current_context
155
158
  when Context::ModuleContext
156
159
  handle_send_for_modules exp
157
160
  when Context::MethodContext
158
161
  handle_send_for_methods exp
159
162
  end
160
- process(exp)
161
163
  end
162
164
 
163
165
  # Handles `op_asgn` nodes a.k.a. Ruby's assignment operators.
@@ -172,7 +174,7 @@ module Reek
172
174
  #
173
175
  # We record one reference to `x` given the example above.
174
176
  #
175
- def process_op_asgn(exp)
177
+ def process_op_asgn(exp, _parent)
176
178
  current_context.record_call_to(exp)
177
179
  process(exp)
178
180
  end
@@ -191,7 +193,7 @@ module Reek
191
193
  #
192
194
  # We record one reference to `self`.
193
195
  #
194
- def process_ivar(exp)
196
+ def process_ivar(exp, _parent)
195
197
  current_context.record_use_of_self
196
198
  process(exp)
197
199
  end
@@ -204,7 +206,7 @@ module Reek
204
206
  #
205
207
  # def self.foo; end
206
208
  #
207
- def process_self(_)
209
+ def process_self(_exp, _parent)
208
210
  current_context.record_use_of_self
209
211
  end
210
212
 
@@ -224,7 +226,7 @@ module Reek
224
226
  #
225
227
  # We record one reference to `self`.
226
228
  #
227
- def process_zsuper(_)
229
+ def process_zsuper(_exp, _parent)
228
230
  current_context.record_use_of_self
229
231
  end
230
232
 
@@ -248,7 +250,7 @@ module Reek
248
250
  #
249
251
  # We record one reference to `self`.
250
252
  #
251
- def process_super(exp)
253
+ def process_super(exp, _parent)
252
254
  current_context.record_use_of_self
253
255
  process(exp)
254
256
  end
@@ -261,7 +263,7 @@ module Reek
261
263
  #
262
264
  # Counts non-empty blocks as one statement.
263
265
  #
264
- def process_block(exp)
266
+ def process_block(exp, _parent)
265
267
  increase_statement_count_by(exp.block)
266
268
  process(exp)
267
269
  end
@@ -281,7 +283,7 @@ module Reek
281
283
  # At the end we subtract one statement because the surrounding context was already counted
282
284
  # as one (e.g. via `process_def`).
283
285
  #
284
- def process_begin(exp)
286
+ def process_begin(exp, _parent)
285
287
  increase_statement_count_by(exp.children)
286
288
  decrease_statement_count
287
289
  process(exp)
@@ -307,7 +309,7 @@ module Reek
307
309
  # `children[1]` refers to the `if` body (so `puts 'bingo'` from above) and
308
310
  # `children[2]` to the `else` body (so `3` from above), which might be nil.
309
311
  #
310
- def process_if(exp)
312
+ def process_if(exp, _parent)
311
313
  children = exp.children
312
314
  increase_statement_count_by(children[1])
313
315
  increase_statement_count_by(children[2])
@@ -330,7 +332,7 @@ module Reek
330
332
  #
331
333
  # `children[1]` below refers to the `while` body (so `puts 'bingo'` from above)
332
334
  #
333
- def process_while(exp)
335
+ def process_while(exp, _parent)
334
336
  increase_statement_count_by(exp.children[1])
335
337
  decrease_statement_count
336
338
  process(exp)
@@ -353,7 +355,7 @@ module Reek
353
355
  #
354
356
  # `children[2]` below refers to the `while` body (so `puts i` from above)
355
357
  #
356
- def process_for(exp)
358
+ def process_for(exp, _parent)
357
359
  increase_statement_count_by(exp.children[2])
358
360
  decrease_statement_count
359
361
  process(exp)
@@ -383,7 +385,7 @@ module Reek
383
385
  # `exp` would be the whole method body wrapped under a `rescue` node.
384
386
  # See `process_resbody` for additional reference.
385
387
  #
386
- def process_rescue(exp)
388
+ def process_rescue(exp, _parent)
387
389
  increase_statement_count_by(exp.children.first)
388
390
  decrease_statement_count
389
391
  process(exp)
@@ -411,7 +413,7 @@ module Reek
411
413
  # `exp` would be the whole `rescue` body.
412
414
  # See `process_rescue` for additional reference.
413
415
  #
414
- def process_resbody(exp)
416
+ def process_resbody(exp, _parent)
415
417
  increase_statement_count_by(exp.children[1..-1].compact)
416
418
  process(exp)
417
419
  end
@@ -433,7 +435,7 @@ module Reek
433
435
  # At the end we subtract one statement because the surrounding context was already counted
434
436
  # as one (e.g. via `process_def`).
435
437
  #
436
- def process_case(exp)
438
+ def process_case(exp, _parent)
437
439
  increase_statement_count_by(exp.else_body)
438
440
  decrease_statement_count
439
441
  process(exp)
@@ -459,7 +461,7 @@ module Reek
459
461
  #
460
462
  # Counts the `when` body.
461
463
  #
462
- def process_when(exp)
464
+ def process_when(exp, _parent)
463
465
  increase_statement_count_by(exp.body)
464
466
  process(exp)
465
467
  end
@@ -468,7 +470,7 @@ module Reek
468
470
  self.class.private_method_defined?(name)
469
471
  end
470
472
 
471
- # :reek:ControlParameter
473
+ # @quality :reek:ControlParameter
472
474
  def increase_statement_count_by(sexp)
473
475
  current_context.statement_counter.increase_by sexp
474
476
  end
@@ -480,37 +482,35 @@ module Reek
480
482
  # Stores a reference to the current context, creates a nested new one,
481
483
  # yields to the given block and then restores the previous context.
482
484
  #
483
- # @param klass [Context::*Context] - context class
484
- # @param exp - current expression
485
+ # @param klass [Context::*Context] context class
486
+ # @param args arguments for the class initializer
485
487
  # @yield block
486
488
  #
487
- def inside_new_context(klass, exp)
488
- new_context = append_new_context(klass, exp)
489
+ def inside_new_context(klass, *args)
490
+ new_context = append_new_context(klass, *args)
489
491
 
490
492
  orig, self.current_context = current_context, new_context
491
493
  yield
492
494
  self.current_context = orig
493
495
  end
494
496
 
495
- # Append a new child context to the current context but does not change the
496
- # current context.
497
+ # Appends a new child context to the current context but does not change
498
+ # the current context.
497
499
  #
498
- # @param klass [Context::*Context] - context class
499
- # @param args - arguments for the class initializer
500
+ # @param klass [Context::*Context] context class
501
+ # @param args arguments for the class initializer
500
502
  #
501
- # @return [Context::*Context] - the context that was appended
503
+ # @return [Context::*Context] the context that was appended
502
504
  #
503
505
  def append_new_context(klass, *args)
504
- klass.new(current_context, *args).tap do |new_context|
506
+ klass.new(*args).tap do |new_context|
505
507
  new_context.register_with_parent(current_context)
506
508
  end
507
509
  end
508
510
 
509
511
  def handle_send_for_modules(exp)
510
- method_name = exp.name
511
- arg_names = exp.arg_names
512
- current_context.track_visibility(method_name, arg_names)
513
- current_context.track_singleton_visibility(method_name, arg_names)
512
+ arg_names = exp.args.map { |arg| arg.children.first }
513
+ current_context.track_visibility(exp.name, arg_names)
514
514
  register_attributes(exp)
515
515
  end
516
516