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
|
@@ -7,7 +7,7 @@ module Reek
|
|
|
7
7
|
#
|
|
8
8
|
# A context wrapper for any method definition found in a syntax tree.
|
|
9
9
|
#
|
|
10
|
-
# :reek:Attribute
|
|
10
|
+
# @quality :reek:Attribute
|
|
11
11
|
class MethodContext < CodeContext
|
|
12
12
|
attr_accessor :visibility
|
|
13
13
|
attr_reader :refs
|
|
@@ -35,7 +35,7 @@ module Reek
|
|
|
35
35
|
local_nodes([:lvar, :lvasgn]).find { |node| node.var_name == param.name }
|
|
36
36
|
end
|
|
37
37
|
|
|
38
|
-
# :reek:FeatureEnvy
|
|
38
|
+
# @quality :reek:FeatureEnvy
|
|
39
39
|
def unused_params
|
|
40
40
|
exp.arguments.reject do |param|
|
|
41
41
|
param.anonymous_splat? ||
|
data/lib/reek/context_builder.rb
CHANGED
|
@@ -20,9 +20,9 @@ module Reek
|
|
|
20
20
|
# counting. Ideally `ContextBuilder` would only build up the context tree and leave the
|
|
21
21
|
# statement and reference counting to the contexts.
|
|
22
22
|
#
|
|
23
|
-
# :reek:TooManyMethods
|
|
24
|
-
# :reek:UnusedPrivateMethod
|
|
25
|
-
# :reek:DataClump
|
|
23
|
+
# @quality :reek:TooManyMethods { max_methods: 31 }
|
|
24
|
+
# @quality :reek:UnusedPrivateMethod { exclude: [ !ruby/regexp /process_/ ] }
|
|
25
|
+
# @quality :reek:DataClump
|
|
26
26
|
class ContextBuilder
|
|
27
27
|
attr_reader :context_tree
|
|
28
28
|
|
|
@@ -470,7 +470,7 @@ module Reek
|
|
|
470
470
|
self.class.private_method_defined?(name)
|
|
471
471
|
end
|
|
472
472
|
|
|
473
|
-
# :reek:ControlParameter
|
|
473
|
+
# @quality :reek:ControlParameter
|
|
474
474
|
def increase_statement_count_by(sexp)
|
|
475
475
|
current_context.statement_counter.increase_by sexp
|
|
476
476
|
end
|
|
@@ -482,8 +482,8 @@ module Reek
|
|
|
482
482
|
# Stores a reference to the current context, creates a nested new one,
|
|
483
483
|
# yields to the given block and then restores the previous context.
|
|
484
484
|
#
|
|
485
|
-
# @param klass [Context::*Context]
|
|
486
|
-
# @param args
|
|
485
|
+
# @param klass [Context::*Context] context class
|
|
486
|
+
# @param args arguments for the class initializer
|
|
487
487
|
# @yield block
|
|
488
488
|
#
|
|
489
489
|
def inside_new_context(klass, *args)
|
|
@@ -497,10 +497,10 @@ module Reek
|
|
|
497
497
|
# Appends a new child context to the current context but does not change
|
|
498
498
|
# the current context.
|
|
499
499
|
#
|
|
500
|
-
# @param klass [Context::*Context]
|
|
501
|
-
# @param args
|
|
500
|
+
# @param klass [Context::*Context] context class
|
|
501
|
+
# @param args arguments for the class initializer
|
|
502
502
|
#
|
|
503
|
-
# @return [Context::*Context]
|
|
503
|
+
# @return [Context::*Context] the context that was appended
|
|
504
504
|
#
|
|
505
505
|
def append_new_context(klass, *args)
|
|
506
506
|
klass.new(*args).tap do |new_context|
|
|
@@ -44,6 +44,12 @@ module Reek
|
|
|
44
44
|
end
|
|
45
45
|
end
|
|
46
46
|
|
|
47
|
+
# @return [Array<String>] The names of all known SmellDetectors
|
|
48
|
+
# e.g. ["BooleanParameter", "ClassVariable"].
|
|
49
|
+
def self.available_detector_names
|
|
50
|
+
smell_types.map(&:smell_type)
|
|
51
|
+
end
|
|
52
|
+
|
|
47
53
|
private
|
|
48
54
|
|
|
49
55
|
attr_reader :configuration, :smell_types
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Reek
|
|
4
|
+
# Generate versioned links to our documentation
|
|
5
|
+
module DocumentationLink
|
|
6
|
+
HELP_LINK_TEMPLATE = 'https://github.com/troessner/reek/blob/v%<version>s/docs/%<item>s.md'
|
|
7
|
+
|
|
8
|
+
module_function
|
|
9
|
+
|
|
10
|
+
# Build link to the documentation about the given subject for the current
|
|
11
|
+
# version of Reek. The subject can be either a smell type like
|
|
12
|
+
# 'FeatureEnvy' or a general subject like 'Rake Task'.
|
|
13
|
+
#
|
|
14
|
+
# @param subject [String]
|
|
15
|
+
# @return [String] the full URL for the relevant documentation
|
|
16
|
+
def build(subject)
|
|
17
|
+
Kernel.format(HELP_LINK_TEMPLATE, version: Version::STRING, item: name_to_param(subject))
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Convert the given subject name to a form that is acceptable in a URL.
|
|
21
|
+
def name_to_param(name)
|
|
22
|
+
# Splits the subject on the start of capitalized words, optionally
|
|
23
|
+
# preceded by a space. The space is discarded, the start of the word is
|
|
24
|
+
# not.
|
|
25
|
+
name.split(/ *(?=[A-Z][a-z])/).join('-')
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative 'base_error'
|
|
4
|
+
require_relative '../documentation_link'
|
|
4
5
|
|
|
5
6
|
module Reek
|
|
6
7
|
module Errors
|
|
7
8
|
# Gets raised when trying to configure a detector with an option
|
|
8
9
|
# which is unknown to it.
|
|
9
10
|
class BadDetectorConfigurationKeyInCommentError < BaseError
|
|
10
|
-
UNKNOWN_SMELL_DETECTOR_MESSAGE = <<-MESSAGE
|
|
11
|
+
UNKNOWN_SMELL_DETECTOR_MESSAGE = <<-MESSAGE
|
|
11
12
|
|
|
12
13
|
Error: You are trying to configure the smell detector '%<detector>s'
|
|
13
14
|
in one of your source code comments with the unknown option %<option>s.
|
|
@@ -17,8 +18,8 @@ module Reek
|
|
|
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 basic options are available:
|
|
21
|
+
* how to configure Reek via source code comments: #{DocumentationLink.build('Smell Suppression')}
|
|
22
|
+
* what basic options are available: #{DocumentationLink.build('Basic Smell Options')}
|
|
22
23
|
* what custom options are available by checking the detector specific documentation in /docs
|
|
23
24
|
Update the offensive comment (or remove it if no longer applicable) and re-run Reek.
|
|
24
25
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative 'base_error'
|
|
4
|
+
require_relative '../documentation_link'
|
|
4
5
|
|
|
5
6
|
module Reek
|
|
6
7
|
module Errors
|
|
@@ -8,7 +9,7 @@ module Reek
|
|
|
8
9
|
# This might happen for multiple reasons. The users might have a typo in
|
|
9
10
|
# his comment or he might use a detector that does not exist anymore.
|
|
10
11
|
class BadDetectorInCommentError < BaseError
|
|
11
|
-
UNKNOWN_SMELL_DETECTOR_MESSAGE = <<-MESSAGE
|
|
12
|
+
UNKNOWN_SMELL_DETECTOR_MESSAGE = <<-MESSAGE
|
|
12
13
|
|
|
13
14
|
Error: You are trying to configure an unknown smell detector '%<detector>s' in one
|
|
14
15
|
of your source code comments.
|
|
@@ -18,8 +19,8 @@ module Reek
|
|
|
18
19
|
%<comment>s
|
|
19
20
|
|
|
20
21
|
Please see the Reek docs for:
|
|
21
|
-
* how to configure Reek via source code comments:
|
|
22
|
-
* what smell detectors are available:
|
|
22
|
+
* how to configure Reek via source code comments: #{DocumentationLink.build('Smell Suppression')}
|
|
23
|
+
* what smell detectors are available: #{DocumentationLink.build('Code Smells')}
|
|
23
24
|
Update the offensive comment (or remove it if no longer applicable) and re-run Reek.
|
|
24
25
|
|
|
25
26
|
MESSAGE
|
|
@@ -6,9 +6,9 @@ module Reek
|
|
|
6
6
|
module Errors
|
|
7
7
|
# Gets raised when Reek is unable to process the source due to an EncodingError
|
|
8
8
|
class EncodingError < BaseError
|
|
9
|
-
TEMPLATE = "Source '%<source>s' cannot be processed by Reek due to an encoding error in the source file."
|
|
9
|
+
TEMPLATE = "Source '%<source>s' cannot be processed by Reek due to an encoding error in the source file."
|
|
10
10
|
|
|
11
|
-
LONG_TEMPLATE = <<-MESSAGE
|
|
11
|
+
LONG_TEMPLATE = <<-MESSAGE
|
|
12
12
|
!!!
|
|
13
13
|
%<message>s
|
|
14
14
|
|
|
@@ -1,13 +1,14 @@
|
|
|
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
13
|
Error: You are trying to configure the smell detector '%<detector>s'.
|
|
13
14
|
Unfortunately we cannot parse the configuration you have given.
|
|
@@ -17,8 +18,8 @@ module Reek
|
|
|
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
|
|
@@ -6,9 +6,9 @@ module Reek
|
|
|
6
6
|
module Errors
|
|
7
7
|
# Gets raised when Reek is unable to process the source
|
|
8
8
|
class IncomprehensibleSourceError < BaseError
|
|
9
|
-
TEMPLATE = 'Source %<source>s cannot be processed by Reek.'
|
|
9
|
+
TEMPLATE = 'Source %<source>s cannot be processed by Reek.'
|
|
10
10
|
|
|
11
|
-
LONG_TEMPLATE = <<-MESSAGE
|
|
11
|
+
LONG_TEMPLATE = <<-MESSAGE
|
|
12
12
|
!!!
|
|
13
13
|
%<message>s
|
|
14
14
|
|
|
@@ -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
|
@@ -4,6 +4,7 @@ require_relative 'context_builder'
|
|
|
4
4
|
require_relative 'detector_repository'
|
|
5
5
|
require_relative 'errors/incomprehensible_source_error'
|
|
6
6
|
require_relative 'errors/encoding_error'
|
|
7
|
+
require_relative 'errors/syntax_error'
|
|
7
8
|
require_relative 'source/source_code'
|
|
8
9
|
|
|
9
10
|
module Reek
|
|
@@ -11,6 +12,7 @@ module Reek
|
|
|
11
12
|
# Applies all available smell detectors to a source.
|
|
12
13
|
#
|
|
13
14
|
# @public
|
|
15
|
+
# @quality :reek:TooManyInstanceVariables { max_instance_variables: 5 }
|
|
14
16
|
class Examiner
|
|
15
17
|
# Handles no errors
|
|
16
18
|
class NullHandler
|
|
@@ -39,26 +41,17 @@ module Reek
|
|
|
39
41
|
detector_repository_class: DetectorRepository,
|
|
40
42
|
error_handler: NullHandler.new)
|
|
41
43
|
@source = Source::SourceCode.from(source)
|
|
44
|
+
@origin = @source.origin
|
|
42
45
|
@smell_types = detector_repository_class.eligible_smell_types(filter_by_smells)
|
|
43
46
|
@detector_repository = detector_repository_class.new(smell_types: @smell_types,
|
|
44
|
-
configuration: configuration.directive_for(
|
|
47
|
+
configuration: configuration.directive_for(@origin))
|
|
45
48
|
@error_handler = error_handler
|
|
46
49
|
end
|
|
47
50
|
|
|
48
51
|
# @return [String] origin of the source being analysed
|
|
49
52
|
#
|
|
50
53
|
# @public
|
|
51
|
-
|
|
52
|
-
@origin ||= source.origin
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
# @return [String] description of the source being analysed
|
|
56
|
-
#
|
|
57
|
-
# @public
|
|
58
|
-
# @deprecated Use origin
|
|
59
|
-
def description
|
|
60
|
-
origin
|
|
61
|
-
end
|
|
54
|
+
attr_reader :origin
|
|
62
55
|
|
|
63
56
|
#
|
|
64
57
|
# @return [Array<SmellWarning>] the smells found in the source
|
|
@@ -98,20 +91,22 @@ module Reek
|
|
|
98
91
|
#
|
|
99
92
|
def run
|
|
100
93
|
wrap_exceptions do
|
|
101
|
-
examine_tree
|
|
94
|
+
examine_tree
|
|
102
95
|
end
|
|
103
96
|
rescue StandardError => exception
|
|
104
97
|
raise unless @error_handler.handle exception
|
|
105
98
|
[]
|
|
106
99
|
end
|
|
107
100
|
|
|
108
|
-
# :reek:TooManyStatements { max_statements: 6 }
|
|
101
|
+
# @quality :reek:TooManyStatements { max_statements: 6 }
|
|
109
102
|
def wrap_exceptions
|
|
110
103
|
yield
|
|
111
104
|
rescue Errors::BaseError
|
|
112
105
|
raise
|
|
113
106
|
rescue EncodingError
|
|
114
107
|
raise Errors::EncodingError, origin: origin
|
|
108
|
+
rescue Parser::SyntaxError
|
|
109
|
+
raise Errors::SyntaxError, origin: origin
|
|
115
110
|
rescue StandardError
|
|
116
111
|
raise Errors::IncomprehensibleSourceError, origin: origin
|
|
117
112
|
end
|
|
@@ -121,14 +116,9 @@ module Reek
|
|
|
121
116
|
end
|
|
122
117
|
|
|
123
118
|
def examine_tree
|
|
124
|
-
return unless syntax_tree
|
|
125
119
|
ContextBuilder.new(syntax_tree).context_tree.flat_map do |element|
|
|
126
120
|
detector_repository.examine(element)
|
|
127
121
|
end
|
|
128
122
|
end
|
|
129
|
-
|
|
130
|
-
def report_syntax_errors
|
|
131
|
-
SmellDetectors::Syntax.smells_from_source(source)
|
|
132
|
-
end
|
|
133
123
|
end
|
|
134
124
|
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
|
|
@@ -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
|