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
@@ -0,0 +1,210 @@
1
+ ---
2
+ type: map
3
+ mapping:
4
+ "detectors":
5
+ type: map
6
+ mapping: &all_detectors
7
+ Attribute:
8
+ type: map
9
+ mapping: &detector_base
10
+ "enabled":
11
+ type: bool
12
+ "exclude":
13
+ type: seq
14
+ sequence:
15
+ - type: str
16
+ BooleanParameter:
17
+ type: map
18
+ mapping:
19
+ <<: *detector_base
20
+ ClassVariable:
21
+ type: map
22
+ mapping:
23
+ <<: *detector_base
24
+ ControlParameter:
25
+ type: map
26
+ mapping:
27
+ <<: *detector_base
28
+ DataClump:
29
+ type: map
30
+ mapping:
31
+ <<: *detector_base
32
+ max_copies:
33
+ type: number
34
+ min_clump_size:
35
+ type: number
36
+ DuplicateMethodCall:
37
+ type: map
38
+ mapping:
39
+ <<: *detector_base
40
+ max_calls:
41
+ type: number
42
+ allow_calls:
43
+ type: seq
44
+ sequence:
45
+ - type: str
46
+ FeatureEnvy:
47
+ type: map
48
+ mapping:
49
+ <<: *detector_base
50
+ InstanceVariableAssumption:
51
+ type: map
52
+ mapping:
53
+ <<: *detector_base
54
+ IrresponsibleModule:
55
+ type: map
56
+ mapping:
57
+ <<: *detector_base
58
+ LongParameterList:
59
+ type: map
60
+ mapping:
61
+ <<: *detector_base
62
+ max_params:
63
+ type: number
64
+ overrides:
65
+ type: map
66
+ mapping:
67
+ initialize:
68
+ type: map
69
+ mapping:
70
+ max_params:
71
+ type: number
72
+ LongYieldList:
73
+ type: map
74
+ mapping:
75
+ <<: *detector_base
76
+ max_params:
77
+ type: number
78
+ ManualDispatch:
79
+ type: map
80
+ mapping:
81
+ <<: *detector_base
82
+ MissingSafeMethod:
83
+ type: map
84
+ mapping:
85
+ <<: *detector_base
86
+ ModuleInitialize:
87
+ type: map
88
+ mapping:
89
+ <<: *detector_base
90
+ NestedIterators:
91
+ type: map
92
+ mapping:
93
+ <<: *detector_base
94
+ max_allowed_nesting:
95
+ type: number
96
+ ignore_iterators:
97
+ type: seq
98
+ sequence:
99
+ - type: str
100
+ NilCheck:
101
+ type: map
102
+ mapping:
103
+ <<: *detector_base
104
+ RepeatedConditional:
105
+ type: map
106
+ mapping:
107
+ <<: *detector_base
108
+ max_ifs:
109
+ type: number
110
+ SubclassedFromCoreClass:
111
+ type: map
112
+ mapping:
113
+ <<: *detector_base
114
+ Syntax:
115
+ type: map
116
+ mapping:
117
+ <<: *detector_base
118
+ TooManyConstants:
119
+ type: map
120
+ mapping:
121
+ <<: *detector_base
122
+ max_constants:
123
+ type: number
124
+ TooManyInstanceVariables:
125
+ type: map
126
+ mapping:
127
+ <<: *detector_base
128
+ max_instance_variables:
129
+ type: number
130
+ TooManyMethods:
131
+ type: map
132
+ mapping:
133
+ <<: *detector_base
134
+ max_methods:
135
+ type: number
136
+ TooManyStatements:
137
+ type: map
138
+ mapping:
139
+ <<: *detector_base
140
+ max_statements:
141
+ type: number
142
+ UncommunicativeMethodName:
143
+ type: map
144
+ mapping:
145
+ <<: *detector_base
146
+ reject: &reject_settings
147
+ type: seq
148
+ sequence:
149
+ - type: str
150
+ accept: &accept_settings
151
+ type: seq
152
+ sequence:
153
+ - type: str
154
+ UncommunicativeModuleName:
155
+ type: map
156
+ mapping:
157
+ <<: *detector_base
158
+ reject: *reject_settings
159
+ accept: *accept_settings
160
+ UncommunicativeParameterName:
161
+ type: map
162
+ mapping:
163
+ <<: *detector_base
164
+ reject: *reject_settings
165
+ accept: *accept_settings
166
+ UncommunicativeVariableName:
167
+ type: map
168
+ mapping:
169
+ <<: *detector_base
170
+ reject: *reject_settings
171
+ accept: *accept_settings
172
+ UnusedParameters:
173
+ type: map
174
+ mapping:
175
+ <<: *detector_base
176
+ UnusedPrivateMethod:
177
+ type: map
178
+ mapping:
179
+ <<: *detector_base
180
+ UtilityFunction:
181
+ type: map
182
+ mapping:
183
+ <<: *detector_base
184
+ public_methods_only:
185
+ type: bool
186
+
187
+ "directories":
188
+ type: map
189
+ mapping:
190
+ # For any given key that is not matched somewhere else we'll apply the schema below.
191
+ # So this will just slurp in every directory we throw at it and then validate everything under
192
+ # it against all detectors - for instance:
193
+ #
194
+ # directories:
195
+ # "web_app/app/controllers":
196
+ # NestedIterators:
197
+ # enabled: false
198
+ # "web_app/app/helpers":
199
+ # UtilityFunction:
200
+ # enabled: false
201
+ #
202
+ # For details check out: http://www.kuwata-lab.com/kwalify/ruby/users-guide.02.html#tips-default
203
+ =:
204
+ type: map
205
+ mapping: *all_detectors
206
+
207
+ "exclude_paths":
208
+ type: seq
209
+ sequence:
210
+ - type: str
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+ require_relative '../cli/silencer'
5
+ Reek::CLI::Silencer.without_warnings { require 'kwalify' }
6
+ require_relative '../errors/config_file_error'
7
+
8
+ module Reek
9
+ module Configuration
10
+ #
11
+ # Schema validator module.
12
+ #
13
+ class SchemaValidator
14
+ SCHEMA_FILE_PATH = File.expand_path('./schema.yml', __dir__)
15
+
16
+ def initialize(configuration)
17
+ @configuration = configuration
18
+ @validator = CLI::Silencer.without_warnings do
19
+ schema_file = Kwalify::Yaml.load_file(SCHEMA_FILE_PATH)
20
+ Kwalify::Validator.new(schema_file)
21
+ end
22
+ end
23
+
24
+ def validate
25
+ errors = CLI::Silencer.without_warnings { @validator.validate @configuration }
26
+ return if !errors || errors.empty?
27
+ raise Errors::ConfigFileError, error_message(errors)
28
+ end
29
+
30
+ private
31
+
32
+ # :reek:UtilityFunction
33
+ def error_message(errors)
34
+ "We found some problems with your configuration file: #{CLI::Silencer.silently { errors.join(', ') }}"
35
+ end
36
+ end
37
+ end
38
+ end
@@ -7,7 +7,7 @@ module Reek
7
7
  #
