reek 4.8.1 → 5.0.2
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 +5 -5
- data/.rubocop.yml +17 -3
- data/.simplecov +1 -0
- data/.travis.yml +0 -5
- data/.yardopts +1 -1
- data/CHANGELOG.md +44 -0
- data/Gemfile +1 -1
- data/README.md +115 -100
- data/Rakefile +16 -3
- data/bin/reek +1 -3
- data/docs/API.md +2 -9
- data/docs/Basic-Smell-Options.md +51 -11
- data/docs/Code-Smells.md +1 -1
- data/docs/Command-Line-Options.md +14 -4
- data/docs/Duplicate-Method-Call.md +49 -1
- data/docs/Feature-Envy.md +44 -0
- data/docs/How-To-Write-New-Detectors.md +2 -3
- data/docs/Instance-Variable-Assumption.md +1 -1
- data/docs/{Prima-Donna-Method.md → Missing-Safe-Method.md} +11 -9
- data/docs/Rake-Task.md +1 -1
- data/docs/Reek-4-to-Reek-5-migration.md +193 -0
- data/docs/Reek-Driven-Development.md +1 -1
- data/docs/Uncommunicative-Method-Name.md +45 -6
- data/docs/Uncommunicative-Module-Name.md +49 -7
- data/docs/Uncommunicative-Parameter-Name.md +43 -5
- data/docs/Uncommunicative-Variable-Name.md +73 -2
- data/docs/Unused-Private-Method.md +1 -1
- data/docs/defaults.reek.yml +129 -0
- data/docs/yard_plugin.rb +1 -0
- data/features/command_line_interface/basic_usage.feature +2 -2
- data/features/command_line_interface/options.feature +46 -4
- data/features/command_line_interface/show_progress.feature +4 -4
- data/features/command_line_interface/smell_selection.feature +1 -1
- data/features/command_line_interface/smells_count.feature +6 -6
- data/features/command_line_interface/stdin.feature +30 -8
- data/features/configuration_files/accept_setting.feature +45 -28
- data/features/configuration_files/directory_specific_directives.feature +78 -73
- data/features/configuration_files/exclude_directives.feature +11 -10
- data/features/configuration_files/exclude_paths_directives.feature +4 -4
- data/features/configuration_files/masking_smells.feature +38 -9
- data/features/configuration_files/mix_accept_reject_setting.feature +31 -28
- data/features/configuration_files/reject_setting.feature +52 -41
- data/features/configuration_files/schema_validation.feature +59 -0
- data/features/configuration_files/unused_private_method.feature +18 -16
- data/features/configuration_loading.feature +53 -10
- data/features/configuration_via_source_comments/erroneous_source_comments.feature +2 -2
- data/features/configuration_via_source_comments/well_formed_source_comments.feature +2 -2
- data/features/locales.feature +2 -2
- data/features/rake_task/rake_task.feature +15 -15
- data/features/reports/json.feature +3 -3
- data/features/reports/reports.feature +34 -34
- data/features/reports/yaml.feature +3 -3
- data/features/rspec_matcher.feature +9 -1
- data/features/samples.feature +287 -287
- data/features/step_definitions/reek_steps.rb +4 -0
- data/features/step_definitions/sample_file_steps.rb +9 -4
- data/features/support/env.rb +2 -2
- data/features/todo_list.feature +26 -23
- data/lib/reek/ast/node.rb +3 -6
- data/lib/reek/ast/object_refs.rb +1 -1
- data/lib/reek/ast/sexp_extensions/case.rb +3 -1
- data/lib/reek/ast/sexp_extensions/if.rb +2 -2
- data/lib/reek/ast/sexp_extensions/logical_operators.rb +1 -1
- data/lib/reek/ast/sexp_extensions/methods.rb +1 -1
- data/lib/reek/cli/application.rb +4 -3
- data/lib/reek/cli/command/report_command.rb +1 -2
- data/lib/reek/cli/command/todo_list_command.rb +4 -2
- data/lib/reek/cli/options.rb +29 -14
- data/lib/reek/cli/silencer.rb +14 -3
- data/lib/reek/code_comment.rb +14 -16
- data/lib/reek/configuration/app_configuration.rb +32 -28
- data/lib/reek/configuration/configuration_converter.rb +110 -0
- data/lib/reek/configuration/configuration_file_finder.rb +15 -40
- data/lib/reek/configuration/configuration_validator.rb +12 -23
- data/lib/reek/configuration/default_directive.rb +17 -3
- data/lib/reek/configuration/directory_directives.rb +17 -11
- data/lib/reek/configuration/excluded_paths.rb +1 -1
- data/lib/reek/configuration/rake_task_converter.rb +29 -0
- data/lib/reek/configuration/schema.yml +210 -0
- data/lib/reek/configuration/schema_validator.rb +38 -0
- data/lib/reek/context/attribute_context.rb +1 -1
- data/lib/reek/context/code_context.rb +4 -4
- data/lib/reek/context/method_context.rb +2 -2
- data/lib/reek/context/module_context.rb +1 -1
- data/lib/reek/context_builder.rb +9 -9
- data/lib/reek/detector_repository.rb +6 -0
- data/lib/reek/documentation_link.rb +28 -0
- data/lib/reek/errors/bad_detector_configuration_key_in_comment_error.rb +4 -3
- data/lib/reek/errors/bad_detector_in_comment_error.rb +4 -3
- data/lib/reek/errors/config_file_error.rb +11 -0
- data/lib/reek/errors/encoding_error.rb +2 -2
- data/lib/reek/errors/garbage_detector_configuration_in_comment_error.rb +4 -3
- data/lib/reek/errors/incomprehensible_source_error.rb +2 -2
- data/lib/reek/errors/syntax_error.rb +41 -0
- data/lib/reek/examiner.rb +9 -19
- data/lib/reek/rake/task.rb +3 -3
- data/lib/reek/report/base_report.rb +8 -12
- data/lib/reek/report/code_climate/code_climate_configuration.yml +6 -10
- data/lib/reek/report/documentation_link_warning_formatter.rb +17 -0
- data/lib/reek/report/heading_formatter.rb +54 -0
- data/lib/reek/report/json_report.rb +1 -1
- data/lib/reek/report/location_formatter.rb +40 -0
- data/lib/reek/report/progress_formatter.rb +79 -0
- data/lib/reek/report/simple_warning_formatter.rb +34 -0
- data/lib/reek/report/text_report.rb +1 -2
- data/lib/reek/report/xml_report.rb +3 -3
- data/lib/reek/report/yaml_report.rb +1 -1
- data/lib/reek/report.rb +15 -10
- data/lib/reek/smell_configuration.rb +2 -2
- data/lib/reek/smell_detectors/attribute.rb +0 -1
- data/lib/reek/smell_detectors/base_detector.rb +8 -11
- 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 +17 -32
- data/lib/reek/smell_detectors/data_clump.rb +3 -4
- data/lib/reek/smell_detectors/duplicate_method_call.rb +5 -6
- data/lib/reek/smell_detectors/feature_envy.rb +1 -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 +1 -2
- data/lib/reek/smell_detectors/long_yield_list.rb +2 -3
- data/lib/reek/smell_detectors/manual_dispatch.rb +2 -2
- data/lib/reek/smell_detectors/{prima_donna_method.rb → missing_safe_method.rb} +6 -7
- data/lib/reek/smell_detectors/module_initialize.rb +0 -1
- data/lib/reek/smell_detectors/nested_iterators.rb +4 -5
- data/lib/reek/smell_detectors/nil_check.rb +0 -1
- data/lib/reek/smell_detectors/repeated_conditional.rb +3 -4
- data/lib/reek/smell_detectors/subclassed_from_core_class.rb +0 -1
- data/lib/reek/smell_detectors/too_many_constants.rb +1 -2
- data/lib/reek/smell_detectors/too_many_instance_variables.rb +1 -2
- data/lib/reek/smell_detectors/too_many_methods.rb +1 -2
- data/lib/reek/smell_detectors/too_many_statements.rb +1 -2
- data/lib/reek/smell_detectors/uncommunicative_method_name.rb +2 -3
- data/lib/reek/smell_detectors/uncommunicative_module_name.rb +2 -3
- data/lib/reek/smell_detectors/uncommunicative_parameter_name.rb +2 -3
- data/lib/reek/smell_detectors/uncommunicative_variable_name.rb +4 -5
- data/lib/reek/smell_detectors/unused_parameters.rb +0 -1
- data/lib/reek/smell_detectors/unused_private_method.rb +0 -1
- data/lib/reek/smell_detectors/utility_function.rb +1 -2
- data/lib/reek/smell_detectors.rb +1 -2
- data/lib/reek/smell_warning.rb +15 -8
- data/lib/reek/source/source_code.rb +40 -55
- data/lib/reek/source/source_locator.rb +7 -7
- data/lib/reek/spec/should_reek.rb +2 -2
- data/lib/reek/spec/should_reek_of.rb +9 -16
- data/lib/reek/spec/should_reek_only_of.rb +4 -4
- data/lib/reek/spec.rb +6 -6
- data/lib/reek/tree_dresser.rb +5 -5
- data/lib/reek/version.rb +1 -1
- data/reek.gemspec +4 -4
- data/samples/checkstyle.xml +1 -1
- data/samples/configuration/accepts_rejects_and_excludes_for_detectors.reek.yml +29 -0
- data/samples/configuration/accepts_rejects_and_excludes_for_directory_directives.reek.yml +30 -0
- data/samples/configuration/full_configuration.reek +8 -4
- data/samples/configuration/full_mask.reek +5 -4
- data/samples/configuration/partial_mask.reek +3 -2
- data/samples/configuration/regular_configuration/.reek.yml +4 -0
- data/samples/paths.rb +5 -4
- data/samples/source_with_hidden_directories/.hidden/hidden.rb +1 -0
- data/samples/source_with_hidden_directories/not_hidden.rb +1 -0
- data/spec/reek/ast/node_spec.rb +5 -5
- data/spec/reek/cli/application_spec.rb +18 -4
- data/spec/reek/cli/command/todo_list_command_spec.rb +4 -2
- data/spec/reek/cli/silencer_spec.rb +28 -0
- data/spec/reek/code_comment_spec.rb +0 -7
- data/spec/reek/configuration/app_configuration_spec.rb +44 -31
- data/spec/reek/configuration/configuration_file_finder_spec.rb +141 -49
- data/spec/reek/configuration/default_directive_spec.rb +1 -1
- data/spec/reek/configuration/directory_directives_spec.rb +3 -4
- data/spec/reek/configuration/excluded_paths_spec.rb +5 -5
- data/spec/reek/configuration/rake_task_converter_spec.rb +33 -0
- data/spec/reek/configuration/schema_validator_spec.rb +165 -0
- data/spec/reek/context/code_context_spec.rb +1 -1
- data/spec/reek/documentation_link_spec.rb +20 -0
- data/spec/reek/examiner_spec.rb +28 -1
- data/spec/reek/report/json_report_spec.rb +13 -46
- data/spec/reek/report/{formatter/location_formatter_spec.rb → location_formatter_spec.rb} +5 -5
- data/spec/reek/report/{formatter/progress_formatter_spec.rb → progress_formatter_spec.rb} +4 -4
- data/spec/reek/report/text_report_spec.rb +4 -4
- data/spec/reek/report/xml_report_spec.rb +1 -1
- data/spec/reek/report/yaml_report_spec.rb +9 -38
- data/spec/reek/report_spec.rb +3 -3
- data/spec/reek/smell_detectors/feature_envy_spec.rb +47 -2
- data/spec/reek/smell_detectors/{prima_donna_method_spec.rb → missing_safe_method_spec.rb} +9 -9
- data/spec/reek/smell_detectors/too_many_constants_spec.rb +3 -3
- data/spec/reek/smell_detectors/too_many_instance_variables_spec.rb +1 -1
- data/spec/reek/smell_detectors/uncommunicative_method_name_spec.rb +6 -6
- data/spec/reek/smell_detectors/uncommunicative_module_name_spec.rb +6 -4
- data/spec/reek/smell_detectors/uncommunicative_parameter_name_spec.rb +6 -4
- data/spec/reek/smell_detectors/uncommunicative_variable_name_spec.rb +6 -6
- data/spec/reek/smell_detectors/unused_private_method_spec.rb +1 -1
- data/spec/reek/smell_warning_spec.rb +4 -0
- data/spec/reek/source/source_code_spec.rb +16 -22
- data/spec/reek/source/source_locator_spec.rb +11 -11
- data/spec/reek/spec/should_reek_of_spec.rb +0 -4
- data/spec/reek/spec/should_reek_only_of_spec.rb +2 -2
- data/spec/reek/spec/should_reek_spec.rb +1 -1
- data/spec/reek/tree_dresser_spec.rb +2 -6
- data/spec/spec_helper.rb +3 -5
- data/tasks/configuration.rake +8 -5
- metadata +64 -36
- data/.codeclimate.yml +0 -21
- data/defaults.reek +0 -131
- data/features/configuration_files/warn_about_multiple_configuration_files.feature +0 -44
- data/lib/reek/report/formatter/heading_formatter.rb +0 -52
- data/lib/reek/report/formatter/location_formatter.rb +0 -42
- data/lib/reek/report/formatter/progress_formatter.rb +0 -81
- data/lib/reek/report/formatter/simple_warning_formatter.rb +0 -35
- data/lib/reek/report/formatter/wiki_link_warning_formatter.rb +0 -36
- data/lib/reek/report/formatter.rb +0 -33
- data/lib/reek/smell_detectors/syntax.rb +0 -37
- data/samples/configuration/non_public_modifiers_mask.reek +0 -3
- data/samples/smelly_with_inline_mask.rb +0 -8
- data/samples/smelly_with_modifiers.rb +0 -12
- data/samples/source_with_hidden_directories/.hidden/uncommunicative_method_name.rb +0 -5
- data/samples/source_with_non_ruby_files/uncommunicative_parameter_name.rb +0 -6
- data/spec/reek/smell_detectors/syntax_spec.rb +0 -17
- /data/{samples/configuration/more_than_one_configuration_file/regular.reek → .reek.yml} +0 -0
- /data/samples/{clean.rb → clean_source/clean.rb} +0 -0
- /data/samples/{exceptions.reek → configuration/home/home.reek.yml} +0 -0
- /data/samples/configuration/{more_than_one_configuration_file/todo.reek → regular_configuration/empty_sub_directory/.gitignore} +0 -0
- /data/samples/{configuration/single_configuration_file/.reek → no_config_file/.keep} +0 -0
- /data/samples/{inline.rb → smelly_source/inline.rb} +0 -0
- /data/samples/{optparse.rb → smelly_source/optparse.rb} +0 -0
- /data/samples/{redcloth.rb → smelly_source/redcloth.rb} +0 -0
- /data/samples/{smelly.rb → smelly_source/smelly.rb} +0 -0
- /data/samples/{source_with_hidden_directories/uncommunicative_parameter_name.rb → source_with_non_ruby_files/ruby.rb} +0 -0
|
@@ -6,6 +6,10 @@ When /^I pass "([^\"]*)" to reek *(.*)$/ do |stdin, args|
|
|
|
6
6
|
reek_with_pipe(stdin, args)
|
|
7
7
|
end
|
|
8
8
|
|
|
9
|
+
When /^I pass a stdin to reek *(.*) with:$/ do |args, stdin|
|
|
10
|
+
reek_with_pipe(stdin, args)
|
|
11
|
+
end
|
|
12
|
+
|
|
9
13
|
Then /^it reports nothing$/ do
|
|
10
14
|
expect(last_command_started).to have_output_on_stdout('')
|
|
11
15
|
end
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
require_relative '../../samples/paths'
|
|
2
2
|
|
|
3
3
|
Given(/^the smelly file '(.+)'$/) do |filename|
|
|
4
|
-
write_file(filename,
|
|
4
|
+
write_file(filename, SAMPLES_DIR.join('smelly_source').join(filename).read)
|
|
5
5
|
end
|
|
6
6
|
|
|
7
7
|
Given(/^the clean file 'clean.rb'$/) do
|
|
@@ -28,13 +28,18 @@ Given(/^a directory called 'smelly' containing two smelly files$/) do
|
|
|
28
28
|
end
|
|
29
29
|
|
|
30
30
|
Given(/^the smelly file '(.+)' in a subdirectory$/) do |filename|
|
|
31
|
-
contents =
|
|
31
|
+
contents = SAMPLES_DIR.join('smelly_source').join(filename).read
|
|
32
32
|
|
|
33
33
|
write_file("subdir/#{filename}", contents)
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
Given(/^a configuration file '(.+)'$/) do |filename|
|
|
37
|
-
write_file(filename,
|
|
37
|
+
write_file(filename, CONFIGURATION_DIR.join(filename).read)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
Given(/^our default configuration file$/) do
|
|
41
|
+
default_configuration = File.read SAMPLES_DIR.join('..').join('docs').join('defaults.reek.yml')
|
|
42
|
+
write_file('defaults.reek', default_configuration)
|
|
38
43
|
end
|
|
39
44
|
|
|
40
45
|
When(/^I run "reek (.*?)" in a subdirectory$/) do |args|
|
|
@@ -44,7 +49,7 @@ When(/^I run "reek (.*?)" in a subdirectory$/) do |args|
|
|
|
44
49
|
end
|
|
45
50
|
|
|
46
51
|
Given(/^a configuration file '(.+)' in a subdirectory$/) do |filename|
|
|
47
|
-
contents =
|
|
52
|
+
contents = CONFIGURATION_DIR.join(filename).read
|
|
48
53
|
|
|
49
54
|
write_file("subdir/#{filename}", contents)
|
|
50
55
|
end
|
data/features/support/env.rb
CHANGED
|
@@ -13,11 +13,11 @@ end
|
|
|
13
13
|
#
|
|
14
14
|
class ReekWorld
|
|
15
15
|
def reek(args)
|
|
16
|
-
run_simple("reek --no-color #{args}", false)
|
|
16
|
+
run_simple("reek --no-color --no-documentation #{args}", false)
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
def reek_with_pipe(stdin, args)
|
|
20
|
-
run "reek --no-color #{args}"
|
|
20
|
+
run "reek --no-color --no-documentation #{args}"
|
|
21
21
|
type(stdin)
|
|
22
22
|
close_input
|
|
23
23
|
end
|
data/features/todo_list.feature
CHANGED
|
@@ -15,8 +15,8 @@ Feature: Auto-generate a todo file
|
|
|
15
15
|
And it reports:
|
|
16
16
|
"""
|
|
17
17
|
smelly.rb -- 2 warnings:
|
|
18
|
-
[4]:UncommunicativeMethodName: Smelly#x has the name 'x'
|
|
19
|
-
[5]:UncommunicativeVariableName: Smelly#x has the variable name 'y'
|
|
18
|
+
[4]:UncommunicativeMethodName: Smelly#x has the name 'x'
|
|
19
|
+
[5]:UncommunicativeVariableName: Smelly#x has the variable name 'y'
|
|
20
20
|
"""
|
|
21
21
|
When I run reek --todo smelly.rb
|
|
22
22
|
Then it succeeds
|
|
@@ -29,13 +29,14 @@ Feature: Auto-generate a todo file
|
|
|
29
29
|
And the file ".todo.reek" should contain:
|
|
30
30
|
"""
|
|
31
31
|
---
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
32
|
+
detectors:
|
|
33
|
+
UncommunicativeMethodName:
|
|
34
|
+
exclude:
|
|
35
|
+
- Smelly#x
|
|
36
|
+
UncommunicativeVariableName:
|
|
37
|
+
exclude:
|
|
38
|
+
- Smelly#x
|
|
39
|
+
"""
|
|
39
40
|
When I run reek -c .todo.reek smelly.rb
|
|
40
41
|
Then it succeeds
|
|
41
42
|
|
|
@@ -54,20 +55,21 @@ Feature: Auto-generate a todo file
|
|
|
54
55
|
And a file named ".todo.reek" with:
|
|
55
56
|
"""
|
|
56
57
|
---
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
detectors:
|
|
59
|
+
# smelly.rb reeks of UncommunicativeMethodName and UncommunicativeVariableName
|
|
60
|
+
# so the configuration below will partially mask this
|
|
61
|
+
UncommunicativeMethodName:
|
|
62
|
+
enabled: false
|
|
61
63
|
"""
|
|
62
|
-
When I run
|
|
64
|
+
When I run reek -c .todo.reek smelly.rb
|
|
63
65
|
Then it reports:
|
|
64
66
|
"""
|
|
65
67
|
smelly.rb -- 1 warning:
|
|
66
|
-
[5]:UncommunicativeVariableName: Smelly#x has the variable name 'y'
|
|
68
|
+
[5]:UncommunicativeVariableName: Smelly#x has the variable name 'y'
|
|
67
69
|
"""
|
|
68
|
-
When I run
|
|
70
|
+
When I run reek --todo smelly.rb
|
|
69
71
|
Then it succeeds
|
|
70
|
-
When I run
|
|
72
|
+
When I run reek -c .todo.reek smelly.rb
|
|
71
73
|
Then it reports nothing
|
|
72
74
|
|
|
73
75
|
Scenario: Ignore existing other configuration files that are passed explicitly
|
|
@@ -77,16 +79,17 @@ Feature: Auto-generate a todo file
|
|
|
77
79
|
---
|
|
78
80
|
# smelly.rb reeks of UncommunicativeMethodName and UncommunicativeVariableName
|
|
79
81
|
# so the configuration below will partially mask this
|
|
80
|
-
|
|
81
|
-
|
|
82
|
+
detectors:
|
|
83
|
+
UncommunicativeMethodName:
|
|
84
|
+
enabled: false
|
|
82
85
|
"""
|
|
83
|
-
When I run
|
|
86
|
+
When I run reek -c config.reek smelly.rb
|
|
84
87
|
Then it reports:
|
|
85
88
|
"""
|
|
86
89
|
smelly.rb -- 1 warning:
|
|
87
|
-
[5]:UncommunicativeVariableName: Smelly#x has the variable name 'y'
|
|
90
|
+
[5]:UncommunicativeVariableName: Smelly#x has the variable name 'y'
|
|
88
91
|
"""
|
|
89
|
-
When I run
|
|
92
|
+
When I run reek -c config.reek --todo smelly.rb
|
|
90
93
|
Then it succeeds
|
|
91
|
-
When I run
|
|
94
|
+
When I run reek -c .todo.reek smelly.rb
|
|
92
95
|
Then it reports nothing
|
data/lib/reek/ast/node.rb
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative '../cli/silencer'
|
|
4
|
-
|
|
5
|
-
Reek::CLI::Silencer.silently do
|
|
6
|
-
require 'parser'
|
|
7
|
-
end
|
|
4
|
+
Reek::CLI::Silencer.without_warnings { require 'parser' }
|
|
8
5
|
|
|
9
6
|
module Reek
|
|
10
7
|
module AST
|
|
@@ -72,8 +69,8 @@ module Reek
|
|
|
72
69
|
each_node(target_type).any?
|
|
73
70
|
end
|
|
74
71
|
|
|
75
|
-
# :reek:DuplicateMethodCall { max_calls: 2 } is ok for lines.first
|
|
76
|
-
# :reek:FeatureEnvy
|
|
72
|
+
# @quality :reek:DuplicateMethodCall { max_calls: 2 } is ok for lines.first
|
|
73
|
+
# @quality :reek:FeatureEnvy
|
|
77
74
|
def format_to_ruby
|
|
78
75
|
if location
|
|
79
76
|
lines = location.expression.source.split("\n").map(&:strip)
|
data/lib/reek/ast/object_refs.rb
CHANGED
|
@@ -10,7 +10,9 @@ module Reek
|
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
def body_nodes(type, ignoring = [])
|
|
13
|
-
children[1..-1].compact.flat_map
|
|
13
|
+
children[1..-1].compact.flat_map do |child|
|
|
14
|
+
child.each_node(type, ignoring | type).to_a
|
|
15
|
+
end
|
|
14
16
|
end
|
|
15
17
|
|
|
16
18
|
def else_body
|
|
@@ -9,13 +9,13 @@ module Reek
|
|
|
9
9
|
children.first
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
-
# :reek:FeatureEnvy
|
|
12
|
+
# @quality :reek:FeatureEnvy
|
|
13
13
|
def body_nodes(type, ignoring = [])
|
|
14
14
|
children[1..-1].compact.flat_map do |child|
|
|
15
15
|
if ignoring.include? child.type
|
|
16
16
|
[]
|
|
17
17
|
else
|
|
18
|
-
child.each_node(type, ignoring).to_a
|
|
18
|
+
child.each_node(type, ignoring | type).to_a
|
|
19
19
|
end
|
|
20
20
|
end
|
|
21
21
|
end
|
data/lib/reek/cli/application.rb
CHANGED
|
@@ -6,6 +6,7 @@ require_relative '../configuration/app_configuration'
|
|
|
6
6
|
require_relative '../source/source_locator'
|
|
7
7
|
require_relative 'command/report_command'
|
|
8
8
|
require_relative 'command/todo_list_command'
|
|
9
|
+
require_relative '../errors/config_file_error'
|
|
9
10
|
|
|
10
11
|
module Reek
|
|
11
12
|
module CLI
|
|
@@ -42,7 +43,7 @@ module Reek
|
|
|
42
43
|
|
|
43
44
|
def configure_app_configuration(config_file)
|
|
44
45
|
Configuration::AppConfiguration.from_path(config_file)
|
|
45
|
-
rescue
|
|
46
|
+
rescue Errors::ConfigFileError => error
|
|
46
47
|
warn "Error: #{error}"
|
|
47
48
|
exit Status::DEFAULT_ERROR_EXIT_CODE
|
|
48
49
|
end
|
|
@@ -68,7 +69,7 @@ module Reek
|
|
|
68
69
|
options.argv
|
|
69
70
|
end
|
|
70
71
|
|
|
71
|
-
# :reek:UtilityFunction
|
|
72
|
+
# @quality :reek:UtilityFunction
|
|
72
73
|
def input_was_piped?
|
|
73
74
|
!$stdin.tty?
|
|
74
75
|
end
|
|
@@ -88,7 +89,7 @@ module Reek
|
|
|
88
89
|
end
|
|
89
90
|
|
|
90
91
|
def source_from_pipe
|
|
91
|
-
[$stdin]
|
|
92
|
+
[Source::SourceCode.from($stdin, origin: options.stdin_filename)]
|
|
92
93
|
end
|
|
93
94
|
|
|
94
95
|
def disable_progress_output_unless_verbose
|
|
@@ -38,7 +38,6 @@ module Reek
|
|
|
38
38
|
@reporter ||=
|
|
39
39
|
report_class.new(
|
|
40
40
|
warning_formatter: warning_formatter,
|
|
41
|
-
report_formatter: Report::Formatter,
|
|
42
41
|
sort_by_issue_count: sort_by_issue_count,
|
|
43
42
|
heading_formatter: heading_formatter,
|
|
44
43
|
progress_formatter: progress_formatter.new(sources.length))
|
|
@@ -53,7 +52,7 @@ module Reek
|
|
|
53
52
|
end
|
|
54
53
|
|
|
55
54
|
def warning_formatter_class
|
|
56
|
-
Report.warning_formatter_class(options.show_links ? :
|
|
55
|
+
Report.warning_formatter_class(options.show_links ? :documentation_links : :simple)
|
|
57
56
|
end
|
|
58
57
|
|
|
59
58
|
def location_formatter
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative 'base_command'
|
|
4
4
|
require_relative '../../examiner'
|
|
5
|
+
require_relative '../../configuration/app_configuration'
|
|
5
6
|
|
|
6
7
|
module Reek
|
|
7
8
|
module CLI
|
|
@@ -11,14 +12,15 @@ module Reek
|
|
|
11
12
|
# file that can serve as a todo list.
|
|
12
13
|
#
|
|
13
14
|
class TodoListCommand < BaseCommand
|
|
14
|
-
FILE_NAME = '.todo.reek'
|
|
15
|
+
FILE_NAME = '.todo.reek'
|
|
15
16
|
|
|
16
17
|
def execute
|
|
17
18
|
if smells.empty?
|
|
18
19
|
puts "\n'.todo.reek' not generated because "\
|
|
19
20
|
'there were no smells found!'
|
|
20
21
|
else
|
|
21
|
-
File.write FILE_NAME,
|
|
22
|
+
File.write FILE_NAME,
|
|
23
|
+
{ Configuration::AppConfiguration::DETECTORS_KEY => groups }.to_yaml
|
|
22
24
|
puts "\n'.todo.reek' generated! You can now use "\
|
|
23
25
|
'this as a starting point for your configuration.'
|
|
24
26
|
end
|
data/lib/reek/cli/options.rb
CHANGED
|
@@ -4,6 +4,8 @@ require 'optparse'
|
|
|
4
4
|
require 'rainbow'
|
|
5
5
|
require_relative '../version'
|
|
6
6
|
require_relative 'status'
|
|
7
|
+
require_relative '../detector_repository'
|
|
8
|
+
require_relative '../documentation_link'
|
|
7
9
|
|
|
8
10
|
module Reek
|
|
9
11
|
module CLI
|
|
@@ -12,9 +14,9 @@ module Reek
|
|
|
12
14
|
#
|
|
13
15
|
# See {file:docs/Command-Line-Options.md} for details.
|
|
14
16
|
#
|
|
15
|
-
# :reek:TooManyInstanceVariables
|
|
16
|
-
# :reek:TooManyMethods
|
|
17
|
-
# :reek:Attribute
|
|
17
|
+
# @quality :reek:TooManyInstanceVariables { max_instance_variables: 12 }
|
|
18
|
+
# @quality :reek:TooManyMethods { max_methods: 18 }
|
|
19
|
+
# @quality :reek:Attribute { enabled: false }
|
|
18
20
|
#
|
|
19
21
|
class Options
|
|
20
22
|
attr_reader :argv, :parser, :smells_to_detect
|
|
@@ -26,6 +28,7 @@ module Reek
|
|
|
26
28
|
:show_empty,
|
|
27
29
|
:show_links,
|
|
28
30
|
:sorting,
|
|
31
|
+
:stdin_filename,
|
|
29
32
|
:success_exit_code,
|
|
30
33
|
:failure_exit_code,
|
|
31
34
|
:generate_todo_list,
|
|
@@ -64,12 +67,12 @@ module Reek
|
|
|
64
67
|
# processing by a machine, but will be viewed by a human. This means
|
|
65
68
|
# features like coloring can be safely enabled by default.
|
|
66
69
|
#
|
|
67
|
-
# :reek:UtilityFunction
|
|
70
|
+
# @quality :reek:UtilityFunction
|
|
68
71
|
def tty_output?
|
|
69
72
|
$stdout.tty?
|
|
70
73
|
end
|
|
71
74
|
|
|
72
|
-
# :reek:TooManyStatements
|
|
75
|
+
# @quality :reek:TooManyStatements { max_statements: 7 }
|
|
73
76
|
def set_up_parser
|
|
74
77
|
set_banner
|
|
75
78
|
set_configuration_options
|
|
@@ -91,12 +94,12 @@ module Reek
|
|
|
91
94
|
#{program_name} -s lib
|
|
92
95
|
cat my_class.rb | #{program_name}
|
|
93
96
|
|
|
94
|
-
See https://
|
|
97
|
+
See https://github.com/troessner/reek for detailed help.
|
|
95
98
|
|
|
96
99
|
BANNER
|
|
97
100
|
end
|
|
98
101
|
|
|
99
|
-
# :reek:TooManyStatements
|
|
102
|
+
# @quality :reek:TooManyStatements { max_statements: 7 }
|
|
100
103
|
def set_configuration_options
|
|
101
104
|
parser.separator 'Configuration:'
|
|
102
105
|
parser.on('-c', '--config FILE', 'Read configuration options from FILE') do |file|
|
|
@@ -104,11 +107,16 @@ module Reek
|
|
|
104
107
|
end
|
|
105
108
|
parser.on('--smell SMELL',
|
|
106
109
|
'Only look for a specific smell.',
|
|
107
|
-
'Call it like this: reek --smell
|
|
108
|
-
|
|
110
|
+
'Call it like this: reek --smell MissingSafeMethod source.rb',
|
|
111
|
+
"Check out #{DocumentationLink.build('Code Smells')} "\
|
|
109
112
|
'for a list of smells') do |smell|
|
|
110
113
|
smells_to_detect << smell
|
|
111
114
|
end
|
|
115
|
+
parser.on('--stdin-filename FILE',
|
|
116
|
+
'When passing code in via pipe, assume this filename when '\
|
|
117
|
+
'checking file or directory rules in the config.') do |file|
|
|
118
|
+
self.stdin_filename = file
|
|
119
|
+
end
|
|
112
120
|
end
|
|
113
121
|
|
|
114
122
|
def set_generate_todo_list_options
|
|
@@ -128,7 +136,7 @@ module Reek
|
|
|
128
136
|
end
|
|
129
137
|
end
|
|
130
138
|
|
|
131
|
-
# :reek:TooManyStatements
|
|
139
|
+
# @quality :reek:TooManyStatements { max_statements: 7 }
|
|
132
140
|
def set_report_formatting_options
|
|
133
141
|
parser.separator "\nText format options:"
|
|
134
142
|
set_up_color_option
|
|
@@ -150,8 +158,8 @@ module Reek
|
|
|
150
158
|
'Show headings for smell-free source files (default: false)') do |show_empty|
|
|
151
159
|
self.show_empty = show_empty
|
|
152
160
|
end
|
|
153
|
-
parser.on('-U', '--[no-]
|
|
154
|
-
'Show link to related
|
|
161
|
+
parser.on('-U', '--[no-]documentation',
|
|
162
|
+
'Show link to related documentation page for each smell (default: true)') do |show_links|
|
|
155
163
|
self.show_links = show_links
|
|
156
164
|
end
|
|
157
165
|
end
|
|
@@ -191,7 +199,7 @@ module Reek
|
|
|
191
199
|
end
|
|
192
200
|
end
|
|
193
201
|
|
|
194
|
-
# :reek:DuplicateMethodCall
|
|
202
|
+
# @quality :reek:DuplicateMethodCall { max_calls: 2 }
|
|
195
203
|
def set_exit_codes
|
|
196
204
|
parser.separator "\nExit codes:"
|
|
197
205
|
parser.on('--success-exit-code CODE',
|
|
@@ -206,13 +214,20 @@ module Reek
|
|
|
206
214
|
end
|
|
207
215
|
end
|
|
208
216
|
|
|
209
|
-
# :reek:TooManyStatements
|
|
217
|
+
# @quality :reek:TooManyStatements { max_statements: 12 }
|
|
210
218
|
def set_utility_options
|
|
211
219
|
parser.separator "\nUtility options:"
|
|
212
220
|
parser.on_tail('-h', '--help', 'Show this message') do
|
|
213
221
|
puts parser
|
|
214
222
|
exit
|
|
215
223
|
end
|
|
224
|
+
parser.on_tail('-l', '--list', 'List all available smell detectors') do
|
|
225
|
+
puts "All available smell detectors:\n\n"
|
|
226
|
+
puts DetectorRepository.available_detector_names
|
|
227
|
+
puts "\nCheck out #{DocumentationLink.build('Code Smells')} "\
|
|
228
|
+
'for a details on each detector'
|
|
229
|
+
exit
|
|
230
|
+
end
|
|
216
231
|
parser.on_tail('-v', '--version', 'Show version') do
|
|
217
232
|
puts "#{parser.program_name} #{Reek::Version::STRING}\n"
|
|
218
233
|
exit
|
data/lib/reek/cli/silencer.rb
CHANGED
|
@@ -8,17 +8,28 @@ module Reek
|
|
|
8
8
|
module Silencer
|
|
9
9
|
module_function
|
|
10
10
|
|
|
11
|
-
# :reek:TooManyStatements
|
|
11
|
+
# @quality :reek:TooManyStatements { max_statements: 9 }
|
|
12
12
|
def silently
|
|
13
13
|
old_verbose = $VERBOSE
|
|
14
|
+
old_stderr = $stderr
|
|
15
|
+
old_stdout = $stdout
|
|
16
|
+
|
|
14
17
|
$VERBOSE = false
|
|
15
18
|
$stderr = StringIO.new
|
|
16
19
|
$stdout = StringIO.new
|
|
17
20
|
yield
|
|
18
21
|
ensure
|
|
19
22
|
$VERBOSE = old_verbose
|
|
20
|
-
$stderr =
|
|
21
|
-
$stdout =
|
|
23
|
+
$stderr = old_stderr
|
|
24
|
+
$stdout = old_stdout
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def without_warnings
|
|
28
|
+
old_verbose = $VERBOSE
|
|
29
|
+
$VERBOSE = false
|
|
30
|
+
yield
|
|
31
|
+
ensure
|
|
32
|
+
$VERBOSE = old_verbose
|
|
22
33
|
end
|
|
23
34
|
end
|
|
24
35
|
end
|
data/lib/reek/code_comment.rb
CHANGED
|
@@ -17,22 +17,20 @@ module Reek
|
|
|
17
17
|
:reek: # prefix
|
|
18
18
|
(\w+) # smell detector e.g.: UncommunicativeVariableName
|
|
19
19
|
(
|
|
20
|
-
:? # legacy separator
|
|
21
20
|
\s*
|
|
22
21
|
(\{.*?\}) # optional details in hash style e.g.: { max_methods: 30 }
|
|
23
22
|
)?
|
|
24
23
|
/x
|
|
25
24
|
SANITIZE_REGEX = /(#|\n|\s)+/ # Matches '#', newlines and > 1 whitespaces.
|
|
26
|
-
DISABLE_DETECTOR_CONFIGURATION = '{ enabled: false }'
|
|
25
|
+
DISABLE_DETECTOR_CONFIGURATION = '{ enabled: false }'
|
|
27
26
|
MINIMUM_CONTENT_LENGTH = 2
|
|
28
|
-
LEGACY_SEPARATOR = ':'.freeze
|
|
29
27
|
|
|
30
28
|
attr_reader :config
|
|
31
29
|
|
|
32
30
|
#
|
|
33
|
-
# @param comment [String]
|
|
34
|
-
# @param line [Integer]
|
|
35
|
-
# @param source [String]
|
|
31
|
+
# @param comment [String] the original comment as found in the source code
|
|
32
|
+
# @param line [Integer] start of the expression the comment belongs to
|
|
33
|
+
# @param source [String] Path to source file or "string"
|
|
36
34
|
#
|
|
37
35
|
def initialize(comment:, line: nil, source: nil)
|
|
38
36
|
@original_comment = comment
|
|
@@ -80,15 +78,15 @@ module Reek
|
|
|
80
78
|
# This class validates [1], [2] and [3] at the moment but will also validate
|
|
81
79
|
# [4] in the future.
|
|
82
80
|
#
|
|
83
|
-
# :reek:TooManyInstanceVariables
|
|
81
|
+
# @quality :reek:TooManyInstanceVariables { max_instance_variables: 7 }
|
|
84
82
|
class CodeCommentValidator
|
|
85
83
|
#
|
|
86
|
-
# @param detector_name [String]
|
|
84
|
+
# @param detector_name [String] the detector class that was parsed out of the original
|
|
87
85
|
# comment, e.g. "DuplicateMethodCall" or "UnknownSmellDetector"
|
|
88
|
-
# @param original_comment [String]
|
|
89
|
-
# @param line [Integer]
|
|
90
|
-
# @param source [String]
|
|
91
|
-
# @param options [String]
|
|
86
|
+
# @param original_comment [String] the original comment as found in the source code
|
|
87
|
+
# @param line [Integer] start of the expression the comment belongs to
|
|
88
|
+
# @param source [String] path to source file or "string"
|
|
89
|
+
# @param options [String] the configuration options as String for the detector that were
|
|
92
90
|
# extracted from the original comment
|
|
93
91
|
def initialize(detector_name:, original_comment:, line:, source:, options: {})
|
|
94
92
|
@detector_name = detector_name
|
|
@@ -151,24 +149,24 @@ module Reek
|
|
|
151
149
|
line: line
|
|
152
150
|
end
|
|
153
151
|
|
|
154
|
-
# @return [Boolean]
|
|
152
|
+
# @return [Boolean] all keys in code comment are applicable to the detector in question
|
|
155
153
|
def given_keys_legit?
|
|
156
154
|
given_configuration_keys.subset? valid_detector_keys
|
|
157
155
|
end
|
|
158
156
|
|
|
159
|
-
# @return [Set]
|
|
157
|
+
# @return [Set] the configuration keys that are found in the code comment
|
|
160
158
|
def given_configuration_keys
|
|
161
159
|
parsed_options.keys.map(&:to_sym).to_set
|
|
162
160
|
end
|
|
163
161
|
|
|
164
|
-
# @return [String]
|
|
162
|
+
# @return [String] all keys from the code comment that look bad
|
|
165
163
|
def configuration_keys_difference
|
|
166
164
|
given_configuration_keys.difference(valid_detector_keys).
|
|
167
165
|
to_a.map { |key| "'#{key}'" }.
|
|
168
166
|
join(', ')
|
|
169
167
|
end
|
|
170
168
|
|
|
171
|
-
# @return [Set]
|
|
169
|
+
# @return [Set] all keys that are legit for the given detector
|
|
172
170
|
def valid_detector_keys
|
|
173
171
|
detector_class.configuration_keys
|
|
174
172
|
end
|
|
@@ -15,20 +15,30 @@ module Reek
|
|
|
15
15
|
# @public
|
|
16
16
|
class AppConfiguration
|
|
17
17
|
include ConfigurationValidator
|
|
18
|
-
EXCLUDE_PATHS_KEY = 'exclude_paths'
|
|
18
|
+
EXCLUDE_PATHS_KEY = 'exclude_paths'
|
|
19
|
+
DIRECTORIES_KEY = 'directories'
|
|
20
|
+
DETECTORS_KEY = 'detectors'
|
|
19
21
|
|
|
20
|
-
# Instantiate a configuration via
|
|
22
|
+
# Instantiate a configuration via the given path.
|
|
21
23
|
#
|
|
22
|
-
# @param path [Pathname] the path to the config file
|
|
23
|
-
# default path.
|
|
24
|
+
# @param path [Pathname] the path to the config file.
|
|
24
25
|
#
|
|
25
26
|
# @return [AppConfiguration]
|
|
26
27
|
#
|
|
27
28
|
# @public
|
|
28
|
-
def self.from_path(path
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
def self.from_path(path)
|
|
30
|
+
values = ConfigurationFileFinder.find_and_load(path: path)
|
|
31
|
+
new(values: values)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Instantiate a configuration via the default path.
|
|
35
|
+
#
|
|
36
|
+
# @return [AppConfiguration]
|
|
37
|
+
#
|
|
38
|
+
# @public
|
|
39
|
+
def self.from_default_path
|
|
40
|
+
values = ConfigurationFileFinder.find_and_load(path: nil)
|
|
41
|
+
new(values: values)
|
|
32
42
|
end
|
|
33
43
|
|
|
34
44
|
# Instantiate a configuration by passing everything in.
|
|
@@ -40,21 +50,17 @@ module Reek
|
|
|
40
50
|
# @return [AppConfiguration]
|
|
41
51
|
#
|
|
42
52
|
# @public
|
|
43
|
-
def self.from_hash(hash
|
|
44
|
-
|
|
45
|
-
instance.instance_eval do
|
|
46
|
-
load_values hash
|
|
47
|
-
end
|
|
48
|
-
end
|
|
53
|
+
def self.from_hash(hash)
|
|
54
|
+
new(values: hash)
|
|
49
55
|
end
|
|
50
56
|
|
|
51
57
|
def self.default
|
|
52
|
-
new
|
|
58
|
+
new(values: {})
|
|
53
59
|
end
|
|
54
60
|
|
|
55
61
|
# Returns the directive for a given directory.
|
|
56
62
|
#
|
|
57
|
-
# @param source_via [String]
|
|
63
|
+
# @param source_via [String] the source of the code inspected
|
|
58
64
|
#
|
|
59
65
|
# @return [Hash] the directory directive for the source with the default directive
|
|
60
66
|
# reverse-merged into it.
|
|
@@ -67,18 +73,22 @@ module Reek
|
|
|
67
73
|
excluded_paths.map(&:expand_path).include?(path.expand_path)
|
|
68
74
|
end
|
|
69
75
|
|
|
70
|
-
def load_values(
|
|
71
|
-
|
|
76
|
+
def load_values(values)
|
|
77
|
+
values.each do |key, value|
|
|
72
78
|
if key == EXCLUDE_PATHS_KEY
|
|
73
79
|
excluded_paths.add value
|
|
74
|
-
elsif
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
80
|
+
elsif key == DIRECTORIES_KEY
|
|
81
|
+
directory_directives.add value
|
|
82
|
+
elsif key == DETECTORS_KEY
|
|
83
|
+
default_directive.add value
|
|
78
84
|
end
|
|
79
85
|
end
|
|
80
86
|
end
|
|
81
87
|
|
|
88
|
+
def initialize(values: {})
|
|
89
|
+
load_values(values)
|
|
90
|
+
end
|
|
91
|
+
|
|
82
92
|
private
|
|
83
93
|
|
|
84
94
|
attr_writer :directory_directives, :default_directive, :excluded_paths
|
|
@@ -94,12 +104,6 @@ module Reek
|
|
|
94
104
|
def excluded_paths
|
|
95
105
|
@excluded_paths ||= [].extend(ExcludedPaths)
|
|
96
106
|
end
|
|
97
|
-
|
|
98
|
-
def find_and_load(path: nil)
|
|
99
|
-
configuration_hash = ConfigurationFileFinder.find_and_load(path: path)
|
|
100
|
-
|
|
101
|
-
load_values(configuration_hash)
|
|
102
|
-
end
|
|
103
107
|
end
|
|
104
108
|
end
|
|
105
109
|
end
|