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.
- checksums.yaml +4 -4
- data/.codeclimate.yml +17 -12
- data/.rubocop.yml +18 -3
- data/.simplecov +1 -0
- data/.travis.yml +3 -4
- data/.yardopts +1 -1
- data/CHANGELOG.md +45 -0
- data/Gemfile +4 -4
- data/README.md +134 -104
- 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 +2 -2
- 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 +40 -55
- data/lib/reek/ast/object_refs.rb +1 -1
- data/lib/reek/ast/reference_collector.rb +2 -4
- data/lib/reek/ast/sexp_extensions/case.rb +1 -1
- data/lib/reek/ast/sexp_extensions/if.rb +8 -1
- data/lib/reek/ast/sexp_extensions/logical_operators.rb +1 -1
- data/lib/reek/ast/sexp_extensions/methods.rb +4 -6
- 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 +8 -8
- 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 +8 -11
- data/lib/reek/context/method_context.rb +7 -12
- data/lib/reek/context/module_context.rb +4 -4
- data/lib/reek/context_builder.rb +11 -11
- 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 +13 -12
- data/lib/reek/errors/bad_detector_in_comment_error.rb +11 -10
- data/lib/reek/errors/base_error.rb +3 -0
- data/lib/reek/errors/config_file_error.rb +11 -0
- data/lib/reek/errors/encoding_error.rb +16 -11
- data/lib/reek/errors/garbage_detector_configuration_in_comment_error.rb +11 -10
- data/lib/reek/errors/incomprehensible_source_error.rb +20 -22
- data/lib/reek/errors/syntax_error.rb +41 -0
- data/lib/reek/examiner.rb +19 -25
- data/lib/reek/logging_error_handler.rb +7 -5
- 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.rb +1 -1
- 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 +9 -12
- data/lib/reek/smell_detectors/boolean_parameter.rb +0 -1
- data/lib/reek/smell_detectors/class_variable.rb +3 -11
- 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 +6 -7
- data/lib/reek/smell_detectors/feature_envy.rb +1 -1
- data/lib/reek/smell_detectors/instance_variable_assumption.rb +1 -10
- 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 +3 -3
- data/lib/reek/smell_detectors/{prima_donna_method.rb → missing_safe_method.rb} +6 -7
- data/lib/reek/smell_detectors/module_initialize.rb +1 -2
- data/lib/reek/smell_detectors/nested_iterators.rb +6 -6
- 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 +2 -3
- 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 +6 -7
- 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 +2 -3
- data/lib/reek/smell_detectors.rb +1 -2
- data/lib/reek/smell_warning.rb +15 -8
- data/lib/reek/source/source_code.rb +50 -72
- 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 +5 -5
- 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/factories/factories.rb +2 -13
- data/spec/reek/ast/node_spec.rb +103 -10
- data/spec/reek/ast/reference_collector_spec.rb +1 -1
- data/spec/reek/ast/sexp_extensions_spec.rb +2 -2
- data/spec/reek/cli/application_spec.rb +50 -38
- data/spec/reek/cli/command/todo_list_command_spec.rb +6 -4
- data/spec/reek/cli/silencer_spec.rb +28 -0
- data/spec/reek/code_comment_spec.rb +31 -38
- data/spec/reek/configuration/app_configuration_spec.rb +46 -33
- data/spec/reek/configuration/configuration_file_finder_spec.rb +133 -49
- data/spec/reek/configuration/default_directive_spec.rb +1 -1
- data/spec/reek/configuration/directory_directives_spec.rb +6 -7
- data/spec/reek/configuration/excluded_paths_spec.rb +6 -6
- 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 +60 -96
- data/spec/reek/context/ghost_context_spec.rb +1 -1
- data/spec/reek/context/root_context_spec.rb +1 -1
- data/spec/reek/documentation_link_spec.rb +20 -0
- data/spec/reek/errors/base_error_spec.rb +13 -0
- data/spec/reek/examiner_spec.rb +100 -30
- data/spec/reek/report/code_climate/code_climate_fingerprint_spec.rb +82 -80
- data/spec/reek/report/code_climate/code_climate_formatter_spec.rb +6 -6
- 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 +3 -3
- data/spec/reek/report/yaml_report_spec.rb +9 -38
- data/spec/reek/report_spec.rb +3 -3
- data/spec/reek/smell_detectors/boolean_parameter_spec.rb +2 -2
- data/spec/reek/smell_detectors/class_variable_spec.rb +26 -32
- data/spec/reek/smell_detectors/control_parameter_spec.rb +34 -4
- data/spec/reek/smell_detectors/duplicate_method_call_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/module_initialize_spec.rb +14 -0
- data/spec/reek/smell_detectors/nested_iterators_spec.rb +1 -1
- 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 +9 -9
- data/spec/reek/smell_detectors/unused_parameters_spec.rb +3 -3
- data/spec/reek/smell_detectors/unused_private_method_spec.rb +10 -10
- data/spec/reek/smell_detectors/utility_function_spec.rb +5 -5
- data/spec/reek/smell_warning_spec.rb +12 -8
- data/spec/reek/source/source_code_spec.rb +17 -43
- data/spec/reek/source/source_locator_spec.rb +17 -17
- data/spec/reek/spec/should_reek_of_spec.rb +7 -11
- data/spec/reek/spec/should_reek_only_of_spec.rb +2 -2
- data/spec/reek/spec/should_reek_spec.rb +3 -3
- data/spec/reek/spec/smell_matcher_spec.rb +3 -3
- data/spec/reek/tree_dresser_spec.rb +12 -17
- data/spec/spec_helper.rb +6 -17
- data/tasks/configuration.rake +8 -5
- metadata +71 -41
- 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
|
@@ -1,34 +1,35 @@
|
|
|
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 use a configuration for a detector
|
|
8
9
|
# that can't be parsed into a hash.
|
|
9
10
|
class GarbageDetectorConfigurationInCommentError < BaseError
|
|
10
|
-
BAD_DETECTOR_CONFIGURATION_MESSAGE = <<-MESSAGE
|
|
11
|
+
BAD_DETECTOR_CONFIGURATION_MESSAGE = <<-MESSAGE
|
|
11
12
|
|
|
12
|
-
Error: You are trying to configure the smell detector '
|
|
13
|
+
Error: You are trying to configure the smell detector '%<detector>s'.
|
|
13
14
|
Unfortunately we cannot parse the configuration you have given.
|
|
14
|
-
The source is '
|
|
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
|
-
|
|
18
|
+
%<comment>s
|
|
18
19
|
|
|
19
20
|
Please see the Reek docs for:
|
|
20
|
-
* how to configure Reek via source code comments:
|
|
21
|
-
* what smell detectors are available:
|
|
21
|
+
* how to configure Reek via source code comments: #{DocumentationLink.build('Smell Suppression')}
|
|
22
|
+
* what smell detectors are available: #{DocumentationLink.build('Code Smells')}
|
|
22
23
|
Update the offensive comment (or remove it if no longer applicable) and re-run Reek.
|
|
23
24
|
|
|
24
25
|
MESSAGE
|
|
25
26
|
|
|
26
27
|
def initialize(detector_name:, source:, line:, original_comment:)
|
|
27
28
|
message = format(BAD_DETECTOR_CONFIGURATION_MESSAGE,
|
|
28
|
-
detector_name,
|
|
29
|
-
source,
|
|
30
|
-
line,
|
|
31
|
-
original_comment)
|
|
29
|
+
detector: detector_name,
|
|
30
|
+
source: source,
|
|
31
|
+
line: line,
|
|
32
|
+
comment: original_comment)
|
|
32
33
|
super message
|
|
33
34
|
end
|
|
34
35
|
end
|
|
@@ -6,42 +6,40 @@ module Reek
|
|
|
6
6
|
module Errors
|
|
7
7
|
# Gets raised when Reek is unable to process the source
|
|
8
8
|
class IncomprehensibleSourceError < BaseError
|
|
9
|
-
|
|
10
|
-
!!!
|
|
11
|
-
Source %s cannot be processed by Reek.
|
|
9
|
+
TEMPLATE = 'Source %<source>s cannot be processed by Reek.'
|
|
12
10
|
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
LONG_TEMPLATE = <<-MESSAGE
|
|
12
|
+
!!!
|
|
13
|
+
%<message>s
|
|
15
14
|
|
|
16
|
-
|
|
17
|
-
below into account - you might have misspelled a smell detector for instance.
|
|
18
|
-
(In the future Reek will handle configuration errors more gracefully, something
|
|
19
|
-
we are working on already).
|
|
15
|
+
This is most likely a Reek bug.
|
|
20
16
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
team by opening up a corresponding issue at https://github.com/troessner/reek/issues.
|
|
17
|
+
It would be great if you could report this back to the Reek team by opening a
|
|
18
|
+
corresponding issue at https://github.com/troessner/reek/issues.
|
|
24
19
|
|
|
25
|
-
Please make sure to include the source in question, the Reek version
|
|
26
|
-
|
|
20
|
+
Please make sure to include the source in question, the Reek version and the
|
|
21
|
+
original exception below.
|
|
27
22
|
|
|
28
23
|
Exception message:
|
|
29
24
|
|
|
30
|
-
|
|
25
|
+
%<exception>s
|
|
31
26
|
|
|
32
27
|
Original exception:
|
|
33
28
|
|
|
34
|
-
|
|
29
|
+
%<original>s
|
|
35
30
|
|
|
36
31
|
!!!
|
|
37
32
|
MESSAGE
|
|
38
33
|
|
|
39
|
-
def initialize(origin
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
34
|
+
def initialize(origin:)
|
|
35
|
+
super format(TEMPLATE, source: origin)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def long_message
|
|
39
|
+
format(LONG_TEMPLATE,
|
|
40
|
+
message: message,
|
|
41
|
+
exception: cause.inspect,
|
|
42
|
+
original: cause.backtrace.join("\n\t"))
|
|
45
43
|
end
|
|
46
44
|
end
|
|
47
45
|
end
|
|
@@ -0,0 +1,41 @@
|
|
|
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 a SyntaxError
|
|
8
|
+
class SyntaxError < BaseError
|
|
9
|
+
TEMPLATE = "Source '%<source>s' cannot be processed by Reek due to a syntax error in the source file."
|
|
10
|
+
|
|
11
|
+
LONG_TEMPLATE = <<-MESSAGE
|
|
12
|
+
!!!
|
|
13
|
+
%<message>s
|
|
14
|
+
|
|
15
|
+
This is a problem that is outside of Reek's scope and should be fixed by you, the
|
|
16
|
+
user, in order for Reek being able to continue.
|
|
17
|
+
|
|
18
|
+
Exception message:
|
|
19
|
+
|
|
20
|
+
%<exception>s
|
|
21
|
+
|
|
22
|
+
Original backtrace:
|
|
23
|
+
|
|
24
|
+
%<original>s
|
|
25
|
+
|
|
26
|
+
!!!
|
|
27
|
+
MESSAGE
|
|
28
|
+
|
|
29
|
+
def initialize(origin:)
|
|
30
|
+
super format(TEMPLATE, source: origin)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def long_message
|
|
34
|
+
format(LONG_TEMPLATE,
|
|
35
|
+
message: message,
|
|
36
|
+
exception: cause.inspect,
|
|
37
|
+
original: cause.backtrace.join("\n\t"))
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
data/lib/reek/examiner.rb
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
require_relative 'context_builder'
|
|
4
4
|
require_relative 'detector_repository'
|
|
5
5
|
require_relative 'errors/incomprehensible_source_error'
|
|
6
|
+
require_relative 'errors/encoding_error'
|
|
7
|
+
require_relative 'errors/syntax_error'
|
|
6
8
|
require_relative 'source/source_code'
|
|
7
9
|
|
|
8
10
|
module Reek
|
|
@@ -10,6 +12,7 @@ module Reek
|
|
|
10
12
|
# Applies all available smell detectors to a source.
|
|
11
13
|
#
|
|
12
14
|
# @public
|
|
15
|
+
# @quality :reek:TooManyInstanceVariables { max_instance_variables: 5 }
|
|
13
16
|
class Examiner
|
|
14
17
|
# Handles no errors
|
|
15
18
|
class NullHandler
|
|
@@ -38,26 +41,17 @@ module Reek
|
|
|
38
41
|
detector_repository_class: DetectorRepository,
|
|
39
42
|
error_handler: NullHandler.new)
|
|
40
43
|
@source = Source::SourceCode.from(source)
|
|
44
|
+
@origin = @source.origin
|
|
41
45
|
@smell_types = detector_repository_class.eligible_smell_types(filter_by_smells)
|
|
42
46
|
@detector_repository = detector_repository_class.new(smell_types: @smell_types,
|
|
43
|
-
configuration: configuration.directive_for(
|
|
47
|
+
configuration: configuration.directive_for(@origin))
|
|
44
48
|
@error_handler = error_handler
|
|
45
49
|
end
|
|
46
50
|
|
|
47
51
|
# @return [String] origin of the source being analysed
|
|
48
52
|
#
|
|
49
53
|
# @public
|
|
50
|
-
|
|
51
|
-
@origin ||= source.origin
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
# @return [String] description of the source being analysed
|
|
55
|
-
#
|
|
56
|
-
# @public
|
|
57
|
-
# @deprecated Use origin
|
|
58
|
-
def description
|
|
59
|
-
origin
|
|
60
|
-
end
|
|
54
|
+
attr_reader :origin
|
|
61
55
|
|
|
62
56
|
#
|
|
63
57
|
# @return [Array<SmellWarning>] the smells found in the source
|
|
@@ -95,26 +89,26 @@ module Reek
|
|
|
95
89
|
#
|
|
96
90
|
# @return [Array<SmellWarning>] the smells found in the source
|
|
97
91
|
#
|
|
98
|
-
# :reek:TooManyStatements { max_statements: 6 }
|
|
99
92
|
def run
|
|
100
|
-
|
|
93
|
+
wrap_exceptions do
|
|
101
94
|
examine_tree
|
|
102
|
-
else
|
|
103
|
-
SmellDetectors::Syntax.smells_from_source(source)
|
|
104
95
|
end
|
|
105
96
|
rescue StandardError => exception
|
|
106
|
-
|
|
107
|
-
raise wrapper unless @error_handler.handle wrapper
|
|
97
|
+
raise unless @error_handler.handle exception
|
|
108
98
|
[]
|
|
109
99
|
end
|
|
110
100
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
101
|
+
# @quality :reek:TooManyStatements { max_statements: 6 }
|
|
102
|
+
def wrap_exceptions
|
|
103
|
+
yield
|
|
104
|
+
rescue Errors::BaseError
|
|
105
|
+
raise
|
|
106
|
+
rescue EncodingError
|
|
107
|
+
raise Errors::EncodingError, origin: origin
|
|
108
|
+
rescue Parser::SyntaxError
|
|
109
|
+
raise Errors::SyntaxError, origin: origin
|
|
110
|
+
rescue StandardError
|
|
111
|
+
raise Errors::IncomprehensibleSourceError, origin: origin
|
|
118
112
|
end
|
|
119
113
|
|
|
120
114
|
def syntax_tree
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative 'errors/
|
|
4
|
-
require_relative 'errors/bad_detector_in_comment_error'
|
|
5
|
-
require_relative 'errors/garbage_detector_configuration_in_comment_error'
|
|
6
|
-
require_relative 'errors/incomprehensible_source_error'
|
|
3
|
+
require_relative 'errors/base_error'
|
|
7
4
|
|
|
8
5
|
module Reek
|
|
9
6
|
# Handles errors by logging to stderr
|
|
10
7
|
class LoggingErrorHandler
|
|
11
8
|
def handle(exception)
|
|
12
|
-
|
|
9
|
+
case exception
|
|
10
|
+
when Errors::BaseError
|
|
11
|
+
warn exception.long_message
|
|
12
|
+
else
|
|
13
|
+
warn exception
|
|
14
|
+
end
|
|
13
15
|
true
|
|
14
16
|
end
|
|
15
17
|
end
|
data/lib/reek/rake/task.rb
CHANGED
|
@@ -33,8 +33,8 @@ module Reek
|
|
|
33
33
|
#
|
|
34
34
|
# @public
|
|
35
35
|
#
|
|
36
|
-
# :reek:TooManyInstanceVariables
|
|
37
|
-
# :reek:Attribute
|
|
36
|
+
# @quality :reek:TooManyInstanceVariables { max_instance_variables: 6 }
|
|
37
|
+
# @quality :reek:Attribute
|
|
38
38
|
class Task < ::Rake::TaskLib
|
|
39
39
|
# Name of Reek task. Defaults to :reek.
|
|
40
40
|
# @public
|
|
@@ -114,7 +114,7 @@ module Reek
|
|
|
114
114
|
reject(&:empty?)
|
|
115
115
|
end
|
|
116
116
|
|
|
117
|
-
# :reek:UtilityFunction
|
|
117
|
+
# @quality :reek:UtilityFunction
|
|
118
118
|
def sys_call_failed?
|
|
119
119
|
!$CHILD_STATUS.success?
|
|
120
120
|
end
|
|
@@ -4,8 +4,6 @@ require 'json'
|
|
|
4
4
|
require 'pathname'
|
|
5
5
|
require 'rainbow'
|
|
6
6
|
|
|
7
|
-
require_relative 'formatter'
|
|
8
|
-
|
|
9
7
|
module Reek
|
|
10
8
|
# @public
|
|
11
9
|
module Report
|
|
@@ -16,22 +14,20 @@ module Reek
|
|
|
16
14
|
#
|
|
17
15
|
# @public
|
|
18
16
|
#
|
|
19
|
-
# :reek:TooManyInstanceVariables
|
|
17
|
+
# @quality :reek:TooManyInstanceVariables { max_instance_variables: 7 }
|
|
20
18
|
class BaseReport
|
|
21
19
|
NO_WARNINGS_COLOR = :green
|
|
22
20
|
WARNINGS_COLOR = :red
|
|
23
21
|
|
|
24
22
|
# @public
|
|
25
23
|
#
|
|
26
|
-
# :reek:BooleanParameter
|
|
27
|
-
def initialize(heading_formatter:
|
|
28
|
-
report_formatter: Formatter,
|
|
24
|
+
# @quality :reek:BooleanParameter
|
|
25
|
+
def initialize(heading_formatter: QuietHeadingFormatter,
|
|
29
26
|
sort_by_issue_count: false,
|
|
30
|
-
warning_formatter:
|
|
31
|
-
progress_formatter:
|
|
27
|
+
warning_formatter: SimpleWarningFormatter.new,
|
|
28
|
+
progress_formatter: ProgressFormatter::Quiet.new(0))
|
|
32
29
|
@examiners = []
|
|
33
|
-
@heading_formatter = heading_formatter.new
|
|
34
|
-
@report_formatter = report_formatter
|
|
30
|
+
@heading_formatter = heading_formatter.new
|
|
35
31
|
@progress_formatter = progress_formatter
|
|
36
32
|
@sort_by_issue_count = sort_by_issue_count
|
|
37
33
|
@total_smell_count = 0
|
|
@@ -58,7 +54,7 @@ module Reek
|
|
|
58
54
|
end
|
|
59
55
|
|
|
60
56
|
def smells?
|
|
61
|
-
total_smell_count
|
|
57
|
+
total_smell_count.positive?
|
|
62
58
|
end
|
|
63
59
|
|
|
64
60
|
def smells
|
|
@@ -71,7 +67,7 @@ module Reek
|
|
|
71
67
|
|
|
72
68
|
private
|
|
73
69
|
|
|
74
|
-
attr_reader :examiners, :heading_formatter,
|
|
70
|
+
attr_reader :examiners, :heading_formatter,
|
|
75
71
|
:sort_by_issue_count, :warning_formatter, :progress_formatter
|
|
76
72
|
end
|
|
77
73
|
end
|
|
@@ -5,7 +5,7 @@ module Reek
|
|
|
5
5
|
# loads the smell type metadata to present in Code Climate
|
|
6
6
|
module CodeClimateConfiguration
|
|
7
7
|
def self.load
|
|
8
|
-
config_file = File.expand_path('
|
|
8
|
+
config_file = File.expand_path('code_climate_configuration.yml', __dir__)
|
|
9
9
|
YAML.load_file config_file
|
|
10
10
|
end
|
|
11
11
|
end
|
|
@@ -284,7 +284,7 @@ InstanceVariableAssumption:
|
|
|
284
284
|
would report:
|
|
285
285
|
|
|
286
286
|
```Bash
|
|
287
|
-
[1]:InstanceVariableAssumption: Dummy assumes too much for instance variable @ivar
|
|
287
|
+
[1]:InstanceVariableAssumption: Dummy assumes too much for instance variable @ivar
|
|
288
288
|
```
|
|
289
289
|
|
|
290
290
|
Note that this example would trigger this smell warning as well:
|
|
@@ -565,10 +565,10 @@ NilCheck:
|
|
|
565
565
|
test.rb -- 1 warning:
|
|
566
566
|
[3]:Klass#nil_checker performs a nil-check. (NilCheck)
|
|
567
567
|
```
|
|
568
|
-
|
|
568
|
+
MissingSafeMethod:
|
|
569
569
|
remediation_points: 250_000
|
|
570
570
|
content: |
|
|
571
|
-
A candidate method for the `
|
|
571
|
+
A candidate method for the `Missing Safe Method` smell are methods whose names end with an exclamation mark.
|
|
572
572
|
|
|
573
573
|
An exclamation mark in method names means (the explanation below is taken from [here](http://dablog.rubypal.com/2007/8/15/bang-methods-or-danger-will-rubyist) ):
|
|
574
574
|
|
|
@@ -576,7 +576,7 @@ PrimaDonnaMethod:
|
|
|
576
576
|
The ! in method names that end with ! means, “This method is dangerous”—or, more precisely, this method is the “dangerous” version of an otherwise equivalent method, with the same name minus the !. “Danger” is relative; the ! doesn’t mean anything at all unless the method name it’s in corresponds to a similar but bang-less method name.
|
|
577
577
|
So, for example, gsub! is the dangerous version of gsub. exit! is the dangerous version of exit. flatten! is the dangerous version of flatten. And so forth.
|
|
578
578
|
|
|
579
|
-
Such a method is called `
|
|
579
|
+
Such a method is called `Missing Safe Method` if and only if her non-bang version does not exist and this method is reported as a smell.
|
|
580
580
|
|
|
581
581
|
## Example
|
|
582
582
|
|
|
@@ -590,7 +590,7 @@ PrimaDonnaMethod:
|
|
|
590
590
|
end
|
|
591
591
|
```
|
|
592
592
|
|
|
593
|
-
Reek would report `bar!` as `
|
|
593
|
+
Reek would report `bar!` as `Missing Safe Method` smell but not `foo!`.
|
|
594
594
|
|
|
595
595
|
Reek reports this smell only in a class context, not in a module context in order to allow perfectly legit code like this:
|
|
596
596
|
|
|
@@ -612,7 +612,7 @@ PrimaDonnaMethod:
|
|
|
612
612
|
end
|
|
613
613
|
```
|
|
614
614
|
|
|
615
|
-
In this example, Reek would not report the `
|
|
615
|
+
In this example, Reek would not report the `Missing Safe Method` smell for the method `foo` of the `Dangerous` module.
|
|
616
616
|
RepeatedConditional:
|
|
617
617
|
remediation_points: 400_000
|
|
618
618
|
content: |
|
|
@@ -860,7 +860,3 @@ SubclassedFromCoreClass:
|
|
|
860
860
|
you are gonna have a bad time.
|
|
861
861
|
|
|
862
862
|
Source: http://words.steveklabnik.com/beware-subclassing-ruby-core-classes
|
|
863
|
-
Syntax:
|
|
864
|
-
remediation_points: 250_000
|
|
865
|
-
content: |
|
|
866
|
-
Check syntax errors.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'simple_warning_formatter'
|
|
4
|
+
|
|
5
|
+
module Reek
|
|
6
|
+
module Report
|
|
7
|
+
#
|
|
8
|
+
# Formatter that adds a link to the docs to the basic message from
|
|
9
|
+
# SimpleWarningFormatter.
|
|
10
|
+
#
|
|
11
|
+
class DocumentationLinkWarningFormatter < SimpleWarningFormatter
|
|
12
|
+
def format(warning)
|
|
13
|
+
"#{super} [#{warning.explanatory_link}]"
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Reek
|
|
4
|
+
module Report
|
|
5
|
+
#
|
|
6
|
+
# Base class for heading formatters.
|
|
7
|
+
# Is responsible for formatting the heading emitted for each examiner
|
|
8
|
+
#
|
|
9
|
+
# @abstract Override {#show_header?} to implement a heading formatter.
|
|
10
|
+
class HeadingFormatterBase
|
|
11
|
+
# @quality :reek:UtilityFunction
|
|
12
|
+
def show_header?(_examiner)
|
|
13
|
+
raise NotImplementedError
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def header(examiner)
|
|
17
|
+
if show_header?(examiner)
|
|
18
|
+
formatted_header examiner
|
|
19
|
+
else
|
|
20
|
+
''
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def formatted_header(examiner)
|
|
27
|
+
count = examiner.smells_count
|
|
28
|
+
result = Rainbow("#{examiner.origin} -- ").cyan +
|
|
29
|
+
Rainbow("#{count} warning").yellow
|
|
30
|
+
result += Rainbow('s').yellow unless count == 1
|
|
31
|
+
result
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
#
|
|
36
|
+
# Lists out each examiner, even if it has no smell
|
|
37
|
+
#
|
|
38
|
+
class VerboseHeadingFormatter < HeadingFormatterBase
|
|
39
|
+
def show_header?(_examiner)
|
|
40
|
+
true
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
#
|
|
45
|
+
# Lists only smelly examiners
|
|
46
|
+
#
|
|
47
|
+
class QuietHeadingFormatter < HeadingFormatterBase
|
|
48
|
+
# @quality :reek:UtilityFunction
|
|
49
|
+
def show_header?(examiner)
|
|
50
|
+
examiner.smelly?
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Reek
|
|
4
|
+
module Report
|
|
5
|
+
#
|
|
6
|
+
# Formats the location of a warning as an empty string.
|
|
7
|
+
#
|
|
8
|
+
module BlankLocationFormatter
|
|
9
|
+
module_function
|
|
10
|
+
|
|
11
|
+
def format(_warning)
|
|
12
|
+
''
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
#
|
|
17
|
+
# Formats the location of a warning as an array of line numbers.
|
|
18
|
+
#
|
|
19
|
+
module DefaultLocationFormatter
|
|
20
|
+
module_function
|
|
21
|
+
|
|
22
|
+
def format(warning)
|
|
23
|
+
"#{warning.lines.sort.inspect}:"
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
#
|
|
28
|
+
# Formats the location of a warning as a combination of source file name
|
|
29
|
+
# and line number. In this format, it is not possible to show more than
|
|
30
|
+
# one line number, so the first number is displayed.
|
|
31
|
+
#
|
|
32
|
+
module SingleLineLocationFormatter
|
|
33
|
+
module_function
|
|
34
|
+
|
|
35
|
+
def format(warning)
|
|
36
|
+
"#{warning.source}:#{warning.lines.min}: "
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Reek
|
|
4
|
+
module Report
|
|
5
|
+
module ProgressFormatter
|
|
6
|
+
#
|
|
7
|
+
# Base class for progress formatters.
|
|
8
|
+
# Is responsible for formatting the progress emitted for each examiner
|
|
9
|
+
#
|
|
10
|
+
# @abstract Override {#header, #progress, #footer} to implement a progress formatter.
|
|
11
|
+
class Base
|
|
12
|
+
attr_reader :sources_count
|
|
13
|
+
|
|
14
|
+
def initialize(sources_count)
|
|
15
|
+
@sources_count = sources_count
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def header
|
|
19
|
+
raise NotImplementedError
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def progress(_examiner)
|
|
23
|
+
raise NotImplementedError
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def footer
|
|
27
|
+
raise NotImplementedError
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
#
|
|
32
|
+
# Shows the status of each source as either a dot (.) or an S
|
|
33
|
+
#
|
|
34
|
+
class Dots < Base
|
|
35
|
+
NO_WARNINGS_COLOR = :green
|
|
36
|
+
WARNINGS_COLOR = :red
|
|
37
|
+
|
|
38
|
+
def header
|
|
39
|
+
"Inspecting #{sources_count} file(s):\n"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def progress(examiner)
|
|
43
|
+
examiner.smelly? ? display_smelly : display_clean
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def footer
|
|
47
|
+
"\n\n"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
def display_clean
|
|
53
|
+
Rainbow('.').color(NO_WARNINGS_COLOR)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def display_smelly
|
|
57
|
+
Rainbow('S').color(WARNINGS_COLOR)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
#
|
|
62
|
+
# Does not show progress
|
|
63
|
+
#
|
|
64
|
+
class Quiet < Base
|
|
65
|
+
def header
|
|
66
|
+
''
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def progress(_examiner)
|
|
70
|
+
''
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def footer
|
|
74
|
+
''
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'code_climate/code_climate_formatter'
|
|
4
|
+
|
|
5
|
+
module Reek
|
|
6
|
+
module Report
|
|
7
|
+
#
|
|
8
|
+
# Basic formatter that just shows a simple message for each warning,
|
|
9
|
+
# prepended with the result of the passed-in location formatter.
|
|
10
|
+
#
|
|
11
|
+
class SimpleWarningFormatter
|
|
12
|
+
def initialize(location_formatter: BlankLocationFormatter)
|
|
13
|
+
@location_formatter = location_formatter
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def format(warning)
|
|
17
|
+
"#{location_formatter.format(warning)}#{warning.base_message}"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# @quality :reek:UtilityFunction
|
|
21
|
+
def format_code_climate_hash(warning)
|
|
22
|
+
CodeClimateFormatter.new(warning).render
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def format_list(warnings)
|
|
26
|
+
warnings.map { |warning| " #{format(warning)}" }.join("\n")
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
attr_reader :location_formatter
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -49,8 +49,7 @@ module Reek
|
|
|
49
49
|
def summarize_single_examiner(examiner)
|
|
50
50
|
result = heading_formatter.header(examiner)
|
|
51
51
|
if examiner.smelly?
|
|
52
|
-
formatted_list =
|
|
53
|
-
formatter: warning_formatter)
|
|
52
|
+
formatted_list = warning_formatter.format_list(examiner.smells)
|
|
54
53
|
result += ":\n#{formatted_list}"
|
|
55
54
|
end
|
|
56
55
|
result
|
|
@@ -34,8 +34,8 @@ module Reek
|
|
|
34
34
|
end
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
-
# :reek:FeatureEnvy
|
|
38
|
-
# :reek:NestedIterators
|
|
37
|
+
# @quality :reek:FeatureEnvy
|
|
38
|
+
# @quality :reek:NestedIterators { max_allowed_nesting: 2 }
|
|
39
39
|
def file(name, smells)
|
|
40
40
|
REXML::Element.new('file').tap do |file|
|
|
41
41
|
file.add_attribute 'name', File.realpath(name)
|
|
@@ -47,7 +47,7 @@ module Reek
|
|
|
47
47
|
end
|
|
48
48
|
end
|
|
49
49
|
|
|
50
|
-
# :reek:UtilityFunction
|
|
50
|
+
# @quality :reek:UtilityFunction
|
|
51
51
|
def error(smell, line)
|
|
52
52
|
REXML::Element.new('error').tap do |error|
|
|
53
53
|
error.add_attributes 'column' => 0,
|