8
8
  # A context wrapper for attribute definitions found in a syntax tree.
9
9
  #
10
- # :reek:Attribute
10
+ # @quality :reek:Attribute
11
11
  class AttributeContext < CodeContext
12
12
  attr_accessor :visibility
13
13
 
@@ -14,12 +14,11 @@ module Reek
14
14
  # code element. CodeContexts form a tree in the same way the code does,
15
15
  # with each context holding a reference to a unique outer context.
16
16
  #
17
- # :reek:TooManyMethods: { max_methods: 19 }
18
- # :reek:TooManyInstanceVariables: { max_instance_variables: 8 }
17
+ # @quality :reek:TooManyMethods { max_methods: 19 }
18
+ # @quality :reek:TooManyInstanceVariables { max_instance_variables: 8 }
19
19
  class CodeContext
20
20
  include Enumerable
21
21
  extend Forwardable
22
- delegate each_node: :exp
23
22
  delegate [:name, :type] => :exp
24
23
 
25
24
  attr_reader :children, :parent, :exp, :statement_counter
@@ -40,12 +39,12 @@ module Reek
40
39
  # @yield block that is executed for every node.
41
40
  #
42
41
  def local_nodes(type, ignored = [], &blk)
43
- ignored += [:casgn, :class, :module]
44
- each_node(type, ignored, &blk)
42
+ ignored |= [:class, :module]
43
+ exp.each_node(type, ignored, &blk)
45
44
  end
46
45
 
47
46
  # Iterate over `self` and child contexts.
48
- # 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
49
48
  # `CodeContexts` here, not AST nodes (see `Reek::AST::Node`).
50
49
  #
51
50
  # @yield block that is executed for every node.
@@ -111,17 +110,15 @@ module Reek
111
110
  self
112
111
  end
113
112
 
114
- # :reek:TooManyStatements: { max_statements: 6 }
115
- # :reek:FeatureEnvy
113
+ # @quality :reek:TooManyStatements { max_statements: 6 }
114
+ # @quality :reek:FeatureEnvy
116
115
  def record_call_to(exp)
