reek 4.5.0 → 4.6.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.
- checksums.yaml +4 -4
- data/.rubocop.yml +10 -7
- data/.travis.yml +13 -9
- data/CHANGELOG.md +36 -3
- data/Dockerfile +6 -5
- data/Gemfile +8 -7
- data/README.md +45 -27
- data/docs/How-To-Write-New-Detectors.md +6 -6
- data/docs/Irresponsible-Module.md +8 -1
- data/docs/Nil-Check.md +1 -1
- data/docs/Prima-Donna-Method.md +27 -0
- data/docs/RSpec-matchers.md +5 -10
- data/docs/Unused-Private-Method.md +27 -0
- data/docs/templates/default/docstring/setup.rb +1 -8
- data/features/command_line_interface/options.feature +5 -1
- data/features/command_line_interface/stdin.feature +1 -1
- data/features/configuration_files/exclude_paths_directives.feature +43 -0
- data/features/configuration_files/warn_about_multiple_configuration_files.feature +44 -0
- data/features/configuration_via_source_comments/erroneous_source_comments.feature +15 -0
- data/features/samples.feature +3 -8
- data/features/step_definitions/reek_steps.rb +5 -5
- data/features/todo_list.feature +1 -2
- data/lib/reek/ast/reference_collector.rb +2 -4
- data/lib/reek/ast/sexp_extensions/arguments.rb +0 -5
- data/lib/reek/ast/sexp_extensions/constant.rb +1 -0
- data/lib/reek/cli/application.rb +7 -8
- data/lib/reek/cli/command/report_command.rb +3 -1
- data/lib/reek/cli/options.rb +29 -13
- data/lib/reek/cli/status.rb +10 -0
- data/lib/reek/code_comment.rb +48 -6
- data/lib/reek/configuration/configuration_file_finder.rb +87 -27
- data/lib/reek/context/ghost_context.rb +1 -2
- data/lib/reek/context_builder.rb +26 -1
- data/lib/reek/detector_repository.rb +64 -0
- data/lib/reek/errors/bad_detector_configuration_key_in_comment_error.rb +37 -0
- data/lib/reek/errors/bad_detector_in_comment_error.rb +2 -1
- data/lib/reek/errors/base_error.rb +9 -0
- data/lib/reek/errors/garbage_detector_configuration_in_comment_error.rb +2 -1
- data/lib/reek/errors/incomprehensible_source_error.rb +47 -0
- data/lib/reek/errors/parse_error.rb +19 -0
- data/lib/reek/examiner.rb +17 -41
- data/lib/reek/logging_error_handler.rb +15 -0
- data/lib/reek/report/code_climate/code_climate_configuration.rb +12 -0
- data/lib/reek/report/code_climate/code_climate_configuration.yml +156 -0
- data/lib/reek/report/code_climate/code_climate_fingerprint.rb +4 -2
- data/lib/reek/report/code_climate/code_climate_formatter.rb +2 -2
- data/lib/reek/smell_configuration.rb +64 -0
- data/lib/reek/smell_detectors/attribute.rb +0 -2
- data/lib/reek/smell_detectors/base_detector.rb +24 -4
- data/lib/reek/smell_detectors/boolean_parameter.rb +0 -1
- data/lib/reek/smell_detectors/class_variable.rb +0 -1
- data/lib/reek/smell_detectors/control_parameter.rb +0 -1
- data/lib/reek/smell_detectors/data_clump.rb +0 -1
- data/lib/reek/smell_detectors/duplicate_method_call.rb +0 -1
- data/lib/reek/smell_detectors/feature_envy.rb +0 -1
- data/lib/reek/smell_detectors/instance_variable_assumption.rb +0 -1
- data/lib/reek/smell_detectors/irresponsible_module.rb +0 -1
- data/lib/reek/smell_detectors/long_parameter_list.rb +0 -2
- data/lib/reek/smell_detectors/long_yield_list.rb +0 -1
- data/lib/reek/smell_detectors/manual_dispatch.rb +4 -6
- data/lib/reek/smell_detectors/module_initialize.rb +5 -8
- data/lib/reek/smell_detectors/nested_iterators.rb +0 -1
- data/lib/reek/smell_detectors/nil_check.rb +0 -1
- data/lib/reek/smell_detectors/prima_donna_method.rb +30 -1
- data/lib/reek/smell_detectors/repeated_conditional.rb +0 -1
- data/lib/reek/smell_detectors/subclassed_from_core_class.rb +0 -1
- data/lib/reek/smell_detectors/too_many_constants.rb +0 -1
- data/lib/reek/smell_detectors/too_many_instance_variables.rb +0 -1
- data/lib/reek/smell_detectors/too_many_methods.rb +0 -1
- data/lib/reek/smell_detectors/too_many_statements.rb +0 -1
- data/lib/reek/smell_detectors/uncommunicative_method_name.rb +0 -1
- data/lib/reek/smell_detectors/uncommunicative_module_name.rb +0 -1
- data/lib/reek/smell_detectors/uncommunicative_parameter_name.rb +0 -1
- data/lib/reek/smell_detectors/uncommunicative_variable_name.rb +0 -1
- data/lib/reek/smell_detectors/unused_parameters.rb +0 -1
- data/lib/reek/smell_detectors/unused_private_method.rb +0 -2
- data/lib/reek/smell_detectors/utility_function.rb +0 -1
- data/lib/reek/smell_warning.rb +85 -0
- data/lib/reek/source/source_code.rb +2 -1
- data/lib/reek/source/source_locator.rb +15 -3
- data/lib/reek/spec/should_reek_of.rb +1 -1
- data/lib/reek/spec.rb +6 -4
- data/lib/reek/version.rb +1 -1
- data/reek.gemspec +1 -1
- data/samples/configuration/more_than_one_configuration_file/todo.reek +0 -0
- data/samples/configuration/single_configuration_file/.reek +0 -0
- data/spec/factories/factories.rb +2 -10
- data/spec/quality/reek_source_spec.rb +5 -3
- data/spec/reek/ast/reference_collector_spec.rb +0 -17
- data/spec/reek/cli/application_spec.rb +25 -1
- data/spec/reek/cli/command/report_command_spec.rb +2 -2
- data/spec/reek/cli/command/todo_list_command_spec.rb +14 -71
- data/spec/reek/cli/options_spec.rb +4 -0
- data/spec/reek/code_comment_spec.rb +47 -0
- data/spec/reek/configuration/configuration_file_finder_spec.rb +38 -15
- data/spec/reek/context/code_context_spec.rb +10 -10
- data/spec/reek/context/method_context_spec.rb +1 -1
- data/spec/reek/context/module_context_spec.rb +8 -4
- data/spec/reek/{smell_detectors/detector_repository_spec.rb → detector_repository_spec.rb} +3 -3
- data/spec/reek/examiner_spec.rb +39 -40
- data/spec/reek/logging_error_handler_spec.rb +24 -0
- data/spec/reek/report/code_climate/code_climate_configuration_spec.rb +24 -0
- data/spec/reek/report/code_climate/code_climate_fingerprint_spec.rb +9 -9
- data/spec/reek/report/yaml_report_spec.rb +4 -4
- data/spec/reek/{smell_detectors/smell_configuration_spec.rb → smell_configuration_spec.rb} +3 -3
- data/spec/reek/smell_detectors/base_detector_spec.rb +18 -0
- data/spec/reek/smell_detectors/duplicate_method_call_spec.rb +2 -2
- data/spec/reek/smell_detectors/feature_envy_spec.rb +18 -8
- data/spec/reek/smell_detectors/manual_dispatch_spec.rb +13 -0
- data/spec/reek/smell_detectors/module_initialize_spec.rb +23 -2
- data/spec/reek/smell_detectors/nested_iterators_spec.rb +1 -1
- data/spec/reek/smell_detectors/prima_donna_method_spec.rb +12 -0
- data/spec/reek/smell_detectors/subclassed_from_core_class_spec.rb +0 -5
- data/spec/reek/smell_detectors/uncommunicative_variable_name_spec.rb +2 -2
- data/spec/reek/smell_detectors/unused_parameters_spec.rb +1 -1
- data/spec/reek/{smell_detectors/smell_warning_spec.rb → smell_warning_spec.rb} +9 -9
- data/spec/reek/source/source_code_spec.rb +6 -29
- data/spec/reek/source/source_locator_spec.rb +48 -16
- data/spec/reek/spec/should_reek_of_spec.rb +1 -1
- data/spec/reek/spec/should_reek_only_of_spec.rb +4 -4
- data/spec/spec_helper.rb +4 -3
- data/tasks/configuration.rake +2 -2
- metadata +26 -14
- data/lib/reek/smell_detectors/detector_repository.rb +0 -66
- data/lib/reek/smell_detectors/smell_configuration.rb +0 -66
- data/lib/reek/smell_detectors/smell_warning.rb +0 -88
- data/tasks/mutant.rake +0 -14
- /data/samples/configuration/{.reek → more_than_one_configuration_file/regular.reek} +0 -0
data/features/samples.feature
CHANGED
|
@@ -63,7 +63,7 @@ Feature: Basic smell detection
|
|
|
63
63
|
UncommunicativeVariableName: Inline::C#module_name has the variable name 'x' [https://github.com/troessner/reek/blob/master/docs/Uncommunicative-Variable-Name.md]
|
|
64
64
|
UncommunicativeVariableName: Inline::C#parse_signature has the variable name 'x' [https://github.com/troessner/reek/blob/master/docs/Uncommunicative-Variable-Name.md]
|
|
65
65
|
UtilityFunction: Inline::C#strip_comments doesn't depend on instance state (maybe move it to another class?) [https://github.com/troessner/reek/blob/master/docs/Utility-Function.md]
|
|
66
|
-
optparse.rb --
|
|
66
|
+
optparse.rb -- 126 warnings:
|
|
67
67
|
Attribute: OptionParser#banner is a writable attribute [https://github.com/troessner/reek/blob/master/docs/Attribute.md]
|
|
68
68
|
Attribute: OptionParser#default_argv is a writable attribute [https://github.com/troessner/reek/blob/master/docs/Attribute.md]
|
|
69
69
|
Attribute: OptionParser#program_name is a writable attribute [https://github.com/troessner/reek/blob/master/docs/Attribute.md]
|
|
@@ -127,10 +127,6 @@ Feature: Basic smell detection
|
|
|
127
127
|
LongParameterList: OptionParser::Switch#initialize has 7 parameters [https://github.com/troessner/reek/blob/master/docs/Long-Parameter-List.md]
|
|
128
128
|
LongParameterList: OptionParser::Switch#summarize has 5 parameters [https://github.com/troessner/reek/blob/master/docs/Long-Parameter-List.md]
|
|
129
129
|
ManualDispatch: OptionParser#make_switch manually dispatches method call [https://github.com/troessner/reek/blob/master/docs/Manual-Dispatch.md]
|
|
130
|
-
ManualDispatch: OptionParser#make_switch manually dispatches method call [https://github.com/troessner/reek/blob/master/docs/Manual-Dispatch.md]
|
|
131
|
-
ManualDispatch: OptionParser#make_switch manually dispatches method call [https://github.com/troessner/reek/blob/master/docs/Manual-Dispatch.md]
|
|
132
|
-
ManualDispatch: OptionParser::List#accept manually dispatches method call [https://github.com/troessner/reek/blob/master/docs/Manual-Dispatch.md]
|
|
133
|
-
ManualDispatch: OptionParser::List#accept manually dispatches method call [https://github.com/troessner/reek/blob/master/docs/Manual-Dispatch.md]
|
|
134
130
|
ManualDispatch: OptionParser::List#accept manually dispatches method call [https://github.com/troessner/reek/blob/master/docs/Manual-Dispatch.md]
|
|
135
131
|
ManualDispatch: OptionParser::List#add_banner manually dispatches method call [https://github.com/troessner/reek/blob/master/docs/Manual-Dispatch.md]
|
|
136
132
|
ManualDispatch: OptionParser::List#summarize manually dispatches method call [https://github.com/troessner/reek/blob/master/docs/Manual-Dispatch.md]
|
|
@@ -194,7 +190,7 @@ Feature: Basic smell detection
|
|
|
194
190
|
UnusedParameters: OptionParser::Completion#convert has unused parameter 'opt' [https://github.com/troessner/reek/blob/master/docs/Unused-Parameters.md]
|
|
195
191
|
UnusedParameters: OptionParser::Switch::NoArgument#parse has unused parameter 'argv' [https://github.com/troessner/reek/blob/master/docs/Unused-Parameters.md]
|
|
196
192
|
UnusedParameters: OptionParser::Switch::OptionalArgument#parse has unused parameter 'argv' [https://github.com/troessner/reek/blob/master/docs/Unused-Parameters.md]
|
|
197
|
-
redcloth.rb --
|
|
193
|
+
redcloth.rb -- 110 warnings:
|
|
198
194
|
Attribute: RedCloth#filter_html is a writable attribute [https://github.com/troessner/reek/blob/master/docs/Attribute.md]
|
|
199
195
|
Attribute: RedCloth#filter_styles is a writable attribute [https://github.com/troessner/reek/blob/master/docs/Attribute.md]
|
|
200
196
|
Attribute: RedCloth#hard_breaks is a writable attribute [https://github.com/troessner/reek/blob/master/docs/Attribute.md]
|
|
@@ -238,7 +234,6 @@ Feature: Basic smell detection
|
|
|
238
234
|
LongParameterList: RedCloth#textile_fn_ has 5 parameters [https://github.com/troessner/reek/blob/master/docs/Long-Parameter-List.md]
|
|
239
235
|
LongParameterList: RedCloth#textile_p has 4 parameters [https://github.com/troessner/reek/blob/master/docs/Long-Parameter-List.md]
|
|
240
236
|
ManualDispatch: RedCloth#block_textile_prefix manually dispatches method call [https://github.com/troessner/reek/blob/master/docs/Manual-Dispatch.md]
|
|
241
|
-
ManualDispatch: RedCloth#block_textile_prefix manually dispatches method call [https://github.com/troessner/reek/blob/master/docs/Manual-Dispatch.md]
|
|
242
237
|
NestedIterators: RedCloth#block_textile_lists contains iterators nested 3 deep [https://github.com/troessner/reek/blob/master/docs/Nested-Iterators.md]
|
|
243
238
|
NestedIterators: RedCloth#block_textile_table contains iterators nested 2 deep [https://github.com/troessner/reek/blob/master/docs/Nested-Iterators.md]
|
|
244
239
|
NestedIterators: RedCloth#block_textile_table contains iterators nested 3 deep [https://github.com/troessner/reek/blob/master/docs/Nested-Iterators.md]
|
|
@@ -306,5 +301,5 @@ Feature: Basic smell detection
|
|
|
306
301
|
UtilityFunction: RedCloth#lT doesn't depend on instance state (maybe move it to another class?) [https://github.com/troessner/reek/blob/master/docs/Utility-Function.md]
|
|
307
302
|
UtilityFunction: RedCloth#no_textile doesn't depend on instance state (maybe move it to another class?) [https://github.com/troessner/reek/blob/master/docs/Utility-Function.md]
|
|
308
303
|
UtilityFunction: RedCloth#v_align doesn't depend on instance state (maybe move it to another class?) [https://github.com/troessner/reek/blob/master/docs/Utility-Function.md]
|
|
309
|
-
|
|
304
|
+
287 total warnings
|
|
310
305
|
"""
|
|
@@ -23,17 +23,17 @@ Then /^stdout includes "(.*)"$/ do |text|
|
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
Then /^it succeeds$/ do
|
|
26
|
-
success = Reek::CLI::
|
|
26
|
+
success = Reek::CLI::Status::DEFAULT_SUCCESS_EXIT_CODE
|
|
27
27
|
expect(last_command_started).to have_exit_status(success)
|
|
28
28
|
end
|
|
29
29
|
|
|
30
30
|
Then /^the exit status indicates an error$/ do
|
|
31
|
-
error = Reek::CLI::
|
|
31
|
+
error = Reek::CLI::Status::DEFAULT_ERROR_EXIT_CODE
|
|
32
32
|
expect(last_command_started).to have_exit_status(error)
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
Then /^the exit status indicates smells$/ do
|
|
36
|
-
smells = Reek::CLI::
|
|
36
|
+
smells = Reek::CLI::Status::DEFAULT_FAILURE_EXIT_CODE
|
|
37
37
|
expect(last_command_started).to have_exit_status(smells)
|
|
38
38
|
end
|
|
39
39
|
|
|
@@ -46,8 +46,8 @@ Then /^it reports:$/ do |report|
|
|
|
46
46
|
end
|
|
47
47
|
|
|
48
48
|
Then /^it reports this yaml:$/ do |expected_yaml|
|
|
49
|
-
expected_warnings = YAML.
|
|
50
|
-
actual_warnings = YAML.
|
|
49
|
+
expected_warnings = YAML.safe_load(expected_yaml.chomp)
|
|
50
|
+
actual_warnings = YAML.safe_load(last_command_started.stdout)
|
|
51
51
|
expect(actual_warnings).to eq expected_warnings
|
|
52
52
|
end
|
|
53
53
|
|
data/features/todo_list.feature
CHANGED
|
@@ -6,8 +6,6 @@ module Reek
|
|
|
6
6
|
# of an abstract syntax tree.
|
|
7
7
|
#
|
|
8
8
|
class ReferenceCollector
|
|
9
|
-
STOP_NODES = [:class, :module, :def, :defs].freeze
|
|
10
|
-
|
|
11
9
|
def initialize(ast)
|
|
12
10
|
@ast = ast
|
|
13
11
|
end
|
|
@@ -22,12 +20,12 @@ module Reek
|
|
|
22
20
|
|
|
23
21
|
def explicit_self_calls
|
|
24
22
|
[:self, :super, :zsuper, :ivar, :ivasgn].flat_map do |node_type|
|
|
25
|
-
ast.each_node(node_type
|
|
23
|
+
ast.each_node(node_type)
|
|
26
24
|
end
|
|
27
25
|
end
|
|
28
26
|
|
|
29
27
|
def implicit_self_calls
|
|
30
|
-
ast.each_node(:send
|
|
28
|
+
ast.each_node(:send).reject(&:receiver)
|
|
31
29
|
end
|
|
32
30
|
end
|
|
33
31
|
end
|
data/lib/reek/cli/application.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
require_relative 'options'
|
|
3
|
+
require_relative 'status'
|
|
3
4
|
require_relative '../configuration/app_configuration'
|
|
4
5
|
require_relative '../source/source_locator'
|
|
5
6
|
require_relative 'command/report_command'
|
|
@@ -17,7 +18,6 @@ module Reek
|
|
|
17
18
|
|
|
18
19
|
def initialize(argv)
|
|
19
20
|
@options = configure_options(argv)
|
|
20
|
-
@status = options.success_exit_code
|
|
21
21
|
@configuration = configure_app_configuration(options.config_file)
|
|
22
22
|
@command = command_class.new(options: options,
|
|
23
23
|
sources: sources,
|
|
@@ -30,21 +30,20 @@ module Reek
|
|
|
30
30
|
|
|
31
31
|
private
|
|
32
32
|
|
|
33
|
-
attr_accessor :status
|
|
34
33
|
attr_reader :command, :options
|
|
35
34
|
|
|
36
35
|
def configure_options(argv)
|
|
37
36
|
Options.new(argv).parse
|
|
38
37
|
rescue OptionParser::InvalidOption => error
|
|
39
|
-
|
|
40
|
-
exit
|
|
38
|
+
warn "Error: #{error}"
|
|
39
|
+
exit Status::DEFAULT_ERROR_EXIT_CODE
|
|
41
40
|
end
|
|
42
41
|
|
|
43
42
|
def configure_app_configuration(config_file)
|
|
44
43
|
Configuration::AppConfiguration.from_path(config_file)
|
|
45
44
|
rescue Reek::Configuration::ConfigFileException => error
|
|
46
|
-
|
|
47
|
-
exit
|
|
45
|
+
warn "Error: #{error}"
|
|
46
|
+
exit Status::DEFAULT_ERROR_EXIT_CODE
|
|
48
47
|
end
|
|
49
48
|
|
|
50
49
|
def command_class
|
|
@@ -80,11 +79,11 @@ module Reek
|
|
|
80
79
|
end
|
|
81
80
|
|
|
82
81
|
def working_directory_as_source
|
|
83
|
-
Source::SourceLocator.new(['.'], configuration: configuration).sources
|
|
82
|
+
Source::SourceLocator.new(['.'], configuration: configuration, options: options).sources
|
|
84
83
|
end
|
|
85
84
|
|
|
86
85
|
def sources_from_argv
|
|
87
|
-
Source::SourceLocator.new(argv).sources
|
|
86
|
+
Source::SourceLocator.new(argv, configuration: configuration, options: options).sources
|
|
88
87
|
end
|
|
89
88
|
|
|
90
89
|
def source_from_pipe
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
require_relative 'base_command'
|
|
3
3
|
require_relative '../../examiner'
|
|
4
|
+
require_relative '../../logging_error_handler'
|
|
4
5
|
require_relative '../../report'
|
|
5
6
|
|
|
6
7
|
module Reek
|
|
@@ -23,7 +24,8 @@ module Reek
|
|
|
23
24
|
sources.each do |source|
|
|
24
25
|
reporter.add_examiner Examiner.new(source,
|
|
25
26
|
filter_by_smells: smell_names,
|
|
26
|
-
configuration: configuration
|
|
27
|
+
configuration: configuration,
|
|
28
|
+
error_handler: LoggingErrorHandler.new)
|
|
27
29
|
end
|
|
28
30
|
end
|
|
29
31
|
|
data/lib/reek/cli/options.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
require 'optparse'
|
|
3
3
|
require 'rainbow'
|
|
4
4
|
require_relative '../version'
|
|
5
|
+
require_relative 'status'
|
|
5
6
|
|
|
6
7
|
module Reek
|
|
7
8
|
module CLI
|
|
@@ -10,15 +11,11 @@ module Reek
|
|
|
10
11
|
#
|
|
11
12
|
# See {file:docs/Command-Line-Options.md} for details.
|
|
12
13
|
#
|
|
13
|
-
# :reek:TooManyInstanceVariables: { max_instance_variables:
|
|
14
|
-
# :reek:TooManyMethods: { max_methods:
|
|
14
|
+
# :reek:TooManyInstanceVariables: { max_instance_variables: 12 }
|
|
15
|
+
# :reek:TooManyMethods: { max_methods: 18 }
|
|
15
16
|
# :reek:Attribute: { enabled: false }
|
|
16
17
|
#
|
|
17
18
|
class Options
|
|
18
|
-
DEFAULT_SUCCESS_EXIT_CODE = 0
|
|
19
|
-
DEFAULT_ERROR_EXIT_CODE = 1
|
|
20
|
-
DEFAULT_FAILURE_EXIT_CODE = 2
|
|
21
|
-
|
|
22
19
|
attr_reader :argv, :parser, :smells_to_detect
|
|
23
20
|
attr_accessor :colored,
|
|
24
21
|
:config_file,
|
|
@@ -30,7 +27,8 @@ module Reek
|
|
|
30
27
|
:sorting,
|
|
31
28
|
:success_exit_code,
|
|
32
29
|
:failure_exit_code,
|
|
33
|
-
:generate_todo_list
|
|
30
|
+
:generate_todo_list,
|
|
31
|
+
:force_exclusion
|
|
34
32
|
|
|
35
33
|
def initialize(argv = [])
|
|
36
34
|
@argv = argv
|
|
@@ -41,9 +39,10 @@ module Reek
|
|
|
41
39
|
@show_links = true
|
|
42
40
|
@smells_to_detect = []
|
|
43
41
|
@colored = tty_output?
|
|
44
|
-
@success_exit_code = DEFAULT_SUCCESS_EXIT_CODE
|
|
45
|
-
@failure_exit_code = DEFAULT_FAILURE_EXIT_CODE
|
|
42
|
+
@success_exit_code = Status::DEFAULT_SUCCESS_EXIT_CODE
|
|
43
|
+
@failure_exit_code = Status::DEFAULT_FAILURE_EXIT_CODE
|
|
46
44
|
@generate_todo_list = false
|
|
45
|
+
@force_exclusion = false
|
|
47
46
|
|
|
48
47
|
set_up_parser
|
|
49
48
|
end
|
|
@@ -54,6 +53,10 @@ module Reek
|
|
|
54
53
|
self
|
|
55
54
|
end
|
|
56
55
|
|
|
56
|
+
def force_exclusion?
|
|
57
|
+
@force_exclusion
|
|
58
|
+
end
|
|
59
|
+
|
|
57
60
|
private
|
|
58
61
|
|
|
59
62
|
# TTY output generally means the output will not undergo further
|
|
@@ -98,7 +101,11 @@ module Reek
|
|
|
98
101
|
parser.on('-c', '--config FILE', 'Read configuration options from FILE') do |file|
|
|
99
102
|
self.config_file = Pathname.new(file)
|
|
100
103
|
end
|
|
101
|
-
parser.on('--smell SMELL',
|
|
104
|
+
parser.on('--smell SMELL',
|
|
105
|
+
'Only look for a specific smell.',
|
|
106
|
+
'Call it like this: reek --smell PrimaDonnaMethod source.rb',
|
|
107
|
+
'Check out https://github.com/troessner/reek/blob/master/docs/Code-Smells.md '\
|
|
108
|
+
'for a list of smells') do |smell|
|
|
102
109
|
smells_to_detect << smell
|
|
103
110
|
end
|
|
104
111
|
end
|
|
@@ -120,7 +127,7 @@ module Reek
|
|
|
120
127
|
end
|
|
121
128
|
end
|
|
122
129
|
|
|
123
|
-
# :reek:TooManyStatements: { max_statements:
|
|
130
|
+
# :reek:TooManyStatements: { max_statements: 7 }
|
|
124
131
|
def set_report_formatting_options
|
|
125
132
|
parser.separator "\nText format options:"
|
|
126
133
|
set_up_color_option
|
|
@@ -128,6 +135,7 @@ module Reek
|
|
|
128
135
|
set_up_location_formatting_options
|
|
129
136
|
set_up_progress_formatting_options
|
|
130
137
|
set_up_sorting_option
|
|
138
|
+
set_up_force_exclusion_option
|
|
131
139
|
end
|
|
132
140
|
|
|
133
141
|
def set_up_color_option
|
|
@@ -174,17 +182,25 @@ module Reek
|
|
|
174
182
|
end
|
|
175
183
|
end
|
|
176
184
|
|
|
185
|
+
def set_up_force_exclusion_option
|
|
186
|
+
parser.on('--force-exclusion',
|
|
187
|
+
'Force excluding files specified in the configuration `exclude_paths`',
|
|
188
|
+
' even if they are explicitly passed as arguments') do |force_exclusion|
|
|
189
|
+
self.force_exclusion = force_exclusion
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
177
193
|
# :reek:DuplicateMethodCall: { max_calls: 2 }
|
|
178
194
|
def set_exit_codes
|
|
179
195
|
parser.separator "\nExit codes:"
|
|
180
196
|
parser.on('--success-exit-code CODE',
|
|
181
197
|
'The exit code when no smells are found '\
|
|
182
|
-
"(default: #{DEFAULT_SUCCESS_EXIT_CODE})") do |option|
|
|
198
|
+
"(default: #{Status::DEFAULT_SUCCESS_EXIT_CODE})") do |option|
|
|
183
199
|
self.success_exit_code = Integer(option)
|
|
184
200
|
end
|
|
185
201
|
parser.on('--failure-exit-code CODE',
|
|
186
202
|
'The exit code when smells are found '\
|
|
187
|
-
"(default: #{DEFAULT_FAILURE_EXIT_CODE})") do |option|
|
|
203
|
+
"(default: #{Status::DEFAULT_FAILURE_EXIT_CODE})") do |option|
|
|
188
204
|
self.failure_exit_code = Integer(option)
|
|
189
205
|
end
|
|
190
206
|
end
|
data/lib/reek/code_comment.rb
CHANGED
|
@@ -4,6 +4,7 @@ require 'yaml'
|
|
|
4
4
|
|
|
5
5
|
require_relative 'smell_detectors/base_detector'
|
|
6
6
|
require_relative 'errors/bad_detector_in_comment_error'
|
|
7
|
+
require_relative 'errors/bad_detector_configuration_key_in_comment_error'
|
|
7
8
|
require_relative 'errors/garbage_detector_configuration_in_comment_error'
|
|
8
9
|
|
|
9
10
|
module Reek
|
|
@@ -45,7 +46,8 @@ module Reek
|
|
|
45
46
|
line: line,
|
|
46
47
|
source: source,
|
|
47
48
|
options: options).validate
|
|
48
|
-
@config.merge! detector_name => YAML.
|
|
49
|
+
@config.merge! detector_name => YAML.safe_load(options || DISABLE_DETECTOR_CONFIGURATION,
|
|
50
|
+
[Regexp])
|
|
49
51
|
end
|
|
50
52
|
end
|
|
51
53
|
|
|
@@ -75,10 +77,10 @@ module Reek
|
|
|
75
77
|
# 2.) Garbage in the detector configuration like { thats: a: bad: config }
|
|
76
78
|
# 3.) Unknown configuration keys (e.g. by doing a simple typo: "exclude" vs. "exlude" )
|
|
77
79
|
# 4.) Bad data types given as values for those keys
|
|
78
|
-
# This class validates [1] and [
|
|
79
|
-
#
|
|
80
|
+
# This class validates [1], [2] and [3] at the moment but will also validate
|
|
81
|
+
# [4] in the future.
|
|
80
82
|
#
|
|
81
|
-
# :reek:TooManyInstanceVariables: { max_instance_variables:
|
|
83
|
+
# :reek:TooManyInstanceVariables: { max_instance_variables: 7 }
|
|
82
84
|
class CodeCommentValidator
|
|
83
85
|
#
|
|
84
86
|
# @param detector_name [String] - the detector class that was parsed out of the original
|
|
@@ -94,16 +96,20 @@ module Reek
|
|
|
94
96
|
@line = line
|
|
95
97
|
@source = source
|
|
96
98
|
@options = options
|
|
99
|
+
@detector_class = nil # We only know this one after our first initial checks
|
|
100
|
+
@parsed_options = nil # We only know this one after our first initial checks
|
|
97
101
|
end
|
|
98
102
|
|
|
99
103
|
#
|
|
100
104
|
# Method can raise the following errors:
|
|
101
105
|
# * Errors::BadDetectorInCommentError
|
|
102
106
|
# * Errors::GarbageDetectorConfigurationInCommentError
|
|
107
|
+
# * Errors::BadDetectorConfigurationKeyInCommentError
|
|
103
108
|
# @return [undefined]
|
|
104
109
|
def validate
|
|
105
110
|
escalate_bad_detector
|
|
106
111
|
escalate_bad_detector_configuration
|
|
112
|
+
escalate_unknown_configuration_key
|
|
107
113
|
end
|
|
108
114
|
|
|
109
115
|
private
|
|
@@ -112,7 +118,9 @@ module Reek
|
|
|
112
118
|
:original_comment,
|
|
113
119
|
:line,
|
|
114
120
|
:source,
|
|
115
|
-
:options
|
|
121
|
+
:options,
|
|
122
|
+
:detector_class,
|
|
123
|
+
:parsed_options
|
|
116
124
|
|
|
117
125
|
def escalate_bad_detector
|
|
118
126
|
return if SmellDetectors::BaseDetector.valid_detector?(detector_name)
|
|
@@ -123,13 +131,47 @@ module Reek
|
|
|
123
131
|
end
|
|
124
132
|
|
|
125
133
|
def escalate_bad_detector_configuration
|
|
126
|
-
YAML.
|
|
134
|
+
@parsed_options = YAML.safe_load(options || CodeComment::DISABLE_DETECTOR_CONFIGURATION,
|
|
135
|
+
[Regexp])
|
|
127
136
|
rescue Psych::SyntaxError
|
|
128
137
|
raise Errors::GarbageDetectorConfigurationInCommentError, detector_name: detector_name,
|
|
129
138
|
original_comment: original_comment,
|
|
130
139
|
source: source,
|
|
131
140
|
line: line
|
|
132
141
|
end
|
|
142
|
+
|
|
143
|
+
def escalate_unknown_configuration_key
|
|
144
|
+
@detector_class = SmellDetectors::BaseDetector.to_detector(detector_name)
|
|
145
|
+
|
|
146
|
+
return if given_keys_legit?
|
|
147
|
+
raise Errors::BadDetectorConfigurationKeyInCommentError, detector_name: detector_name,
|
|
148
|
+
offensive_keys: configuration_keys_difference,
|
|
149
|
+
original_comment: original_comment,
|
|
150
|
+
source: source,
|
|
151
|
+
line: line
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# @return [Boolean] - all keys in code comment are applicable to the detector in question
|
|
155
|
+
def given_keys_legit?
|
|
156
|
+
given_configuration_keys.subset? valid_detector_keys
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# @return [Set] - the configuration keys that are found in the code comment
|
|
160
|
+
def given_configuration_keys
|
|
161
|
+
parsed_options.keys.map(&:to_sym).to_set
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# @return [String] - all keys from the code comment that look bad
|
|
165
|
+
def configuration_keys_difference
|
|
166
|
+
given_configuration_keys.difference(valid_detector_keys).
|
|
167
|
+
to_a.map { |key| "'#{key}'" }.
|
|
168
|
+
join(', ')
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# @return [Set] - all keys that are legit for the given detector
|
|
172
|
+
def valid_detector_keys
|
|
173
|
+
detector_class.configuration_keys
|
|
174
|
+
end
|
|
133
175
|
end
|
|
134
176
|
end
|
|
135
177
|
end
|
|
@@ -17,42 +17,102 @@ module Reek
|
|
|
17
17
|
# The order in which ConfigurationFileFinder tries to find such a
|
|
18
18
|
# configuration file is exactly like above.
|
|
19
19
|
module ConfigurationFileFinder
|
|
20
|
-
|
|
20
|
+
TOO_MANY_CONFIGURATION_FILES_MESSAGE = <<-EOS.freeze
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
end
|
|
22
|
+
Error: Found multiple configuration files %s
|
|
23
|
+
while scanning directory %s.
|
|
25
24
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
Reek supports only one configuration file. You have 2 options now:
|
|
26
|
+
1) Remove all offending files.
|
|
27
|
+
2) Be specific about which one you want to load via the -c switch.
|
|
28
|
+
|
|
29
|
+
EOS
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
class << self
|
|
32
|
+
#
|
|
33
|
+
# Finds and loads a configuration file from a given path.
|
|
34
|
+
#
|
|
35
|
+
# @return [Hash]
|
|
36
|
+
#
|
|
37
|
+
def find_and_load(path: nil)
|
|
38
|
+
load_from_file(find(path: path))
|
|
35
39
|
end
|
|
36
|
-
end
|
|
37
40
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
#
|
|
42
|
+
# Tries to find a configuration file via:
|
|
43
|
+
# * given path (e.g. via cli switch)
|
|
44
|
+
# * ascending down from the current directory
|
|
45
|
+
# * looking into the home directory
|
|
46
|
+
#
|
|
47
|
+
# @return [File|nil]
|
|
48
|
+
#
|
|
49
|
+
# :reek:ControlParameter
|
|
50
|
+
def find(path: nil, current: Pathname.pwd, home: Pathname.new(Dir.home))
|
|
51
|
+
path || find_by_dir(current) || find_in_dir(home)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
#
|
|
55
|
+
# Loads a configuration file from a given path.
|
|
56
|
+
# Raises on invalid data.
|
|
57
|
+
#
|
|
58
|
+
# @param path [String]
|
|
59
|
+
# @return [Hash]
|
|
60
|
+
#
|
|
61
|
+
# :reek:TooManyStatements: { max_statements: 6 }
|
|
62
|
+
def load_from_file(path)
|
|
63
|
+
return {} unless path
|
|
64
|
+
begin
|
|
65
|
+
configuration = YAML.load_file(path) || {}
|
|
66
|
+
rescue => error
|
|
67
|
+
raise ConfigFileException, "Invalid configuration file #{path}, error is #{error}"
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
unless configuration.is_a? Hash
|
|
71
|
+
raise ConfigFileException, "Invalid configuration file \"#{path}\" -- Not a hash"
|
|
72
|
+
end
|
|
73
|
+
configuration
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
private
|
|
77
|
+
|
|
78
|
+
#
|
|
79
|
+
# Recursively traverse directories down to find a configuration file.
|
|
80
|
+
#
|
|
81
|
+
# @return [File|nil]
|
|
82
|
+
#
|
|
83
|
+
def find_by_dir(start)
|
|
84
|
+
start.ascend do |dir|
|
|
85
|
+
file = find_in_dir(dir)
|
|
86
|
+
return file if file
|
|
87
|
+
end
|
|
88
|
+
end
|
|
42
89
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
90
|
+
#
|
|
91
|
+
# Checks a given directory for a configuration file and returns it.
|
|
92
|
+
# Raises an exception if we find more than one.
|
|
93
|
+
#
|
|
94
|
+
# @return [File|nil]
|
|
95
|
+
#
|
|
96
|
+
# :reek:FeatureEnvy
|
|
97
|
+
def find_in_dir(dir)
|
|
98
|
+
found = dir.children.select { |item| item.file? && item.to_s.end_with?('.reek') }.sort
|
|
99
|
+
if found.size > 1
|
|
100
|
+
escalate_too_many_configuration_files found, dir
|
|
101
|
+
else
|
|
102
|
+
found.first
|
|
103
|
+
end
|
|
50
104
|
end
|
|
51
105
|
|
|
52
|
-
|
|
53
|
-
|
|
106
|
+
#
|
|
107
|
+
# Writes a proper warning message to STDERR and then exits the program.
|
|
108
|
+
#
|
|
109
|
+
# @return [undefined]
|
|
110
|
+
#
|
|
111
|
+
def escalate_too_many_configuration_files(found, directory)
|
|
112
|
+
offensive_files = found.map { |file| "'#{file.basename}'" }.join(', ')
|
|
113
|
+
warn format(TOO_MANY_CONFIGURATION_FILES_MESSAGE, offensive_files, directory)
|
|
114
|
+
exit 1
|
|
54
115
|
end
|
|
55
|
-
configuration
|
|
56
116
|
end
|
|
57
117
|
end
|
|
58
118
|
end
|
data/lib/reek/context_builder.rb
CHANGED
|
@@ -19,7 +19,7 @@ module Reek
|
|
|
19
19
|
# counting. Ideally `ContextBuilder` would only build up the context tree and leave the
|
|
20
20
|
# statement and reference counting to the contexts.
|
|
21
21
|
#
|
|
22
|
-
# :reek:TooManyMethods: { max_methods:
|
|
22
|
+
# :reek:TooManyMethods: { max_methods: 31 }
|
|
23
23
|
# :reek:UnusedPrivateMethod: { exclude: [ !ruby/regexp /process_/ ] }
|
|
24
24
|
class ContextBuilder
|
|
25
25
|
attr_reader :context_tree
|
|
@@ -228,6 +228,31 @@ module Reek
|
|
|
228
228
|
current_context.record_use_of_self
|
|
229
229
|
end
|
|
230
230
|
|
|
231
|
+
# Handles `super` nodes a.k.a. calls to `super` with arguments
|
|
232
|
+
#
|
|
233
|
+
# An input example that would trigger this method would be:
|
|
234
|
+
#
|
|
235
|
+
# def call_me; super(); end
|
|
236
|
+
#
|
|
237
|
+
# or
|
|
238
|
+
#
|
|
239
|
+
# def call_me; super(bar); end
|
|
240
|
+
#
|
|
241
|
+
# but not
|
|
242
|
+
#
|
|
243
|
+
# def call_me; super; end
|
|
244
|
+
#
|
|
245
|
+
# and not
|
|
246
|
+
#
|
|
247
|
+
# def call_me; super do end; end
|
|
248
|
+
#
|
|
249
|
+
# We record one reference to `self`.
|
|
250
|
+
#
|
|
251
|
+
def process_super(exp)
|
|
252
|
+
current_context.record_use_of_self
|
|
253
|
+
process(exp)
|
|
254
|
+
end
|
|
255
|
+
|
|
231
256
|
# Handles `block` nodes.
|
|
232
257
|
#
|
|
233
258
|
# An input example that would trigger this method would be:
|