reek 2.1.0 → 2.2.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/.rubocop.yml +2 -21
- data/.travis.yml +1 -0
- data/.yardopts +3 -6
- data/CHANGELOG +6 -0
- data/CONTRIBUTING.md +8 -3
- data/README.md +94 -42
- data/config/defaults.reek +0 -1
- data/docs/API.md +50 -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 +38 -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 +29 -0
- data/docs/How-reek-works-internally.md +44 -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 +133 -0
- data/docs/Rake-Task.md +58 -0
- data/docs/Reek-Driven-Development.md +45 -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 +46 -0
- data/docs/Versioning-Policy.md +7 -0
- data/docs/YAML-Reports.md +111 -0
- data/docs/yard_plugin.rb +14 -0
- data/features/command_line_interface/options.feature +1 -0
- data/features/programmatic_access.feature +1 -1
- data/features/samples.feature +3 -3
- data/lib/reek.rb +2 -2
- data/lib/reek/cli/input.rb +2 -2
- data/lib/reek/cli/option_interpreter.rb +2 -0
- data/lib/reek/cli/options.rb +10 -4
- data/lib/reek/cli/reek_command.rb +2 -2
- data/lib/reek/cli/report/report.rb +60 -0
- data/lib/reek/cli/silencer.rb +13 -0
- data/lib/reek/{source → core}/ast_node.rb +1 -1
- data/lib/reek/{source → core}/ast_node_class_map.rb +10 -11
- data/lib/reek/{source → core}/code_comment.rb +1 -1
- data/lib/reek/core/code_context.rb +1 -1
- data/lib/reek/core/examiner.rb +85 -0
- data/lib/reek/core/method_context.rb +1 -1
- data/lib/reek/core/module_context.rb +2 -2
- data/lib/reek/core/reference_collector.rb +31 -0
- data/lib/reek/core/singleton_method_context.rb +0 -4
- data/lib/reek/core/smell_repository.rb +4 -2
- data/lib/reek/{source → core}/tree_dresser.rb +1 -1
- data/lib/reek/{source → sexp}/sexp_extensions.rb +5 -5
- data/lib/reek/sexp/sexp_formatter.rb +29 -0
- data/lib/reek/sexp/sexp_node.rb +91 -0
- data/lib/reek/smells.rb +4 -2
- data/lib/reek/smells/attribute.rb +35 -7
- data/lib/reek/smells/boolean_parameter.rb +1 -1
- data/lib/reek/smells/class_variable.rb +1 -1
- data/lib/reek/smells/control_parameter.rb +1 -1
- data/lib/reek/smells/data_clump.rb +1 -1
- data/lib/reek/smells/duplicate_method_call.rb +12 -4
- data/lib/reek/smells/feature_envy.rb +1 -1
- data/lib/reek/smells/irresponsible_module.rb +3 -3
- data/lib/reek/smells/long_parameter_list.rb +1 -1
- data/lib/reek/smells/long_yield_list.rb +1 -1
- data/lib/reek/smells/module_initialize.rb +1 -1
- data/lib/reek/smells/nested_iterators.rb +1 -1
- data/lib/reek/smells/nil_check.rb +3 -2
- data/lib/reek/smells/prima_donna_method.rb +18 -11
- data/lib/reek/smells/repeated_conditional.rb +3 -3
- data/lib/reek/smells/smell_detector.rb +5 -1
- data/lib/reek/smells/smell_warning.rb +99 -0
- data/lib/reek/smells/too_many_instance_variables.rb +1 -1
- data/lib/reek/smells/too_many_methods.rb +1 -1
- data/lib/reek/smells/too_many_statements.rb +1 -1
- data/lib/reek/smells/uncommunicative_method_name.rb +1 -1
- data/lib/reek/smells/uncommunicative_module_name.rb +1 -1
- data/lib/reek/smells/uncommunicative_parameter_name.rb +1 -1
- data/lib/reek/smells/uncommunicative_variable_name.rb +1 -1
- data/lib/reek/smells/unused_parameters.rb +1 -1
- data/lib/reek/smells/utility_function.rb +3 -16
- data/lib/reek/source/source_code.rb +31 -13
- data/lib/reek/source/source_locator.rb +16 -17
- data/lib/reek/source/source_repository.rb +10 -11
- data/lib/reek/spec/should_reek.rb +2 -2
- data/lib/reek/spec/should_reek_of.rb +2 -2
- data/lib/reek/spec/should_reek_only_of.rb +2 -2
- data/lib/reek/version.rb +1 -1
- data/reek.gemspec +3 -4
- data/spec/factories/factories.rb +1 -1
- data/spec/gem/yard_spec.rb +1 -1
- data/spec/quality/reek_source_spec.rb +2 -2
- data/spec/reek/cli/html_report_spec.rb +3 -3
- data/spec/reek/cli/json_report_spec.rb +3 -3
- data/spec/reek/cli/{option_interperter_spec.rb → option_interpreter_spec.rb} +1 -1
- data/spec/reek/cli/options_spec.rb +19 -0
- data/spec/reek/cli/text_report_spec.rb +7 -7
- data/spec/reek/cli/xml_report_spec.rb +34 -0
- data/spec/reek/cli/yaml_report_spec.rb +3 -3
- data/spec/reek/configuration/app_configuration_spec.rb +1 -1
- data/spec/reek/configuration/configuration_file_finder_spec.rb +22 -1
- data/spec/reek/{source → core}/code_comment_spec.rb +14 -14
- data/spec/reek/core/code_context_spec.rb +1 -1
- data/spec/reek/{examiner_spec.rb → core/examiner_spec.rb} +12 -12
- data/spec/reek/core/method_context_spec.rb +27 -22
- data/spec/reek/core/module_context_spec.rb +2 -2
- data/spec/reek/core/object_refs_spec.rb +1 -1
- data/spec/reek/{source → core}/object_source_spec.rb +1 -1
- data/spec/reek/{source → core}/reference_collector_spec.rb +25 -16
- data/spec/reek/core/singleton_method_context_spec.rb +12 -2
- data/spec/reek/core/smell_configuration_spec.rb +1 -1
- data/spec/reek/core/smell_repository_spec.rb +12 -1
- data/spec/reek/core/stop_context_spec.rb +1 -1
- data/spec/reek/core/tree_dresser_spec.rb +16 -0
- data/spec/reek/core/tree_walker_spec.rb +3 -3
- data/spec/reek/core/warning_collector_spec.rb +6 -6
- data/spec/reek/{source → sexp}/sexp_extensions_spec.rb +8 -8
- data/spec/reek/{source → sexp}/sexp_formatter_spec.rb +11 -5
- data/spec/reek/{source → sexp}/sexp_node_spec.rb +3 -3
- data/spec/reek/smells/attribute_spec.rb +89 -85
- data/spec/reek/smells/behaves_like_variable_detector.rb +1 -1
- data/spec/reek/smells/boolean_parameter_spec.rb +1 -1
- data/spec/reek/smells/class_variable_spec.rb +1 -1
- data/spec/reek/smells/control_parameter_spec.rb +1 -1
- data/spec/reek/smells/data_clump_spec.rb +2 -2
- data/spec/reek/smells/duplicate_method_call_spec.rb +1 -1
- data/spec/reek/smells/feature_envy_spec.rb +2 -2
- data/spec/reek/smells/irresponsible_module_spec.rb +1 -1
- data/spec/reek/smells/long_parameter_list_spec.rb +2 -2
- data/spec/reek/smells/long_yield_list_spec.rb +1 -1
- data/spec/reek/smells/module_initialize_spec.rb +1 -1
- data/spec/reek/smells/nested_iterators_spec.rb +2 -2
- data/spec/reek/smells/nil_check_spec.rb +1 -1
- data/spec/reek/smells/prima_donna_method_spec.rb +1 -1
- data/spec/reek/smells/repeated_conditional_spec.rb +1 -1
- data/spec/reek/smells/smell_detector_shared.rb +2 -2
- data/spec/reek/{smell_warning_spec.rb → smells/smell_warning_spec.rb} +7 -7
- data/spec/reek/smells/too_many_instance_variables_spec.rb +1 -1
- data/spec/reek/smells/too_many_methods_spec.rb +1 -1
- data/spec/reek/smells/too_many_statements_spec.rb +4 -4
- data/spec/reek/smells/uncommunicative_method_name_spec.rb +1 -1
- data/spec/reek/smells/uncommunicative_module_name_spec.rb +1 -1
- data/spec/reek/smells/uncommunicative_parameter_name_spec.rb +1 -1
- data/spec/reek/smells/uncommunicative_variable_name_spec.rb +1 -1
- data/spec/reek/smells/unused_parameters_spec.rb +1 -1
- data/spec/reek/smells/utility_function_spec.rb +1 -1
- data/spec/reek/source/source_code_spec.rb +1 -1
- data/spec/reek/spec/should_reek_of_spec.rb +1 -1
- data/spec/reek/spec/should_reek_only_of_spec.rb +1 -1
- data/spec/reek/spec/should_reek_spec.rb +1 -1
- data/spec/samples/checkstyle.xml +2 -0
- data/spec/spec_helper.rb +15 -3
- metadata +68 -38
- data/.ruby-gemset +0 -1
- data/lib/reek/examiner.rb +0 -79
- data/lib/reek/smell_warning.rb +0 -87
- 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/spec/reek/source/tree_dresser_spec.rb +0 -16
data/lib/reek/smells.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
require_relative 'smell_detector'
|
2
|
-
require_relative '../smell_warning'
|
3
2
|
require_relative '../core/smell_configuration'
|
4
3
|
|
5
4
|
module Reek
|
@@ -13,10 +12,20 @@ module Reek
|
|
13
12
|
# +attr_reader+, +attr_writer+ and +attr_accessor+ -- including those
|
14
13
|
# that are private.
|
15
14
|
#
|
16
|
-
# TODO: Eliminate private attributes
|
17
15
|
# TODO: Catch attributes declared "by hand"
|
16
|
+
# See docs/Attribute for details.
|
18
17
|
#
|
19
18
|
class Attribute < SmellDetector
|
19
|
+
ATTR_DEFN_METHODS = [:attr, :attr_reader, :attr_writer, :attr_accessor]
|
20
|
+
VISIBILITY_MODIFIERS = [:private, :public, :protected]
|
21
|
+
|
22
|
+
def initialize(*args)
|
23
|
+
@visiblity_tracker = {}
|
24
|
+
@visiblity_mode = :public
|
25
|
+
@result = Set.new
|
26
|
+
super
|
27
|
+
end
|
28
|
+
|
20
29
|
def self.contexts # :nodoc:
|
21
30
|
[:class, :module]
|
22
31
|
end
|
@@ -43,14 +52,33 @@ module Reek
|
|
43
52
|
private
|
44
53
|
|
45
54
|
def attributes_in(module_ctx)
|
46
|
-
result = Set.new
|
47
|
-
attr_defn_methods = [:attr, :attr_reader, :attr_writer, :attr_accessor]
|
48
55
|
module_ctx.local_nodes(:send) do |call_node|
|
49
|
-
if
|
50
|
-
call_node
|
56
|
+
if visibility_modifier?(call_node)
|
57
|
+
track_visibility(call_node)
|
58
|
+
elsif ATTR_DEFN_METHODS.include?(call_node.method_name)
|
59
|
+
call_node.arg_names.each do |arg|
|
60
|
+
@visiblity_tracker[arg] = @visiblity_mode
|
61
|
+
@result << [arg, call_node.line]
|
62
|
+
end
|
51
63
|
end
|
52
64
|
end
|
53
|
-
result
|
65
|
+
@result.select { |args| recorded_public_methods.include?(args[0]) }
|
66
|
+
end
|
67
|
+
|
68
|
+
def visibility_modifier?(call_node)
|
69
|
+
VISIBILITY_MODIFIERS.include?(call_node.method_name)
|
70
|
+
end
|
71
|
+
|
72
|
+
def track_visibility(call_node)
|
73
|
+
if call_node.arg_names.any?
|
74
|
+
call_node.arg_names.each { |arg| @visiblity_tracker[arg] = call_node.method_name }
|
75
|
+
else
|
76
|
+
@visiblity_mode = call_node.method_name
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def recorded_public_methods
|
81
|
+
@visiblity_tracker.select { |_, visbility| visbility == :public }
|
54
82
|
end
|
55
83
|
end
|
56
84
|
end
|
@@ -1,5 +1,4 @@
|
|
1
1
|
require_relative 'smell_detector'
|
2
|
-
require_relative '../smell_warning'
|
3
2
|
|
4
3
|
module Reek
|
5
4
|
module Smells
|
@@ -11,6 +10,7 @@ module Reek
|
|
11
10
|
# Currently Reek can only detect a Boolean parameter when it has a
|
12
11
|
# default initializer.
|
13
12
|
#
|
13
|
+
# See docs/Boolean-Parameter for details.
|
14
14
|
class BooleanParameter < SmellDetector
|
15
15
|
def self.smell_category
|
16
16
|
'ControlCouple'
|
@@ -1,6 +1,5 @@
|
|
1
1
|
require 'set'
|
2
2
|
require_relative 'smell_detector'
|
3
|
-
require_relative '../smell_warning'
|
4
3
|
|
5
4
|
module Reek
|
6
5
|
module Smells
|
@@ -12,6 +11,7 @@ module Reek
|
|
12
11
|
# In particular, class variables can make it hard to set up tests (because
|
13
12
|
# the context of the test includes all global state).
|
14
13
|
#
|
14
|
+
# See docs/Class-Variable for details.
|
15
15
|
class ClassVariable < SmellDetector
|
16
16
|
def self.contexts # :nodoc:
|
17
17
|
[:class, :module]
|
@@ -1,5 +1,4 @@
|
|
1
1
|
require_relative 'smell_detector'
|
2
|
-
require_relative '../smell_warning'
|
3
2
|
|
4
3
|
module Reek
|
5
4
|
module Smells
|
@@ -40,6 +39,7 @@ module Reek
|
|
40
39
|
# that remains next to where the caller invokes it in
|
41
40
|
# the source code.
|
42
41
|
#
|
42
|
+
# See docs/Control-Parameter for details.
|
43
43
|
class ControlParameter < SmellDetector
|
44
44
|
def self.smell_category
|
45
45
|
'ControlCouple'
|
@@ -1,5 +1,4 @@
|
|
1
1
|
require_relative 'smell_detector'
|
2
|
-
require_relative '../smell_warning'
|
3
2
|
|
4
3
|
module Reek
|
5
4
|
module Smells
|
@@ -15,6 +14,7 @@ module Reek
|
|
15
14
|
# Currently Reek looks for a group of two or more parameters with
|
16
15
|
# the same names that are expected by three or more methods of a class.
|
17
16
|
#
|
17
|
+
# See docs/Data-Clump for details.
|
18
18
|
class DataClump < SmellDetector
|
19
19
|
#
|
20
20
|
# The name of the config field that sets the maximum allowed
|
@@ -1,5 +1,4 @@
|
|
1
1
|
require_relative 'smell_detector'
|
2
|
-
require_relative '../smell_warning'
|
3
2
|
|
4
3
|
module Reek
|
5
4
|
module Smells
|
@@ -16,6 +15,7 @@ module Reek
|
|
16
15
|
# @other.thing + @other.thing
|
17
16
|
# end
|
18
17
|
#
|
18
|
+
# See docs/Duplicate-Method-Call for details.
|
19
19
|
class DuplicateMethodCall < SmellDetector
|
20
20
|
# The name of the config field that sets the maximum number of
|
21
21
|
# identical calls to be permitted within any single method.
|
@@ -70,7 +70,7 @@ module Reek
|
|
70
70
|
end
|
71
71
|
|
72
72
|
def call
|
73
|
-
@call ||= @call_node.
|
73
|
+
@call ||= @call_node.format_to_ruby
|
74
74
|
end
|
75
75
|
|
76
76
|
def occurs
|
@@ -106,8 +106,8 @@ module Reek
|
|
106
106
|
|
107
107
|
def collect_calls(result)
|
108
108
|
context.each_node(:send, [:mlhs]) do |call_node|
|
109
|
-
next if call_node
|
110
|
-
next if
|
109
|
+
next if initializer_call? call_node
|
110
|
+
next if simple_method_call? call_node
|
111
111
|
result[call_node].record(call_node)
|
112
112
|
end
|
113
113
|
context.local_nodes(:block) do |call_node|
|
@@ -119,6 +119,14 @@ module Reek
|
|
119
119
|
found_call.occurs > @max_allowed_calls && !allow_calls?(found_call.call)
|
120
120
|
end
|
121
121
|
|
122
|
+
def simple_method_call?(call_node)
|
123
|
+
!call_node.receiver && call_node.args.empty?
|
124
|
+
end
|
125
|
+
|
126
|
+
def initializer_call?(call_node)
|
127
|
+
call_node.method_name == :new
|
128
|
+
end
|
129
|
+
|
122
130
|
def allow_calls?(method)
|
123
131
|
@allow_calls.any? { |allow| /#{allow}/ =~ method }
|
124
132
|
end
|
@@ -1,5 +1,4 @@
|
|
1
1
|
require_relative 'smell_detector'
|
2
|
-
require_relative '../smell_warning'
|
3
2
|
|
4
3
|
module Reek
|
5
4
|
module Smells
|
@@ -33,6 +32,7 @@ module Reek
|
|
33
32
|
# If the method doesn't reference self at all, +UtilityFunction+ is
|
34
33
|
# reported instead.
|
35
34
|
#
|
35
|
+
# See docs/Feature-Envy for details.
|
36
36
|
class FeatureEnvy < SmellDetector
|
37
37
|
def self.smell_category
|
38
38
|
'LowCohesion'
|
@@ -1,6 +1,5 @@
|
|
1
1
|
require_relative 'smell_detector'
|
2
|
-
require_relative '../
|
3
|
-
require_relative '../source/code_comment'
|
2
|
+
require_relative '../core/code_comment'
|
4
3
|
|
5
4
|
module Reek
|
6
5
|
module Smells
|
@@ -8,6 +7,7 @@ module Reek
|
|
8
7
|
# It is considered good practice to annotate every class and module
|
9
8
|
# with a brief comment outlining its responsibilities.
|
10
9
|
#
|
10
|
+
# See docs/Irresponsible-Module for details.
|
11
11
|
class IrresponsibleModule < SmellDetector
|
12
12
|
def self.contexts # :nodoc:
|
13
13
|
[:class]
|
@@ -23,7 +23,7 @@ module Reek
|
|
23
23
|
# @return [Array<SmellWarning>]
|
24
24
|
#
|
25
25
|
def examine_context(ctx)
|
26
|
-
comment =
|
26
|
+
comment = Core::CodeComment.new(ctx.exp.comments)
|
27
27
|
return [] if self.class.descriptive[ctx.full_name] ||= comment.descriptive?
|
28
28
|
[SmellWarning.new(self,
|
29
29
|
context: ctx.full_name,
|
@@ -1,5 +1,4 @@
|
|
1
1
|
require_relative 'smell_detector'
|
2
|
-
require_relative '../smell_warning'
|
3
2
|
require_relative '../core/smell_configuration'
|
4
3
|
|
5
4
|
module Reek
|
@@ -12,6 +11,7 @@ module Reek
|
|
12
11
|
# Currently +LongParameterList+ reports any method or block with too
|
13
12
|
# many parameters.
|
14
13
|
#
|
14
|
+
# See docs/Long-Parameter-List for details.
|
15
15
|
class LongParameterList < SmellDetector
|
16
16
|
# The name of the config field that sets the maximum number of
|
17
17
|
# parameters permitted in any method or block.
|
@@ -1,5 +1,4 @@
|
|
1
1
|
require_relative 'smell_detector'
|
2
|
-
require_relative '../smell_warning'
|
3
2
|
|
4
3
|
module Reek
|
5
4
|
module Smells
|
@@ -7,6 +6,7 @@ module Reek
|
|
7
6
|
# A variant on LongParameterList that checks the number of items
|
8
7
|
# passed to a block by a +yield+ call.
|
9
8
|
#
|
9
|
+
# See docs/Long-Yield-List for details.
|
10
10
|
class LongYieldList < SmellDetector
|
11
11
|
# The name of the config field that sets the maximum number of
|
12
12
|
# parameters permitted in any method or block.
|
@@ -1,5 +1,4 @@
|
|
1
1
|
require_relative 'smell_detector'
|
2
|
-
require_relative '../smell_warning'
|
3
2
|
|
4
3
|
module Reek
|
5
4
|
module Smells
|
@@ -8,6 +7,7 @@ module Reek
|
|
8
7
|
# hard to tell initialization order and parameters so having 'initialize'
|
9
8
|
# in a module is usually a bad idea
|
10
9
|
#
|
10
|
+
# See docs/Module-Initialize for details.
|
11
11
|
class ModuleInitialize < SmellDetector
|
12
12
|
def self.contexts # :nodoc:
|
13
13
|
[:module]
|
@@ -1,5 +1,4 @@
|
|
1
1
|
require_relative 'smell_detector'
|
2
|
-
require_relative '../smell_warning'
|
3
2
|
|
4
3
|
module Reek
|
5
4
|
module Smells
|
@@ -8,6 +7,7 @@ module Reek
|
|
8
7
|
#
|
9
8
|
# +NestedIterators+ reports failing methods only once.
|
10
9
|
#
|
10
|
+
# See docs/Nested-Iterators for details.
|
11
11
|
class NestedIterators < SmellDetector
|
12
12
|
# The name of the config field that sets the maximum depth
|
13
13
|
# of nested iterators to be permitted within any single method.
|
@@ -1,10 +1,11 @@
|
|
1
1
|
require_relative 'smell_detector'
|
2
|
-
require_relative '../smell_warning'
|
3
2
|
|
4
3
|
module Reek
|
5
4
|
module Smells
|
6
5
|
# Checking for nil is a special kind of type check, and therefore a case of
|
7
6
|
# SimulatedPolymorphism.
|
7
|
+
#
|
8
|
+
# See docs/Nil-Check for details.
|
8
9
|
class NilCheck < SmellDetector
|
9
10
|
def self.smell_category
|
10
11
|
'SimulatedPolymorphism'
|
@@ -19,7 +20,7 @@ module Reek
|
|
19
20
|
SmellWarning.new self,
|
20
21
|
context: ctx.full_name,
|
21
22
|
lines: [node.line],
|
22
|
-
message: 'performs a nil-check
|
23
|
+
message: 'performs a nil-check'
|
23
24
|
end
|
24
25
|
end
|
25
26
|
|
@@ -1,5 +1,4 @@
|
|
1
1
|
require_relative 'smell_detector'
|
2
|
-
require_relative '../smell_warning'
|
3
2
|
|
4
3
|
module Reek
|
5
4
|
module Smells
|
@@ -22,6 +21,7 @@ module Reek
|
|
22
21
|
#
|
23
22
|
# Such a method is called PrimaDonnaMethod and is reported as a smell.
|
24
23
|
#
|
24
|
+
# See docs/Prima-Donna-Method for details.
|
25
25
|
class PrimaDonnaMethod < SmellDetector
|
26
26
|
def self.contexts # :nodoc:
|
27
27
|
[:class]
|
@@ -29,18 +29,25 @@ module Reek
|
|
29
29
|
|
30
30
|
def examine_context(ctx)
|
31
31
|
ctx.node_instance_methods.map do |method_sexp|
|
32
|
-
|
32
|
+
check_for_smells(method_sexp, ctx)
|
33
|
+
end.compact
|
34
|
+
end
|
33
35
|
|
34
|
-
|
35
|
-
sexp_item.name.to_s == method_sexp.name_without_bang
|
36
|
-
end
|
37
|
-
next if version_without_bang
|
36
|
+
private
|
38
37
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
38
|
+
def check_for_smells(method_sexp, ctx)
|
39
|
+
return unless method_sexp.ends_with_bang?
|
40
|
+
|
41
|
+
version_without_bang = ctx.node_instance_methods.find do |sexp_item|
|
42
|
+
sexp_item.name.to_s == method_sexp.name_without_bang
|
43
|
+
end
|
44
|
+
|
45
|
+
return if version_without_bang
|
46
|
+
|
47
|
+
SmellWarning.new self,
|
48
|
+
context: ctx.full_name,
|
49
|
+
lines: [ctx.exp.line],
|
50
|
+
message: "has prima donna method `#{method_sexp.name}`"
|
44
51
|
end
|
45
52
|
end
|
46
53
|
end
|
@@ -1,6 +1,5 @@
|
|
1
1
|
require_relative 'smell_detector'
|
2
|
-
require_relative '../
|
3
|
-
require_relative '../source/ast_node'
|
2
|
+
require_relative '../core/ast_node'
|
4
3
|
|
5
4
|
module Reek
|
6
5
|
module Smells
|
@@ -23,6 +22,7 @@ module Reek
|
|
23
22
|
# +RepeatedConditional+ checks for multiple conditionals
|
24
23
|
# testing the same value throughout a single class.
|
25
24
|
#
|
25
|
+
# See docs/Repeated-Conditional for details.
|
26
26
|
class RepeatedConditional < SmellDetector
|
27
27
|
# The name of the config field that sets the maximum number of
|
28
28
|
# identical conditionals permitted within any single class.
|
@@ -54,7 +54,7 @@ module Reek
|
|
54
54
|
lines.length > @max_identical_ifs
|
55
55
|
end.map do |key, lines|
|
56
56
|
occurs = lines.length
|
57
|
-
expression = key.
|
57
|
+
expression = key.format_to_ruby
|
58
58
|
SmellWarning.new self,
|
59
59
|
context: ctx.full_name,
|
60
60
|
lines: lines,
|
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'set'
|
2
|
-
require_relative '../smell_warning'
|
3
2
|
require_relative '../core/smell_configuration'
|
4
3
|
|
5
4
|
module Reek
|
@@ -7,6 +6,11 @@ module Reek
|
|
7
6
|
#
|
8
7
|
# Shared responsibilities of all smell detectors.
|
9
8
|
#
|
9
|
+
# See
|
10
|
+
# - docs/Basic-Smell-Options
|
11
|
+
# - docs/Code-Smells
|
12
|
+
# - docs/Configuration-Files
|
13
|
+
# for details.
|
10
14
|
class SmellDetector
|
11
15
|
attr_reader :source
|
12
16
|
|
@@ -0,0 +1,99 @@
|
|
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 smell_classes
|
23
|
+
[smell_detector.smell_category, smell_detector.smell_type]
|
24
|
+
end
|
25
|
+
|
26
|
+
def hash
|
27
|
+
sort_key.hash
|
28
|
+
end
|
29
|
+
|
30
|
+
def <=>(other)
|
31
|
+
sort_key <=> other.sort_key
|
32
|
+
end
|
33
|
+
|
34
|
+
def eql?(other)
|
35
|
+
(self <=> other) == 0
|
36
|
+
end
|
37
|
+
|
38
|
+
def matches?(klass, other_parameters = {})
|
39
|
+
smell_classes.include?(klass.to_s) && common_parameters_equal?(other_parameters)
|
40
|
+
end
|
41
|
+
|
42
|
+
def report_on(listener)
|
43
|
+
listener.found_smell(self)
|
44
|
+
end
|
45
|
+
|
46
|
+
def yaml_hash(warning_formatter = nil)
|
47
|
+
stringified_params = Hash[parameters.map { |key, val| [key.to_s, val] }]
|
48
|
+
core_yaml_hash.
|
49
|
+
merge(stringified_params).
|
50
|
+
merge(wiki_link_hash(warning_formatter))
|
51
|
+
end
|
52
|
+
|
53
|
+
protected
|
54
|
+
|
55
|
+
def sort_key
|
56
|
+
[context, message, smell_category]
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def common_parameters_equal?(other_parameters)
|
62
|
+
other_parameters.keys.each do |key|
|
63
|
+
unless parameters.key?(key)
|
64
|
+
raise ArgumentError, "The parameter #{key} you want to check for doesn't exist"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Why not check for strict parameter equality instead of just the common ones?
|
69
|
+
#
|
70
|
+
# In `self`, `parameters` might look like this: {:name=>"@other.thing", :count=>2}
|
71
|
+
# Coming from specs, 'other_parameters' might look like this, e.g.:
|
72
|
+
# {:name=>"@other.thing"}
|
73
|
+
# So in this spec we are just specifying the "name" parameter but not the "count".
|
74
|
+
# In order to allow for this kind of leniency we just test for common parameter equality,
|
75
|
+
# not for a strict one.
|
76
|
+
parameters.values_at(*other_parameters.keys) == other_parameters.values
|
77
|
+
end
|
78
|
+
|
79
|
+
def core_yaml_hash
|
80
|
+
{
|
81
|
+
'context' => context,
|
82
|
+
'lines' => lines,
|
83
|
+
'message' => message,
|
84
|
+
'smell_category' => smell_detector.smell_category,
|
85
|
+
'smell_type' => smell_detector.smell_type,
|
86
|
+
'source' => smell_detector.source
|
87
|
+
}
|
88
|
+
end
|
89
|
+
|
90
|
+
def wiki_link_hash(warning_formatter)
|
91
|
+
if warning_formatter.respond_to?(:explanatory_link)
|
92
|
+
{ 'wiki_link' => warning_formatter.explanatory_link(smell_detector) }
|
93
|
+
else
|
94
|
+
{}
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|