117
116
  receiver = exp.receiver
118
117
  type = receiver ? receiver.type : :self
119
118
  line = exp.line
120
119
  case type
121
120
  when :lvar, :lvasgn
122
- unless exp.object_creation_call?
123
- refs.record_reference(name: receiver.name, line: line)
124
- end
121
+ refs.record_reference(name: receiver.name, line: line) unless exp.object_creation_call?
125
122
  when :self
126
123
  refs.record_reference(name: :self, line: line)
127
124
  end
@@ -7,7 +7,7 @@ module Reek
7
7
  #
8
8
  # A context wrapper for any method definition found in a syntax tree.
9
9
  #
10
- # :reek:Attribute
10
+ # @quality :reek:Attribute
11
11
  class MethodContext < CodeContext
12
12
  attr_accessor :visibility
13
13
  attr_reader :refs
@@ -23,24 +23,19 @@ module Reek
23
23
  end
24
24
 
25
25
  def uses_param?(param)
26
- # local_nodes(:lvasgn) catches:
26
+ # :lvasgn catches:
27
+ #
27
28
  # def foo(bar); bar += 1; end
28
- # In this example there is no `lvar` node present.
29
29
  #
30
- # local_nodes(:lvar) catches:
30
+ # :lvar catches:
31
+ #
31
32
  # def foo(bar); other(bar); end
32
33
  # def foo(bar); tmp = other(bar); tmp[0]; end
33
34
  #
34
- # Note that in the last example the `lvar` node for `bar` is part of an `lvasgn` node for `tmp`.
35
- # This means that if we would just search for [:lvar, :lvasgn]
36
- # (e.g. via Reek::AST::Node#find_nodes) this would fail for this example since we would
37
- # stop at the `lvasgn` and not detect the contained `lvar`.
38
- # Hence we first get all `lvar` nodes followed by all `lvasgn` nodes.
39
- #
40
- (local_nodes(:lvar) + local_nodes(:lvasgn)).find { |node| node.var_name == param.name }
35
+ local_nodes([:lvar, :lvasgn]).find { |node| node.var_name == param.name }
41
36
  end
42
37
 
43
- # :reek:FeatureEnvy
38
+ # @quality :reek:FeatureEnvy
44
39
  def unused_params
45
40
  exp.arguments.reject do |param|
46
41
  param.anonymous_splat? ||
@@ -43,9 +43,9 @@ module Reek
43
43
  AttributeContext
44
44
  end
45
45
 
46
- def defined_instance_methods(visibility: :public)
46
+ def defined_instance_methods(visibility: :any)
47
47
  instance_method_children.select do |context|
48
- context.visibility == visibility
48
+ visibility == :any || context.visibility == visibility
49
49
  end
50
50
  end
51
51
 
@@ -59,7 +59,7 @@ module Reek
59
59
  # @deprecated use `defined_instance_methods` instead
60
60
  #
61
61
  def node_instance_methods
62
- local_nodes(:def)
62
+ local_nodes(:def).to_a
63
63
  end
64
64
 
65
65
  def descriptively_commented?
@@ -73,7 +73,7 @@ module Reek
73
73
  #
74
74
  # @return true if the module is a namespace module
75
75
  #
76
- # :reek:FeatureEnvy
76
+ # @quality :reek:FeatureEnvy
77
77
  def namespace_module?
78
78
  return false if exp.type == :casgn
79
79
  children = exp.direct_children
@@ -20,9 +20,9 @@ module Reek
20
20
  # counting. Ideally `ContextBuilder` would only build up the context tree and leave the
21
21
  # statement and reference counting to the contexts.
22
22
  #
23
- # :reek:TooManyMethods: { max_methods: 31 }
24
- # :reek:UnusedPrivateMethod: { exclude: [ !ruby/regexp /process_/ ] }
25
- # :reek:DataClump
23
+ # @quality :reek:TooManyMethods { max_methods: 31 }
24
+ # @quality :reek:UnusedPrivateMethod { exclude: [ !ruby/regexp /process_/ ] }
25
+ # @quality :reek:DataClump
26
26
  class ContextBuilder
27
27
  attr_reader :context_tree
28
28
 
@@ -206,7 +206,7 @@ module Reek
206
206
  #
207
207
  # def self.foo; end
208
208
  #
209
- def process_self(_, _parent)
209
+ def process_self(_exp, _parent)
210
210
  current_context.record_use_of_self
211
211
  end
212
212
 
