reek 4.7.1 → 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 +36 -7
- data/.simplecov +1 -0
- data/.travis.yml +3 -9
- data/.yardopts +1 -1
- data/CHANGELOG.md +59 -0
- data/CONTRIBUTING.md +1 -1
- data/Gemfile +5 -5
- 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 +7 -7
- 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 +3 -3
- 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 +80 -75
- 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 +3 -3
- data/features/configuration_via_source_comments/well_formed_source_comments.feature +2 -2
- data/features/locales.feature +32 -0
- data/features/rake_task/rake_task.feature +58 -18
- 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 +40 -0
- data/features/samples.feature +287 -287
- data/features/step_definitions/reek_steps.rb +2 -2
- data/features/step_definitions/sample_file_steps.rb +9 -4
- data/features/support/env.rb +2 -11
- data/features/todo_list.feature +26 -23
- data/lib/reek/ast/builder.rb +1 -1
- data/lib/reek/ast/node.rb +40 -58
- 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/ast/sexp_extensions/send.rb +0 -4
- 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 +31 -16
- data/lib/reek/cli/silencer.rb +14 -3
- data/lib/reek/code_comment.rb +14 -16
- data/lib/reek/configuration/app_configuration.rb +32 -27
- data/lib/reek/configuration/configuration_converter.rb +110 -0
- data/lib/reek/configuration/configuration_file_finder.rb +16 -41
- 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 +3 -3
- data/lib/reek/context/code_context.rb +47 -43
- data/lib/reek/context/ghost_context.rb +0 -2
- data/lib/reek/context/method_context.rb +22 -15
- data/lib/reek/context/module_context.rb +5 -9
- data/lib/reek/context/root_context.rb +0 -4
- data/lib/reek/context/send_context.rb +2 -2
- data/lib/reek/context_builder.rb +43 -44
- data/lib/reek/detector_repository.rb +11 -11
- data/lib/reek/documentation_link.rb +28 -0
- data/lib/reek/errors/bad_detector_configuration_key_in_comment_error.rb +14 -13
- data/lib/reek/errors/bad_detector_in_comment_error.rb +12 -11
- 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 +43 -0
- data/lib/reek/errors/garbage_detector_configuration_in_comment_error.rb +13 -12
- data/lib/reek/errors/incomprehensible_source_error.rb +22 -24
- data/lib/reek/errors/syntax_error.rb +41 -0
- data/lib/reek/examiner.rb +24 -26
- data/lib/reek/logging_error_handler.rb +7 -5
- data/lib/reek/rake/task.rb +8 -4
- 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 +5 -11
- data/lib/reek/smell_detectors/base_detector.rb +30 -26
- data/lib/reek/smell_detectors/boolean_parameter.rb +3 -5
- data/lib/reek/smell_detectors/class_variable.rb +5 -14
- data/lib/reek/smell_detectors/control_parameter.rb +18 -33
- data/lib/reek/smell_detectors/data_clump.rb +15 -9
- data/lib/reek/smell_detectors/duplicate_method_call.rb +23 -17
- data/lib/reek/smell_detectors/feature_envy.rb +9 -7
- data/lib/reek/smell_detectors/instance_variable_assumption.rb +14 -23
- data/lib/reek/smell_detectors/irresponsible_module.rb +5 -12
- data/lib/reek/smell_detectors/long_parameter_list.rb +10 -7
- data/lib/reek/smell_detectors/long_yield_list.rb +10 -7
- data/lib/reek/smell_detectors/manual_dispatch.rb +4 -5
- data/lib/reek/smell_detectors/{prima_donna_method.rb → missing_safe_method.rb} +20 -20
- data/lib/reek/smell_detectors/module_initialize.rb +3 -5
- data/lib/reek/smell_detectors/nested_iterators.rb +16 -24
- data/lib/reek/smell_detectors/nil_check.rb +8 -15
- data/lib/reek/smell_detectors/repeated_conditional.rb +13 -11
- data/lib/reek/smell_detectors/subclassed_from_core_class.rb +7 -8
- data/lib/reek/smell_detectors/too_many_constants.rb +10 -9
- data/lib/reek/smell_detectors/too_many_instance_variables.rb +10 -6
- data/lib/reek/smell_detectors/too_many_methods.rb +11 -7
- data/lib/reek/smell_detectors/too_many_statements.rb +10 -6
- data/lib/reek/smell_detectors/uncommunicative_method_name.rb +10 -11
- data/lib/reek/smell_detectors/uncommunicative_module_name.rb +14 -18
- data/lib/reek/smell_detectors/uncommunicative_parameter_name.rb +17 -22
- data/lib/reek/smell_detectors/uncommunicative_variable_name.rb +26 -27
- data/lib/reek/smell_detectors/unused_parameters.rb +4 -6
- data/lib/reek/smell_detectors/unused_private_method.rb +11 -18
- data/lib/reek/smell_detectors/utility_function.rb +12 -16
- data/lib/reek/smell_detectors.rb +1 -2
- data/lib/reek/smell_warning.rb +15 -8
- data/lib/reek/source/source_code.rb +57 -58
- data/lib/reek/source/source_locator.rb +8 -8
- 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 +8 -8
- data/lib/reek/version.rb +1 -1
- data/reek.gemspec +5 -7
- 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 +14 -34
- data/spec/reek/cli/application_spec.rb +52 -42
- 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 -51
- 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 +70 -106
- data/spec/reek/context/ghost_context_spec.rb +9 -9
- data/spec/reek/context/method_context_spec.rb +2 -2
- data/spec/reek/context/module_context_spec.rb +3 -3
- data/spec/reek/context/root_context_spec.rb +1 -1
- data/spec/reek/context_builder_spec.rb +20 -0
- 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 +136 -29
- data/spec/reek/rake/task_spec.rb +25 -2
- 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/base_detector_spec.rb +4 -5
- 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/irresponsible_module_spec.rb +0 -11
- data/spec/reek/smell_detectors/{prima_donna_method_spec.rb → missing_safe_method_spec.rb} +10 -10
- 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 +36 -15
- 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 +21 -10
- data/spec/reek/smell_detectors/utility_function_spec.rb +57 -5
- data/spec/reek/smell_warning_spec.rb +12 -8
- data/spec/reek/source/source_code_spec.rb +22 -21
- data/spec/reek/source/source_locator_spec.rb +17 -17
- data/spec/reek/spec/should_reek_of_spec.rb +25 -29
- data/spec/reek/spec/should_reek_only_of_spec.rb +2 -2
- data/spec/reek/spec/should_reek_spec.rb +8 -8
- data/spec/reek/spec/smell_matcher_spec.rb +23 -23
- data/spec/reek/tree_dresser_spec.rb +12 -17
- data/spec/spec_helper.rb +6 -17
- data/tasks/configuration.rake +8 -5
- metadata +74 -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
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,18 +89,26 @@ module Reek
|
|
|
95
89
|
#
|
|
96
90
|
# @return [Array<SmellWarning>] the smells found in the source
|
|
97
91
|
#
|
|
98
|
-
# :reek:TooManyStatements { max_statements: 8 }
|
|
99
92
|
def run
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
examine_tree
|
|
103
|
-
rescue Errors::BaseError => exception
|
|
104
|
-
raise unless @error_handler.handle exception
|
|
105
|
-
[]
|
|
106
|
-
end
|
|
107
|
-
else
|
|
108
|
-
SmellDetectors::Syntax.smells_from_source(source)
|
|
93
|
+
wrap_exceptions do
|
|
94
|
+
examine_tree
|
|
109
95
|
end
|
|
96
|
+
rescue StandardError => exception
|
|
97
|
+
raise unless @error_handler.handle exception
|
|
98
|
+
[]
|
|
99
|
+
end
|
|
100
|
+
|
|
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
|
|
110
112
|
end
|
|
111
113
|
|
|
112
114
|
def syntax_tree
|
|
@@ -117,10 +119,6 @@ module Reek
|
|
|
117
119
|
ContextBuilder.new(syntax_tree).context_tree.flat_map do |element|
|
|
118
120
|
detector_repository.examine(element)
|
|
119
121
|
end
|
|
120
|
-
rescue Errors::BaseError
|
|
121
|
-
raise
|
|
122
|
-
rescue StandardError => exception
|
|
123
|
-
raise Errors::IncomprehensibleSourceError, origin: origin, original_exception: exception
|
|
124
122
|
end
|
|
125
123
|
end
|
|
126
124
|
end
|
|
@@ -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
|
|
@@ -73,10 +73,14 @@ module Reek
|
|
|
73
73
|
@name = name
|
|
74
74
|
@reek_opts = ENV['REEK_OPTS'] || ''
|
|
75
75
|
@fail_on_error = true
|
|
76
|
-
@source_files = FileList[ENV['REEK_SRC'] || 'lib/**/*.rb']
|
|
77
76
|
@verbose = false
|
|
78
77
|
|
|
79
78
|
yield self if block_given?
|
|
79
|
+
|
|
80
|
+
if (reek_src = ENV['REEK_SRC'])
|
|
81
|
+
@source_files = FileList[reek_src]
|
|
82
|
+
end
|
|
83
|
+
@source_files ||= FileList['lib/**/*.rb']
|
|
80
84
|
define_task
|
|
81
85
|
end
|
|
82
86
|
|
|
@@ -110,7 +114,7 @@ module Reek
|
|
|
110
114
|
reject(&:empty?)
|
|
111
115
|
end
|
|
112
116
|
|
|
113
|
-
# :reek:UtilityFunction
|
|
117
|
+
# @quality :reek:UtilityFunction
|
|
114
118
|
def sys_call_failed?
|
|
115
119
|
!$CHILD_STATUS.success?
|
|
116
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,
|
data/lib/reek/report.rb
CHANGED
|
@@ -6,7 +6,12 @@ require_relative 'report/json_report'
|
|
|
6
6
|
require_relative 'report/text_report'
|
|
7
7
|
require_relative 'report/xml_report'
|
|
8
8
|
require_relative 'report/yaml_report'
|
|
9
|
-
|
|
9
|
+
|
|
10
|
+
require_relative 'report/heading_formatter'
|
|
11
|
+
require_relative 'report/location_formatter'
|
|
12
|
+
require_relative 'report/progress_formatter'
|
|
13
|
+
require_relative 'report/simple_warning_formatter'
|
|
14
|
+
require_relative 'report/documentation_link_warning_formatter'
|
|
10
15
|
|
|
11
16
|
module Reek
|
|
12
17
|
# Reek reporting functionality.
|
|
@@ -21,24 +26,24 @@ module Reek
|
|
|
21
26
|
}.freeze
|
|
22
27
|
|
|
23
28
|
LOCATION_FORMATTERS = {
|
|
24
|
-
single_line:
|
|
25
|
-
plain:
|
|
26
|
-
numbers:
|
|
29
|
+
single_line: SingleLineLocationFormatter,
|
|
30
|
+
plain: BlankLocationFormatter,
|
|
31
|
+
numbers: DefaultLocationFormatter
|
|
27
32
|
}.freeze
|
|
28
33
|
|
|
29
34
|
HEADING_FORMATTERS = {
|
|
30
|
-
verbose:
|
|
31
|
-
quiet:
|
|
35
|
+
verbose: VerboseHeadingFormatter,
|
|
36
|
+
quiet: QuietHeadingFormatter
|
|
32
37
|
}.freeze
|
|
33
38
|
|
|
34
39
|
PROGRESS_FORMATTERS = {
|
|
35
|
-
dots:
|
|
36
|
-
quiet:
|
|
40
|
+
dots: ProgressFormatter::Dots,
|
|
41
|
+
quiet: ProgressFormatter::Quiet
|
|
37
42
|
}.freeze
|
|
38
43
|
|
|
39
44
|
WARNING_FORMATTER_CLASSES = {
|
|
40
|
-
|
|
41
|
-
simple:
|
|
45
|
+
documentation_links: DocumentationLinkWarningFormatter,
|
|
46
|
+
simple: SimpleWarningFormatter
|
|
42
47
|
}.freeze
|
|
43
48
|
|
|
44
49
|
# Map report format symbol to a report class.
|
|
@@ -7,11 +7,11 @@ module Reek
|
|
|
7
7
|
class SmellConfiguration
|
|
8
8
|
# The name of the config field that specifies whether a smell is
|
|
9
9
|
# enabled. Set to +true+ or +false+.
|
|
10
|
-
ENABLED_KEY = 'enabled'
|
|
10
|
+
ENABLED_KEY = 'enabled'
|
|
11
11
|
|
|
12
12
|
# The name of the config field that sets scope-specific overrides
|
|
13
13
|
# for other values in the current smell detector's configuration.
|
|
14
|
-
OVERRIDES_KEY = 'overrides'
|
|
14
|
+
OVERRIDES_KEY = 'overrides'
|
|
15
15
|
|
|
16
16
|
def initialize(hash)
|
|
17
17
|
@options = hash
|
|
@@ -16,10 +16,6 @@ module Reek
|
|
|
16
16
|
#
|
|
17
17
|
# TODO: Catch attributes declared "by hand"
|
|
18
18
|
class Attribute < BaseDetector
|
|
19
|
-
def initialize(*args)
|
|
20
|
-
super
|
|
21
|
-
end
|
|
22
|
-
|
|
23
19
|
def self.contexts # :nodoc:
|
|
24
20
|
[:sym]
|
|
25
21
|
end
|
|
@@ -29,10 +25,9 @@ module Reek
|
|
|
29
25
|
#
|
|
30
26
|
# @return [Array<SmellWarning>]
|
|
31
27
|
#
|
|
32
|
-
def sniff
|
|
33
|
-
|
|
28
|
+
def sniff
|
|
29
|
+
attributes_in_context.map do |_attribute, line|
|
|
34
30
|
smell_warning(
|
|
35
|
-
context: ctx,
|
|
36
31
|
lines: [line],
|
|
37
32
|
message: 'is a writable attribute')
|
|
38
33
|
end
|
|
@@ -40,10 +35,9 @@ module Reek
|
|
|
40
35
|
|
|
41
36
|
private
|
|
42
37
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
call_node = module_ctx.exp
|
|
38
|
+
def attributes_in_context
|
|
39
|
+
if context.visibility == :public
|
|
40
|
+
call_node = expression
|
|
47
41
|
[[call_node.name, call_node.line]]
|
|
48
42
|
else
|
|
49
43
|
[]
|