reek 2.1.0 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|