@@ -226,7 +226,7 @@ module Reek
226
226
  #
227
227
  # We record one reference to `self`.
228
228
  #
229
- def process_zsuper(_, _parent)
229
+ def process_zsuper(_exp, _parent)
230
230
  current_context.record_use_of_self
231
231
  end
232
232
 
@@ -470,7 +470,7 @@ module Reek
470
470
  self.class.private_method_defined?(name)
471
471
  end
472
472
 
473
- # :reek:ControlParameter
473
+ # @quality :reek:ControlParameter
474
474
  def increase_statement_count_by(sexp)
475
475
  current_context.statement_counter.increase_by sexp
476
476
  end
@@ -482,8 +482,8 @@ module Reek
482
482
  # Stores a reference to the current context, creates a nested new one,
483
483
  # yields to the given block and then restores the previous context.
484
484
  #
485
- # @param klass [Context::*Context] - context class
486
- # @param args - arguments for the class initializer
485
+ # @param klass [Context::*Context] context class
486
+ # @param args arguments for the class initializer
487
487
  # @yield block
488
488
  #
489
489
  def inside_new_context(klass, *args)
@@ -497,10 +497,10 @@ module Reek
497
497
  # Appends a new child context to the current context but does not change
498
498
  # the current context.
499
499
  #
500
- # @param klass [Context::*Context] - context class
501
- # @param args - arguments for the class initializer
500
+ # @param klass [Context::*Context] context class
501
+ # @param args arguments for the class initializer
502
502
  #
503
- # @return [Context::*Context] - the context that was appended
503
+ # @return [Context::*Context] the context that was appended
504
504
  #
505
505
  def append_new_context(klass, *args)
506
506
  klass.new(*args).tap do |new_context|
@@ -44,6 +44,12 @@ module Reek
44
44
  end
45
45
  end
46
46
 
47
+ # @return [Array<String>] The names of all known SmellDetectors
48
+ # e.g. ["BooleanParameter", "ClassVariable"].
49
+ def self.available_detector_names
50
+ smell_types.map(&:smell_type)
51
+ end
52
+
47
53
  private
48
54
 
49
55
  attr_reader :configuration, :smell_types
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Reek
4
+ # Generate versioned links to our documentation
5
+ module DocumentationLink
6
+ HELP_LINK_TEMPLATE = 'https://github.com/troessner/reek/blob/v%<version>s/docs/%<item>s.md'
7
+
8
+ module_function
9
+
10
+ # Build link to the documentation about the given subject for the current
11
+ # version of Reek. The subject can be either a smell type like
12
+ # 'FeatureEnvy' or a general subject like 'Rake Task'.
13
+ #
14
+ # @param subject [String]
15
+ # @return [String] the full URL for the relevant documentation
16
+ def build(subject)
17
+ Kernel.format(HELP_LINK_TEMPLATE, version: Version::STRING, item: name_to_param(subject))
18
+ end
19
+
20
+ # Convert the given subject name to a form that is acceptable in a URL.
21
+ def name_to_param(name)
22
+ # Splits the subject on the start of capitalized words, optionally
23
+ # preceded by a space. The space is discarded, the start of the word is
24
+ # not.
25
+ name.split(/ *(?=[A-Z][a-z])/).join('-')
26
+ end
27
+ end
28
+ end
@@ -1,24 +1,25 @@
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 configure a detector with an option
8
9
  # which is unknown to it.
9
10
  class BadDetectorConfigurationKeyInCommentError < BaseError
10
- UNKNOWN_SMELL_DETECTOR_MESSAGE = <<-MESSAGE.freeze
11
+ UNKNOWN_SMELL_DETECTOR_MESSAGE = <<-MESSAGE
11
12
 
12
- Error: You are trying to configure the smell detector '%s'
13
- in one of your source code comments with the unknown option %s.
14
- The source is '%s' and the comment belongs to the expression starting in line %d.
13
+ Error: You are trying to configure the smell detector '%<detector>s'
14
+ in one of your source code comments with the unknown option %<option>s.
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 basic options are available: https://github.com/troessner/reek/blob/master/docs/Basic-Smell-Options.md
21
+ * how to configure Reek via source code comments: #{DocumentationLink.build('Smell Suppression')}
22
+ * what basic options are available: #{DocumentationLink.build('Basic Smell Options')}
22
23
  * what custom options are available by checking the detector specific documentation in /docs
23
24
  Update the offensive comment (or remove it if no longer applicable) and re-run Reek.
24
25
 
@@ -26,11 +27,11 @@ module Reek
26
27
 
