reek 2.0.4 → 3.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/.gitignore +13 -0
- data/.rubocop.yml +47 -0
- data/.travis.yml +31 -0
- data/.yardopts +3 -6
- data/CHANGELOG +24 -0
- data/CONTRIBUTING.md +111 -0
- data/Gemfile +16 -0
- data/README.md +112 -59
- data/bin/reek +2 -2
- data/config/cucumber.yml +3 -0
- data/config/defaults.reek +0 -1
- data/docs/API.md +79 -0
- data/docs/Attribute.md +43 -0
- data/docs/Basic-Smell-Options.md +44 -0
- data/docs/Boolean-Parameter.md +52 -0
- data/docs/Class-Variable.md +40 -0
- data/docs/Code-Smells.md +34 -0
- data/docs/Command-Line-Options.md +84 -0
- data/docs/Configuration-Files.md +49 -0
- data/docs/Control-Couple.md +22 -0
- data/docs/Control-Parameter.md +29 -0
- data/docs/Data-Clump.md +44 -0
- data/docs/Duplicate-Method-Call.md +49 -0
- data/docs/Feature-Envy.md +49 -0
- data/docs/How-reek-works-internally.md +114 -0
- data/docs/Irresponsible-Module.md +39 -0
- data/docs/Large-Class.md +20 -0
- data/docs/Long-Parameter-List.md +38 -0
- data/docs/Long-Yield-List.md +36 -0
- data/docs/Module-Initialize.md +62 -0
- data/docs/Nested-Iterators.md +38 -0
- data/docs/Nil-Check.md +39 -0
- data/docs/Prima-Donna-Method.md +53 -0
- data/docs/RSpec-matchers.md +137 -0
- data/docs/Rake-Task.md +58 -0
- data/docs/Reek-Driven-Development.md +37 -0
- data/docs/Repeated-Conditional.md +44 -0
- data/docs/Simulated-Polymorphism.md +16 -0
- data/docs/Smell-Suppression.md +32 -0
- data/docs/Too-Many-Instance-Variables.md +43 -0
- data/docs/Too-Many-Methods.md +55 -0
- data/docs/Too-Many-Statements.md +50 -0
- data/docs/Uncommunicative-Method-Name.md +24 -0
- data/docs/Uncommunicative-Module-Name.md +23 -0
- data/docs/Uncommunicative-Name.md +16 -0
- data/docs/Uncommunicative-Parameter-Name.md +24 -0
- data/docs/Uncommunicative-Variable-Name.md +24 -0
- data/docs/Unused-Parameters.md +27 -0
- data/docs/Utility-Function.md +44 -0
- data/docs/Versioning-Policy.md +7 -0
- data/docs/YAML-Reports.md +111 -0
- data/docs/yard_plugin.rb +14 -0
- data/features/{ruby_api/api.feature → command_line_interface/basic_usage.feature} +6 -5
- data/features/command_line_interface/options.feature +1 -0
- data/features/command_line_interface/smell_selection.feature +4 -8
- data/features/command_line_interface/smells_count.feature +25 -32
- data/features/command_line_interface/stdin.feature +2 -2
- data/features/configuration_files/masking_smells.feature +30 -41
- data/features/configuration_files/overrides_defaults.feature +5 -3
- data/features/configuration_loading.feature +26 -23
- data/features/programmatic_access.feature +43 -0
- data/features/rake_task/rake_task.feature +25 -22
- data/features/reports/json.feature +24 -26
- data/features/reports/reports.feature +77 -103
- data/features/reports/yaml.feature +26 -20
- data/features/samples.feature +26 -31
- data/features/step_definitions/.rubocop.yml +5 -0
- data/features/step_definitions/reek_steps.rb +16 -28
- data/features/step_definitions/sample_file_steps.rb +158 -0
- data/features/support/env.rb +16 -27
- data/lib/reek/{source → ast}/ast_node_class_map.rb +11 -12
- data/lib/reek/ast/node.rb +132 -0
- data/lib/reek/{core → ast}/object_refs.rb +2 -1
- data/lib/reek/ast/reference_collector.rb +32 -0
- data/lib/reek/{source → ast}/sexp_extensions.rb +43 -7
- data/lib/reek/ast/sexp_formatter.rb +31 -0
- data/lib/reek/cli/application.rb +5 -4
- data/lib/reek/cli/command.rb +2 -1
- data/lib/reek/cli/input.rb +6 -5
- data/lib/reek/cli/option_interpreter.rb +13 -9
- data/lib/reek/cli/options.rb +16 -8
- data/lib/reek/cli/reek_command.rb +4 -3
- data/lib/reek/cli/silencer.rb +14 -0
- data/lib/reek/{core → cli}/warning_collector.rb +2 -1
- data/lib/reek/code_comment.rb +36 -0
- data/lib/reek/configuration/app_configuration.rb +18 -3
- data/lib/reek/configuration/configuration_file_finder.rb +18 -32
- data/lib/reek/{core → context}/code_context.rb +8 -6
- data/lib/reek/{core → context}/method_context.rb +11 -5
- data/lib/reek/{core → context}/module_context.rb +9 -4
- data/lib/reek/{core/stop_context.rb → context/root_context.rb} +4 -2
- data/lib/reek/{core → context}/singleton_method_context.rb +3 -6
- data/lib/reek/examiner.rb +32 -29
- data/lib/reek/report/formatter.rb +70 -0
- data/lib/reek/report/heading_formatter.rb +45 -0
- data/lib/reek/report/location_formatter.rb +35 -0
- data/lib/reek/report/report.rb +198 -0
- data/lib/reek/smells/attribute.rb +41 -11
- data/lib/reek/smells/boolean_parameter.rb +4 -2
- data/lib/reek/smells/class_variable.rb +4 -2
- data/lib/reek/smells/control_parameter.rb +4 -2
- data/lib/reek/smells/data_clump.rb +4 -3
- data/lib/reek/smells/duplicate_method_call.rb +15 -5
- data/lib/reek/smells/feature_envy.rb +8 -2
- data/lib/reek/smells/irresponsible_module.rb +13 -8
- data/lib/reek/smells/long_parameter_list.rb +6 -4
- data/lib/reek/smells/long_yield_list.rb +4 -2
- data/lib/reek/smells/module_initialize.rb +4 -2
- data/lib/reek/smells/nested_iterators.rb +4 -2
- data/lib/reek/smells/nil_check.rb +6 -3
- data/lib/reek/smells/prima_donna_method.rb +21 -12
- data/lib/reek/smells/repeated_conditional.rb +7 -4
- data/lib/reek/{core → smells}/smell_configuration.rb +3 -1
- data/lib/reek/smells/smell_detector.rb +11 -5
- data/lib/reek/{core → smells}/smell_repository.rb +9 -5
- data/lib/reek/smells/smell_warning.rb +101 -0
- data/lib/reek/smells/too_many_instance_variables.rb +4 -2
- data/lib/reek/smells/too_many_methods.rb +4 -2
- data/lib/reek/smells/too_many_statements.rb +4 -2
- data/lib/reek/smells/uncommunicative_method_name.rb +4 -2
- data/lib/reek/smells/uncommunicative_module_name.rb +4 -2
- data/lib/reek/smells/uncommunicative_parameter_name.rb +4 -2
- data/lib/reek/smells/uncommunicative_variable_name.rb +4 -2
- data/lib/reek/smells/unused_parameters.rb +4 -2
- data/lib/reek/smells/utility_function.rb +11 -22
- data/lib/reek/smells.rb +24 -11
- data/lib/reek/source/source_code.rb +69 -12
- data/lib/reek/source/source_locator.rb +34 -19
- data/lib/reek/spec/should_reek.rb +4 -3
- data/lib/reek/spec/should_reek_of.rb +2 -1
- data/lib/reek/spec/should_reek_only_of.rb +4 -3
- data/lib/reek/spec.rb +3 -3
- data/lib/reek/tree_dresser.rb +32 -0
- data/lib/reek/tree_walker.rb +182 -0
- data/lib/reek/version.rb +1 -1
- data/lib/reek.rb +3 -3
- data/reek.gemspec +18 -25
- data/spec/factories/factories.rb +4 -2
- data/spec/gem/updates_spec.rb +1 -1
- data/spec/gem/yard_spec.rb +2 -2
- data/spec/quality/reek_source_spec.rb +3 -3
- data/spec/reek/{source/sexp_node_spec.rb → ast/node_spec.rb} +4 -6
- data/spec/reek/{core → ast}/object_refs_spec.rb +4 -6
- data/spec/reek/{source → ast}/reference_collector_spec.rb +27 -19
- data/spec/reek/{source → ast}/sexp_extensions_spec.rb +7 -19
- data/spec/reek/{source → ast}/sexp_formatter_spec.rb +13 -8
- data/spec/reek/cli/option_interpreter_spec.rb +15 -0
- data/spec/reek/cli/options_spec.rb +19 -0
- data/spec/reek/cli/warning_collector_spec.rb +28 -0
- data/spec/reek/{source/code_comment_spec.rb → code_comment_spec.rb} +19 -17
- data/spec/reek/configuration/app_configuration_spec.rb +37 -26
- data/spec/reek/configuration/configuration_file_finder_spec.rb +67 -21
- data/spec/reek/{core → context}/code_context_spec.rb +18 -21
- data/spec/reek/{core → context}/method_context_spec.rb +33 -27
- data/spec/reek/{core → context}/module_context_spec.rb +5 -7
- data/spec/reek/context/root_context_spec.rb +14 -0
- data/spec/reek/context/singleton_method_context_spec.rb +17 -0
- data/spec/reek/examiner_spec.rb +9 -50
- data/spec/reek/{cli → report}/html_report_spec.rb +6 -9
- data/spec/reek/report/json_report_spec.rb +20 -0
- data/spec/reek/{cli → report}/text_report_spec.rb +16 -19
- data/spec/reek/report/xml_report_spec.rb +34 -0
- data/spec/reek/report/yaml_report_spec.rb +20 -0
- data/spec/reek/smells/attribute_spec.rb +93 -88
- data/spec/reek/smells/behaves_like_variable_detector.rb +1 -1
- data/spec/reek/smells/boolean_parameter_spec.rb +31 -5
- data/spec/reek/smells/class_variable_spec.rb +10 -10
- data/spec/reek/smells/control_parameter_spec.rb +5 -5
- data/spec/reek/smells/data_clump_spec.rb +6 -6
- data/spec/reek/smells/duplicate_method_call_spec.rb +7 -7
- data/spec/reek/smells/feature_envy_spec.rb +12 -13
- data/spec/reek/smells/irresponsible_module_spec.rb +28 -32
- data/spec/reek/smells/long_parameter_list_spec.rb +7 -7
- data/spec/reek/smells/long_yield_list_spec.rb +6 -6
- data/spec/reek/smells/module_initialize_spec.rb +4 -4
- data/spec/reek/smells/nested_iterators_spec.rb +6 -6
- data/spec/reek/smells/nil_check_spec.rb +6 -6
- data/spec/reek/smells/prima_donna_method_spec.rb +8 -5
- data/spec/reek/smells/repeated_conditional_spec.rb +18 -17
- data/spec/reek/{core → smells}/smell_configuration_spec.rb +5 -7
- data/spec/reek/smells/smell_detector_shared.rb +6 -7
- data/spec/reek/smells/smell_repository_spec.rb +29 -0
- data/spec/reek/{smell_warning_spec.rb → smells/smell_warning_spec.rb} +7 -9
- data/spec/reek/smells/too_many_instance_variables_spec.rb +5 -5
- data/spec/reek/smells/too_many_methods_spec.rb +12 -8
- data/spec/reek/smells/too_many_statements_spec.rb +10 -9
- data/spec/reek/smells/uncommunicative_method_name_spec.rb +5 -5
- data/spec/reek/smells/uncommunicative_module_name_spec.rb +8 -8
- data/spec/reek/smells/uncommunicative_parameter_name_spec.rb +18 -6
- data/spec/reek/smells/uncommunicative_variable_name_spec.rb +10 -7
- data/spec/reek/smells/unused_parameters_spec.rb +4 -4
- data/spec/reek/smells/utility_function_spec.rb +65 -39
- data/spec/reek/source/source_code_spec.rb +10 -10
- data/spec/reek/source/source_locator_spec.rb +30 -0
- data/spec/reek/spec/should_reek_of_spec.rb +9 -25
- data/spec/reek/spec/should_reek_only_of_spec.rb +4 -7
- data/spec/reek/spec/should_reek_spec.rb +4 -32
- data/spec/reek/tree_dresser_spec.rb +16 -0
- data/spec/reek/{core/tree_walker_spec.rb → tree_walker_spec.rb} +5 -7
- data/spec/samples/checkstyle.xml +2 -0
- data/spec/samples/configuration/with_excluded_paths.reek +4 -0
- data/spec/samples/{overrides/masked/lower.reek → masked_by_dotfile/.reek} +3 -1
- data/spec/samples/source_with_exclude_paths/ignore_me/uncommunicative_method_name.rb +5 -0
- data/spec/samples/source_with_exclude_paths/nested/ignore_me_as_well/irresponsible_module.rb +2 -0
- data/spec/samples/source_with_exclude_paths/nested/uncommunicative_parameter_name.rb +6 -0
- data/spec/samples/{ruby20_syntax.rb → unusual_syntax.rb} +8 -0
- data/spec/spec_helper.rb +21 -10
- data/tasks/develop.rake +2 -2
- data/tasks/reek.rake +1 -1
- metadata +158 -141
- data/features/support/hooks.rb +0 -15
- data/lib/reek/cli/report/formatter.rb +0 -69
- data/lib/reek/cli/report/heading_formatter.rb +0 -45
- data/lib/reek/cli/report/location_formatter.rb +0 -34
- data/lib/reek/cli/report/report.rb +0 -131
- data/lib/reek/core/tree_walker.rb +0 -180
- data/lib/reek/smell_warning.rb +0 -87
- data/lib/reek/source/ast_node.rb +0 -38
- data/lib/reek/source/code_comment.rb +0 -37
- data/lib/reek/source/core_extras.rb +0 -46
- data/lib/reek/source/reference_collector.rb +0 -27
- data/lib/reek/source/sexp_formatter.rb +0 -22
- data/lib/reek/source/sexp_node.rb +0 -79
- data/lib/reek/source/source_file.rb +0 -16
- data/lib/reek/source/source_repository.rb +0 -45
- data/lib/reek/source/tree_dresser.rb +0 -24
- data/lib/reek/source.rb +0 -16
- data/spec/reek/cli/json_report_spec.rb +0 -20
- data/spec/reek/cli/option_interperter_spec.rb +0 -14
- data/spec/reek/cli/yaml_report_spec.rb +0 -23
- data/spec/reek/core/singleton_method_context_spec.rb +0 -9
- data/spec/reek/core/smell_repository_spec.rb +0 -17
- data/spec/reek/core/stop_context_spec.rb +0 -17
- data/spec/reek/core/warning_collector_spec.rb +0 -30
- data/spec/reek/source/object_source_spec.rb +0 -20
- data/spec/reek/source/tree_dresser_spec.rb +0 -18
- data/spec/samples/config/allow_duplication.reek +0 -3
- data/spec/samples/config/deeper_nested_iterators.reek +0 -3
- data/spec/samples/configuration_loading/minimal_dirty.rb +0 -4
- data/spec/samples/configuration_loading/reek-test-run-disable_smells.reek +0 -7
- data/spec/samples/configuration_loading/reek-test-run-enable_smells.reek +0 -7
- data/spec/samples/corrupt_config_file/corrupt.reek +0 -1
- data/spec/samples/corrupt_config_file/dirty.rb +0 -8
- data/spec/samples/demo/demo.rb +0 -8
- data/spec/samples/empty_config_file/dirty.rb +0 -8
- data/spec/samples/empty_config_file/empty.reek +0 -0
- data/spec/samples/inline_config/dirty.rb +0 -16
- data/spec/samples/inline_config/masked.reek +0 -7
- data/spec/samples/mask_some/dirty.rb +0 -8
- data/spec/samples/mask_some/some.reek +0 -8
- data/spec/samples/masked/dirty.rb +0 -8
- data/spec/samples/masked/masked.reek +0 -5
- data/spec/samples/minimal_smelly_and_masked/config.reek +0 -7
- data/spec/samples/minimal_smelly_and_masked/minimal_dirty.rb +0 -4
- data/spec/samples/mixed_results/clean_one.rb +0 -7
- data/spec/samples/mixed_results/clean_three.rb +0 -7
- data/spec/samples/mixed_results/clean_two.rb +0 -7
- data/spec/samples/mixed_results/dirty_one.rb +0 -8
- data/spec/samples/mixed_results/dirty_two.rb +0 -8
- data/spec/samples/not_quite_masked/dirty.rb +0 -8
- data/spec/samples/not_quite_masked/masked.reek +0 -5
- data/spec/samples/not_quite_masked/smelly.rb +0 -3
- data/spec/samples/one_smelly_file/dirty.rb +0 -3
- data/spec/samples/overrides/masked/dirty.rb +0 -8
- data/spec/samples/overrides/upper.reek +0 -5
- data/spec/samples/overrides_defaults/camel_case.rb +0 -14
- data/spec/samples/overrides_defaults/config.reek +0 -6
- data/spec/samples/ruby21_syntax.rb +0 -5
- data/spec/samples/standard_smelly/dirty.rb +0 -8
- data/spec/samples/standard_smelly/minimal_dirty.rb +0 -4
- data/spec/samples/three_smelly_files/dirty_one.rb +0 -3
- data/spec/samples/three_smelly_files/dirty_three.rb +0 -5
- data/spec/samples/three_smelly_files/dirty_two.rb +0 -4
- /data/spec/samples/{simple_configuration.reek → configuration/simple_configuration.reek} +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
require_relative '../ast/node'
|
|
2
|
+
require_relative 'smell_detector'
|
|
3
|
+
require_relative 'smell_warning'
|
|
3
4
|
|
|
4
5
|
module Reek
|
|
5
6
|
module Smells
|
|
@@ -22,6 +23,8 @@ module Reek
|
|
|
22
23
|
# +RepeatedConditional+ checks for multiple conditionals
|
|
23
24
|
# testing the same value throughout a single class.
|
|
24
25
|
#
|
|
26
|
+
# See {file:docs/Repeated-Conditional.md} for details.
|
|
27
|
+
# @api private
|
|
25
28
|
class RepeatedConditional < SmellDetector
|
|
26
29
|
# The name of the config field that sets the maximum number of
|
|
27
30
|
# identical conditionals permitted within any single class.
|
|
@@ -32,7 +35,7 @@ module Reek
|
|
|
32
35
|
'SimulatedPolymorphism'
|
|
33
36
|
end
|
|
34
37
|
|
|
35
|
-
BLOCK_GIVEN_CONDITION = AST::Node.new(:send, [nil, :block_given?])
|
|
38
|
+
BLOCK_GIVEN_CONDITION = ::Parser::AST::Node.new(:send, [nil, :block_given?])
|
|
36
39
|
|
|
37
40
|
def self.contexts # :nodoc:
|
|
38
41
|
[:class]
|
|
@@ -53,7 +56,7 @@ module Reek
|
|
|
53
56
|
lines.length > @max_identical_ifs
|
|
54
57
|
end.map do |key, lines|
|
|
55
58
|
occurs = lines.length
|
|
56
|
-
expression = key.
|
|
59
|
+
expression = key.format_to_ruby
|
|
57
60
|
SmellWarning.new self,
|
|
58
61
|
context: ctx.full_name,
|
|
59
62
|
lines: lines,
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
module Reek
|
|
2
|
-
module
|
|
2
|
+
module Smells
|
|
3
3
|
#
|
|
4
4
|
# Represents a single set of configuration options for a smell detector
|
|
5
5
|
#
|
|
6
|
+
# @api private
|
|
6
7
|
class SmellConfiguration
|
|
7
8
|
# The name of the config field that specifies whether a smell is
|
|
8
9
|
# enabled. Set to +true+ or +false+.
|
|
@@ -45,6 +46,7 @@ module Reek
|
|
|
45
46
|
#
|
|
46
47
|
# A set of context-specific overrides for smell detectors.
|
|
47
48
|
#
|
|
49
|
+
# @api private
|
|
48
50
|
class Overrides
|
|
49
51
|
def initialize(hash)
|
|
50
52
|
@hash = hash
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
require 'set'
|
|
2
|
-
|
|
3
|
-
require 'reek/core/smell_configuration'
|
|
2
|
+
require_relative 'smell_configuration'
|
|
4
3
|
|
|
5
4
|
module Reek
|
|
6
5
|
module Smells
|
|
7
6
|
#
|
|
8
7
|
# Shared responsibilities of all smell detectors.
|
|
9
8
|
#
|
|
9
|
+
# See
|
|
10
|
+
# - {file:docs/Basic-Smell-Options.md}
|
|
11
|
+
# - {file:docs/Code-Smells.md}
|
|
12
|
+
# - {file:docs/Configuration-Files.md}
|
|
13
|
+
# for details.
|
|
14
|
+
#
|
|
15
|
+
# @api private
|
|
10
16
|
class SmellDetector
|
|
11
17
|
attr_reader :source
|
|
12
18
|
|
|
@@ -26,7 +32,7 @@ module Reek
|
|
|
26
32
|
|
|
27
33
|
def default_config
|
|
28
34
|
{
|
|
29
|
-
|
|
35
|
+
SmellConfiguration::ENABLED_KEY => true,
|
|
30
36
|
EXCLUDE_KEY => DEFAULT_EXCLUDE_SET.dup
|
|
31
37
|
}
|
|
32
38
|
end
|
|
@@ -67,7 +73,7 @@ module Reek
|
|
|
67
73
|
|
|
68
74
|
def initialize(source, config = self.class.default_config)
|
|
69
75
|
@source = source
|
|
70
|
-
@config =
|
|
76
|
+
@config = SmellConfiguration.new(config)
|
|
71
77
|
@smells_found = []
|
|
72
78
|
end
|
|
73
79
|
|
|
@@ -94,7 +100,7 @@ module Reek
|
|
|
94
100
|
end
|
|
95
101
|
|
|
96
102
|
def enabled_for?(context)
|
|
97
|
-
enabled? && config_for(context)[
|
|
103
|
+
enabled? && config_for(context)[SmellConfiguration::ENABLED_KEY] != false
|
|
98
104
|
end
|
|
99
105
|
|
|
100
106
|
def exception?(context)
|
|
@@ -1,16 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
require_relative '../smells'
|
|
2
|
+
require_relative 'smell_detector'
|
|
3
|
+
require_relative '../configuration/app_configuration'
|
|
3
4
|
|
|
4
5
|
module Reek
|
|
5
|
-
module
|
|
6
|
+
module Smells
|
|
6
7
|
#
|
|
7
8
|
# Contains all the existing smells and exposes operations on them.
|
|
8
9
|
#
|
|
10
|
+
# @api private
|
|
9
11
|
class SmellRepository
|
|
10
12
|
attr_reader :detectors
|
|
11
13
|
|
|
12
14
|
def self.smell_types
|
|
13
|
-
Reek::Smells::SmellDetector.descendants
|
|
15
|
+
Reek::Smells::SmellDetector.descendants.sort_by(&:name)
|
|
14
16
|
end
|
|
15
17
|
|
|
16
18
|
def initialize(source_description = nil, smell_types = self.class.smell_types)
|
|
@@ -23,7 +25,9 @@ module Reek
|
|
|
23
25
|
end
|
|
24
26
|
|
|
25
27
|
def configure(klass, config)
|
|
26
|
-
|
|
28
|
+
detector = @detectors[klass]
|
|
29
|
+
raise ArgumentError, "Unknown smell type #{klass} found in configuration" unless detector
|
|
30
|
+
detector.configure_with(config)
|
|
27
31
|
end
|
|
28
32
|
|
|
29
33
|
def report_on(listener)
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
require 'forwardable'
|
|
2
|
+
|
|
3
|
+
module Reek
|
|
4
|
+
module Smells
|
|
5
|
+
#
|
|
6
|
+
# Reports a warning that a smell has been found.
|
|
7
|
+
#
|
|
8
|
+
class SmellWarning
|
|
9
|
+
include Comparable
|
|
10
|
+
extend Forwardable
|
|
11
|
+
attr_accessor :smell_detector, :context, :lines, :message, :parameters
|
|
12
|
+
def_delegators :smell_detector, :smell_category, :smell_type, :source
|
|
13
|
+
|
|
14
|
+
def initialize(smell_detector, options = {})
|
|
15
|
+
self.smell_detector = smell_detector
|
|
16
|
+
self.context = options.fetch(:context, '').to_s
|
|
17
|
+
self.lines = options.fetch(:lines)
|
|
18
|
+
self.message = options.fetch(:message)
|
|
19
|
+
self.parameters = options.fetch(:parameters, {})
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def hash
|
|
23
|
+
sort_key.hash
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def <=>(other)
|
|
27
|
+
sort_key <=> other.sort_key
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def eql?(other)
|
|
31
|
+
(self <=> other) == 0
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# @api private
|
|
35
|
+
def matches?(klass, other_parameters = {})
|
|
36
|
+
smell_classes.include?(klass.to_s) && common_parameters_equal?(other_parameters)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# @api private
|
|
40
|
+
def report_on(listener)
|
|
41
|
+
listener.found_smell(self)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def yaml_hash(warning_formatter = nil)
|
|
45
|
+
stringified_params = Hash[parameters.map { |key, val| [key.to_s, val] }]
|
|
46
|
+
core_yaml_hash.
|
|
47
|
+
merge(stringified_params).
|
|
48
|
+
merge(wiki_link_hash(warning_formatter))
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
protected
|
|
52
|
+
|
|
53
|
+
def sort_key
|
|
54
|
+
[context, message, smell_category]
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
private
|
|
58
|
+
|
|
59
|
+
def smell_classes
|
|
60
|
+
[smell_detector.smell_category, smell_detector.smell_type]
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def common_parameters_equal?(other_parameters)
|
|
64
|
+
other_parameters.keys.each do |key|
|
|
65
|
+
unless parameters.key?(key)
|
|
66
|
+
raise ArgumentError, "The parameter #{key} you want to check for doesn't exist"
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Why not check for strict parameter equality instead of just the common ones?
|
|
71
|
+
#
|
|
72
|
+
# In `self`, `parameters` might look like this: {:name=>"@other.thing", :count=>2}
|
|
73
|
+
# Coming from specs, 'other_parameters' might look like this, e.g.:
|
|
74
|
+
# {:name=>"@other.thing"}
|
|
75
|
+
# So in this spec we are just specifying the "name" parameter but not the "count".
|
|
76
|
+
# In order to allow for this kind of leniency we just test for common parameter equality,
|
|
77
|
+
# not for a strict one.
|
|
78
|
+
parameters.values_at(*other_parameters.keys) == other_parameters.values
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def core_yaml_hash
|
|
82
|
+
{
|
|
83
|
+
'context' => context,
|
|
84
|
+
'lines' => lines,
|
|
85
|
+
'message' => message,
|
|
86
|
+
'smell_category' => smell_detector.smell_category,
|
|
87
|
+
'smell_type' => smell_detector.smell_type,
|
|
88
|
+
'source' => smell_detector.source
|
|
89
|
+
}
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def wiki_link_hash(warning_formatter)
|
|
93
|
+
if warning_formatter.respond_to?(:explanatory_link)
|
|
94
|
+
{ 'wiki_link' => warning_formatter.explanatory_link(smell_detector) }
|
|
95
|
+
else
|
|
96
|
+
{}
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
require_relative 'smell_detector'
|
|
2
|
+
require_relative 'smell_warning'
|
|
3
3
|
|
|
4
4
|
module Reek
|
|
5
5
|
module Smells
|
|
@@ -10,6 +10,8 @@ module Reek
|
|
|
10
10
|
# +TooManyInstanceVariables' reports classes having more than a
|
|
11
11
|
# configurable number of instance variables.
|
|
12
12
|
#
|
|
13
|
+
# See {file:docs/Too-Many-Instance-Variables.md} for details.
|
|
14
|
+
# @api private
|
|
13
15
|
class TooManyInstanceVariables < SmellDetector
|
|
14
16
|
# The name of the config field that sets the maximum number of instance
|
|
15
17
|
# variables permitted in a class.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
require_relative 'smell_detector'
|
|
2
|
+
require_relative 'smell_warning'
|
|
3
3
|
|
|
4
4
|
module Reek
|
|
5
5
|
module Smells
|
|
@@ -12,6 +12,8 @@ module Reek
|
|
|
12
12
|
# methods, and excludes methods inherited from superclasses or included
|
|
13
13
|
# modules.
|
|
14
14
|
#
|
|
15
|
+
# See {file:docs/Too-Many-Methods.md} for details.
|
|
16
|
+
# @api private
|
|
15
17
|
class TooManyMethods < SmellDetector
|
|
16
18
|
# The name of the config field that sets the maximum number of methods
|
|
17
19
|
# permitted in a class.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
require_relative 'smell_detector'
|
|
2
|
+
require_relative 'smell_warning'
|
|
3
3
|
|
|
4
4
|
module Reek
|
|
5
5
|
module Smells
|
|
@@ -8,6 +8,8 @@ module Reek
|
|
|
8
8
|
#
|
|
9
9
|
# +TooManyStatements+ reports any method with more than 5 statements.
|
|
10
10
|
#
|
|
11
|
+
# See {file:docs/Too-Many-Statements.md} for details.
|
|
12
|
+
# @api private
|
|
11
13
|
class TooManyStatements < SmellDetector
|
|
12
14
|
# The name of the config field that sets the maximum number of
|
|
13
15
|
# statements permitted in any method.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
require_relative 'smell_detector'
|
|
2
|
+
require_relative 'smell_warning'
|
|
3
3
|
|
|
4
4
|
module Reek
|
|
5
5
|
module Smells
|
|
@@ -16,6 +16,8 @@ module Reek
|
|
|
16
16
|
# * 1-character names
|
|
17
17
|
# * names ending with a number
|
|
18
18
|
#
|
|
19
|
+
# See {file:docs/Uncommunicative-Method-Name.md} for details.
|
|
20
|
+
# @api private
|
|
19
21
|
class UncommunicativeMethodName < SmellDetector
|
|
20
22
|
# The name of the config field that lists the regexps of
|
|
21
23
|
# smelly names to be reported.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
require_relative 'smell_detector'
|
|
2
|
+
require_relative 'smell_warning'
|
|
3
3
|
|
|
4
4
|
module Reek
|
|
5
5
|
module Smells
|
|
@@ -16,6 +16,8 @@ module Reek
|
|
|
16
16
|
# * 1-character names
|
|
17
17
|
# * names ending with a number
|
|
18
18
|
#
|
|
19
|
+
# See {file:docs/Uncommunicative-Module-Name.md} for details.
|
|
20
|
+
# @api private
|
|
19
21
|
class UncommunicativeModuleName < SmellDetector
|
|
20
22
|
# The name of the config field that lists the regexps of
|
|
21
23
|
# smelly names to be reported.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
require_relative 'smell_detector'
|
|
2
|
+
require_relative 'smell_warning'
|
|
3
3
|
|
|
4
4
|
module Reek
|
|
5
5
|
module Smells
|
|
@@ -16,6 +16,8 @@ module Reek
|
|
|
16
16
|
# * 1-character names
|
|
17
17
|
# * names ending with a number
|
|
18
18
|
#
|
|
19
|
+
# See {file:docs/Uncommunicative-Parameter-Name.md} for details.
|
|
20
|
+
# @api private
|
|
19
21
|
class UncommunicativeParameterName < SmellDetector
|
|
20
22
|
# The name of the config field that lists the regexps of
|
|
21
23
|
# smelly names to be reported.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
require_relative 'smell_detector'
|
|
2
|
+
require_relative 'smell_warning'
|
|
3
3
|
|
|
4
4
|
module Reek
|
|
5
5
|
module Smells
|
|
@@ -16,6 +16,8 @@ module Reek
|
|
|
16
16
|
# * 1-character names
|
|
17
17
|
# * names ending with a number
|
|
18
18
|
#
|
|
19
|
+
# See {file:docs/Uncommunicative-Variable-Name.md} for details.
|
|
20
|
+
# @api private
|
|
19
21
|
class UncommunicativeVariableName < SmellDetector
|
|
20
22
|
# The name of the config field that lists the regexps of
|
|
21
23
|
# smelly names to be reported.
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
require_relative 'smell_detector'
|
|
2
|
+
require_relative 'smell_warning'
|
|
3
3
|
|
|
4
4
|
module Reek
|
|
5
5
|
module Smells
|
|
6
6
|
#
|
|
7
7
|
# Methods should use their parameters.
|
|
8
8
|
#
|
|
9
|
+
# See {file:docs/Unused-Parameters.md} for details.
|
|
10
|
+
# @api private
|
|
9
11
|
class UnusedParameters < SmellDetector
|
|
10
12
|
def self.smell_category
|
|
11
13
|
'UnusedCode'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
require_relative '../ast/reference_collector'
|
|
2
|
+
require_relative 'smell_detector'
|
|
3
|
+
require_relative 'smell_warning'
|
|
4
4
|
|
|
5
5
|
module Reek
|
|
6
6
|
module Smells
|
|
@@ -32,14 +32,12 @@ module Reek
|
|
|
32
32
|
# reified into a class, and the utility method will most
|
|
33
33
|
# likely belong there.
|
|
34
34
|
#
|
|
35
|
+
# If the method does refer to self, but refers to some other object more,
|
|
36
|
+
# +FeatureEnvy+ is reported instead.
|
|
37
|
+
#
|
|
38
|
+
# See {file:docs/Utility-Function.md} for details.
|
|
39
|
+
# @api private
|
|
35
40
|
class UtilityFunction < SmellDetector
|
|
36
|
-
# The name of the config field that sets the maximum number of
|
|
37
|
-
# calls permitted within a helper method. Any method with more than
|
|
38
|
-
# this number of method calls on other objects will be considered a
|
|
39
|
-
# candidate Utility Function.
|
|
40
|
-
HELPER_CALLS_LIMIT_KEY = 'max_helper_calls'
|
|
41
|
-
DEFAULT_HELPER_CALLS_LIMIT = 1
|
|
42
|
-
|
|
43
41
|
def self.smell_category
|
|
44
42
|
'LowCohesion'
|
|
45
43
|
end
|
|
@@ -48,10 +46,6 @@ module Reek
|
|
|
48
46
|
def contexts # :nodoc:
|
|
49
47
|
[:def]
|
|
50
48
|
end
|
|
51
|
-
|
|
52
|
-
def default_config
|
|
53
|
-
super.merge(HELPER_CALLS_LIMIT_KEY => DEFAULT_HELPER_CALLS_LIMIT)
|
|
54
|
-
end
|
|
55
49
|
end
|
|
56
50
|
|
|
57
51
|
#
|
|
@@ -60,11 +54,10 @@ module Reek
|
|
|
60
54
|
# @return [Array<SmellWarning>]
|
|
61
55
|
#
|
|
62
56
|
def examine_context(method_ctx)
|
|
57
|
+
return [] if method_ctx.exp.singleton_method?
|
|
63
58
|
return [] if method_ctx.num_statements == 0
|
|
64
|
-
return [] if
|
|
65
|
-
return [] if num_helper_methods(method_ctx)
|
|
66
|
-
method_ctx,
|
|
67
|
-
DEFAULT_HELPER_CALLS_LIMIT)
|
|
59
|
+
return [] if method_ctx.references_self?
|
|
60
|
+
return [] if num_helper_methods(method_ctx).zero?
|
|
68
61
|
|
|
69
62
|
[SmellWarning.new(self,
|
|
70
63
|
context: method_ctx.full_name,
|
|
@@ -75,10 +68,6 @@ module Reek
|
|
|
75
68
|
|
|
76
69
|
private
|
|
77
70
|
|
|
78
|
-
def depends_on_instance?(exp)
|
|
79
|
-
Reek::Source::ReferenceCollector.new(exp).num_refs_to_self > 0
|
|
80
|
-
end
|
|
81
|
-
|
|
82
71
|
def num_helper_methods(method_ctx)
|
|
83
72
|
method_ctx.local_nodes(:send).length
|
|
84
73
|
end
|
data/lib/reek/smells.rb
CHANGED
|
@@ -1,11 +1,24 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
1
|
+
require_relative 'smells/attribute'
|
|
2
|
+
require_relative 'smells/boolean_parameter'
|
|
3
|
+
require_relative 'smells/class_variable'
|
|
4
|
+
require_relative 'smells/control_parameter'
|
|
5
|
+
require_relative 'smells/data_clump'
|
|
6
|
+
require_relative 'smells/duplicate_method_call'
|
|
7
|
+
require_relative 'smells/feature_envy'
|
|
8
|
+
require_relative 'smells/irresponsible_module'
|
|
9
|
+
require_relative 'smells/long_parameter_list'
|
|
10
|
+
require_relative 'smells/long_yield_list'
|
|
11
|
+
require_relative 'smells/module_initialize'
|
|
12
|
+
require_relative 'smells/nested_iterators'
|
|
13
|
+
require_relative 'smells/nil_check'
|
|
14
|
+
require_relative 'smells/prima_donna_method'
|
|
15
|
+
require_relative 'smells/repeated_conditional'
|
|
16
|
+
require_relative 'smells/too_many_instance_variables'
|
|
17
|
+
require_relative 'smells/too_many_methods'
|
|
18
|
+
require_relative 'smells/too_many_statements'
|
|
19
|
+
require_relative 'smells/uncommunicative_method_name'
|
|
20
|
+
require_relative 'smells/uncommunicative_module_name'
|
|
21
|
+
require_relative 'smells/uncommunicative_parameter_name'
|
|
22
|
+
require_relative 'smells/uncommunicative_variable_name'
|
|
23
|
+
require_relative 'smells/unused_parameters'
|
|
24
|
+
require_relative 'smells/utility_function'
|
|
@@ -1,32 +1,89 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
require_relative '../cli/silencer'
|
|
2
|
+
Reek::CLI::Silencer.silently do
|
|
3
|
+
require 'parser/ruby22'
|
|
4
|
+
end
|
|
5
|
+
require_relative '../tree_dresser'
|
|
6
|
+
require_relative '../ast/node'
|
|
6
7
|
|
|
7
8
|
module Reek
|
|
9
|
+
# @api private
|
|
8
10
|
module Source
|
|
9
11
|
#
|
|
10
12
|
# A +Source+ object represents a chunk of Ruby source code.
|
|
11
13
|
#
|
|
14
|
+
# @api private
|
|
12
15
|
class SourceCode
|
|
13
|
-
attr_reader :
|
|
16
|
+
attr_reader :description
|
|
17
|
+
|
|
18
|
+
# Initializer.
|
|
19
|
+
#
|
|
20
|
+
# code - ruby code as String
|
|
21
|
+
# description - in case of STDIN this is "STDIN" otherwise it's a filepath as String
|
|
22
|
+
# parser - the parser to use for generating AST's out of the given source
|
|
23
|
+
def initialize(code, description, parser = Parser::Ruby22)
|
|
24
|
+
@source = code
|
|
25
|
+
@description = description
|
|
26
|
+
@parser = parser
|
|
27
|
+
end
|
|
14
28
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
29
|
+
# Initializes an instance of SourceCode given a source.
|
|
30
|
+
# This source can come via 3 different ways:
|
|
31
|
+
# - from files a la `reek lib/reek/`
|
|
32
|
+
# - from IO (STDIN) a la `echo "class Foo; end" | reek`
|
|
33
|
+
# - from String via our rspec matchers a la `expect("class Foo; end").to reek`
|
|
34
|
+
#
|
|
35
|
+
# @param source [File|IO|String] - the given source
|
|
36
|
+
#
|
|
37
|
+
# @return an instance of SourceCode
|
|
38
|
+
def self.from(source)
|
|
39
|
+
case source
|
|
40
|
+
when File then new(source.read, source.path)
|
|
41
|
+
when IO then new(source.readlines.join, 'STDIN')
|
|
42
|
+
when String then new(source, 'string')
|
|
43
|
+
end
|
|
19
44
|
end
|
|
20
45
|
|
|
46
|
+
# Parses the given source into an AST and associates the source code comments with it.
|
|
47
|
+
# This AST is then traversed by a TreeDresser which adorns the nodes in the AST
|
|
48
|
+
# with our SexpExtensions.
|
|
49
|
+
# Finally this AST is returned where each node is an anonymous subclass of Reek::AST::Node
|
|
50
|
+
#
|
|
51
|
+
# Important to note is that reek will not fail on unparseable files but rather print out
|
|
52
|
+
# a warning and then just continue.
|
|
53
|
+
#
|
|
54
|
+
# Given this @source:
|
|
55
|
+
#
|
|
56
|
+
# # comment about C
|
|
57
|
+
# class C
|
|
58
|
+
# def m
|
|
59
|
+
# puts 'nada'
|
|
60
|
+
# end
|
|
61
|
+
# end
|
|
62
|
+
#
|
|
63
|
+
# this method would return something that looks like
|
|
64
|
+
#
|
|
65
|
+
# (class
|
|
66
|
+
# (const nil :C) nil
|
|
67
|
+
# (def :m
|
|
68
|
+
# (args)
|
|
69
|
+
# (send nil :puts
|
|
70
|
+
# (str "nada"))))
|
|
71
|
+
#
|
|
72
|
+
# where each node is possibly adorned with our SexpExtensions (see ast/ast_node_class_map
|
|
73
|
+
# and ast/sexp_extensions for details).
|
|
74
|
+
#
|
|
75
|
+
# @return [Anonymous subclass of Reek::AST::Node] the AST presentation
|
|
76
|
+
# for the given source
|
|
21
77
|
def syntax_tree
|
|
22
78
|
@syntax_tree ||=
|
|
23
79
|
begin
|
|
24
80
|
begin
|
|
25
|
-
ast, comments = @parser.parse_with_comments(@source, @
|
|
81
|
+
ast, comments = @parser.parse_with_comments(@source, @description)
|
|
26
82
|
rescue Racc::ParseError, Parser::SyntaxError => error
|
|
27
|
-
$stderr.puts "#{
|
|
83
|
+
$stderr.puts "#{description}: #{error.class.name}: #{error}"
|
|
28
84
|
end
|
|
29
85
|
|
|
86
|
+
# See https://whitequark.github.io/parser/Parser/Source/Comment/Associator.html
|
|
30
87
|
comment_map = Parser::Source::Comment.associate(ast, comments) if ast
|
|
31
88
|
TreeDresser.new.dress(ast, comment_map)
|
|
32
89
|
end
|
|
@@ -1,40 +1,55 @@
|
|
|
1
|
-
require '
|
|
1
|
+
require 'find'
|
|
2
2
|
|
|
3
3
|
module Reek
|
|
4
4
|
module Source
|
|
5
5
|
#
|
|
6
6
|
# Finds Ruby source files in a filesystem.
|
|
7
7
|
#
|
|
8
|
+
# @api private
|
|
8
9
|
class SourceLocator
|
|
10
|
+
# Initialize with the paths we want to search.
|
|
11
|
+
#
|
|
12
|
+
# paths - a list of paths as Strings
|
|
9
13
|
def initialize(paths)
|
|
10
14
|
@paths = paths.map { |path| path.chomp('/') }
|
|
11
15
|
end
|
|
12
16
|
|
|
13
|
-
|
|
14
|
-
|
|
17
|
+
# Traverses all paths we initialized the SourceLocator with, finds
|
|
18
|
+
# all relevant ruby files and returns them as a list.
|
|
19
|
+
#
|
|
20
|
+
# @return [Array<File>] - Ruby files found
|
|
21
|
+
def sources
|
|
22
|
+
source_paths.map { |pathname| File.new(pathname) }
|
|
15
23
|
end
|
|
16
24
|
|
|
17
25
|
private
|
|
18
26
|
|
|
19
|
-
def
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
path
|
|
27
|
+
def source_paths
|
|
28
|
+
relevant_paths = []
|
|
29
|
+
@paths.map do |given_path|
|
|
30
|
+
print_no_such_file_error(given_path) && next unless path_exists?(given_path)
|
|
31
|
+
Find.find(given_path) do |path|
|
|
32
|
+
pathname = Pathname.new(path)
|
|
33
|
+
if pathname.directory?
|
|
34
|
+
exclude_path?(pathname) ? Find.prune : next
|
|
35
|
+
else
|
|
36
|
+
relevant_paths << pathname
|
|
37
|
+
end
|
|
25
38
|
end
|
|
26
|
-
end
|
|
39
|
+
end
|
|
40
|
+
relevant_paths.flatten.sort
|
|
27
41
|
end
|
|
28
42
|
|
|
29
|
-
def
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
43
|
+
def exclude_path?(pathname)
|
|
44
|
+
Configuration::AppConfiguration.exclude_paths.include? pathname.to_s
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def path_exists?(path)
|
|
48
|
+
Pathname.new(path).exist?
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def print_no_such_file_error(path)
|
|
52
|
+
$stderr.puts "Error: No such file - #{path}"
|
|
38
53
|
end
|
|
39
54
|
end
|
|
40
55
|
end
|