27
28
  def initialize(detector_name:, offensive_keys:, source:, line:, original_comment:)
28
29
  message = format(UNKNOWN_SMELL_DETECTOR_MESSAGE,
29
- detector_name,
30
- offensive_keys,
31
- source,
32
- line,
33
- original_comment)
30
+ detector: detector_name,
31
+ option: offensive_keys,
32
+ source: source,
33
+ line: line,
34
+ comment: original_comment)
34
35
  super message
35
36
  end
36
37
  end
@@ -1,6 +1,7 @@
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
@@ -8,28 +9,28 @@ module Reek
8
9
  # This might happen for multiple reasons. The users might have a typo in
9
10
  # his comment or he might use a detector that does not exist anymore.
10
11
  class BadDetectorInCommentError < BaseError
11
- UNKNOWN_SMELL_DETECTOR_MESSAGE = <<-MESSAGE.freeze
12
+ UNKNOWN_SMELL_DETECTOR_MESSAGE = <<-MESSAGE
12
13
 
13
- Error: You are trying to configure an unknown smell detector '%s' in one
14
+ Error: You are trying to configure an unknown smell detector '%<detector>s' in one
14
15
  of your source code comments.
15
- The source is '%s' and the comment belongs to the expression starting in line %d.
16
+ The source is '%<source>s' and the comment belongs to the expression starting in line %<line>d.
16
17
  Here's the original comment:
17
18
 
18
- %s
19
+ %<comment>s
19
20
 
20
21
  Please see the Reek docs for:
21
- * how to configure Reek via source code comments: https://github.com/troessner/reek/blob/master/docs/Smell-Suppression.md
22
- * what smell detectors are available: https://github.com/troessner/reek/blob/master/docs/Code-Smells.md
22
+ * how to configure Reek via source code comments: #{DocumentationLink.build('Smell Suppression')}
23
+ * what smell detectors are available: #{DocumentationLink.build('Code Smells')}
23
24
  Update the offensive comment (or remove it if no longer applicable) and re-run Reek.
24
25
 
25
26
  MESSAGE
26
27
 
27
28
  def initialize(detector_name:, source:, line:, original_comment:)
28
29
  message = format(UNKNOWN_SMELL_DETECTOR_MESSAGE,
29
- detector_name,
30
- source,
31
- line,
32
- original_comment)
30
+ detector: detector_name,
31
+ source: source,
32
+ line: line,
33
+ comment: original_comment)
33
34
  super message
34
35
  end
35
36
  end
@@ -4,6 +4,9 @@ module Reek
4
4
  module Errors
5
5
  # Base class for all runtime Reek errors
6
6
  class BaseError < ::RuntimeError
7
+ def long_message
8
+ message
9
+ end
7
10
  end
8
11
  end
9
12
  end
@@ -0,0 +1,11 @@
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 bad config file
8
+ class ConfigFileError < BaseError
9
+ end
10
+ end
11
+ end
@@ -6,9 +6,11 @@ module Reek
6
6
  module Errors
7
7
  # Gets raised when Reek is unable to process the source due to an EncodingError
8
8
  class EncodingError < BaseError
9
- ENCODING_ERROR_TEMPLATE = <<-MESSAGE.freeze
9
+ TEMPLATE = "Source '%<source>s' cannot be processed by Reek due to an encoding error in the source file."
10
+
11
+ LONG_TEMPLATE = <<-MESSAGE
10
12
  !!!
11
- Source '%s' cannot be processed by Reek due to an encoding error in the source file.
13
+ %<message>s
12
14
 
13
15
  This is a problem that is outside of Reek's scope and should be fixed by you, the
14
16
  user, in order for Reek being able to continue.
@@ -17,21 +19,24 @@ module Reek
17
19
 
18
20
  Exception message:
19
21
 
20
- %s
22
+ %<exception>s
21
23
 
22
- Original exception:
24
+ Original backtrace:
23
25
 
24
- %s
26
+ %<original>s
25
27
 
26
28
  !!!
27
29
  MESSAGE
28
30
 
29
- def initialize(origin:, original_exception:)
30
- message = format(ENCODING_ERROR_TEMPLATE,
31
- origin,
32
- original_exception.message,
33
- original_exception.backtrace.join("\n\t"))
34
- super message
31
+ def initialize(origin:)
32
+ super format(TEMPLATE, source: origin)
33
+ end
34
+
35
+ def long_message
36
+ format(LONG_TEMPLATE,
37
+ message: message,
38
+ exception: cause.inspect,
39
+ original: cause.backtrace.join("\n\t"))
35
40
  end
36
41
  end
37
42
  end